diff options
Diffstat (limited to 'lib')
44 files changed, 2474 insertions, 1059 deletions
diff --git a/lib/atomlist.h b/lib/atomlist.h index e4098ccb54..621db6824f 100644 --- a/lib/atomlist.h +++ b/lib/atomlist.h @@ -135,8 +135,10 @@ macro_inline void prefix ## _add_tail(struct prefix##_head *h, type *item) \ macro_inline void prefix ## _del_hint(struct prefix##_head *h, type *item, \ _Atomic atomptr_t *hint) \ { atomlist_del_hint(&h->ah, &item->field.ai, hint); } \ -macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ -{ atomlist_del_hint(&h->ah, &item->field.ai, NULL); } \ +macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ +{ atomlist_del_hint(&h->ah, &item->field.ai, NULL); \ + /* TODO: Return NULL if not found */ \ + return item; } \ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ { char *p = (char *)atomlist_pop(&h->ah); \ return p ? (type *)(p - offsetof(type, field)) : NULL; } \ @@ -273,9 +275,11 @@ macro_inline void prefix ## _del_hint(struct prefix##_head *h, type *item, \ { \ atomsort_del_hint(&h->ah, &item->field.ai, hint); \ } \ -macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ { \ atomsort_del_hint(&h->ah, &item->field.ai, NULL); \ + /* TODO: Return NULL if not found */ \ + return item; \ } \ macro_inline size_t prefix ## _count(struct prefix##_head *h) \ { \ diff --git a/lib/command.c b/lib/command.c index c8fbf22721..9dabc2af7e 100644 --- a/lib/command.c +++ b/lib/command.c @@ -89,7 +89,6 @@ const char *node_names[] = { "aaa", // AAA_NODE, "keychain", // KEYCHAIN_NODE, "keychain key", // KEYCHAIN_KEY_NODE, - "logical-router", // LOGICALROUTER_NODE, "static ip", // IP_NODE, "vrf", // VRF_NODE, "interface", // INTERFACE_NODE, @@ -290,7 +289,7 @@ vector cmd_make_strvec(const char *string) const char *copy = string; /* skip leading whitespace */ - while (isspace((int)*copy) && *copy != '\0') + while (isspace((unsigned char)*copy) && *copy != '\0') copy++; /* if the entire string was whitespace or a comment, return */ @@ -1456,7 +1455,6 @@ void cmd_exit(struct vty *vty) break; case INTERFACE_NODE: case PW_NODE: - case LOGICALROUTER_NODE: case VRF_NODE: case NH_GROUP_NODE: case ZEBRA_NODE: @@ -1936,7 +1934,7 @@ DEFUN(config_domainname, { struct cmd_token *word = argv[1]; - if (!isalpha((int)word->arg[0])) { + if (!isalpha((unsigned char)word->arg[0])) { vty_out(vty, "Please specify string starting with alphabet\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -1970,7 +1968,7 @@ DEFUN (config_hostname, { struct cmd_token *word = argv[1]; - if (!isalnum((int)word->arg[0])) { + if (!isalnum((unsigned char)word->arg[0])) { vty_out(vty, "Please specify string starting with alphabet or number\n"); return CMD_WARNING_CONFIG_FAILED; @@ -2018,7 +2016,7 @@ DEFUN (config_password, return CMD_SUCCESS; } - if (!isalnum((int)argv[idx_8]->arg[0])) { + if (!isalnum((unsigned char)argv[idx_8]->arg[0])) { vty_out(vty, "Please specify string starting with alphanumeric\n"); return CMD_WARNING_CONFIG_FAILED; @@ -2098,7 +2096,7 @@ DEFUN (config_enable_password, } } - if (!isalnum((int)argv[idx_8]->arg[0])) { + if (!isalnum((unsigned char)argv[idx_8]->arg[0])) { vty_out(vty, "Please specify string starting with alphanumeric\n"); return CMD_WARNING_CONFIG_FAILED; diff --git a/lib/command.h b/lib/command.h index 08d6128af4..8dc35a0fdc 100644 --- a/lib/command.h +++ b/lib/command.h @@ -98,7 +98,6 @@ enum node_type { AAA_NODE, /* AAA node. */ KEYCHAIN_NODE, /* Key-chain node. */ KEYCHAIN_KEY_NODE, /* Key-chain key node. */ - LOGICALROUTER_NODE, /* Logical-Router node. */ IP_NODE, /* Static ip route node. */ VRF_NODE, /* VRF mode node. */ INTERFACE_NODE, /* Interface mode node. */ diff --git a/lib/command_graph.c b/lib/command_graph.c index 4757fd951f..d30d9ab702 100644 --- a/lib/command_graph.c +++ b/lib/command_graph.c @@ -97,7 +97,7 @@ void cmd_token_varname_set(struct cmd_token *token, const char *varname) token->varname[i] = '_'; break; default: - token->varname[i] = tolower((int)varname[i]); + token->varname[i] = tolower((unsigned char)varname[i]); } token->varname[len] = '\0'; } diff --git a/lib/command_match.c b/lib/command_match.c index 9456e1585a..26d763849d 100644 --- a/lib/command_match.c +++ b/lib/command_match.c @@ -714,7 +714,7 @@ static enum match_type match_ipv4(const char *str) dots++; break; } - if (!isdigit((int)*str)) + if (!isdigit((unsigned char)*str)) return no_match; str++; @@ -765,7 +765,7 @@ static enum match_type match_ipv4_prefix(const char *str) break; } - if (!isdigit((int)*str)) + if (!isdigit((unsigned char)*str)) return no_match; str++; @@ -797,7 +797,7 @@ static enum match_type match_ipv4_prefix(const char *str) sp = str; while (*str != '\0') { - if (!isdigit((int)*str)) + if (!isdigit((unsigned char)*str)) return no_match; str++; diff --git a/lib/compiler.h b/lib/compiler.h index 7c7f4ce294..6700ca9e8b 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -165,6 +165,13 @@ extern "C" { _min_a < _min_b ? _min_a : _min_b; \ }) +#define numcmp(a, b) \ + ({ \ + typeof(a) _cmp_a = (a); \ + typeof(b) _cmp_b = (b); \ + (_cmp_a < _cmp_b) ? -1 : ((_cmp_a > _cmp_b) ? 1 : 0); \ + }) + #ifndef offsetof #ifdef __compiler_offsetof #define offsetof(TYPE, MEMBER) __compiler_offsetof(TYPE,MEMBER) diff --git a/lib/ferr.c b/lib/ferr.c index 65c0cf886d..8afc926c41 100644 --- a/lib/ferr.c +++ b/lib/ferr.c @@ -126,10 +126,8 @@ void log_ref_display(struct vty *vty, uint32_t code, bool json) if (code) { ref = log_ref_get(code); - if (!ref) { - vty_out(vty, "Code %"PRIu32" - Unknown\n", code); + if (!ref) return; - } listnode_add(errlist, ref); } @@ -197,8 +195,6 @@ void log_ref_init(void) "Error Reference Texts"); } pthread_mutex_unlock(&refs_mtx); - - install_element(VIEW_NODE, &show_error_code_cmd); } void log_ref_fini(void) @@ -212,6 +208,12 @@ void log_ref_fini(void) pthread_mutex_unlock(&refs_mtx); } +void log_ref_vty_init(void) +{ + install_element(VIEW_NODE, &show_error_code_cmd); +} + + const struct ferr *ferr_get_last(ferr_r errval) { struct ferr *last_error = pthread_getspecific(errkey); diff --git a/lib/ferr.h b/lib/ferr.h index 93d0ced538..a89b595e87 100644 --- a/lib/ferr.h +++ b/lib/ferr.h @@ -158,6 +158,7 @@ void log_ref_display(struct vty *vty, uint32_t code, bool json); */ void log_ref_init(void); void log_ref_fini(void); +void log_ref_vty_init(void); /* get error details. * diff --git a/lib/filter.c b/lib/filter.c index 276df4b4d7..fe62ca1c13 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -60,6 +60,9 @@ struct filter { /* Filter type information. */ enum filter_type type; + /* Sequence number */ + int64_t seq; + /* Cisco access-list */ int cisco; @@ -270,7 +273,7 @@ static struct access_list *access_list_insert(afi_t afi, const char *name) /* If name is made by all digit character. We treat it as number. */ for (number = 0, i = 0; i < strlen(name); i++) { - if (isdigit((int)name[i])) + if (isdigit((unsigned char)name[i])) number = (number * 10) + (name[i] - '0'); else break; @@ -406,23 +409,35 @@ void access_list_delete_hook(void (*func)(struct access_list *access)) access_master_mac.delete_hook = func; } -/* Add new filter to the end of specified access_list. */ -static void access_list_filter_add(struct access_list *access, - struct filter *filter) +/* Calculate new sequential number. */ +static int64_t filter_new_seq_get(struct access_list *access) { - filter->next = NULL; - filter->prev = access->tail; + int64_t maxseq; + int64_t newseq; + struct filter *filter; - if (access->tail) - access->tail->next = filter; - else - access->head = filter; - access->tail = filter; + maxseq = newseq = 0; - /* Run hook function. */ - if (access->master->add_hook) - (*access->master->add_hook)(access); - route_map_notify_dependencies(access->name, RMAP_EVENT_FILTER_ADDED); + for (filter = access->head; filter; filter = filter->next) { + if (maxseq < filter->seq) + maxseq = filter->seq; + } + + newseq = ((maxseq / 5) * 5) + 5; + + return (newseq > UINT_MAX) ? UINT_MAX : newseq; +} + +/* Return access list entry which has same seq number. */ +static struct filter *filter_seq_check(struct access_list *access, + int64_t seq) +{ + struct filter *filter; + + for (filter = access->head; filter; filter = filter->next) + if (filter->seq == seq) + return filter; + return NULL; } /* If access_list has no filter then return 1. */ @@ -465,6 +480,58 @@ static void access_list_filter_delete(struct access_list *access, access_list_delete(access); } +/* Add new filter to the end of specified access_list. */ +static void access_list_filter_add(struct access_list *access, + struct filter *filter) +{ + struct filter *replace; + struct filter *point; + + /* Automatic asignment of seq no. */ + if (filter->seq == -1) + filter->seq = filter_new_seq_get(access); + + if (access->tail && filter->seq > access->tail->seq) + point = NULL; + else { + /* Is there any same seq access list filter? */ + replace = filter_seq_check(access, filter->seq); + if (replace) + access_list_filter_delete(access, replace); + + /* Check insert point. */ + for (point = access->head; point; point = point->next) + if (point->seq >= filter->seq) + break; + } + + /* In case of this is the first element of the list. */ + filter->next = point; + + if (point) { + if (point->prev) + point->prev->next = filter; + else + access->head = filter; + + filter->prev = point->prev; + point->prev = filter; + } else { + if (access->tail) + access->tail->next = filter; + else + access->head = filter; + + filter->prev = access->tail; + access->tail = filter; + } + + /* Run hook function. */ + if (access->master->add_hook) + (*access->master->add_hook)(access); + route_map_notify_dependencies(access->name, RMAP_EVENT_FILTER_ADDED); +} + /* deny Specify packets to reject permit Specify packets to forward @@ -553,12 +620,13 @@ static int vty_access_list_remark_unset(struct vty *vty, afi_t afi, } static int filter_set_cisco(struct vty *vty, const char *name_str, - const char *type_str, const char *addr_str, - const char *addr_mask_str, const char *mask_str, - const char *mask_mask_str, int extended, int set) + const char *seq, const char *type_str, + const char *addr_str, const char *addr_mask_str, + const char *mask_str, const char *mask_mask_str, + int extended, int set) { int ret; - enum filter_type type; + enum filter_type type = FILTER_DENY; struct filter *mfilter; struct filter_cisco *filter; struct access_list *access; @@ -566,15 +634,21 @@ static int filter_set_cisco(struct vty *vty, const char *name_str, struct in_addr addr_mask; struct in_addr mask; struct in_addr mask_mask; + int64_t seqnum = -1; + + if (seq) + seqnum = (int64_t)atol(seq); /* Check of filter type. */ - if (strncmp(type_str, "p", 1) == 0) - type = FILTER_PERMIT; - else if (strncmp(type_str, "d", 1) == 0) - type = FILTER_DENY; - else { - vty_out(vty, "%% filter type must be permit or deny\n"); - return CMD_WARNING_CONFIG_FAILED; + if (type_str) { + if (strncmp(type_str, "p", 1) == 0) + type = FILTER_PERMIT; + else if (strncmp(type_str, "d", 1) == 0) + type = FILTER_DENY; + else { + vty_out(vty, "%% filter type must be permit or deny\n"); + return CMD_WARNING_CONFIG_FAILED; + } } ret = inet_aton(addr_str, &addr); @@ -606,6 +680,7 @@ static int filter_set_cisco(struct vty *vty, const char *name_str, mfilter = filter_new(); mfilter->type = type; mfilter->cisco = 1; + mfilter->seq = seqnum; filter = &mfilter->u.cfilter; filter->extended = extended; filter->addr.s_addr = addr.s_addr & ~addr_mask.s_addr; @@ -640,163 +715,311 @@ static int filter_set_cisco(struct vty *vty, const char *name_str, /* Standard access-list */ DEFUN (access_list_standard, access_list_standard_cmd, - "access-list <(1-99)|(1300-1999)> <deny|permit> A.B.C.D A.B.C.D", + "access-list <(1-99)|(1300-1999)> [seq (1-4294967295)] <deny|permit> A.B.C.D A.B.C.D", "Add an access list entry\n" "IP standard access list\n" "IP standard access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Address to match\n" "Wildcard bits\n") { int idx_acl = 1; - int idx_permit_deny = 2; - int idx_ipv4 = 3; - int idx_ipv4_2 = 4; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - argv[idx_ipv4_2]->arg, NULL, NULL, 0, 1); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + char *address = NULL; + char *wildcard = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "A.B.C.D", &idx); + if (idx) { + address = argv[idx]->arg; + wildcard = argv[idx + 1]->arg; + } + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, + address, wildcard, NULL, NULL, 0, 1); } DEFUN (access_list_standard_nomask, access_list_standard_nomask_cmd, - "access-list <(1-99)|(1300-1999)> <deny|permit> A.B.C.D", + "access-list <(1-99)|(1300-1999)> [seq (1-4294967295)] <deny|permit> A.B.C.D", "Add an access list entry\n" "IP standard access list\n" "IP standard access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Address to match\n") { int idx_acl = 1; - int idx_permit_deny = 2; - int idx_ipv4 = 3; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - "0.0.0.0", NULL, NULL, 0, 1); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + char *address = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "A.B.C.D", &idx); + if (idx) + address = argv[idx]->arg; + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, + address, "0.0.0.0", NULL, NULL, 0, 1); } DEFUN (access_list_standard_host, access_list_standard_host_cmd, - "access-list <(1-99)|(1300-1999)> <deny|permit> host A.B.C.D", + "access-list <(1-99)|(1300-1999)> [seq (1-4294967295)] <deny|permit> host A.B.C.D", "Add an access list entry\n" "IP standard access list\n" "IP standard access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "A single host address\n" "Address to match\n") { int idx_acl = 1; - int idx_permit_deny = 2; - int idx_ipv4 = 4; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - "0.0.0.0", NULL, NULL, 0, 1); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + char *address = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "A.B.C.D", &idx); + if (idx) + address = argv[idx]->arg; + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, + address, "0.0.0.0", NULL, NULL, 0, 1); } DEFUN (access_list_standard_any, access_list_standard_any_cmd, - "access-list <(1-99)|(1300-1999)> <deny|permit> any", + "access-list <(1-99)|(1300-1999)> [seq (1-4294967295)] <deny|permit> any", "Add an access list entry\n" "IP standard access list\n" "IP standard access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Any source host\n") { int idx_acl = 1; - int idx_permit_deny = 2; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, "0.0.0.0", - "255.255.255.255", NULL, NULL, 0, 1); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, + "0.0.0.0", "255.255.255.255", NULL, NULL, 0, 1); } DEFUN (no_access_list_standard, no_access_list_standard_cmd, - "no access-list <(1-99)|(1300-1999)> <deny|permit> A.B.C.D A.B.C.D", + "no access-list <(1-99)|(1300-1999)> [seq (1-4294967295)] <deny|permit> A.B.C.D A.B.C.D", NO_STR "Add an access list entry\n" "IP standard access list\n" "IP standard access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Address to match\n" "Wildcard bits\n") { - int idx_acl = 2; - int idx_permit_deny = 3; - int idx_ipv4 = 4; - int idx_ipv4_2 = 5; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - argv[idx_ipv4_2]->arg, NULL, NULL, 0, 0); + int idx_acl = 1; + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + char *address = NULL; + char *wildcard = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "A.B.C.D", &idx); + if (idx) { + address = argv[idx]->arg; + wildcard = argv[idx + 1]->arg; + } + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, + address, wildcard, NULL, NULL, 0, 0); } DEFUN (no_access_list_standard_nomask, no_access_list_standard_nomask_cmd, - "no access-list <(1-99)|(1300-1999)> <deny|permit> A.B.C.D", + "no access-list <(1-99)|(1300-1999)> [seq (1-4294967295)] <deny|permit> A.B.C.D", NO_STR "Add an access list entry\n" "IP standard access list\n" "IP standard access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Address to match\n") { int idx_acl = 2; - int idx_permit_deny = 3; - int idx_ipv4 = 4; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - "0.0.0.0", NULL, NULL, 0, 0); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + char *address = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "A.B.C.D", &idx); + if (idx) + address = argv[idx]->arg; + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, + address, "0.0.0.0", NULL, NULL, 0, 0); } DEFUN (no_access_list_standard_host, no_access_list_standard_host_cmd, - "no access-list <(1-99)|(1300-1999)> <deny|permit> host A.B.C.D", + "no access-list <(1-99)|(1300-1999)> [seq (1-4294967295)] <deny|permit> host A.B.C.D", NO_STR "Add an access list entry\n" "IP standard access list\n" "IP standard access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "A single host address\n" "Address to match\n") { int idx_acl = 2; - int idx_permit_deny = 3; - int idx_ipv4 = 5; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - "0.0.0.0", NULL, NULL, 0, 0); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + char *address = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "A.B.C.D", &idx); + if (idx) + address = argv[idx]->arg; + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, + address, "0.0.0.0", NULL, NULL, 0, 0); } DEFUN (no_access_list_standard_any, no_access_list_standard_any_cmd, - "no access-list <(1-99)|(1300-1999)> <deny|permit> any", + "no access-list <(1-99)|(1300-1999)> [seq (1-4294967295)] <deny|permit> any", NO_STR "Add an access list entry\n" "IP standard access list\n" "IP standard access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Any source host\n") { int idx_acl = 2; - int idx_permit_deny = 3; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, "0.0.0.0", - "255.255.255.255", NULL, NULL, 0, 0); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, + "0.0.0.0", "255.255.255.255", NULL, NULL, 0, 0); } /* Extended access-list */ DEFUN (access_list_extended, access_list_extended_cmd, - "access-list <(100-199)|(2000-2699)> <deny|permit> ip A.B.C.D A.B.C.D A.B.C.D A.B.C.D", + "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip A.B.C.D A.B.C.D A.B.C.D A.B.C.D", "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Any Internet Protocol\n" @@ -806,23 +1029,45 @@ DEFUN (access_list_extended, "Destination Wildcard bits\n") { int idx_acl = 1; - int idx_permit_deny = 2; - int idx_ipv4 = 4; - int idx_ipv4_2 = 5; - int idx_ipv4_3 = 6; - int idx_ipv4_4 = 7; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - argv[idx_ipv4_2]->arg, argv[idx_ipv4_3]->arg, - argv[idx_ipv4_4]->arg, 1, 1); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + char *src = NULL; + char *dst = NULL; + char *src_wildcard = NULL; + char *dst_wildcard = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "A.B.C.D", &idx); + if (idx) { + src = argv[idx]->arg; + src_wildcard = argv[idx + 1]->arg; + dst = argv[idx + 2]->arg; + dst_wildcard = argv[idx + 3]->arg; + } + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src, + src_wildcard, dst, dst_wildcard, 1, 1); } DEFUN (access_list_extended_mask_any, access_list_extended_mask_any_cmd, - "access-list <(100-199)|(2000-2699)> <deny|permit> ip A.B.C.D A.B.C.D any", + "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip A.B.C.D A.B.C.D any", "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Any Internet Protocol\n" @@ -831,21 +1076,42 @@ DEFUN (access_list_extended_mask_any, "Any destination host\n") { int idx_acl = 1; - int idx_permit_deny = 2; - int idx_ipv4 = 4; - int idx_ipv4_2 = 5; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - argv[idx_ipv4_2]->arg, "0.0.0.0", - "255.255.255.255", 1, 1); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + char *src = NULL; + char *src_wildcard = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "A.B.C.D", &idx); + if (idx) { + src = argv[idx]->arg; + src_wildcard = argv[idx + 1]->arg; + } + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src, + src_wildcard, "0.0.0.0", "255.255.255.255", 1, + 1); } DEFUN (access_list_extended_any_mask, access_list_extended_any_mask_cmd, - "access-list <(100-199)|(2000-2699)> <deny|permit> ip any A.B.C.D A.B.C.D", + "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip any A.B.C.D A.B.C.D", "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Any Internet Protocol\n" @@ -854,21 +1120,42 @@ DEFUN (access_list_extended_any_mask, "Destination Wildcard bits\n") { int idx_acl = 1; - int idx_permit_deny = 2; - int idx_ipv4 = 5; - int idx_ipv4_2 = 6; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, "0.0.0.0", - "255.255.255.255", argv[idx_ipv4]->arg, - argv[idx_ipv4_2]->arg, 1, 1); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + char *dst = NULL; + char *dst_wildcard = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "A.B.C.D", &idx); + if (idx) { + dst = argv[idx]->arg; + dst_wildcard = argv[idx + 1]->arg; + } + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, + "0.0.0.0", "255.255.255.255", dst, dst_wildcard, + 1, 1); } DEFUN (access_list_extended_any_any, access_list_extended_any_any_cmd, - "access-list <(100-199)|(2000-2699)> <deny|permit> ip any any", + "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip any any", "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Any Internet Protocol\n" @@ -876,18 +1163,33 @@ DEFUN (access_list_extended_any_any, "Any destination host\n") { int idx_acl = 1; - int idx_permit_deny = 2; - return filter_set_cisco( - vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, "0.0.0.0", - "255.255.255.255", "0.0.0.0", "255.255.255.255", 1, 1); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, + "0.0.0.0", "255.255.255.255", "0.0.0.0", + "255.255.255.255", 1, 1); } DEFUN (access_list_extended_mask_host, access_list_extended_mask_host_cmd, - "access-list <(100-199)|(2000-2699)> <deny|permit> ip A.B.C.D A.B.C.D host A.B.C.D", + "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip A.B.C.D A.B.C.D host A.B.C.D", "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Any Internet Protocol\n" @@ -897,22 +1199,43 @@ DEFUN (access_list_extended_mask_host, "Destination address\n") { int idx_acl = 1; - int idx_permit_deny = 2; - int idx_ipv4 = 4; - int idx_ipv4_2 = 5; - int idx_ipv4_3 = 7; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - argv[idx_ipv4_2]->arg, argv[idx_ipv4_3]->arg, - "0.0.0.0", 1, 1); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + char *src = NULL; + char *dst = NULL; + char *src_wildcard = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "A.B.C.D", &idx); + if (idx) { + src = argv[idx]->arg; + src_wildcard = argv[idx + 1]->arg; + dst = argv[idx + 3]->arg; + } + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src, + src_wildcard, dst, "0.0.0.0", 1, 1); } DEFUN (access_list_extended_host_mask, access_list_extended_host_mask_cmd, - "access-list <(100-199)|(2000-2699)> <deny|permit> ip host A.B.C.D A.B.C.D A.B.C.D", + "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip host A.B.C.D A.B.C.D A.B.C.D", "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Any Internet Protocol\n" @@ -922,22 +1245,43 @@ DEFUN (access_list_extended_host_mask, "Destination Wildcard bits\n") { int idx_acl = 1; - int idx_permit_deny = 2; - int idx_ipv4 = 5; - int idx_ipv4_2 = 6; - int idx_ipv4_3 = 7; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - "0.0.0.0", argv[idx_ipv4_2]->arg, - argv[idx_ipv4_3]->arg, 1, 1); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + char *src = NULL; + char *dst = NULL; + char *dst_wildcard = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "A.B.C.D", &idx); + if (idx) { + src = argv[idx]->arg; + dst = argv[idx + 1]->arg; + dst_wildcard = argv[idx + 2]->arg; + } + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src, + "0.0.0.0", dst, dst_wildcard, 1, 1); } DEFUN (access_list_extended_host_host, access_list_extended_host_host_cmd, - "access-list <(100-199)|(2000-2699)> <deny|permit> ip host A.B.C.D host A.B.C.D", + "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip host A.B.C.D host A.B.C.D", "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Any Internet Protocol\n" @@ -947,21 +1291,41 @@ DEFUN (access_list_extended_host_host, "Destination address\n") { int idx_acl = 1; - int idx_permit_deny = 2; - int idx_ipv4 = 5; - int idx_ipv4_2 = 7; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - "0.0.0.0", argv[idx_ipv4_2]->arg, "0.0.0.0", 1, - 1); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + char *src = NULL; + char *dst = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "A.B.C.D", &idx); + if (idx) { + src = argv[idx]->arg; + dst = argv[idx + 2]->arg; + } + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src, + "0.0.0.0", dst, "0.0.0.0", 1, 1); } DEFUN (access_list_extended_any_host, access_list_extended_any_host_cmd, - "access-list <(100-199)|(2000-2699)> <deny|permit> ip any host A.B.C.D", + "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip any host A.B.C.D", "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Any Internet Protocol\n" @@ -970,19 +1334,39 @@ DEFUN (access_list_extended_any_host, "Destination address\n") { int idx_acl = 1; - int idx_permit_deny = 2; - int idx_ipv4 = 6; - return filter_set_cisco( - vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, "0.0.0.0", - "255.255.255.255", argv[idx_ipv4]->arg, "0.0.0.0", 1, 1); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + char *dst = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "A.B.C.D", &idx); + if (idx) + dst = argv[idx]->arg; + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, + "0.0.0.0", "255.255.255.255", dst, "0.0.0.0", 1, + 1); } DEFUN (access_list_extended_host_any, access_list_extended_host_any_cmd, - "access-list <(100-199)|(2000-2699)> <deny|permit> ip host A.B.C.D any", + "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip host A.B.C.D any", "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Any Internet Protocol\n" @@ -991,20 +1375,39 @@ DEFUN (access_list_extended_host_any, "Any destination host\n") { int idx_acl = 1; - int idx_permit_deny = 2; - int idx_ipv4 = 5; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + char *src = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "A.B.C.D", &idx); + if (idx) + src = argv[idx]->arg; + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src, "0.0.0.0", "0.0.0.0", "255.255.255.255", 1, 1); } DEFUN (no_access_list_extended, no_access_list_extended_cmd, - "no access-list <(100-199)|(2000-2699)> <deny|permit> ip A.B.C.D A.B.C.D A.B.C.D A.B.C.D", + "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip A.B.C.D A.B.C.D A.B.C.D A.B.C.D", NO_STR "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Any Internet Protocol\n" @@ -1014,24 +1417,46 @@ DEFUN (no_access_list_extended, "Destination Wildcard bits\n") { int idx_acl = 2; - int idx_permit_deny = 3; - int idx_ipv4 = 5; - int idx_ipv4_2 = 6; - int idx_ipv4_3 = 7; - int idx_ipv4_4 = 8; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - argv[idx_ipv4_2]->arg, argv[idx_ipv4_3]->arg, - argv[idx_ipv4_4]->arg, 1, 0); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + char *src = NULL; + char *dst = NULL; + char *src_wildcard = NULL; + char *dst_wildcard = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "A.B.C.D", &idx); + if (idx) { + src = argv[idx]->arg; + src_wildcard = argv[idx + 1]->arg; + dst = argv[idx + 2]->arg; + dst_wildcard = argv[idx + 3]->arg; + } + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src, + src_wildcard, dst, dst_wildcard, 1, 0); } DEFUN (no_access_list_extended_mask_any, no_access_list_extended_mask_any_cmd, - "no access-list <(100-199)|(2000-2699)> <deny|permit> ip A.B.C.D A.B.C.D any", + "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip A.B.C.D A.B.C.D any", NO_STR "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Any Internet Protocol\n" @@ -1040,22 +1465,43 @@ DEFUN (no_access_list_extended_mask_any, "Any destination host\n") { int idx_acl = 2; - int idx_permit_deny = 3; - int idx_ipv4 = 5; - int idx_ipv4_2 = 6; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - argv[idx_ipv4_2]->arg, "0.0.0.0", - "255.255.255.255", 1, 0); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + char *src = NULL; + char *src_wildcard = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "A.B.C.D", &idx); + if (idx) { + src = argv[idx]->arg; + src_wildcard = argv[idx + 1]->arg; + } + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src, + src_wildcard, "0.0.0.0", "255.255.255.255", 1, + 0); } DEFUN (no_access_list_extended_any_mask, no_access_list_extended_any_mask_cmd, - "no access-list <(100-199)|(2000-2699)> <deny|permit> ip any A.B.C.D A.B.C.D", + "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip any A.B.C.D A.B.C.D", NO_STR "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Any Internet Protocol\n" @@ -1064,22 +1510,43 @@ DEFUN (no_access_list_extended_any_mask, "Destination Wildcard bits\n") { int idx_acl = 2; - int idx_permit_deny = 3; - int idx_ipv4 = 6; - int idx_ipv4_2 = 7; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, "0.0.0.0", - "255.255.255.255", argv[idx_ipv4]->arg, - argv[idx_ipv4_2]->arg, 1, 0); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + char *dst = NULL; + char *dst_wildcard = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "A.B.C.D", &idx); + if (idx) { + dst = argv[idx]->arg; + dst_wildcard = argv[idx + 1]->arg; + } + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, + "0.0.0.0", "255.255.255.255", dst, dst_wildcard, + 1, 0); } DEFUN (no_access_list_extended_any_any, no_access_list_extended_any_any_cmd, - "no access-list <(100-199)|(2000-2699)> <deny|permit> ip any any", + "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip any any", NO_STR "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Any Internet Protocol\n" @@ -1087,19 +1554,34 @@ DEFUN (no_access_list_extended_any_any, "Any destination host\n") { int idx_acl = 2; - int idx_permit_deny = 3; - return filter_set_cisco( - vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, "0.0.0.0", - "255.255.255.255", "0.0.0.0", "255.255.255.255", 1, 0); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, + "0.0.0.0", "255.255.255.255", "0.0.0.0", + "255.255.255.255", 1, 0); } DEFUN (no_access_list_extended_mask_host, no_access_list_extended_mask_host_cmd, - "no access-list <(100-199)|(2000-2699)> <deny|permit> ip A.B.C.D A.B.C.D host A.B.C.D", + "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip A.B.C.D A.B.C.D host A.B.C.D", NO_STR "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Any Internet Protocol\n" @@ -1109,23 +1591,44 @@ DEFUN (no_access_list_extended_mask_host, "Destination address\n") { int idx_acl = 2; - int idx_permit_deny = 3; - int idx_ipv4 = 5; - int idx_ipv4_2 = 6; - int idx_ipv4_3 = 8; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - argv[idx_ipv4_2]->arg, argv[idx_ipv4_3]->arg, - "0.0.0.0", 1, 0); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + char *src = NULL; + char *dst = NULL; + char *src_wildcard = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "A.B.C.D", &idx); + if (idx) { + src = argv[idx]->arg; + src_wildcard = argv[idx + 1]->arg; + dst = argv[idx + 3]->arg; + } + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src, + src_wildcard, dst, "0.0.0.0", 1, 0); } DEFUN (no_access_list_extended_host_mask, no_access_list_extended_host_mask_cmd, - "no access-list <(100-199)|(2000-2699)> <deny|permit> ip host A.B.C.D A.B.C.D A.B.C.D", + "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip host A.B.C.D A.B.C.D A.B.C.D", NO_STR "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Any Internet Protocol\n" @@ -1135,23 +1638,44 @@ DEFUN (no_access_list_extended_host_mask, "Destination Wildcard bits\n") { int idx_acl = 2; - int idx_permit_deny = 3; - int idx_ipv4 = 6; - int idx_ipv4_2 = 7; - int idx_ipv4_3 = 8; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - "0.0.0.0", argv[idx_ipv4_2]->arg, - argv[idx_ipv4_3]->arg, 1, 0); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + char *src = NULL; + char *dst = NULL; + char *dst_wildcard = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "A.B.C.D", &idx); + if (idx) { + src = argv[idx]->arg; + dst = argv[idx + 1]->arg; + dst_wildcard = argv[idx + 2]->arg; + } + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src, + "0.0.0.0", dst, dst_wildcard, 1, 0); } DEFUN (no_access_list_extended_host_host, no_access_list_extended_host_host_cmd, - "no access-list <(100-199)|(2000-2699)> <deny|permit> ip host A.B.C.D host A.B.C.D", + "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip host A.B.C.D host A.B.C.D", NO_STR "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Any Internet Protocol\n" @@ -1161,22 +1685,42 @@ DEFUN (no_access_list_extended_host_host, "Destination address\n") { int idx_acl = 2; - int idx_permit_deny = 3; - int idx_ipv4 = 6; - int idx_ipv4_2 = 8; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - "0.0.0.0", argv[idx_ipv4_2]->arg, "0.0.0.0", 1, - 0); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + char *src = NULL; + char *dst = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "A.B.C.D", &idx); + if (idx) { + src = argv[idx]->arg; + dst = argv[idx + 2]->arg; + } + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src, + "0.0.0.0", dst, "0.0.0.0", 1, 0); } DEFUN (no_access_list_extended_any_host, no_access_list_extended_any_host_cmd, - "no access-list <(100-199)|(2000-2699)> <deny|permit> ip any host A.B.C.D", + "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip any host A.B.C.D", NO_STR "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Any Internet Protocol\n" @@ -1185,20 +1729,40 @@ DEFUN (no_access_list_extended_any_host, "Destination address\n") { int idx_acl = 2; - int idx_permit_deny = 3; - int idx_ipv4 = 7; - return filter_set_cisco( - vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, "0.0.0.0", - "255.255.255.255", argv[idx_ipv4]->arg, "0.0.0.0", 1, 0); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + char *dst = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "A.B.C.D", &idx); + if (idx) + dst = argv[idx]->arg; + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, + "0.0.0.0", "255.255.255.255", dst, "0.0.0.0", 1, + 0); } DEFUN (no_access_list_extended_host_any, no_access_list_extended_host_any_cmd, - "no access-list <(100-199)|(2000-2699)> <deny|permit> ip host A.B.C.D any", + "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip host A.B.C.D any", NO_STR "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Any Internet Protocol\n" @@ -1207,23 +1771,41 @@ DEFUN (no_access_list_extended_host_any, "Any destination host\n") { int idx_acl = 2; - int idx_permit_deny = 3; - int idx_ipv4 = 6; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + char *src = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "A.B.C.D", &idx); + if (idx) + src = argv[idx]->arg; + + return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src, "0.0.0.0", "0.0.0.0", "255.255.255.255", 1, 0); } static int filter_set_zebra(struct vty *vty, const char *name_str, - const char *type_str, afi_t afi, + const char *seq, const char *type_str, afi_t afi, const char *prefix_str, int exact, int set) { int ret; - enum filter_type type; + enum filter_type type = FILTER_DENY; struct filter *mfilter; struct filter_zebra *filter; struct access_list *access; struct prefix p; + int64_t seqnum = -1; if (strlen(name_str) > ACL_NAMSIZ) { vty_out(vty, @@ -1233,14 +1815,19 @@ static int filter_set_zebra(struct vty *vty, const char *name_str, return CMD_WARNING_CONFIG_FAILED; } + if (seq) + seqnum = (int64_t)atol(seq); + /* Check of filter type. */ - if (strncmp(type_str, "p", 1) == 0) - type = FILTER_PERMIT; - else if (strncmp(type_str, "d", 1) == 0) - type = FILTER_DENY; - else { - vty_out(vty, "filter type must be [permit|deny]\n"); - return CMD_WARNING_CONFIG_FAILED; + if (type_str) { + if (strncmp(type_str, "p", 1) == 0) + type = FILTER_PERMIT; + else if (strncmp(type_str, "d", 1) == 0) + type = FILTER_DENY; + else { + vty_out(vty, "filter type must be [permit|deny]\n"); + return CMD_WARNING_CONFIG_FAILED; + } } /* Check string format of prefix and prefixlen. */ @@ -1269,6 +1856,7 @@ static int filter_set_zebra(struct vty *vty, const char *name_str, mfilter = filter_new(); mfilter->type = type; + mfilter->seq = seqnum; filter = &mfilter->u.zfilter; prefix_copy(&filter->prefix, &p); @@ -1298,67 +1886,145 @@ static int filter_set_zebra(struct vty *vty, const char *name_str, DEFUN (mac_access_list, mac_access_list_cmd, - "mac access-list WORD <deny|permit> X:X:X:X:X:X", + "mac access-list WORD [seq (1-4294967295)] <deny|permit> X:X:X:X:X:X", "Add a mac access-list\n" "Add an access list entry\n" "MAC zebra access-list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "MAC address to match. e.g. 00:01:00:01:00:01\n") { - return filter_set_zebra(vty, argv[2]->arg, argv[3]->arg, AFI_L2VPN, - argv[4]->arg, 0, 1); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + char *mac = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "X:X:X:X:X:X", &idx); + if (idx) + mac = argv[idx]->arg; + + return filter_set_zebra(vty, argv[2]->arg, seq, permit_deny, AFI_L2VPN, + mac, 0, 1); } DEFUN (no_mac_access_list, no_mac_access_list_cmd, - "no mac access-list WORD <deny|permit> X:X:X:X:X:X", + "no mac access-list WORD [seq (1-4294967295)] <deny|permit> X:X:X:X:X:X", NO_STR "Remove a mac access-list\n" "Remove an access list entry\n" "MAC zebra access-list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "MAC address to match. e.g. 00:01:00:01:00:01\n") { - return filter_set_zebra(vty, argv[3]->arg, argv[4]->arg, AFI_L2VPN, - argv[5]->arg, 0, 0); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + char *mac = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "X:X:X:X:X:X", &idx); + if (idx) + mac = argv[idx]->arg; + + return filter_set_zebra(vty, argv[2]->arg, seq, permit_deny, AFI_L2VPN, + mac, 0, 0); } DEFUN (mac_access_list_any, mac_access_list_any_cmd, - "mac access-list WORD <deny|permit> any", + "mac access-list WORD [seq (1-4294967295)] <deny|permit> any", "Add a mac access-list\n" "Add an access list entry\n" "MAC zebra access-list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "MAC address to match. e.g. 00:01:00:01:00:01\n") { - return filter_set_zebra(vty, argv[2]->arg, argv[3]->arg, AFI_L2VPN, + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + return filter_set_zebra(vty, argv[2]->arg, seq, permit_deny, AFI_L2VPN, "00:00:00:00:00:00", 0, 1); } DEFUN (no_mac_access_list_any, no_mac_access_list_any_cmd, - "no mac access-list WORD <deny|permit> any", + "no mac access-list WORD [seq (1-4294967295)] <deny|permit> any", NO_STR "Remove a mac access-list\n" "Remove an access list entry\n" "MAC zebra access-list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "MAC address to match. e.g. 00:01:00:01:00:01\n") { - return filter_set_zebra(vty, argv[3]->arg, argv[4]->arg, AFI_L2VPN, + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + return filter_set_zebra(vty, argv[2]->arg, seq, permit_deny, AFI_L2VPN, "00:00:00:00:00:00", 0, 0); } DEFUN (access_list_exact, access_list_exact_cmd, - "access-list WORD <deny|permit> A.B.C.D/M [exact-match]", + "access-list WORD [seq (1-4294967295)] <deny|permit> A.B.C.D/M [exact-match]", "Add an access list entry\n" "IP zebra access-list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Prefix to match. e.g. 10.0.0.0/8\n" @@ -1366,41 +2032,71 @@ DEFUN (access_list_exact, { int idx = 0; int exact = 0; - int idx_word = 1; - int idx_permit_deny = 2; - int idx_ipv4_prefixlen = 3; - idx = idx_ipv4_prefixlen; - + char *seq = NULL; + char *permit_deny = NULL; + char *prefix = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "A.B.C.D/M", &idx); + if (idx) + prefix = argv[idx]->arg; + + idx = 0; if (argv_find(argv, argc, "exact-match", &idx)) exact = 1; - return filter_set_zebra(vty, argv[idx_word]->arg, - argv[idx_permit_deny]->arg, AFI_IP, - argv[idx_ipv4_prefixlen]->arg, exact, 1); + return filter_set_zebra(vty, argv[1]->arg, seq, permit_deny, + AFI_IP, prefix, exact, 1); } DEFUN (access_list_any, access_list_any_cmd, - "access-list WORD <deny|permit> any", + "access-list WORD [seq (1-4294967295)] <deny|permit> any", "Add an access list entry\n" "IP zebra access-list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Prefix to match. e.g. 10.0.0.0/8\n") { int idx_word = 1; - int idx_permit_deny = 2; - return filter_set_zebra(vty, argv[idx_word]->arg, - argv[idx_permit_deny]->arg, AFI_IP, "0.0.0.0/0", - 0, 1); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + return filter_set_zebra(vty, argv[idx_word]->arg, seq, permit_deny, + AFI_IP, "0.0.0.0/0", 0, 1); } DEFUN (no_access_list_exact, no_access_list_exact_cmd, - "no access-list WORD <deny|permit> A.B.C.D/M [exact-match]", + "no access-list WORD [seq (1-4294967295)] <deny|permit> A.B.C.D/M [exact-match]", NO_STR "Add an access list entry\n" "IP zebra access-list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Prefix to match. e.g. 10.0.0.0/8\n" @@ -1408,34 +2104,62 @@ DEFUN (no_access_list_exact, { int idx = 0; int exact = 0; - int idx_word = 2; - int idx_permit_deny = 3; - int idx_ipv4_prefixlen = 4; - idx = idx_ipv4_prefixlen; - + char *seq = NULL; + char *permit_deny = NULL; + char *prefix = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "A.B.C.D/M", &idx); + if (idx) + prefix = argv[idx]->arg; + + idx = 0; if (argv_find(argv, argc, "exact-match", &idx)) exact = 1; - return filter_set_zebra(vty, argv[idx_word]->arg, - argv[idx_permit_deny]->arg, AFI_IP, - argv[idx_ipv4_prefixlen]->arg, exact, 0); + return filter_set_zebra(vty, argv[2]->arg, seq, permit_deny, + AFI_IP, prefix, exact, 0); } DEFUN (no_access_list_any, no_access_list_any_cmd, - "no access-list WORD <deny|permit> any", + "no access-list WORD [seq (1-4294967295)] <deny|permit> any", NO_STR "Add an access list entry\n" "IP zebra access-list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Prefix to match. e.g. 10.0.0.0/8\n") { - int idx_word = 2; - int idx_permit_deny = 3; - return filter_set_zebra(vty, argv[idx_word]->arg, - argv[idx_permit_deny]->arg, AFI_IP, "0.0.0.0/0", - 0, 0); + int idx_word = 1; + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + return filter_set_zebra(vty, argv[idx_word]->arg, seq, permit_deny, + AFI_IP, "0.0.0.0/0", 0, 0); } DEFUN (no_access_list_all, @@ -1536,10 +2260,12 @@ DEFUN (no_access_list_remark_comment, DEFUN (ipv6_access_list_exact, ipv6_access_list_exact_cmd, - "ipv6 access-list WORD <deny|permit> X:X::X:X/M [exact-match]", + "ipv6 access-list WORD [seq (1-4294967295)] <deny|permit> X:X::X:X/M [exact-match]", IPV6_STR "Add an access list entry\n" "IPv6 zebra access-list\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "IPv6 prefix\n" @@ -1548,41 +2274,73 @@ DEFUN (ipv6_access_list_exact, int idx = 0; int exact = 0; int idx_word = 2; - int idx_allow = 3; - int idx_addr = 4; - idx = idx_addr; - + char *seq = NULL; + char *permit_deny = NULL; + char *prefix = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "X:X::X:X/M", &idx); + if (idx) + prefix = argv[idx]->arg; + + idx = 0; if (argv_find(argv, argc, "exact-match", &idx)) exact = 1; - return filter_set_zebra(vty, argv[idx_word]->arg, argv[idx_allow]->text, - AFI_IP6, argv[idx_addr]->arg, exact, 1); + return filter_set_zebra(vty, argv[idx_word]->arg, seq, permit_deny, + AFI_IP6, prefix, exact, 1); } DEFUN (ipv6_access_list_any, ipv6_access_list_any_cmd, - "ipv6 access-list WORD <deny|permit> any", + "ipv6 access-list WORD [seq (1-4294967295)] <deny|permit> any", IPV6_STR "Add an access list entry\n" "IPv6 zebra access-list\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Any prefixi to match\n") { int idx_word = 2; - int idx_permit_deny = 3; - return filter_set_zebra(vty, argv[idx_word]->arg, - argv[idx_permit_deny]->arg, AFI_IP6, "::/0", 0, - 1); + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + return filter_set_zebra(vty, argv[idx_word]->arg, seq, permit_deny, + AFI_IP6, "::/0", 0, 1); } DEFUN (no_ipv6_access_list_exact, no_ipv6_access_list_exact_cmd, - "no ipv6 access-list WORD <deny|permit> X:X::X:X/M [exact-match]", + "no ipv6 access-list WORD [seq (1-4294967295)] <deny|permit> X:X::X:X/M [exact-match]", NO_STR IPV6_STR "Add an access list entry\n" "IPv6 zebra access-list\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Prefix to match. e.g. 3ffe:506::/32\n" @@ -1590,35 +2348,64 @@ DEFUN (no_ipv6_access_list_exact, { int idx = 0; int exact = 0; - int idx_word = 3; - int idx_permit_deny = 4; - int idx_ipv6_prefixlen = 5; - idx = idx_ipv6_prefixlen; - + int idx_word = 2; + char *seq = NULL; + char *permit_deny = NULL; + char *prefix = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "X:X::X:X/M", &idx); + if (idx) + prefix = argv[idx]->arg; + + idx = 0; if (argv_find(argv, argc, "exact-match", &idx)) exact = 1; - return filter_set_zebra(vty, argv[idx_word]->arg, - argv[idx_permit_deny]->arg, AFI_IP6, - argv[idx_ipv6_prefixlen]->arg, exact, 0); + return filter_set_zebra(vty, argv[idx_word]->arg, seq, permit_deny, + AFI_IP6, prefix, exact, 0); } DEFUN (no_ipv6_access_list_any, no_ipv6_access_list_any_cmd, - "no ipv6 access-list WORD <deny|permit> any", + "no ipv6 access-list WORD [seq (1-4294967295)] <deny|permit> any", NO_STR IPV6_STR "Add an access list entry\n" "IPv6 zebra access-list\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "Any prefixi to match\n") { - int idx_word = 3; - int idx_permit_deny = 4; - return filter_set_zebra(vty, argv[idx_word]->arg, - argv[idx_permit_deny]->arg, AFI_IP6, "::/0", 0, - 0); + int idx_word = 2; + int idx = 0; + char *seq = NULL; + char *permit_deny = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "permit", &idx); + argv_find(argv, argc, "deny", &idx); + if (idx) + permit_deny = argv[idx]->arg; + + return filter_set_zebra(vty, argv[idx_word]->arg, seq, permit_deny, + AFI_IP6, "::/0", 0, 0); } @@ -1748,7 +2535,8 @@ static int filter_show(struct vty *vty, const char *name, afi_t afi) write = 0; } - vty_out(vty, " %s%s", filter_type_str(mfilter), + vty_out(vty, " seq %" PRId64, mfilter->seq); + vty_out(vty, " %s%s", filter_type_str(mfilter), mfilter->type == FILTER_DENY ? " " : ""); if (!mfilter->cisco) @@ -1795,7 +2583,8 @@ static int filter_show(struct vty *vty, const char *name, afi_t afi) write = 0; } - vty_out(vty, " %s%s", filter_type_str(mfilter), + vty_out(vty, " seq %" PRId64, mfilter->seq); + vty_out(vty, " %s%s", filter_type_str(mfilter), mfilter->type == FILTER_DENY ? " " : ""); if (!mfilter->cisco) @@ -1978,11 +2767,12 @@ static int config_write_access(struct vty *vty, afi_t afi) } for (mfilter = access->head; mfilter; mfilter = mfilter->next) { - vty_out(vty, "%saccess-list %s %s", + vty_out(vty, "%saccess-list %s seq %" PRId64 " %s", (afi == AFI_IP) ? ("") : ((afi == AFI_IP6) ? ("ipv6 ") : ("mac ")), - access->name, filter_type_str(mfilter)); + access->name, mfilter->seq, + filter_type_str(mfilter)); if (mfilter->cisco) config_write_access_cisco(vty, mfilter); @@ -2004,11 +2794,12 @@ static int config_write_access(struct vty *vty, afi_t afi) } for (mfilter = access->head; mfilter; mfilter = mfilter->next) { - vty_out(vty, "%saccess-list %s %s", + vty_out(vty, "%saccess-list %s seq %" PRId64 " %s", (afi == AFI_IP) ? ("") : ((afi == AFI_IP6) ? ("ipv6 ") : ("mac ")), - access->name, filter_type_str(mfilter)); + access->name, mfilter->seq, + filter_type_str(mfilter)); if (mfilter->cisco) config_write_access_cisco(vty, mfilter); diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c index e588571c01..bdb6c2a397 100644 --- a/lib/frr_pthread.c +++ b/lib/frr_pthread.c @@ -133,18 +133,29 @@ int frr_pthread_set_name(struct frr_pthread *fpt) return ret; } +static void *frr_pthread_inner(void *arg) +{ + struct frr_pthread *fpt = arg; + + rcu_thread_start(fpt->rcu_thread); + return fpt->attr.start(fpt); +} + int frr_pthread_run(struct frr_pthread *fpt, const pthread_attr_t *attr) { int ret; - ret = pthread_create(&fpt->thread, attr, fpt->attr.start, fpt); + fpt->rcu_thread = rcu_thread_prepare(); + ret = pthread_create(&fpt->thread, attr, frr_pthread_inner, fpt); /* * 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) + if (ret < 0) { + rcu_thread_unprepare(fpt->rcu_thread); memset(&fpt->thread, 0x00, sizeof(fpt->thread)); + } return ret; } diff --git a/lib/frr_pthread.h b/lib/frr_pthread.h index 3afe7ba966..6096a50370 100644 --- a/lib/frr_pthread.h +++ b/lib/frr_pthread.h @@ -23,6 +23,7 @@ #include <pthread.h> #include "frratomic.h" #include "memory.h" +#include "frrcu.h" #include "thread.h" #ifdef __cplusplus @@ -50,6 +51,8 @@ struct frr_pthread { /* pthread id */ pthread_t thread; + struct rcu_thread *rcu_thread; + /* thread master for this pthread's thread.c event loop */ struct thread_master *master; diff --git a/lib/frrcu.c b/lib/frrcu.c new file mode 100644 index 0000000000..7e6475b648 --- /dev/null +++ b/lib/frrcu.c @@ -0,0 +1,527 @@ +/* + * Copyright (c) 2017-19 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* implementation notes: this is an epoch-based RCU implementation. rcu_seq + * (global variable) counts the current epoch. Threads hold a specific epoch + * in rcu_read_lock(). This is the oldest epoch a thread might be accessing + * data from. + * + * The rcu_seq global is only pushed forward on rcu_read_lock() and + * rcu_read_unlock() calls. This makes things a tad more efficient since + * those are the only places it matters: + * - on rcu_read_lock, we don't want to hold an old epoch pointlessly + * - on rcu_read_unlock, we want to make sure we're not stuck on an old epoch + * when heading into a long idle period where no thread holds RCU + * + * rcu_thread structures themselves are RCU-free'd. + * + * rcu_head structures are the most iffy; normally for an ATOMLIST we would + * need to make sure we use rcu_free or pthread_rwlock to deallocate old items + * to prevent ABA or use-after-free problems. However, our ATOMLIST code + * guarantees that if the list remains non-empty in all cases, we only need + * the "last" pointer to do an "add_tail()", i.e. we can't run into ABA/UAF + * issues - but we do need to keep at least 1 item on the list. + * + * (Search the atomlist code for all uses of "last") + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <pthread.h> +#ifdef HAVE_PTHREAD_NP_H +#include <pthread_np.h> +#endif +#include <string.h> +#include <unistd.h> +#include <signal.h> + +#include "frrcu.h" +#include "seqlock.h" +#include "atomlist.h" + +DEFINE_MTYPE_STATIC(LIB, RCU_THREAD, "RCU thread") +DEFINE_MTYPE_STATIC(LIB, RCU_NEXT, "RCU sequence barrier") + +DECLARE_ATOMLIST(rcu_heads, struct rcu_head, head) + +PREDECL_ATOMLIST(rcu_threads) +struct rcu_thread { + struct rcu_threads_item head; + + struct rcu_head rcu_head; + + struct seqlock rcu; + + /* only accessed by thread itself, not atomic */ + unsigned depth; +}; +DECLARE_ATOMLIST(rcu_threads, struct rcu_thread, head) + +static const struct rcu_action rcua_next = { .type = RCUA_NEXT }; +static const struct rcu_action rcua_end = { .type = RCUA_END }; +static const struct rcu_action rcua_close = { .type = RCUA_CLOSE }; + +struct rcu_next { + struct rcu_head head_free; + struct rcu_head head_next; +}; + +#define rcu_free_internal(mtype, ptr, field) \ + do { \ + typeof(ptr) _ptr = (ptr); \ + struct rcu_head *_rcu_head = &_ptr->field; \ + static const struct rcu_action _rcu_action = { \ + .type = RCUA_FREE, \ + .u.free = { \ + .mt = mtype, \ + .offset = offsetof(typeof(*_ptr), field), \ + }, \ + }; \ + _rcu_head->action = &_rcu_action; \ + rcu_heads_add_tail(&rcu_heads, _rcu_head); \ + } while (0) + +/* primary global RCU position */ +static struct seqlock rcu_seq; +/* this is set to rcu_seq whenever something is added on the RCU queue. + * rcu_read_lock() and rcu_read_unlock() will then bump rcu_seq up one step. + */ +static _Atomic seqlock_val_t rcu_dirty; + +static struct rcu_threads_head rcu_threads; +static struct rcu_heads_head rcu_heads; + +/* main thread & RCU sweeper have pre-setup rcu_thread structures. The + * reasons are different: + * + * - rcu_thread_main is there because the main thread isn't started like + * other threads, it's implicitly created when the program is started. So + * rcu_thread_main matches up implicitly. + * + * - rcu_thread_rcu isn't actually put on the rcu_threads list (makes no + * sense really), it only exists so we can call RCU-using functions from + * the RCU thread without special handling in rcu_read_lock/unlock. + */ +static struct rcu_thread rcu_thread_main; +static struct rcu_thread rcu_thread_rcu; + +static pthread_t rcu_pthread; +static pthread_key_t rcu_thread_key; +static bool rcu_active; + +static void rcu_start(void); +static void rcu_bump(void); + +/* + * preinitialization for main thread + */ +static void rcu_thread_end(void *rcu_thread); + +static void rcu_preinit(void) __attribute__((constructor)); +static void rcu_preinit(void) +{ + struct rcu_thread *rt; + + rt = &rcu_thread_main; + rt->depth = 1; + seqlock_init(&rt->rcu); + seqlock_acquire_val(&rt->rcu, SEQLOCK_STARTVAL); + + pthread_key_create(&rcu_thread_key, rcu_thread_end); + pthread_setspecific(rcu_thread_key, rt); + + rcu_threads_add_tail(&rcu_threads, rt); + + /* RCU sweeper's rcu_thread is a dummy, NOT added to rcu_threads */ + rt = &rcu_thread_rcu; + rt->depth = 1; + + seqlock_init(&rcu_seq); + seqlock_acquire_val(&rcu_seq, SEQLOCK_STARTVAL); +} + +static struct rcu_thread *rcu_self(void) +{ + return (struct rcu_thread *)pthread_getspecific(rcu_thread_key); +} + +/* + * thread management (for the non-main thread) + */ +struct rcu_thread *rcu_thread_prepare(void) +{ + struct rcu_thread *rt, *cur; + + rcu_assert_read_locked(); + + if (!rcu_active) + rcu_start(); + + cur = rcu_self(); + assert(cur->depth); + + /* new thread always starts with rcu_read_lock held at depth 1, and + * holding the same epoch as the parent (this makes it possible to + * use RCU for things passed into the thread through its arg) + */ + rt = XCALLOC(MTYPE_RCU_THREAD, sizeof(*rt)); + rt->depth = 1; + + seqlock_init(&rt->rcu); + seqlock_acquire(&rt->rcu, &cur->rcu); + + rcu_threads_add_tail(&rcu_threads, rt); + + return rt; +} + +void rcu_thread_start(struct rcu_thread *rt) +{ + pthread_setspecific(rcu_thread_key, rt); +} + +void rcu_thread_unprepare(struct rcu_thread *rt) +{ + if (rt == &rcu_thread_rcu) + return; + + rt->depth = 1; + seqlock_acquire(&rt->rcu, &rcu_seq); + + rcu_bump(); + if (rt != &rcu_thread_main) + /* this free() happens after seqlock_release() below */ + rcu_free_internal(MTYPE_RCU_THREAD, rt, rcu_head); + + rcu_threads_del(&rcu_threads, rt); + seqlock_release(&rt->rcu); +} + +static void rcu_thread_end(void *rtvoid) +{ + struct rcu_thread *rt = rtvoid; + rcu_thread_unprepare(rt); +} + +/* + * main RCU control aspects + */ + +static void rcu_bump(void) +{ + struct rcu_next *rn; + + rn = XMALLOC(MTYPE_RCU_NEXT, sizeof(*rn)); + + /* note: each RCUA_NEXT item corresponds to exactly one seqno bump. + * This means we don't need to communicate which seqno is which + * RCUA_NEXT, since we really don't care. + */ + + /* + * Important race condition: while rcu_heads_add_tail is executing, + * there is an intermediate point where the rcu_heads "last" pointer + * already points to rn->head_next, but rn->head_next isn't added to + * the list yet. That means any other "add_tail" calls append to this + * item, which isn't fully on the list yet. Freeze this thread at + * that point and look at another thread doing a rcu_bump. It adds + * these two items and then does a seqlock_bump. But the rcu_heads + * list is still "interrupted" and there's no RCUA_NEXT on the list + * yet (from either the frozen thread or the second thread). So + * rcu_main() might actually hit the end of the list at the + * "interrupt". + * + * This situation is prevented by requiring that rcu_read_lock is held + * for any calls to rcu_bump, since if we're holding the current RCU + * epoch, that means rcu_main can't be chewing on rcu_heads and hit + * that interruption point. Only by the time the thread has continued + * to rcu_read_unlock() - and therefore completed the add_tail - the + * RCU sweeper gobbles up the epoch and can be sure to find at least + * the RCUA_NEXT and RCUA_FREE items on rcu_heads. + */ + rn->head_next.action = &rcua_next; + rcu_heads_add_tail(&rcu_heads, &rn->head_next); + + /* free rn that we allocated above. + * + * This is INTENTIONALLY not built into the RCUA_NEXT action. This + * ensures that after the action above is popped off the queue, there + * is still at least 1 item on the RCU queue. This means we never + * delete the last item, which is extremely important since it keeps + * the atomlist ->last pointer alive and well. + * + * If we were to "run dry" on the RCU queue, add_tail may run into the + * "last item is being deleted - start over" case, and then we may end + * up accessing old RCU queue items that are already free'd. + */ + rcu_free_internal(MTYPE_RCU_NEXT, rn, head_free); + + /* Only allow the RCU sweeper to run after these 2 items are queued. + * + * If another thread enqueues some RCU action in the intermediate + * window here, nothing bad happens - the queued action is associated + * with a larger seq# than strictly necessary. Thus, it might get + * executed a bit later, but that's not a problem. + * + * If another thread acquires the read lock in this window, it holds + * the previous epoch, but its RCU queue actions will be in the next + * epoch. This isn't a problem either, just a tad inefficient. + */ + seqlock_bump(&rcu_seq); +} + +static void rcu_bump_maybe(void) +{ + seqlock_val_t dirty; + + dirty = atomic_load_explicit(&rcu_dirty, memory_order_relaxed); + /* no problem if we race here and multiple threads bump rcu_seq; + * bumping too much causes no issues while not bumping enough will + * result in delayed cleanup + */ + if (dirty == seqlock_cur(&rcu_seq)) + rcu_bump(); +} + +void rcu_read_lock(void) +{ + struct rcu_thread *rt = rcu_self(); + + assert(rt); + if (rt->depth++ > 0) + return; + + seqlock_acquire(&rt->rcu, &rcu_seq); + /* need to hold RCU for bump ... */ + rcu_bump_maybe(); + /* ... but no point in holding the old epoch if we just bumped */ + seqlock_acquire(&rt->rcu, &rcu_seq); +} + +void rcu_read_unlock(void) +{ + struct rcu_thread *rt = rcu_self(); + + assert(rt && rt->depth); + if (--rt->depth > 0) + return; + rcu_bump_maybe(); + seqlock_release(&rt->rcu); +} + +void rcu_assert_read_locked(void) +{ + struct rcu_thread *rt = rcu_self(); + assert(rt && rt->depth && seqlock_held(&rt->rcu)); +} + +void rcu_assert_read_unlocked(void) +{ + struct rcu_thread *rt = rcu_self(); + assert(rt && !rt->depth && !seqlock_held(&rt->rcu)); +} + +/* + * RCU resource-release thread + */ + +static void *rcu_main(void *arg); + +static void rcu_start(void) +{ + /* ensure we never handle signals on the RCU thread by blocking + * everything here (new thread inherits signal mask) + */ + sigset_t oldsigs, blocksigs; + + sigfillset(&blocksigs); + pthread_sigmask(SIG_BLOCK, &blocksigs, &oldsigs); + + rcu_active = true; + + assert(!pthread_create(&rcu_pthread, NULL, rcu_main, NULL)); + + pthread_sigmask(SIG_SETMASK, &oldsigs, NULL); + +#ifdef HAVE_PTHREAD_SETNAME_NP +# ifdef GNU_LINUX + pthread_setname_np(rcu_pthread, "RCU sweeper"); +# elif defined(__NetBSD__) + pthread_setname_np(rcu_pthread, "RCU sweeper", NULL); +# endif +#elif defined(HAVE_PTHREAD_SET_NAME_NP) + pthread_set_name_np(rcu_pthread, "RCU sweeper"); +#endif +} + +static void rcu_do(struct rcu_head *rh) +{ + struct rcu_head_close *rhc; + void *p; + + switch (rh->action->type) { + case RCUA_FREE: + p = (char *)rh - rh->action->u.free.offset; + if (rh->action->u.free.mt) + qfree(rh->action->u.free.mt, p); + else + free(p); + break; + case RCUA_CLOSE: + rhc = container_of(rh, struct rcu_head_close, + rcu_head); + close(rhc->fd); + break; + case RCUA_CALL: + p = (char *)rh - rh->action->u.call.offset; + rh->action->u.call.fptr(p); + break; + + case RCUA_INVALID: + case RCUA_NEXT: + case RCUA_END: + default: + assert(0); + } +} + +static void rcu_watchdog(struct rcu_thread *rt) +{ +#if 0 + /* future work: print a backtrace for the thread that's holding up + * RCU. The only (good) way of doing that is to send a signal to the + * other thread, save away the backtrace in the signal handler, and + * block here until the signal is done processing. + * + * Just haven't implemented that yet. + */ + fprintf(stderr, "RCU watchdog %p\n", rt); +#endif +} + +static void *rcu_main(void *arg) +{ + struct rcu_thread *rt; + struct rcu_head *rh = NULL; + bool end = false; + struct timespec maxwait; + + seqlock_val_t rcuval = SEQLOCK_STARTVAL; + + pthread_setspecific(rcu_thread_key, &rcu_thread_rcu); + + while (!end) { + seqlock_wait(&rcu_seq, rcuval); + + /* RCU watchdog timeout, TODO: configurable value */ + clock_gettime(CLOCK_MONOTONIC, &maxwait); + maxwait.tv_nsec += 100 * 1000 * 1000; + if (maxwait.tv_nsec >= 1000000000) { + maxwait.tv_sec++; + maxwait.tv_nsec -= 1000000000; + } + + frr_each (rcu_threads, &rcu_threads, rt) + if (!seqlock_timedwait(&rt->rcu, rcuval, &maxwait)) { + rcu_watchdog(rt); + seqlock_wait(&rt->rcu, rcuval); + } + + while ((rh = rcu_heads_pop(&rcu_heads))) { + if (rh->action->type == RCUA_NEXT) + break; + else if (rh->action->type == RCUA_END) + end = true; + else + rcu_do(rh); + } + + rcuval += SEQLOCK_INCR; + } + + /* rcu_shutdown can only be called singlethreaded, and it does a + * pthread_join, so it should be impossible that anything ended up + * on the queue after RCUA_END + */ +#if 1 + assert(!rcu_heads_first(&rcu_heads)); +#else + while ((rh = rcu_heads_pop(&rcu_heads))) + if (rh->action->type >= RCUA_FREE) + rcu_do(rh); +#endif + return NULL; +} + +void rcu_shutdown(void) +{ + static struct rcu_head rcu_head_end; + struct rcu_thread *rt = rcu_self(); + void *retval; + + if (!rcu_active) + return; + + rcu_assert_read_locked(); + assert(rcu_threads_count(&rcu_threads) == 1); + + rcu_enqueue(&rcu_head_end, &rcua_end); + + rt->depth = 0; + seqlock_release(&rt->rcu); + seqlock_release(&rcu_seq); + rcu_active = false; + + /* clearing rcu_active is before pthread_join in case we hang in + * pthread_join & get a SIGTERM or something - in that case, just + * ignore the maybe-still-running RCU thread + */ + if (pthread_join(rcu_pthread, &retval) == 0) { + seqlock_acquire_val(&rcu_seq, SEQLOCK_STARTVAL); + seqlock_acquire_val(&rt->rcu, SEQLOCK_STARTVAL); + rt->depth = 1; + } +} + +/* + * RCU'd free functions + */ + +void rcu_enqueue(struct rcu_head *rh, const struct rcu_action *action) +{ + /* refer to rcu_bump() for why we need to hold RCU when adding items + * to rcu_heads + */ + rcu_assert_read_locked(); + + rh->action = action; + + if (!rcu_active) { + rcu_do(rh); + return; + } + rcu_heads_add_tail(&rcu_heads, rh); + atomic_store_explicit(&rcu_dirty, seqlock_cur(&rcu_seq), + memory_order_relaxed); +} + +void rcu_close(struct rcu_head_close *rhc, int fd) +{ + rhc->fd = fd; + rcu_enqueue(&rhc->rcu_head, &rcua_close); +} diff --git a/lib/frrcu.h b/lib/frrcu.h new file mode 100644 index 0000000000..8f789303cc --- /dev/null +++ b/lib/frrcu.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2017-19 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _FRRCU_H +#define _FRRCU_H + +#include "memory.h" +#include "atomlist.h" +#include "seqlock.h" + +/* quick RCU primer: + * There's a global sequence counter. Whenever a thread does a + * rcu_read_lock(), it is marked as holding the current sequence counter. + * When something is cleaned with RCU, the global sequence counter is + * increased and the item is queued for cleanup - *after* all threads are + * at a more recent sequence counter (or no sequence counter / unheld). + * + * So, by delaying resource cleanup, RCU ensures that things don't go away + * while another thread may hold a (stale) reference. + * + * Note that even if a thread is in rcu_read_lock(), it is invalid for that + * thread to access bits after rcu_free() & co on them. This is a design + * choice to allow no-op'ing out the entire RCU mechanism if we're running + * singlethreaded. (Also allows some optimization on the counter bumping.) + * + * differences from Linux Kernel RCU: + * - there's no rcu_synchronize(), if you really need to defer something + * use rcu_call() (and double check it's really necessary) + * - rcu_dereference() and rcu_assign_pointer() don't exist, use atomic_* + * instead (ATOM* list structures do the right thing) + */ + +/* opaque */ +struct rcu_thread; + +/* called before new thread creation, sets up rcu thread info for new thread + * before it actually exits. This ensures possible RCU references are held + * for thread startup. + * + * return value must be passed into the new thread's call to rcu_thread_start() + */ +extern struct rcu_thread *rcu_thread_prepare(void); + +/* cleanup in case pthread_create() fails */ +extern void rcu_thread_unprepare(struct rcu_thread *rcu_thread); + +/* called early in the new thread, with the return value from the above. + * NB: new thread is initially in RCU-held state! (at depth 1) + * + * TBD: maybe inherit RCU state from rcu_thread_prepare()? + */ +extern void rcu_thread_start(struct rcu_thread *rcu_thread); + +/* thread exit is handled through pthread_key_create's destructor function */ + +/* global RCU shutdown - must be called with only 1 active thread left. waits + * until remaining RCU actions are done & RCU thread has exited. + * + * This is mostly here to get a clean exit without memleaks. + */ +extern void rcu_shutdown(void); + +/* enter / exit RCU-held state. counter-based, so can be called nested. */ +extern void rcu_read_lock(void); +extern void rcu_read_unlock(void); + +/* for debugging / safety checks */ +extern void rcu_assert_read_locked(void); +extern void rcu_assert_read_unlocked(void); + +enum rcu_action_type { + RCUA_INVALID = 0, + /* used internally by the RCU code, shouldn't ever show up outside */ + RCUA_NEXT, + RCUA_END, + /* normal RCU actions, for outside use */ + RCUA_FREE, + RCUA_CLOSE, + RCUA_CALL, +}; + +/* since rcu_head is intended to be embedded into structs which may exist + * with lots of copies, rcu_head is shrunk down to its absolute minimum - + * the atomlist pointer + a pointer to this action struct. + */ +struct rcu_action { + enum rcu_action_type type; + + union { + struct { + struct memtype *mt; + ptrdiff_t offset; + } free; + + struct { + void (*fptr)(void *arg); + ptrdiff_t offset; + } call; + } u; +}; + +/* RCU cleanup function queue item */ +PREDECL_ATOMLIST(rcu_heads) +struct rcu_head { + struct rcu_heads_item head; + const struct rcu_action *action; +}; + +/* special RCU head for delayed fd-close */ +struct rcu_head_close { + struct rcu_head rcu_head; + int fd; +}; + +/* enqueue RCU action - use the macros below to get the rcu_action set up */ +extern void rcu_enqueue(struct rcu_head *head, const struct rcu_action *action); + +/* RCU free() and file close() operations. + * + * freed memory / closed fds become _immediately_ unavailable to the calling + * thread, but will remain available for other threads until they have passed + * into RCU-released state. + */ + +/* may be called with NULL mt to do non-MTYPE free() */ +#define rcu_free(mtype, ptr, field) \ + do { \ + typeof(ptr) _ptr = (ptr); \ + struct rcu_head *_rcu_head = &_ptr->field; \ + static const struct rcu_action _rcu_action = { \ + .type = RCUA_FREE, \ + .u.free = { \ + .mt = mtype, \ + .offset = offsetof(typeof(*_ptr), field), \ + }, \ + }; \ + rcu_enqueue(_rcu_head, &_rcu_action); \ + } while (0) + +/* use this sparingly, it runs on (and blocks) the RCU thread */ +#define rcu_call(func, ptr, field) \ + do { \ + typeof(ptr) _ptr = (ptr); \ + void (*fptype)(typeof(ptr)); \ + struct rcu_head *_rcu_head = &_ptr->field; \ + static const struct rcu_action _rcu_action = { \ + .type = RCUA_CALL, \ + .u.call = { \ + .fptr = (void *)func, \ + .offset = offsetof(typeof(*_ptr), field), \ + }, \ + }; \ + (void)(_fptype = func); \ + rcu_enqueue(_rcu_head, &_rcu_action); \ + } while (0) + +extern void rcu_close(struct rcu_head_close *head, int fd); + +#endif /* _FRRCU_H */ diff --git a/lib/frrstr.c b/lib/frrstr.c index c575c0b568..8a72a35af0 100644 --- a/lib/frrstr.c +++ b/lib/frrstr.c @@ -209,7 +209,7 @@ bool frrstr_endswith(const char *str, const char *suffix) int all_digit(const char *str) { for (; *str != '\0'; str++) - if (!isdigit((int)*str)) + if (!isdigit((unsigned char)*str)) return 0; return 1; } @@ -132,18 +132,26 @@ static int if_cmp_index_func(const struct interface *ifp1, } /* Create new interface structure. */ -struct interface *if_create(const char *name, vrf_id_t vrf_id) +static struct interface *if_create_backend(const char *name, ifindex_t ifindex, + vrf_id_t vrf_id) { struct vrf *vrf = vrf_get(vrf_id, NULL); struct interface *ifp; ifp = XCALLOC(MTYPE_IF, sizeof(struct interface)); - ifp->ifindex = IFINDEX_INTERNAL; - - assert(name); - strlcpy(ifp->name, name, sizeof(ifp->name)); ifp->vrf_id = vrf_id; - IFNAME_RB_INSERT(vrf, ifp); + + if (name) { + strlcpy(ifp->name, name, sizeof(ifp->name)); + IFNAME_RB_INSERT(vrf, ifp); + } else + ifp->name[0] = '\0'; + + if (ifindex != IFINDEX_INTERNAL) + if_set_index(ifp, ifindex); + else + ifp->ifindex = ifindex; /* doesn't add it to the list */ + ifp->connected = list_new(); ifp->connected->del = (void (*)(void *))connected_free; @@ -158,6 +166,16 @@ struct interface *if_create(const char *name, vrf_id_t vrf_id) return ifp; } +struct interface *if_create(const char *name, vrf_id_t vrf_id) +{ + return if_create_backend(name, IFINDEX_INTERNAL, vrf_id); +} + +struct interface *if_create_ifindex(ifindex_t ifindex, vrf_id_t vrf_id) +{ + return if_create_backend(NULL, ifindex, vrf_id); +} + /* Create new interface structure. */ void if_update_to_new_vrf(struct interface *ifp, vrf_id_t vrf_id) { @@ -302,6 +320,23 @@ struct interface *if_lookup_by_name_all_vrf(const char *name) return NULL; } +struct interface *if_lookup_by_index_all_vrf(ifindex_t ifindex) +{ + struct vrf *vrf; + struct interface *ifp; + + if (ifindex == IFINDEX_INTERNAL) + return NULL; + + RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { + ifp = if_lookup_by_index(ifindex, vrf->vrf_id); + if (ifp) + return ifp; + } + + return NULL; +} + /* Lookup interface by IPv4 address. */ struct interface *if_lookup_exact_address(void *src, int family, vrf_id_t vrf_id) @@ -447,6 +482,34 @@ struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id) return NULL; } +struct interface *if_get_by_ifindex(ifindex_t ifindex, vrf_id_t vrf_id) +{ + struct interface *ifp; + + switch (vrf_get_backend()) { + case VRF_BACKEND_UNKNOWN: + case VRF_BACKEND_NETNS: + ifp = if_lookup_by_index(ifindex, vrf_id); + if (ifp) + return ifp; + return if_create_ifindex(ifindex, vrf_id); + case VRF_BACKEND_VRF_LITE: + ifp = if_lookup_by_index_all_vrf(ifindex); + if (ifp) { + if (ifp->vrf_id == vrf_id) + return ifp; + /* If it came from the kernel or by way of zclient, + * believe it and update the ifp accordingly. + */ + if_update_to_new_vrf(ifp, vrf_id); + return ifp; + } + return if_create_ifindex(ifindex, vrf_id); + } + + return NULL; +} + void if_set_index(struct interface *ifp, ifindex_t ifindex) { struct vrf *vrf; @@ -478,7 +478,9 @@ extern int if_cmp_name_func(const char *p1, const char *p2); */ extern void if_update_to_new_vrf(struct interface *, vrf_id_t vrf_id); extern struct interface *if_create(const char *name, vrf_id_t vrf_id); +extern struct interface *if_create_ifindex(ifindex_t ifindex, vrf_id_t vrf_id); extern struct interface *if_lookup_by_index(ifindex_t, vrf_id_t vrf_id); +extern struct interface *if_lookup_by_index_all_vrf(ifindex_t); extern struct interface *if_lookup_exact_address(void *matchaddr, int family, vrf_id_t vrf_id); extern struct connected *if_lookup_address(void *matchaddr, int family, @@ -493,6 +495,7 @@ size_t if_lookup_by_hwaddr(const uint8_t *hw_addr, size_t addrsz, extern struct interface *if_lookup_by_name_all_vrf(const char *ifname); extern struct interface *if_lookup_by_name(const char *ifname, vrf_id_t vrf_id); extern struct interface *if_get_by_name(const char *ifname, vrf_id_t vrf_id); +extern struct interface *if_get_by_ifindex(ifindex_t ifindex, vrf_id_t vrf_id); extern void if_set_index(struct interface *ifp, ifindex_t ifindex); /* Delete the interface, but do not free the structure, and leave it in the diff --git a/lib/libfrr.c b/lib/libfrr.c index 3294a61295..4301dc20ad 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -41,6 +41,7 @@ #include "northbound_cli.h" #include "northbound_db.h" #include "debug.h" +#include "frrcu.h" DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm)) DEFINE_KOOH(frr_early_fini, (), ()) @@ -409,7 +410,7 @@ static int frr_opt(int opt) } if (di->zpathspace) fprintf(stderr, - "-N option overriden by -z for zebra named socket path\n"); + "-N option overridden by -z for zebra named socket path\n"); if (strchr(optarg, '/') || strchr(optarg, '.')) { fprintf(stderr, @@ -681,6 +682,7 @@ struct thread_master *frr_init(void) log_filter_cmd_init(); log_ref_init(); + log_ref_vty_init(); lib_error_init(); yang_init(); @@ -1080,6 +1082,7 @@ void frr_fini(void) master = NULL; closezlog(); /* frrmod_init -> nothing needed / hooks */ + rcu_shutdown(); if (!debug_memstats_at_exit) return; @@ -387,10 +387,8 @@ void vzlog(int priority, const char *format, va_list args) /* If it doesn't match on a filter, do nothing with the debug log */ if ((priority == LOG_DEBUG) && zlog_filter_count - && vzlog_filter(zl, &tsctl, proto_str, priority, msg)) { - pthread_mutex_unlock(&loglock); + && vzlog_filter(zl, &tsctl, proto_str, priority, msg)) goto out; - } /* call external hook */ hook_call(zebra_ext_log, priority, format, args); @@ -561,9 +559,7 @@ static void crash_write(struct fbuf *fb, char *msgstart) void zlog_signal(int signo, const char *action, void *siginfo_v, void *program_counter) { -#ifdef SA_SIGINFO siginfo_t *siginfo = siginfo_v; -#endif time_t now; char buf[sizeof("DEFAULT: Received signal S at T (si_addr 0xP, PC 0xP); aborting...") + 100]; @@ -577,7 +573,6 @@ void zlog_signal(int signo, const char *action, void *siginfo_v, msgstart = fb.pos; bprintfrr(&fb, "Received signal %d at %lld", signo, (long long)now); -#ifdef SA_SIGINFO if (program_counter) bprintfrr(&fb, " (si_addr 0x%tx, PC 0x%tx)", (ptrdiff_t)siginfo->si_addr, @@ -585,7 +580,6 @@ void zlog_signal(int signo, const char *action, void *siginfo_v, else bprintfrr(&fb, " (si_addr 0x%tx)", (ptrdiff_t)siginfo->si_addr); -#endif /* SA_SIGINFO */ bprintfrr(&fb, "; %s\n", action); crash_write(&fb, msgstart); @@ -1271,6 +1265,7 @@ void zlog_hexdump(const void *mem, unsigned int len) size_t bs = ((len / 8) + 1) * 53 + 1; char buf[bs]; char *s = buf; + const unsigned char *memch = mem; memset(buf, 0, sizeof(buf)); @@ -1279,12 +1274,11 @@ void zlog_hexdump(const void *mem, unsigned int len) /* print offset */ if (i % columns == 0) s += snprintf(s, bs - (s - buf), - "0x%016lx: ", (unsigned long)mem + i); + "0x%016lx: ", (unsigned long)memch + i); /* print hex data */ if (i < len) - s += snprintf(s, bs - (s - buf), "%02x ", - 0xFF & ((const char *)mem)[i]); + s += snprintf(s, bs - (s - buf), "%02x ", memch[i]); /* end of block, just aligning for ASCII dump */ else @@ -1296,10 +1290,9 @@ void zlog_hexdump(const void *mem, unsigned int len) /* 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 if (isprint(memch[j])) + s += snprintf(s, bs - (s - buf), "%c", + memch[j]); else /* other char */ s += snprintf(s, bs - (s - buf), "."); } diff --git a/lib/logicalrouter.c b/lib/logicalrouter.c deleted file mode 100644 index da69ae5312..0000000000 --- a/lib/logicalrouter.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Logical Router functions. - * Copyright (C) 2018 6WIND S.A. - * - * 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 "ns.h" -#include "log.h" -#include "memory.h" - -#include "command.h" -#include "vty.h" -#include "logicalrouter.h" - -/* Comment that useless define to avoid compilation error - * in order to use it, one could provide the kind of NETNS to NS backend - * so that the allocation will match the logical router - * DEFINE_MTYPE_STATIC(LIB, LOGICALROUTER, "LogicalRouter Context") - */ -DEFINE_MTYPE_STATIC(LIB, LOGICALROUTER_NAME, "Logical Router Name") - -/* Logical Router node has no interface. */ -static struct cmd_node logicalrouter_node = {LOGICALROUTER_NODE, "", 1}; - -static int logicalrouter_backend; - -/* Get a NS. If not found, create one. */ -static struct ns *logicalrouter_get(ns_id_t ns_id) -{ - struct ns *ns; - - ns = ns_lookup(ns_id); - if (ns) - return (ns); - ns = ns_get_created(ns, NULL, ns_id); - return ns; -} - -static int logicalrouter_is_backend_netns(void) -{ - return (logicalrouter_backend == LOGICALROUTER_BACKEND_NETNS); -} - - -DEFUN_NOSH (logicalrouter, - logicalrouter_cmd, - "logical-router (1-65535) ns NAME", - "Enable a logical-router\n" - "Specify the logical-router indentifier\n" - "The Name Space\n" - "The file name in " NS_RUN_DIR ", or a full pathname\n") -{ - int idx_number = 1; - int idx_name = 3; - ns_id_t ns_id; - struct ns *ns = NULL; - char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); - - if (!pathname) - return CMD_WARNING_CONFIG_FAILED; - - ns_id = strtoul(argv[idx_number]->arg, NULL, 10); - ns = logicalrouter_get(ns_id); - - if (ns->name && strcmp(ns->name, pathname) != 0) { - vty_out(vty, "NS %u is already configured with NETNS %s\n", - ns->ns_id, ns->name); - return CMD_WARNING; - } - - if (!ns->name) - ns->name = XSTRDUP(MTYPE_LOGICALROUTER_NAME, pathname); - - if (!ns_enable(ns, NULL)) { - vty_out(vty, "Can not associate NS %u with NETNS %s\n", - ns->ns_id, ns->name); - return CMD_WARNING_CONFIG_FAILED; - } - - return CMD_SUCCESS; -} - -DEFUN (no_logicalrouter, - no_logicalrouter_cmd, - "no logical-router (1-65535) ns NAME", - NO_STR - "Enable a Logical-Router\n" - "Specify the Logical-Router identifier\n" - "The Name Space\n" - "The file name in " NS_RUN_DIR ", or a full pathname\n") -{ - int idx_number = 2; - int idx_name = 4; - ns_id_t ns_id; - struct ns *ns = NULL; - char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); - - if (!pathname) - return CMD_WARNING_CONFIG_FAILED; - - ns_id = strtoul(argv[idx_number]->arg, NULL, 10); - ns = ns_lookup(ns_id); - - if (!ns) { - vty_out(vty, "NS %u is not found\n", ns_id); - return CMD_SUCCESS; - } - - if (ns->name && strcmp(ns->name, pathname) != 0) { - vty_out(vty, "Incorrect NETNS file name\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - ns_disable(ns); - - if (ns->name) { - XFREE(MTYPE_LOGICALROUTER_NAME, ns->name); - ns->name = NULL; - } - - return CMD_SUCCESS; -} - -/* Initialize NS module. */ -void logicalrouter_init(int (*writefunc)(struct vty *vty)) -{ - if (ns_have_netns() && logicalrouter_is_backend_netns()) { - /* Install LogicalRouter commands. */ - install_node(&logicalrouter_node, writefunc); - install_element(CONFIG_NODE, &logicalrouter_cmd); - install_element(CONFIG_NODE, &no_logicalrouter_cmd); - } -} - -void logicalrouter_terminate(void) -{ - ns_terminate(); -} - -void logicalrouter_configure_backend(int backend_netns) -{ - logicalrouter_backend = backend_netns; -} diff --git a/lib/logicalrouter.h b/lib/logicalrouter.h deleted file mode 100644 index d18832ef70..0000000000 --- a/lib/logicalrouter.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Logical Router related header. - * Copyright (C) 2018 6WIND S.A. - * - * 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 _ZEBRA_LOGICAL_ROUTER_H -#define _ZEBRA_LOGICAL_ROUTER_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* Logical Router Backend defines */ -#define LOGICALROUTER_BACKEND_OFF 0 -#define LOGICALROUTER_BACKEND_NETNS 1 - -/* - * Logical Router initializer/destructor - */ -extern void logicalrouter_init(int (*writefunc)(struct vty *vty)); -extern void logicalrouter_terminate(void); - -/* used to configure backend for logical router - * Currently, the whole NETNS feature is exclusively shared - * between logical router and VRF backend NETNS - * However, when logical router feature will be available, - * one can think of having exclusivity only per NETNS - */ -extern void logicalrouter_configure_backend(int backend_netns); - -#ifdef __cplusplus -} -#endif - -#endif /*_ZEBRA_LOGICAL_ROUTER_H*/ diff --git a/lib/netns_other.c b/lib/netns_other.c index 4c7be05fab..b0aae4f8df 100644 --- a/lib/netns_other.c +++ b/lib/netns_other.c @@ -82,7 +82,7 @@ const char *ns_get_name(struct ns *ns) } /* only called from vrf ( when removing netns from vrf) - * or at VRF or logical router termination + * or at VRF termination */ void ns_delete(struct ns *ns) { @@ -41,7 +41,7 @@ typedef uint32_t ns_id_t; #ifdef HAVE_NETNS #define NS_DEFAULT_NAME "/proc/self/ns/net" #else /* !HAVE_NETNS */ -#define NS_DEFAULT_NAME "Default-logical-router" +#define NS_DEFAULT_NAME "default-netns" #endif /* HAVE_NETNS */ struct ns { @@ -82,10 +82,10 @@ extern struct ns_head ns_tree; * NS hooks */ -#define NS_NEW_HOOK 0 /* a new logical-router is just created */ -#define NS_DELETE_HOOK 1 /* a logical-router is to be deleted */ -#define NS_ENABLE_HOOK 2 /* a logical-router is ready to use */ -#define NS_DISABLE_HOOK 3 /* a logical-router is to be unusable */ +#define NS_NEW_HOOK 0 /* a new netns is just created */ +#define NS_DELETE_HOOK 1 /* a netns is to be deleted */ +#define NS_ENABLE_HOOK 2 /* a netns is ready to use */ +#define NS_DISABLE_HOOK 3 /* a netns is to be unusable */ /* * Add a specific hook ns module. @@ -128,7 +128,7 @@ extern void ns_walk_func(int (*func)(struct ns *)); extern const char *ns_get_name(struct ns *ns); /* only called from vrf ( when removing netns from vrf) - * or at VRF or logical router termination + * or at VRF termination */ extern void ns_delete(struct ns *ns); diff --git a/lib/plist.c b/lib/plist.c index 1ba8982499..64571a05b7 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -218,7 +218,7 @@ static struct prefix_list *prefix_list_insert(afi_t afi, int orf, /* If name is made by all digit character. We treat it as number. */ for (number = 0, i = 0; i < strlen(name); i++) { - if (isdigit((int)name[i])) + if (isdigit((unsigned char)name[i])) number = (number * 10) + (name[i] - '0'); else break; diff --git a/lib/pqueue.c b/lib/pqueue.c deleted file mode 100644 index 87b54a681a..0000000000 --- a/lib/pqueue.c +++ /dev/null @@ -1,185 +0,0 @@ -/* Priority queue functions. - * Copyright (C) 2003 Yasuhiro Ohara - * - * This file is part of GNU Zebra. - * - * GNU Zebra 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, or (at your - * option) any later version. - * - * GNU Zebra 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 "memory.h" -#include "pqueue.h" - -DEFINE_MTYPE_STATIC(LIB, PQUEUE, "Priority queue") -DEFINE_MTYPE_STATIC(LIB, PQUEUE_DATA, "Priority queue data") - -/* priority queue using heap sort */ - -/* pqueue->cmp() controls the order of sorting (i.e, ascending or - descending). If you want the left node to move upper of the heap - binary tree, make cmp() to return less than 0. for example, if cmp - (10, 20) returns -1, the sorting is ascending order. if cmp (10, - 20) returns 1, the sorting is descending order. if cmp (10, 20) - returns 0, this library does not do sorting (which will not be what - you want). To be brief, if the contents of cmp_func (left, right) - is left - right, dequeue () returns the smallest node. Otherwise - (if the contents is right - left), dequeue () returns the largest - node. */ - -#define DATA_SIZE (sizeof (void *)) -#define PARENT_OF(x) ((x - 1) / 2) -#define LEFT_OF(x) (2 * x + 1) -#define RIGHT_OF(x) (2 * x + 2) -#define HAVE_CHILD(x,q) (x < (q)->size / 2) - -void trickle_up(int index, struct pqueue *queue) -{ - void *tmp; - - /* Save current node as tmp node. */ - tmp = queue->array[index]; - - /* Continue until the node reaches top or the place where the parent - node should be upper than the tmp node. */ - while (index > 0 - && (*queue->cmp)(tmp, queue->array[PARENT_OF(index)]) < 0) { - /* actually trickle up */ - queue->array[index] = queue->array[PARENT_OF(index)]; - if (queue->update != NULL) - (*queue->update)(queue->array[index], index); - index = PARENT_OF(index); - } - - /* Restore the tmp node to appropriate place. */ - queue->array[index] = tmp; - if (queue->update != NULL) - (*queue->update)(tmp, index); -} - -void trickle_down(int index, struct pqueue *queue) -{ - void *tmp; - int which; - - /* Save current node as tmp node. */ - tmp = queue->array[index]; - - /* Continue until the node have at least one (left) child. */ - while (HAVE_CHILD(index, queue)) { - /* If right child exists, and if the right child is more proper - to be moved upper. */ - if (RIGHT_OF(index) < queue->size - && (*queue->cmp)(queue->array[LEFT_OF(index)], - queue->array[RIGHT_OF(index)]) - > 0) - which = RIGHT_OF(index); - else - which = LEFT_OF(index); - - /* If the tmp node should be upper than the child, break. */ - if ((*queue->cmp)(queue->array[which], tmp) > 0) - break; - - /* Actually trickle down the tmp node. */ - queue->array[index] = queue->array[which]; - if (queue->update != NULL) - (*queue->update)(queue->array[index], index); - index = which; - } - - /* Restore the tmp node to appropriate place. */ - queue->array[index] = tmp; - if (queue->update != NULL) - (*queue->update)(tmp, index); -} - -struct pqueue *pqueue_create(void) -{ - struct pqueue *queue; - - queue = XCALLOC(MTYPE_PQUEUE, sizeof(struct pqueue)); - - queue->array = - XCALLOC(MTYPE_PQUEUE_DATA, DATA_SIZE * PQUEUE_INIT_ARRAYSIZE); - queue->array_size = PQUEUE_INIT_ARRAYSIZE; - - /* By default we want nothing to happen when a node changes. */ - queue->update = NULL; - return queue; -} - -void pqueue_delete(struct pqueue *queue) -{ - XFREE(MTYPE_PQUEUE_DATA, queue->array); - XFREE(MTYPE_PQUEUE, queue); -} - -static int pqueue_expand(struct pqueue *queue) -{ - void **newarray; - - newarray = - XCALLOC(MTYPE_PQUEUE_DATA, queue->array_size * DATA_SIZE * 2); - - memcpy(newarray, queue->array, queue->array_size * DATA_SIZE); - - XFREE(MTYPE_PQUEUE_DATA, queue->array); - queue->array = newarray; - queue->array_size *= 2; - - return 1; -} - -void pqueue_enqueue(void *data, struct pqueue *queue) -{ - if (queue->size + 2 >= queue->array_size && !pqueue_expand(queue)) - return; - - queue->array[queue->size] = data; - if (queue->update != NULL) - (*queue->update)(data, queue->size); - trickle_up(queue->size, queue); - queue->size++; -} - -void *pqueue_dequeue(struct pqueue *queue) -{ - void *data = queue->array[0]; - queue->array[0] = queue->array[--queue->size]; - trickle_down(0, queue); - return data; -} - -void pqueue_remove_at(int index, struct pqueue *queue) -{ - queue->array[index] = queue->array[--queue->size]; - - if (index > 0 - && (*queue->cmp)(queue->array[index], - queue->array[PARENT_OF(index)]) - < 0) { - trickle_up(index, queue); - } else { - trickle_down(index, queue); - } -} - -void pqueue_remove(void *data, struct pqueue *queue) -{ - for (int i = 0; i < queue->size; i++) - if (queue->array[i] == data) - pqueue_remove_at(i, queue); -} diff --git a/lib/pqueue.h b/lib/pqueue.h deleted file mode 100644 index 032ee9db4c..0000000000 --- a/lib/pqueue.h +++ /dev/null @@ -1,54 +0,0 @@ -/* Priority queue functions. - * Copyright (C) 2003 Yasuhiro Ohara - * - * This file is part of GNU Zebra. - * - * GNU Zebra 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, or (at your - * option) any later version. - * - * GNU Zebra 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 _ZEBRA_PQUEUE_H -#define _ZEBRA_PQUEUE_H - -#ifdef __cplusplus -extern "C" { -#endif - -struct pqueue { - void **array; - int array_size; - int size; - - int (*cmp)(void *, void *); - void (*update)(void *node, int actual_position); -}; - -#define PQUEUE_INIT_ARRAYSIZE 32 - -extern struct pqueue *pqueue_create(void); -extern void pqueue_delete(struct pqueue *queue); - -extern void pqueue_enqueue(void *data, struct pqueue *queue); -extern void *pqueue_dequeue(struct pqueue *queue); -extern void pqueue_remove_at(int index, struct pqueue *queue); -extern void pqueue_remove(void *data, struct pqueue *queue); - -extern void trickle_down(int index, struct pqueue *queue); -extern void trickle_up(int index, struct pqueue *queue); - -#ifdef __cplusplus -} -#endif - -#endif /* _ZEBRA_PQUEUE_H */ diff --git a/lib/prefix.c b/lib/prefix.c index 7abeebcd09..1a4a914e05 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -628,8 +628,15 @@ int prefix_match_network_statement(const struct prefix *n, return 1; } -void prefix_copy(struct prefix *dest, const struct prefix *src) +#ifdef __clang_analyzer__ +#undef prefix_copy /* cf. prefix.h */ +#endif + +void prefix_copy(union prefixptr udest, union prefixconstptr usrc) { + struct prefix *dest = udest.p; + const struct prefix *src = usrc.p; + dest->family = src->family; dest->prefixlen = src->prefixlen; @@ -674,8 +681,11 @@ void prefix_copy(struct prefix *dest, const struct prefix *src) * the same. Note that this routine has the same return value sense * as '==' (which is different from prefix_cmp). */ -int prefix_same(const struct prefix *p1, const struct prefix *p2) +int prefix_same(union prefixconstptr up1, union prefixconstptr up2) { + const struct prefix *p1 = up1.p; + const struct prefix *p2 = up2.p; + if ((p1 && !p2) || (!p1 && p2)) return 0; @@ -712,57 +722,59 @@ int prefix_same(const struct prefix *p1, const struct prefix *p2) } /* - * Return 0 if the network prefixes represented by the struct prefix - * arguments are the same prefix, and 1 otherwise. Network prefixes - * are considered the same if the prefix lengths are equal and the - * network parts are the same. Host bits (which are considered masked + * Return -1/0/1 comparing the prefixes in a way that gives a full/linear + * order. + * + * Network prefixes are considered the same if the prefix lengths are equal + * and the network parts are the same. Host bits (which are considered masked * by the prefix length) are not significant. Thus, 10.0.0.1/8 and * 10.0.0.2/8 are considered equivalent by this routine. Note that * this routine has the same return sense as strcmp (which is different * from prefix_same). */ -int prefix_cmp(const struct prefix *p1, const struct prefix *p2) +int prefix_cmp(union prefixconstptr up1, union prefixconstptr up2) { + const struct prefix *p1 = up1.p; + const struct prefix *p2 = up2.p; int offset; int shift; + int i; /* Set both prefix's head pointer. */ const uint8_t *pp1; const uint8_t *pp2; if (p1->family != p2->family) - return 1; + return numcmp(p1->family, p2->family); if (p1->family == AF_FLOWSPEC) { pp1 = (const uint8_t *)p1->u.prefix_flowspec.ptr; pp2 = (const uint8_t *)p2->u.prefix_flowspec.ptr; if (p1->u.prefix_flowspec.prefixlen != p2->u.prefix_flowspec.prefixlen) - return 1; + return numcmp(p1->u.prefix_flowspec.prefixlen, + p2->u.prefix_flowspec.prefixlen); offset = p1->u.prefix_flowspec.prefixlen; while (offset--) if (pp1[offset] != pp2[offset]) - return 1; + return numcmp(pp1[offset], pp2[offset]); return 0; } pp1 = p1->u.val; pp2 = p2->u.val; if (p1->prefixlen != p2->prefixlen) - return 1; + return numcmp(p1->prefixlen, p2->prefixlen); offset = p1->prefixlen / PNBBY; shift = p1->prefixlen % PNBBY; - if (shift) - if (maskbit[shift] & (pp1[offset] ^ pp2[offset])) - return 1; - - while (offset--) - if (pp1[offset] != pp2[offset]) - return 1; + i = memcmp(pp1, pp2, offset); + if (i) + return i; - return 0; + return numcmp(pp1[offset] & maskbit[shift], + pp2[offset] & maskbit[shift]); } /* diff --git a/lib/prefix.h b/lib/prefix.h index d57b43dac6..e338140f1a 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -410,12 +410,20 @@ extern const char *prefix2str(union prefixconstptr, char *, int); extern int prefix_match(const struct prefix *, const struct prefix *); extern int prefix_match_network_statement(const struct prefix *, const struct prefix *); -extern int prefix_same(const struct prefix *, const struct prefix *); -extern int prefix_cmp(const struct prefix *, const struct prefix *); +extern int prefix_same(union prefixconstptr, union prefixconstptr); +extern int prefix_cmp(union prefixconstptr, union prefixconstptr); extern int prefix_common_bits(const struct prefix *, const struct prefix *); -extern void prefix_copy(struct prefix *dest, const struct prefix *src); +extern void prefix_copy(union prefixptr, union prefixconstptr); extern void apply_mask(struct prefix *); +#ifdef __clang_analyzer__ +/* clang-SA doesn't understand transparent unions, making it think that the + * target of prefix_copy is uninitialized. So just memset the target. + * cf. https://bugs.llvm.org/show_bug.cgi?id=42811 + */ +#define prefix_copy(a, b) ({ memset(a, 0, sizeof(*a)); prefix_copy(a, b); }) +#endif + extern struct prefix *sockunion2prefix(const union sockunion *dest, const union sockunion *mask); extern struct prefix *sockunion2hostprefix(const union sockunion *, diff --git a/lib/ptm_lib.c b/lib/ptm_lib.c index 7f868beda4..a2ce9a0e2f 100644 --- a/lib/ptm_lib.c +++ b/lib/ptm_lib.c @@ -125,7 +125,7 @@ static int _ptm_lib_decode_header(csv_t *csv, int *msglen, int *version, } /* remove leading spaces */ for (i = j = 0; i < csv_field_len(fld); i++) { - if (!isspace((int)hdr[i])) { + if (!isspace((unsigned char)hdr[i])) { client_name[j] = hdr[i]; j++; } diff --git a/lib/routemap.c b/lib/routemap.c index 2fee3a479e..eca02e8366 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -927,15 +927,15 @@ static const char *route_map_type_str(enum route_map_type type) return ""; } -static const char *route_map_result_str(route_map_result_t res) +static const char *route_map_cmd_result_str(enum route_map_cmd_result_t res) { switch (res) { case RMAP_MATCH: return "match"; - case RMAP_DENYMATCH: - return "deny"; case RMAP_NOMATCH: return "no match"; + case RMAP_NOOP: + return "noop"; case RMAP_ERROR: return "error"; case RMAP_OKAY: @@ -945,6 +945,18 @@ static const char *route_map_result_str(route_map_result_t res) return "invalid"; } +static const char *route_map_result_str(route_map_result_t res) +{ + switch (res) { + case RMAP_DENYMATCH: + return "deny"; + case RMAP_PERMITMATCH: + return "permit"; + } + + return "invalid"; +} + static int route_map_empty(struct route_map *map) { if (map->head == NULL && map->tail == NULL) @@ -1558,20 +1570,98 @@ int route_map_delete_set(struct route_map_index *index, const char *set_name, return 1; } +static enum route_map_cmd_result_t +route_map_apply_match(struct route_map_rule_list *match_list, + const struct prefix *prefix, route_map_object_t type, + void *object) +{ + enum route_map_cmd_result_t ret = RMAP_NOMATCH; + struct route_map_rule *match; + bool is_matched = false; + + + /* Check all match rule and if there is no match rule, go to the + set statement. */ + if (!match_list->head) + ret = RMAP_MATCH; + else { + for (match = match_list->head; match; match = match->next) { + /* + * Try each match statement. If any match does not + * return RMAP_MATCH or RMAP_NOOP, return. + * Otherwise continue on to next match statement. + * All match statements must MATCH for + * end-result to be a match. + * (Exception:If match stmts result in a mix of + * MATCH/NOOP, then also end-result is a match) + * If all result in NOOP, end-result is NOOP. + */ + ret = (*match->cmd->func_apply)(match->value, prefix, + type, object); + + /* + * If the consolidated result of func_apply is: + * ----------------------------------------------- + * | MATCH | NOMATCH | NOOP | Final Result | + * ------------------------------------------------ + * | yes | yes | yes | NOMATCH | + * | no | no | yes | NOOP | + * | yes | no | yes | MATCH | + * | no | yes | yes | NOMATCH | + * |----------------------------------------------- + * + * Traditionally, all rules within route-map + * should match for it to MATCH. + * If there are noops within the route-map rules, + * it follows the above matrix. + * + * Eg: route-map rm1 permit 10 + * match rule1 + * match rule2 + * match rule3 + * .... + * route-map rm1 permit 20 + * match ruleX + * match ruleY + * ... + */ + + switch (ret) { + case RMAP_MATCH: + is_matched = true; + break; + + case RMAP_NOMATCH: + return ret; + + case RMAP_NOOP: + if (is_matched) + ret = RMAP_MATCH; + break; + + default: + break; + } + + } + } + return ret; +} + /* Apply route map's each index to the object. The matrix for a route-map looks like this: (note, this includes the description for the "NEXT" and "GOTO" frobs now - Match | No Match - | - permit action | cont - | - ------------------+--------------- - | - deny deny | cont - | + | Match | No Match | No op + |-----------|--------------|------- + permit | action | cont | cont. + | | default:deny | default:permit + -------------------+----------------------- + | deny | cont | cont. + deny | | default:deny | default:permit + |-----------|--------------|-------- action) -Apply Set statements, accept route @@ -1604,45 +1694,13 @@ int route_map_delete_set(struct route_map_index *index, const char *set_name, We need to make sure our route-map processing matches the above */ - -static route_map_result_t -route_map_apply_match(struct route_map_rule_list *match_list, - const struct prefix *prefix, route_map_object_t type, - void *object) -{ - route_map_result_t ret = RMAP_NOMATCH; - struct route_map_rule *match; - - - /* Check all match rule and if there is no match rule, go to the - set statement. */ - if (!match_list->head) - ret = RMAP_MATCH; - else { - for (match = match_list->head; match; match = match->next) { - /* Try each match statement in turn, If any do not - return - RMAP_MATCH, return, otherwise continue on to next - match - statement. All match statements must match for - end-result - to be a match. */ - ret = (*match->cmd->func_apply)(match->value, prefix, - type, object); - if (ret != RMAP_MATCH) - return ret; - } - } - return ret; -} - -/* Apply route map to the object. */ route_map_result_t route_map_apply(struct route_map *map, const struct prefix *prefix, route_map_object_t type, void *object) { static int recursion = 0; - int ret = 0; + enum route_map_cmd_result_t match_ret = RMAP_NOMATCH; + route_map_result_t ret = RMAP_PERMITMATCH; struct route_map_index *index; struct route_map_rule *set; char buf[PREFIX_STRLEN]; @@ -1656,7 +1714,7 @@ route_map_result_t route_map_apply(struct route_map *map, return RMAP_DENYMATCH; } - if (map == NULL) { + if (map == NULL || map->head == NULL) { ret = RMAP_DENYMATCH; goto route_map_apply_end; } @@ -1665,29 +1723,64 @@ route_map_result_t route_map_apply(struct route_map *map, for (index = map->head; index; index = index->next) { /* Apply this index. */ index->applied++; - ret = route_map_apply_match(&index->match_list, prefix, type, - object); + match_ret = route_map_apply_match(&index->match_list, prefix, + type, object); if (rmap_debug) { zlog_debug("Route-map: %s, sequence: %d, prefix: %s, result: %s", map->name, index->pref, prefix2str(prefix, buf, sizeof(buf)), - route_map_result_str(ret)); + route_map_cmd_result_str(match_ret)); } /* Now we apply the matrix from above */ - if (ret == RMAP_NOMATCH) - /* 'cont' from matrix - continue to next route-map - * sequence */ + if (match_ret == RMAP_NOOP) + /* + * Do not change the return value. Retain the previous + * return value. Previous values can be: + * 1)permitmatch (if a nomatch was never + * seen before in this route-map.) + * 2)denymatch (if a nomatch was seen earlier in one + * of the previous sequences) + */ + + /* + * 'cont' from matrix - continue to next route-map + * sequence + */ continue; - else if (ret == RMAP_MATCH) { + else if (match_ret == RMAP_NOMATCH) { + + /* + * The return value is now changed to denymatch. + * So from here on out, even if we see more noops, + * we retain this return value and return this + * eventually if there are no matches. + */ + ret = RMAP_DENYMATCH; + + /* + * 'cont' from matrix - continue to next route-map + * sequence + */ + continue; + } else if (match_ret == RMAP_MATCH) { if (index->type == RMAP_PERMIT) /* 'action' */ { + /* Match succeeded, rmap is of type permit */ + ret = RMAP_PERMITMATCH; + /* permit+match must execute sets */ for (set = index->set_list.head; set; set = set->next) - ret = (*set->cmd->func_apply)( + /* + * set cmds return RMAP_OKAY or + * RMAP_ERROR. We do not care if + * set succeeded or not. So, ignore + * return code. + */ + (void) (*set->cmd->func_apply)( set->value, prefix, type, object); @@ -1741,8 +1834,6 @@ route_map_result_t route_map_apply(struct route_map *map, } } } - /* Finally route-map does not match at all. */ - ret = RMAP_DENYMATCH; route_map_apply_end: if (rmap_debug) { diff --git a/lib/routemap.h b/lib/routemap.h index 90df1048ed..f9ad0f64a9 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -38,13 +38,35 @@ DECLARE_MTYPE(ROUTE_MAP_COMPILED) enum route_map_type { RMAP_PERMIT, RMAP_DENY, RMAP_ANY }; typedef enum { - RMAP_MATCH, RMAP_DENYMATCH, - RMAP_NOMATCH, - RMAP_ERROR, - RMAP_OKAY + RMAP_PERMITMATCH } route_map_result_t; +/* + * Route-map match or set result "Eg: match evpn vni xx" + * route-map match cmd always returns match/nomatch/noop + * match--> found a match + * nomatch--> didnt find a match + * noop--> not applicable + * route-map set retuns okay/error + * okay --> set was successful + * error --> set was not successful + */ +enum route_map_cmd_result_t { + /* + * route-map match cmd results + */ + RMAP_MATCH, + RMAP_NOMATCH, + RMAP_NOOP, + /* + * route-map set cmd results + */ + RMAP_OKAY, + RMAP_ERROR +}; + + typedef enum { RMAP_RIP, RMAP_RIPNG, @@ -91,10 +113,10 @@ struct route_map_rule_cmd { const char *str; /* Function for value set or match. */ - route_map_result_t (*func_apply)(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object); + enum route_map_cmd_result_t (*func_apply)(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object); /* Compile argument and return result as void *. */ void *(*func_compile)(const char *); diff --git a/lib/seqlock.c b/lib/seqlock.c index 223d14952c..c05ec19db4 100644 --- a/lib/seqlock.c +++ b/lib/seqlock.c @@ -25,6 +25,7 @@ #include "config.h" #endif +#include <string.h> #include <unistd.h> #include <limits.h> #include <errno.h> @@ -35,44 +36,75 @@ #include "seqlock.h" +/**************************************** + * OS specific synchronization wrappers * + ****************************************/ + +/* + * Linux: sys_futex() + */ #ifdef HAVE_SYNC_LINUX_FUTEX -/* Linux-specific - sys_futex() */ #include <sys/syscall.h> #include <linux/futex.h> -static long sys_futex(void *addr1, int op, int val1, struct timespec *timeout, - void *addr2, int val3) +static long sys_futex(void *addr1, int op, int val1, + const struct timespec *timeout, void *addr2, int val3) { return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3); } #define wait_once(sqlo, val) \ sys_futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0) +#define wait_time(sqlo, val, time, reltime) \ + sys_futex((int *)&sqlo->pos, FUTEX_WAIT_BITSET, (int)val, time, \ + NULL, ~0U) #define wait_poke(sqlo) \ sys_futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0) +/* + * OpenBSD: sys_futex(), almost the same as on Linux + */ #elif defined(HAVE_SYNC_OPENBSD_FUTEX) -/* OpenBSD variant of the above. untested, not upstream in OpenBSD. */ #include <sys/syscall.h> #include <sys/futex.h> +#define TIME_RELATIVE 1 + #define wait_once(sqlo, val) \ futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0) +#define wait_time(sqlo, val, time, reltime) \ + futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, reltime, NULL, 0) #define wait_poke(sqlo) \ futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0) +/* + * FreeBSD: _umtx_op() + */ #elif defined(HAVE_SYNC_UMTX_OP) -/* FreeBSD-specific: umtx_op() */ #include <sys/umtx.h> #define wait_once(sqlo, val) \ _umtx_op((void *)&sqlo->pos, UMTX_OP_WAIT_UINT, val, NULL, NULL) +static int wait_time(struct seqlock *sqlo, uint32_t val, + const struct timespec *abstime, + const struct timespec *reltime) +{ + struct _umtx_time t; + t._flags = UMTX_ABSTIME; + t._clockid = CLOCK_MONOTONIC; + memcpy(&t._timeout, abstime, sizeof(t._timeout)); + return _umtx_op((void *)&sqlo->pos, UMTX_OP_WAIT_UINT, val, + (void *)(uintptr_t) sizeof(t), &t); +} #define wait_poke(sqlo) \ _umtx_op((void *)&sqlo->pos, UMTX_OP_WAKE, INT_MAX, NULL, NULL) -#else -/* generic version. used on *BSD, Solaris and OSX. +/* + * generic version. used on NetBSD, Solaris and OSX. really shitty. */ +#else + +#define TIME_ABS_REALTIME 1 #define wait_init(sqlo) do { \ pthread_mutex_init(&sqlo->lock, NULL); \ @@ -80,6 +112,9 @@ static long sys_futex(void *addr1, int op, int val1, struct timespec *timeout, } while (0) #define wait_prep(sqlo) pthread_mutex_lock(&sqlo->lock) #define wait_once(sqlo, val) pthread_cond_wait(&sqlo->wake, &sqlo->lock) +#define wait_time(sqlo, val, time, reltime) \ + pthread_cond_timedwait(&sqlo->wake, \ + &sqlo->lock, time); #define wait_done(sqlo) pthread_mutex_unlock(&sqlo->lock) #define wait_poke(sqlo) do { \ pthread_mutex_lock(&sqlo->lock); \ @@ -103,18 +138,112 @@ void seqlock_wait(struct seqlock *sqlo, seqlock_val_t val) seqlock_assert_valid(val); wait_prep(sqlo); - while (1) { - cur = atomic_load_explicit(&sqlo->pos, memory_order_acquire); - if (!(cur & 1)) + cur = atomic_load_explicit(&sqlo->pos, memory_order_relaxed); + + while (cur & SEQLOCK_HELD) { + cal = SEQLOCK_VAL(cur) - val - 1; + assert(cal < 0x40000000 || cal > 0xc0000000); + if (cal < 0x80000000) break; - cal = cur - val - 1; + + if ((cur & SEQLOCK_WAITERS) + || atomic_compare_exchange_weak_explicit( + &sqlo->pos, &cur, cur | SEQLOCK_WAITERS, + memory_order_relaxed, memory_order_relaxed)) { + wait_once(sqlo, cur | SEQLOCK_WAITERS); + cur = atomic_load_explicit(&sqlo->pos, + memory_order_relaxed); + } + /* else: we failed to swap in cur because it just changed */ + } + wait_done(sqlo); +} + +bool seqlock_timedwait(struct seqlock *sqlo, seqlock_val_t val, + const struct timespec *abs_monotime_limit) +{ +/* + * ABS_REALTIME - used on NetBSD, Solaris and OSX + */ +#if TIME_ABS_REALTIME +#define time_arg1 &abs_rt +#define time_arg2 NULL +#define time_prep + struct timespec curmono, abs_rt; + + clock_gettime(CLOCK_MONOTONIC, &curmono); + clock_gettime(CLOCK_REALTIME, &abs_rt); + + abs_rt.tv_nsec += abs_monotime_limit->tv_nsec - curmono.tv_nsec; + if (abs_rt.tv_nsec < 0) { + abs_rt.tv_sec--; + abs_rt.tv_nsec += 1000000000; + } else if (abs_rt.tv_nsec >= 1000000000) { + abs_rt.tv_sec++; + abs_rt.tv_nsec -= 1000000000; + } + abs_rt.tv_sec += abs_monotime_limit->tv_sec - curmono.tv_sec; + +/* + * RELATIVE - used on OpenBSD (might get a patch to get absolute monotime) + */ +#elif TIME_RELATIVE + struct timespec reltime; + +#define time_arg1 abs_monotime_limit +#define time_arg2 &reltime +#define time_prep \ + clock_gettime(CLOCK_MONOTONIC, &reltime); \ + reltime.tv_sec = abs_monotime_limit.tv_sec - reltime.tv_sec; \ + reltime.tv_nsec = abs_monotime_limit.tv_nsec - reltime.tv_nsec; \ + if (reltime.tv_nsec < 0) { \ + reltime.tv_sec--; \ + reltime.tv_nsec += 1000000000; \ + } +/* + * FreeBSD & Linux: absolute time re. CLOCK_MONOTONIC + */ +#else +#define time_arg1 abs_monotime_limit +#define time_arg2 NULL +#define time_prep +#endif + + bool ret = true; + seqlock_val_t cur, cal; + + seqlock_assert_valid(val); + + wait_prep(sqlo); + cur = atomic_load_explicit(&sqlo->pos, memory_order_relaxed); + + while (cur & SEQLOCK_HELD) { + cal = SEQLOCK_VAL(cur) - val - 1; assert(cal < 0x40000000 || cal > 0xc0000000); if (cal < 0x80000000) break; - wait_once(sqlo, cur); + if ((cur & SEQLOCK_WAITERS) + || atomic_compare_exchange_weak_explicit( + &sqlo->pos, &cur, cur | SEQLOCK_WAITERS, + memory_order_relaxed, memory_order_relaxed)) { + int rv; + + time_prep + + rv = wait_time(sqlo, cur | SEQLOCK_WAITERS, time_arg1, + time_arg2); + if (rv) { + ret = false; + break; + } + cur = atomic_load_explicit(&sqlo->pos, + memory_order_relaxed); + } } wait_done(sqlo); + + return ret; } bool seqlock_check(struct seqlock *sqlo, seqlock_val_t val) @@ -123,26 +252,32 @@ bool seqlock_check(struct seqlock *sqlo, seqlock_val_t val) seqlock_assert_valid(val); - cur = atomic_load_explicit(&sqlo->pos, memory_order_acquire); - if (!(cur & 1)) + cur = atomic_load_explicit(&sqlo->pos, memory_order_relaxed); + if (!(cur & SEQLOCK_HELD)) return 1; - cur -= val; + cur = SEQLOCK_VAL(cur) - val - 1; assert(cur < 0x40000000 || cur > 0xc0000000); return cur < 0x80000000; } void seqlock_acquire_val(struct seqlock *sqlo, seqlock_val_t val) { + seqlock_val_t prev; + seqlock_assert_valid(val); - atomic_store_explicit(&sqlo->pos, val, memory_order_release); - wait_poke(sqlo); + prev = atomic_exchange_explicit(&sqlo->pos, val, memory_order_relaxed); + if (prev & SEQLOCK_WAITERS) + wait_poke(sqlo); } void seqlock_release(struct seqlock *sqlo) { - atomic_store_explicit(&sqlo->pos, 0, memory_order_release); - wait_poke(sqlo); + seqlock_val_t prev; + + prev = atomic_exchange_explicit(&sqlo->pos, 0, memory_order_relaxed); + if (prev & SEQLOCK_WAITERS) + wait_poke(sqlo); } void seqlock_init(struct seqlock *sqlo) @@ -154,14 +289,23 @@ void seqlock_init(struct seqlock *sqlo) seqlock_val_t seqlock_cur(struct seqlock *sqlo) { - return atomic_load_explicit(&sqlo->pos, memory_order_acquire); + return SEQLOCK_VAL(atomic_load_explicit(&sqlo->pos, + memory_order_relaxed)); } seqlock_val_t seqlock_bump(struct seqlock *sqlo) { - seqlock_val_t val; + seqlock_val_t val, cur; + + cur = atomic_load_explicit(&sqlo->pos, memory_order_relaxed); + seqlock_assert_valid(cur); + + do { + val = SEQLOCK_VAL(cur) + SEQLOCK_INCR; + } while (!atomic_compare_exchange_weak_explicit(&sqlo->pos, &cur, val, + memory_order_relaxed, memory_order_relaxed)); - val = atomic_fetch_add_explicit(&sqlo->pos, 2, memory_order_release); - wait_poke(sqlo); + if (cur & SEQLOCK_WAITERS) + wait_poke(sqlo); return val; } diff --git a/lib/seqlock.h b/lib/seqlock.h index eef05a4307..b551e3ffc4 100644 --- a/lib/seqlock.h +++ b/lib/seqlock.h @@ -54,12 +54,28 @@ */ /* use sequentially increasing "ticket numbers". lowest bit will always - * be 1 to have a 'cleared' indication (i.e., counts 1,3,5,7,etc. ) + * be 1 to have a 'cleared' indication (i.e., counts 1,5,9,13,etc. ) + * 2nd lowest bit is used to indicate we have waiters. */ typedef _Atomic uint32_t seqlock_ctr_t; typedef uint32_t seqlock_val_t; -#define seqlock_assert_valid(val) assert(val & 1) +#define seqlock_assert_valid(val) assert((val) & SEQLOCK_HELD) +/* NB: SEQLOCK_WAITERS is only allowed if SEQLOCK_HELD is also set; can't + * have waiters on an unheld seqlock + */ +#define SEQLOCK_HELD (1U << 0) +#define SEQLOCK_WAITERS (1U << 1) +#define SEQLOCK_VAL(n) ((n) & ~SEQLOCK_WAITERS) +#define SEQLOCK_STARTVAL 1U +#define SEQLOCK_INCR 4U + +/* TODO: originally, this was using "atomic_fetch_add", which is the reason + * bit 0 is used to indicate held state. With SEQLOCK_WAITERS added, there's + * no fetch_add anymore (cmpxchg loop instead), so we don't need to use bit 0 + * for this anymore & can just special-case the value 0 for it and skip it in + * counting. + */ struct seqlock { /* always used */ @@ -74,8 +90,16 @@ struct seqlock { extern void seqlock_init(struct seqlock *sqlo); -/* while (sqlo <= val) - wait until seqlock->pos > val, or seqlock unheld */ +/* basically: "while (sqlo <= val) wait();" + * returns when sqlo > val || !seqlock_held(sqlo) + */ extern void seqlock_wait(struct seqlock *sqlo, seqlock_val_t val); + +/* same, but time-limited (limit is an absolute CLOCK_MONOTONIC value) */ +extern bool seqlock_timedwait(struct seqlock *sqlo, seqlock_val_t val, + const struct timespec *abs_monotime_limit); + +/* one-shot test, returns true if seqlock_wait would return immediately */ extern bool seqlock_check(struct seqlock *sqlo, seqlock_val_t val); static inline bool seqlock_held(struct seqlock *sqlo) @@ -85,12 +109,20 @@ static inline bool seqlock_held(struct seqlock *sqlo) /* sqlo - get seqlock position -- for the "counter" seqlock */ extern seqlock_val_t seqlock_cur(struct seqlock *sqlo); -/* sqlo++ - note: like x++, returns previous value, before bumping */ + +/* ++sqlo (but atomic & wakes waiters) - returns value that we bumped to. + * + * guarantees: + * - each seqlock_bump call bumps the position by exactly one SEQLOCK_INCR. + * There are no skipped/missed or multiple increments. + * - each return value is only returned from one seqlock_bump() call + */ extern seqlock_val_t seqlock_bump(struct seqlock *sqlo); /* sqlo = val - can be used on held seqlock. */ extern void seqlock_acquire_val(struct seqlock *sqlo, seqlock_val_t val); + /* sqlo = ref - standard pattern: acquire relative to other seqlock */ static inline void seqlock_acquire(struct seqlock *sqlo, struct seqlock *ref) { diff --git a/lib/sigevent.c b/lib/sigevent.c index d02b074223..fcd85d0d43 100644 --- a/lib/sigevent.c +++ b/lib/sigevent.c @@ -24,7 +24,6 @@ #include <memory.h> #include <lib_errors.h> -#ifdef SA_SIGINFO #ifdef HAVE_UCONTEXT_H #ifdef GNU_LINUX /* get REG_EIP from ucontext.h */ @@ -34,7 +33,6 @@ #endif /* GNU_LINUX */ #include <ucontext.h> #endif /* HAVE_UCONTEXT_H */ -#endif /* SA_SIGINFO */ /* master signals descriptor struct */ @@ -158,8 +156,6 @@ static int signal_set(int signo) return 0; } -#ifdef SA_SIGINFO - /* XXX This function should be enhanced to support more platforms (it currently works only on Linux/x86). */ static void *program_counter(void *context) @@ -199,41 +195,19 @@ static void *program_counter(void *context) return NULL; } -#endif /* SA_SIGINFO */ - static void __attribute__((noreturn)) -exit_handler(int signo -#ifdef SA_SIGINFO - , - siginfo_t *siginfo, void *context -#endif - ) +exit_handler(int signo, siginfo_t *siginfo, void *context) { -#ifndef SA_SIGINFO - void *siginfo = NULL; - void *pc = NULL; -#else void *pc = program_counter(context); -#endif zlog_signal(signo, "exiting...", siginfo, pc); _exit(128 + signo); } static void __attribute__((noreturn)) -core_handler(int signo -#ifdef SA_SIGINFO - , - siginfo_t *siginfo, void *context -#endif - ) +core_handler(int signo, siginfo_t *siginfo, void *context) { -#ifndef SA_SIGINFO - void *siginfo = NULL; - void *pc = NULL; -#else void *pc = program_counter(context); -#endif /* make sure we don't hang in here. default for SIGALRM is terminate. * - if we're in backtrace for more than a second, abort. */ @@ -290,12 +264,7 @@ static void trap_default_signals(void) static const struct { const int *sigs; unsigned int nsigs; - void (*handler)(int signo -#ifdef SA_SIGINFO - , - siginfo_t *info, void *context -#endif - ); + void (*handler)(int signo, siginfo_t *info, void *context); } sigmap[] = { {core_signals, array_size(core_signals), core_handler}, {exit_signals, array_size(exit_signals), exit_handler}, @@ -316,15 +285,10 @@ static void trap_default_signals(void) act.sa_handler = SIG_IGN; act.sa_flags = 0; } else { -#ifdef SA_SIGINFO /* Request extra arguments to signal * handler. */ act.sa_sigaction = sigmap[i].handler; act.sa_flags = SA_SIGINFO; -#else - act.sa_handler = sigmap[i].handler; - act.sa_flags = 0; -#endif #ifdef SA_RESETHAND /* don't try to print backtraces * recursively */ diff --git a/lib/stream.c b/lib/stream.c index 6c187bd359..c67bc3c993 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -904,20 +904,30 @@ int stream_put_prefix(struct stream *s, struct prefix *p) /* Put NLRI with label */ int stream_put_labeled_prefix(struct stream *s, struct prefix *p, - mpls_label_t *label) + mpls_label_t *label, int addpath_encode, + uint32_t addpath_tx_id) { size_t psize; + size_t psize_with_addpath; uint8_t *label_pnt = (uint8_t *)label; STREAM_VERIFY_SANE(s); psize = PSIZE(p->prefixlen); + psize_with_addpath = psize + (addpath_encode ? 4 : 0); - if (STREAM_WRITEABLE(s) < (psize + 3)) { + if (STREAM_WRITEABLE(s) < (psize_with_addpath + 3)) { STREAM_BOUND_WARN(s, "put"); return 0; } + if (addpath_encode) { + s->data[s->endp++] = (uint8_t)(addpath_tx_id >> 24); + s->data[s->endp++] = (uint8_t)(addpath_tx_id >> 16); + s->data[s->endp++] = (uint8_t)(addpath_tx_id >> 8); + s->data[s->endp++] = (uint8_t)addpath_tx_id; + } + stream_putc(s, (p->prefixlen + 24)); stream_putc(s, label_pnt[0]); stream_putc(s, label_pnt[1]); diff --git a/lib/stream.h b/lib/stream.h index 5341bfa40b..a903ec0ea5 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -199,7 +199,8 @@ extern int stream_put_prefix_addpath(struct stream *, struct prefix *, uint32_t addpath_tx_id); extern int stream_put_prefix(struct stream *, struct prefix *); extern int stream_put_labeled_prefix(struct stream *, struct prefix *, - mpls_label_t *); + mpls_label_t *, int addpath_encode, + uint32_t addpath_tx_id); extern void stream_get(void *, struct stream *, size_t); extern bool stream_get2(void *data, struct stream *s, size_t size); extern void stream_get_from(void *, struct stream *, size_t, size_t); diff --git a/lib/subdir.am b/lib/subdir.am index aa89622028..2be7537bcc 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -21,6 +21,7 @@ lib_libfrr_la_SOURCES = \ lib/distribute.c \ lib/ferr.c \ lib/filter.c \ + lib/frrcu.c \ lib/frrlua.c \ lib/frr_pthread.c \ lib/frrstr.c \ @@ -61,7 +62,6 @@ lib_libfrr_la_SOURCES = \ lib/openbsd-tree.c \ lib/pid_output.c \ lib/plist.c \ - lib/pqueue.c \ lib/prefix.c \ lib/privs.c \ lib/ptm_lib.c \ @@ -95,7 +95,6 @@ lib_libfrr_la_SOURCES = \ lib/yang_translator.c \ lib/yang_wrappers.c \ lib/zclient.c \ - lib/logicalrouter.c \ lib/printf/printf-pos.c \ lib/printf/vfprintf.c \ lib/printf/glue.c \ @@ -113,7 +112,6 @@ vtysh_scan += \ $(top_srcdir)/lib/if.c \ $(top_srcdir)/lib/if_rmap.c \ $(top_srcdir)/lib/keychain.c \ - $(top_srcdir)/lib/logicalrouter.c \ $(top_srcdir)/lib/nexthop_group.c \ $(top_srcdir)/lib/plist.c \ $(top_srcdir)/lib/routemap.c \ @@ -163,6 +161,7 @@ pkginclude_HEADERS += \ lib/frrlua.h \ lib/frr_pthread.h \ lib/frratomic.h \ + lib/frrcu.h \ lib/frrstr.h \ lib/getopt.h \ lib/graph.h \ @@ -200,7 +199,6 @@ pkginclude_HEADERS += \ lib/openbsd-queue.h \ lib/openbsd-tree.h \ lib/plist.h \ - lib/pqueue.h \ lib/prefix.h \ lib/printfrr.h \ lib/privs.h \ @@ -241,7 +239,6 @@ pkginclude_HEADERS += \ lib/zassert.h \ lib/zclient.h \ lib/zebra.h \ - lib/logicalrouter.h \ lib/pbr.h \ # end diff --git a/lib/thread.c b/lib/thread.c index fc2de09df0..943b849ebf 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -25,9 +25,9 @@ #include "thread.h" #include "memory.h" +#include "frrcu.h" #include "log.h" #include "hash.h" -#include "pqueue.h" #include "command.h" #include "sigevent.h" #include "network.h" @@ -42,6 +42,22 @@ DEFINE_MTYPE_STATIC(LIB, THREAD_STATS, "Thread stats") DECLARE_LIST(thread_list, struct thread, threaditem) +static int thread_timer_cmp(const struct thread *a, const struct thread *b) +{ + if (a->u.sands.tv_sec < b->u.sands.tv_sec) + return -1; + if (a->u.sands.tv_sec > b->u.sands.tv_sec) + return 1; + if (a->u.sands.tv_usec < b->u.sands.tv_usec) + return -1; + if (a->u.sands.tv_usec > b->u.sands.tv_usec) + return 1; + return 0; +} + +DECLARE_HEAP(thread_timer_list, struct thread, timeritem, + thread_timer_cmp) + #if defined(__APPLE__) #include <mach/mach.h> #include <mach/mach_time.h> @@ -401,25 +417,6 @@ void thread_cmd_init(void) /* CLI end ------------------------------------------------------------------ */ -static int thread_timer_cmp(void *a, void *b) -{ - struct thread *thread_a = a; - struct thread *thread_b = b; - - if (timercmp(&thread_a->u.sands, &thread_b->u.sands, <)) - return -1; - if (timercmp(&thread_a->u.sands, &thread_b->u.sands, >)) - return 1; - return 0; -} - -static void thread_timer_update(void *node, int actual_position) -{ - struct thread *thread = node; - - thread->index = actual_position; -} - static void cancelreq_del(void *cr) { XFREE(MTYPE_TMP, cr); @@ -464,11 +461,7 @@ struct thread_master *thread_master_create(const char *name) thread_list_init(&rv->event); thread_list_init(&rv->ready); thread_list_init(&rv->unuse); - - /* Initialize the timer queues */ - rv->timer = pqueue_create(); - rv->timer->cmp = thread_timer_cmp; - rv->timer->update = thread_timer_update; + thread_timer_list_init(&rv->timer); /* Initialize thread_fetch() settings */ rv->spin = true; @@ -566,16 +559,6 @@ static void thread_array_free(struct thread_master *m, XFREE(MTYPE_THREAD_POLL, thread_array); } -static void thread_queue_free(struct thread_master *m, struct pqueue *queue) -{ - int i; - - for (i = 0; i < queue->size; i++) - thread_free(m, queue->array[i]); - - pqueue_delete(queue); -} - /* * thread_master_free_unused * @@ -598,6 +581,8 @@ void thread_master_free_unused(struct thread_master *m) /* Stop thread scheduler. */ void thread_master_free(struct thread_master *m) { + struct thread *t; + pthread_mutex_lock(&masters_mtx); { listnode_delete(masters, m); @@ -609,7 +594,8 @@ void thread_master_free(struct thread_master *m) thread_array_free(m, m->read); thread_array_free(m, m->write); - thread_queue_free(m, m->timer); + while ((t = thread_timer_list_pop(&m->timer))) + thread_free(m, t); thread_list_free(m, &m->event); thread_list_free(m, &m->ready); thread_list_free(m, &m->unuse); @@ -683,7 +669,6 @@ static struct thread *thread_get(struct thread_master *m, uint8_t type, thread->add_type = type; thread->master = m; thread->arg = arg; - thread->index = -1; thread->yield = THREAD_YIELD_TIME_SLOT; /* default */ thread->ref = NULL; @@ -729,7 +714,7 @@ static int fd_poll(struct thread_master *m, struct pollfd *pfds, nfds_t pfdsize, { /* If timer_wait is null here, that means poll() should block * indefinitely, - * unless the thread_master has overriden it by setting + * unless the thread_master has overridden it by setting * ->selectpoll_timeout. * If the value is positive, it specifies the maximum number of * milliseconds @@ -753,6 +738,9 @@ static int fd_poll(struct thread_master *m, struct pollfd *pfds, nfds_t pfdsize, < 0) // effect a poll (return immediately) timeout = 0; + rcu_read_unlock(); + rcu_assert_read_unlocked(); + /* add poll pipe poker */ assert(count + 1 < pfdsize); pfds[count].fd = m->io_pipe[0]; @@ -766,6 +754,8 @@ static int fd_poll(struct thread_master *m, struct pollfd *pfds, nfds_t pfdsize, while (read(m->io_pipe[0], &trash, sizeof(trash)) > 0) ; + rcu_read_lock(); + return num; } @@ -854,7 +844,6 @@ funcname_thread_add_timer_timeval(struct thread_master *m, struct thread **t_ptr, debugargdef) { struct thread *thread; - struct pqueue *queue; assert(m != NULL); @@ -870,7 +859,6 @@ funcname_thread_add_timer_timeval(struct thread_master *m, return NULL; } - queue = m->timer; thread = thread_get(m, type, func, arg, debugargpass); pthread_mutex_lock(&thread->mtx); @@ -878,7 +866,7 @@ funcname_thread_add_timer_timeval(struct thread_master *m, monotime(&thread->u.sands); timeradd(&thread->u.sands, time_relative, &thread->u.sands); - pqueue_enqueue(thread, queue); + thread_timer_list_add(&m->timer, thread); if (t_ptr) { *t_ptr = thread; thread->ref = t_ptr; @@ -1055,7 +1043,6 @@ static void thread_cancel_rw(struct thread_master *master, int fd, short state) static void do_thread_cancel(struct thread_master *master) { struct thread_list_head *list = NULL; - struct pqueue *queue = NULL; struct thread **thread_array = NULL; struct thread *thread; @@ -1111,7 +1098,7 @@ static void do_thread_cancel(struct thread_master *master) thread_array = master->write; break; case THREAD_TIMER: - queue = master->timer; + thread_timer_list_del(&master->timer, thread); break; case THREAD_EVENT: list = &master->event; @@ -1124,16 +1111,10 @@ static void do_thread_cancel(struct thread_master *master) break; } - if (queue) { - assert(thread->index >= 0); - assert(thread == queue->array[thread->index]); - pqueue_remove_at(thread->index, queue); - } else if (list) { + if (list) { thread_list_del(list, thread); } else if (thread_array) { thread_array[thread->u.fd] = NULL; - } else { - assert(!"Thread should be either in queue or list or array!"); } if (thread->ref) @@ -1251,15 +1232,15 @@ void thread_cancel_async(struct thread_master *master, struct thread **thread, } /* ------------------------------------------------------------------------- */ -static struct timeval *thread_timer_wait(struct pqueue *queue, +static struct timeval *thread_timer_wait(struct thread_timer_list_head *timers, struct timeval *timer_val) { - if (queue->size) { - struct thread *next_timer = queue->array[0]; - monotime_until(&next_timer->u.sands, timer_val); - return timer_val; - } - return NULL; + if (!thread_timer_list_count(timers)) + return NULL; + + struct thread *next_timer = thread_timer_list_first(timers); + monotime_until(&next_timer->u.sands, timer_val); + return timer_val; } static struct thread *thread_run(struct thread_master *m, struct thread *thread, @@ -1369,17 +1350,16 @@ static void thread_process_io(struct thread_master *m, unsigned int num) } /* Add all timers that have popped to the ready list. */ -static unsigned int thread_process_timers(struct pqueue *queue, +static unsigned int thread_process_timers(struct thread_timer_list_head *timers, struct timeval *timenow) { struct thread *thread; unsigned int ready = 0; - while (queue->size) { - thread = queue->array[0]; + while ((thread = thread_timer_list_first(timers))) { if (timercmp(timenow, &thread->u.sands, <)) return ready; - pqueue_dequeue(queue); + thread_timer_list_pop(timers); thread->type = THREAD_READY; thread_list_add_tail(&thread->master->ready, thread); ready++; @@ -1461,7 +1441,7 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch) * once per loop to avoid starvation by events */ if (!thread_list_count(&m->ready)) - tw = thread_timer_wait(m->timer, &tv); + tw = thread_timer_wait(&m->timer, &tv); if (thread_list_count(&m->ready) || (tw && !timercmp(tw, &zerotime, >))) @@ -1506,7 +1486,7 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch) /* Post timers to ready queue. */ monotime(&now); - thread_process_timers(m->timer, &now); + thread_process_timers(&m->timer, &now); /* Post I/O to ready queue. */ if (num > 0) diff --git a/lib/thread.h b/lib/thread.h index 7897265120..412a4d93bf 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -41,8 +41,7 @@ struct rusage_t { #define GETRUSAGE(X) thread_getrusage(X) PREDECL_LIST(thread_list) - -struct pqueue; +PREDECL_HEAP(thread_timer_list) struct fd_handler { /* number of pfd that fit in the allocated space of pfds. This is a @@ -73,7 +72,7 @@ struct thread_master { struct thread **read; struct thread **write; - struct pqueue *timer; + struct thread_timer_list_head timer; struct thread_list_head event, ready, unuse; struct list *cancel_req; bool canceled; @@ -95,6 +94,7 @@ struct thread { uint8_t type; /* thread type */ uint8_t add_type; /* thread type */ struct thread_list_item threaditem; + struct thread_timer_list_item timeritem; struct thread **ref; /* external reference (if given) */ struct thread_master *master; /* pointer to the struct thread_master */ int (*func)(struct thread *); /* event function */ @@ -104,7 +104,6 @@ struct thread { int fd; /* file descriptor in case of r/w */ struct timeval sands; /* rest of time sands value. */ } u; - int index; /* queue position for timers */ struct timeval real; struct cpu_thread_history *hist; /* cache pointer to cpu_history */ unsigned long yield; /* yield time in microseconds */ diff --git a/lib/typerb.c b/lib/typerb.c index 4c48d6434f..3886fc678e 100644 --- a/lib/typerb.c +++ b/lib/typerb.c @@ -333,9 +333,10 @@ color: return (old); } -void typed_rb_remove(struct rbt_tree *rbt, struct rb_entry *rbe) +struct typed_rb_entry *typed_rb_remove(struct rbt_tree *rbt, + struct rb_entry *rbe) { - rbe_remove(rbt, rbe); + return rbe_remove(rbt, rbe); } struct typed_rb_entry *typed_rb_insert(struct rbt_tree *rbt, diff --git a/lib/typerb.h b/lib/typerb.h index ce8446f853..2d7b0ba637 100644 --- a/lib/typerb.h +++ b/lib/typerb.h @@ -38,29 +38,30 @@ struct typed_rb_root { size_t count; }; -struct typed_rb_entry *typed_rb_insert(struct typed_rb_root *, +struct typed_rb_entry *typed_rb_insert(struct typed_rb_root *rbt, struct typed_rb_entry *rbe, int (*cmpfn)( const struct typed_rb_entry *a, const struct typed_rb_entry *b)); -void typed_rb_remove(struct typed_rb_root *, struct typed_rb_entry *rbe); -struct typed_rb_entry *typed_rb_find(struct typed_rb_root *, +struct typed_rb_entry *typed_rb_remove(struct typed_rb_root *rbt, + struct typed_rb_entry *rbe); +struct typed_rb_entry *typed_rb_find(struct typed_rb_root *rbt, const struct typed_rb_entry *rbe, int (*cmpfn)( const struct typed_rb_entry *a, const struct typed_rb_entry *b)); -struct typed_rb_entry *typed_rb_find_gteq(struct typed_rb_root *, +struct typed_rb_entry *typed_rb_find_gteq(struct typed_rb_root *rbt, const struct typed_rb_entry *rbe, int (*cmpfn)( const struct typed_rb_entry *a, const struct typed_rb_entry *b)); -struct typed_rb_entry *typed_rb_find_lt(struct typed_rb_root *, +struct typed_rb_entry *typed_rb_find_lt(struct typed_rb_root *rbt, const struct typed_rb_entry *rbe, int (*cmpfn)( const struct typed_rb_entry *a, const struct typed_rb_entry *b)); -struct typed_rb_entry *typed_rb_min(struct typed_rb_root *); -struct typed_rb_entry *typed_rb_next(struct typed_rb_entry *); +struct typed_rb_entry *typed_rb_min(struct typed_rb_root *rbt); +struct typed_rb_entry *typed_rb_next(struct typed_rb_entry *rbe); #define _PREDECL_RBTREE(prefix) \ struct prefix ## _head { struct typed_rb_root rr; }; \ @@ -99,9 +100,11 @@ macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \ re = typed_rb_find_lt(&h->rr, &item->field.re, cmpfn_nuq); \ return container_of_null(re, type, field.re); \ } \ -macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ { \ - typed_rb_remove(&h->rr, &item->field.re); \ + struct typed_rb_entry *re; \ + re = typed_rb_remove(&h->rr, &item->field.re); \ + return container_of_null(re, type, field.re); \ } \ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ { \ @@ -130,7 +133,7 @@ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ re = item ? typed_rb_next(&item->field.re) : NULL; \ return container_of_null(re, type, field.re); \ } \ -macro_pure size_t prefix ## _count(struct prefix##_head *h) \ +macro_pure size_t prefix ## _count(const struct prefix##_head *h) \ { \ return h->rr.count; \ } \ diff --git a/lib/typesafe.c b/lib/typesafe.c index f2ca67b7c6..7e5939d5b3 100644 --- a/lib/typesafe.c +++ b/lib/typesafe.c @@ -341,13 +341,14 @@ struct sskip_item *typesafe_skiplist_find_lt(struct sskip_head *head, return best; } -void typesafe_skiplist_del(struct sskip_head *head, struct sskip_item *item, - int (*cmpfn)(const struct sskip_item *a, - const struct sskip_item *b)) +struct sskip_item *typesafe_skiplist_del( + struct sskip_head *head, struct sskip_item *item, + int (*cmpfn)(const struct sskip_item *a, const struct sskip_item *b)) { size_t level = SKIPLIST_MAXDEPTH; struct sskip_item *prev = &head->hitem, *next; int cmpval; + bool found = false; while (level) { next = sl_level_get(prev, level - 1); @@ -359,6 +360,7 @@ void typesafe_skiplist_del(struct sskip_head *head, struct sskip_item *item, sl_level_set(prev, level - 1, sl_level_get(item, level - 1)); level--; + found = true; continue; } cmpval = cmpfn(next, item); @@ -369,6 +371,9 @@ void typesafe_skiplist_del(struct sskip_head *head, struct sskip_item *item, level--; } + if (!found) + return NULL; + /* TBD: assert when trying to remove non-existing item? */ head->count--; @@ -379,6 +384,8 @@ void typesafe_skiplist_del(struct sskip_head *head, struct sskip_item *item, XFREE(MTYPE_SKIPLIST_OFLOW, oflow); } memset(item, 0, sizeof(*item)); + + return item; } struct sskip_item *typesafe_skiplist_pop(struct sskip_head *head) diff --git a/lib/typesafe.h b/lib/typesafe.h index 0a4ed69e4e..c30d73d1b3 100644 --- a/lib/typesafe.h +++ b/lib/typesafe.h @@ -109,17 +109,18 @@ macro_inline void prefix ## _add_after(struct prefix##_head *h, \ typesafe_list_add(&h->sh, nextp, &item->field.si); \ } \ /* TODO: del_hint */ \ -macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ { \ struct slist_item **iter = &h->sh.first; \ while (*iter && *iter != &item->field.si) \ iter = &(*iter)->next; \ if (!*iter) \ - return; \ + return NULL; \ h->sh.count--; \ *iter = item->field.si.next; \ if (!item->field.si.next) \ h->sh.last_next = iter; \ + return item; \ } \ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ { \ @@ -149,7 +150,7 @@ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ sitem = &item->field.si; \ return container_of_null(sitem->next, type, field.si); \ } \ -macro_pure size_t prefix ## _count(struct prefix##_head *h) \ +macro_pure size_t prefix ## _count(const struct prefix##_head *h) \ { \ return h->sh.count; \ } \ @@ -212,13 +213,14 @@ macro_inline void prefix ## _add_after(struct prefix##_head *h, \ prev = after ? &after->field.di : &h->dh.hitem; \ typesafe_dlist_add(&h->dh, prev, &item->field.di); \ } \ -macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ { \ struct dlist_item *ditem = &item->field.di; \ ditem->prev->next = ditem->next; \ ditem->next->prev = ditem->prev; \ h->dh.count--; \ ditem->prev = ditem->next = NULL; \ + return item; \ } \ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ { \ @@ -250,7 +252,7 @@ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ return NULL; \ return prefix ## _next(h, item); \ } \ -macro_pure size_t prefix ## _count(struct prefix##_head *h) \ +macro_pure size_t prefix ## _count(const struct prefix##_head *h) \ { \ return h->dh.count; \ } \ @@ -308,7 +310,7 @@ macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \ h->hh.count++; \ return NULL; \ } \ -macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ { \ struct heap_item *other; \ uint32_t index = item->field.hi.index; \ @@ -321,6 +323,7 @@ macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ typesafe_heap_pushdown(&h->hh, index, other, prefix ## __cmp); \ if (HEAP_RESIZE_TRESH_DN(h)) \ typesafe_heap_resize(&h->hh, false); \ + return item; \ } \ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ { \ @@ -354,7 +357,7 @@ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ return NULL; \ return prefix ## _next(h, item); \ } \ -macro_pure size_t prefix ## _count(struct prefix##_head *h) \ +macro_pure size_t prefix ## _count(const struct prefix##_head *h) \ { \ return h->hh.count; \ } \ @@ -434,7 +437,7 @@ macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \ struct ssort_item *sitem = h->sh.first; \ int cmpval = 0; \ while (sitem && (cmpval = cmpfn_nuq( \ - container_of(sitem, type, field.si), item) < 0)) \ + container_of(sitem, type, field.si), item)) < 0) \ sitem = sitem->next; \ return container_of_null(sitem, type, field.si); \ } \ @@ -444,20 +447,21 @@ macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \ struct ssort_item *prev = NULL, *sitem = h->sh.first; \ int cmpval = 0; \ while (sitem && (cmpval = cmpfn_nuq( \ - container_of(sitem, type, field.si), item) < 0)) \ + container_of(sitem, type, field.si), item)) < 0) \ sitem = (prev = sitem)->next; \ return container_of_null(prev, type, field.si); \ } \ /* TODO: del_hint */ \ -macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ { \ struct ssort_item **iter = &h->sh.first; \ while (*iter && *iter != &item->field.si) \ iter = &(*iter)->next; \ if (!*iter) \ - return; \ + return NULL; \ h->sh.count--; \ *iter = item->field.si.next; \ + return item; \ } \ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ { \ @@ -485,7 +489,7 @@ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ sitem = &item->field.si; \ return container_of_null(sitem->next, type, field.si); \ } \ -macro_pure size_t prefix ## _count(struct prefix##_head *h) \ +macro_pure size_t prefix ## _count(const struct prefix##_head *h) \ { \ return h->sh.count; \ } \ @@ -499,7 +503,7 @@ macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ struct ssort_item *sitem = h->sh.first; \ int cmpval = 0; \ while (sitem && (cmpval = cmpfn( \ - container_of(sitem, type, field.si), item) < 0)) \ + container_of(sitem, type, field.si), item)) < 0) \ sitem = sitem->next; \ if (!sitem || cmpval > 0) \ return NULL; \ @@ -617,10 +621,10 @@ macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ } \ return NULL; \ } \ -macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ { \ if (!h->hh.tabshift) \ - return; \ + return NULL; \ uint32_t hval = item->field.hi.hashval, hbits = HASH_KEY(h->hh, hval); \ struct thash_item **np = &h->hh.entries[hbits]; \ while (*np && (*np)->hashval < hval) \ @@ -628,12 +632,13 @@ macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ while (*np && *np != &item->field.hi && (*np)->hashval == hval) \ np = &(*np)->next; \ if (*np != &item->field.hi) \ - return; \ + return NULL; \ *np = item->field.hi.next; \ item->field.hi.next = NULL; \ h->hh.count--; \ if (HASH_SHRINK_THRESHOLD(h->hh)) \ typesafe_hash_shrink(&h->hh); \ + return item; \ } \ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ { \ @@ -675,7 +680,7 @@ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ return NULL; \ return prefix ## _next(h, item); \ } \ -macro_pure size_t prefix ## _count(struct prefix##_head *h) \ +macro_pure size_t prefix ## _count(const struct prefix##_head *h) \ { \ return h->hh.count; \ } \ @@ -751,9 +756,11 @@ macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \ &item->field.si, cmpfn_nuq); \ return container_of_null(sitem, type, field.si); \ } \ -macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ { \ - typesafe_skiplist_del(&h->sh, &item->field.si, cmpfn_uq); \ + struct sskip_item *sitem = typesafe_skiplist_del(&h->sh, \ + &item->field.si, cmpfn_uq); \ + return container_of_null(sitem, type, field.si); \ } \ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ { \ @@ -776,7 +783,7 @@ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ next = item ? item->field.si.next[0] : NULL; \ return container_of_null(next, type, field.si); \ } \ -macro_pure size_t prefix ## _count(struct prefix##_head *h) \ +macro_pure size_t prefix ## _count(const struct prefix##_head *h) \ { \ return h->sh.count; \ } \ @@ -848,8 +855,8 @@ extern struct sskip_item *typesafe_skiplist_find_lt(struct sskip_head *head, const struct sskip_item *item, int (*cmpfn)( const struct sskip_item *a, const struct sskip_item *b)); -extern void typesafe_skiplist_del(struct sskip_head *head, - struct sskip_item *item, int (*cmpfn)( +extern struct sskip_item *typesafe_skiplist_del( + struct sskip_head *head, struct sskip_item *item, int (*cmpfn)( const struct sskip_item *a, const struct sskip_item *b)); extern struct sskip_item *typesafe_skiplist_pop(struct sskip_head *head); @@ -337,7 +337,8 @@ void vty_hello(struct vty *vty) /* work backwards to ignore trailling isspace() */ for (s = buf + strlen(buf); - (s > buf) && isspace((int)*(s - 1)); s--) + (s > buf) && isspace((unsigned char)s[-1]); + s--) ; *s = '\0'; vty_out(vty, "%s\n", buf); @@ -468,7 +469,7 @@ static int vty_command(struct vty *vty, char *buf) cp = buf; if (cp != NULL) { /* Skip white spaces. */ - while (isspace((int)*cp) && *cp != '\0') + while (isspace((unsigned char)*cp) && *cp != '\0') cp++; } if (cp != NULL && *cp != '\0') { @@ -892,7 +893,7 @@ static void vty_complete_command(struct vty *vty) return; /* In case of 'help \t'. */ - if (isspace((int)vty->buf[vty->length - 1])) + if (isspace((unsigned char)vty->buf[vty->length - 1])) vector_set(vline, NULL); matched = cmd_complete_command(vline, vty, &ret); @@ -1006,7 +1007,7 @@ static void vty_describe_command(struct vty *vty) if (vline == NULL) { vline = vector_init(1); vector_set(vline, NULL); - } else if (isspace((int)vty->buf[vty->length - 1])) + } else if (isspace((unsigned char)vty->buf[vty->length - 1])) vector_set(vline, NULL); describe = cmd_describe_command(vline, vty, &ret); diff --git a/lib/zebra.h b/lib/zebra.h index 22239f8e60..789a93a3c4 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -134,6 +134,10 @@ typedef unsigned char uint8_t; #endif #endif +#ifdef CRYPTO_OPENSSL +#include <openssl/evp.h> +#endif + #include "openbsd-tree.h" #include <netinet/in.h> @@ -356,6 +360,7 @@ typedef enum { /* Subsequent Address Family Identifier. */ typedef enum { + SAFI_UNSPEC = 0, SAFI_UNICAST = 1, SAFI_MULTICAST = 2, SAFI_MPLS_VPN = 3, |
