diff options
35 files changed, 1496 insertions, 218 deletions
diff --git a/.gitignore b/.gitignore index 4c8370375d..fbbb04b60c 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,10 @@ *.pb-c.c *.pb.cc *_clippy.c +*.bc +*.cg.json +*.cg.dot +*.cg.svg ### gcov outputs diff --git a/Makefile.am b/Makefile.am index 1e3311fa7b..a959fd9e5a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -89,9 +89,11 @@ clippy-only: Makefile lib/clippy config.h #AUTODERP# endif EXTRA_DIST = +EXTRA_PROGRAMS = BUILT_SOURCES = CLEANFILES = DISTCLEANFILES = +SUFFIXES = examplesdir = $(exampledir) @@ -231,12 +233,48 @@ EXTRA_DIST += \ vrrpd/Makefile \ # end -clean-local: clean-python -.PHONY: clean-python +AM_V_LLVM_BC = $(am__v_LLVM_BC_$(V)) +am__v_LLVM_BC_ = $(am__v_LLVM_BC_$(AM_DEFAULT_VERBOSITY)) +am__v_LLVM_BC_0 = @echo " LLVM.BC " $@; +am__v_LLVM_BC_1 = + +AM_V_LLVM_LD = $(am__v_LLVM_LD_$(V)) +am__v_LLVM_LD_ = $(am__v_LLVM_LD_$(AM_DEFAULT_VERBOSITY)) +am__v_LLVM_LD_0 = @echo " LLVM.LD " $@; +am__v_LLVM_LD_1 = + +SUFFIXES += .lo.bc .o.bc + +.o.o.bc: + $(AM_V_LLVM_BC)$(COMPILE) -emit-llvm -c -o $@ $(patsubst %.o,%.c,$<) +.lo.lo.bc: + $(AM_V_LLVM_BC)$(COMPILE) -emit-llvm -c -o $@ $(patsubst %.lo,%.c,$<) + +%.cg.json: %.bc tools/frr-llvm-cg + tools/frr-llvm-cg -o $@ $< +%.cg.dot: %.cg.json + $(PYTHON) $(top_srcdir)/python/callgraph-dot.py $< $@ +%.cg.svg: %.cg.dot + @echo if the following command fails, you need to install graphviz. + @echo also, the output is nondeterministic. run it multiple times and use the nicest output. + @echo tuning parameters may yield nicer looking graphs as well. + fdp -GK=0.7 -Gstart=42231337 -Gmaxiter=2000 -Elen=2 -Gnodesep=1.5 -Tsvg -o$@ $< +# don't delete intermediaries +.PRECIOUS: %.cg.json %.cg.dot + +# <lib>.la.bc, <lib>.a.bc and <daemon>.bc targets are generated by +# python/makefile.py +LLVM_LINK = llvm-link-$(llvm_version) + +clean-local: clean-python clean-llvm-bitcode +.PHONY: clean-python clean-llvm-bitcode clean-python: find . -name __pycache__ -o -name .pytest_cache | xargs rm -rf find . -name "*.pyc" -o -name "*_clippy.c" | xargs rm -f +clean-llvm-bitcode: + find . -name "*.bc" -o -name "*.cg.json" -o -name "*.cg.dot" -o -name "*.cg.svg" | xargs rm -f + redistclean: $(MAKE) distclean CONFIG_CLEAN_FILES="$(filter-out $(EXTRA_DIST), $(CONFIG_CLEAN_FILES))" diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index b7e2f45195..d8566fed9f 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -1405,9 +1405,10 @@ static int bgp_attr_aspath(struct bgp_attr_parser_args *args) /* Codification of AS 0 Processing */ if (aspath_check_as_zero(attr->aspath)) { - flog_err(EC_BGP_ATTR_MAL_AS_PATH, - "Malformed AS path, contains BGP_AS_ZERO(0) from %s", - peer->host); + flog_err( + EC_BGP_ATTR_MAL_AS_PATH, + "Malformed AS path, AS number is 0 in the path from %s", + peer->host); return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_MAL_AS_PATH, 0); } @@ -1485,9 +1486,10 @@ static int bgp_attr_as4_path(struct bgp_attr_parser_args *args, /* Codification of AS 0 Processing */ if (aspath_check_as_zero(*as4_path)) { - flog_err(EC_BGP_ATTR_MAL_AS_PATH, - "Malformed AS4 path, contains BGP_AS_ZERO(0) from %s", - peer->host); + flog_err( + EC_BGP_ATTR_MAL_AS_PATH, + "Malformed AS path, AS number is 0 in the path from %s", + peer->host); return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_MAL_AS_PATH, 0); } @@ -1667,13 +1669,10 @@ static int bgp_attr_aggregator(struct bgp_attr_parser_args *args) attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR); /* Codification of AS 0 Processing */ - if (aggregator_as == BGP_AS_ZERO) { + if (aggregator_as == BGP_AS_ZERO) flog_err(EC_BGP_ATTR_LEN, "AGGREGATOR AS number is 0 for aspath: %s", aspath_print(attr->aspath)); - return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_MAL_AS_PATH, - args->total); - } return BGP_ATTR_PARSE_PROCEED; } @@ -1703,13 +1702,10 @@ bgp_attr_as4_aggregator(struct bgp_attr_parser_args *args, attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AS4_AGGREGATOR); /* Codification of AS 0 Processing */ - if (aggregator_as == BGP_AS_ZERO) { + if (aggregator_as == BGP_AS_ZERO) flog_err(EC_BGP_ATTR_LEN, "AS4_AGGREGATOR AS number is 0 for aspath: %s", aspath_print(attr->aspath)); - return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_MAL_AS_PATH, - 0); - } return BGP_ATTR_PARSE_PROCEED; } diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c index 30de84c878..0d60fbf479 100644 --- a/bgpd/bgp_community.c +++ b/bgpd/bgp_community.c @@ -40,6 +40,9 @@ static struct community *community_new(void) /* Free communities value. */ void community_free(struct community **com) { + if (!(*com)) + return; + XFREE(MTYPE_COMMUNITY_VAL, (*com)->val); XFREE(MTYPE_COMMUNITY_STR, (*com)->str); diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 062a6477fa..d13da74b04 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -59,6 +59,9 @@ void ecommunity_strfree(char **s) /* Allocate ecommunities. */ void ecommunity_free(struct ecommunity **ecom) { + if (!(*ecom)) + return; + XFREE(MTYPE_ECOMMUNITY_VAL, (*ecom)->val); XFREE(MTYPE_ECOMMUNITY_STR, (*ecom)->str); XFREE(MTYPE_ECOMMUNITY, *ecom); diff --git a/bgpd/bgp_lcommunity.c b/bgpd/bgp_lcommunity.c index f47ae91663..5900fcf862 100644 --- a/bgpd/bgp_lcommunity.c +++ b/bgpd/bgp_lcommunity.c @@ -44,6 +44,9 @@ static struct lcommunity *lcommunity_new(void) /* Allocate lcommunities. */ void lcommunity_free(struct lcommunity **lcom) { + if (!(*lcom)) + return; + XFREE(MTYPE_LCOMMUNITY_VAL, (*lcom)->val); XFREE(MTYPE_LCOMMUNITY_STR, (*lcom)->str); if ((*lcom)->json) diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 0f09369860..8266b76111 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -2987,8 +2987,7 @@ DEFUN (no_bgp_bestpath_med, /* "bgp bestpath bandwidth" configuration. */ DEFPY (bgp_bestpath_bw, bgp_bestpath_bw_cmd, - "[no$no] bgp bestpath bandwidth [<ignore|skip-missing|default-weight-for-missing>$bw_cfg]", - NO_STR + "bgp bestpath bandwidth <ignore|skip-missing|default-weight-for-missing>$bw_cfg", "BGP specific commands\n" "Change the default bestpath selection\n" "Link Bandwidth attribute\n" @@ -3000,22 +2999,18 @@ DEFPY (bgp_bestpath_bw, afi_t afi; safi_t safi; - if (no) { - bgp->lb_handling = BGP_LINK_BW_ECMP; - } else { - if (!bw_cfg) { - vty_out(vty, "%% Bandwidth configuration must be specified\n"); - return CMD_ERR_INCOMPLETE; - } - if (!strcmp(bw_cfg, "ignore")) - bgp->lb_handling = BGP_LINK_BW_IGNORE_BW; - else if (!strcmp(bw_cfg, "skip-missing")) - bgp->lb_handling = BGP_LINK_BW_SKIP_MISSING; - else if (!strcmp(bw_cfg, "default-weight-for-missing")) - bgp->lb_handling = BGP_LINK_BW_DEFWT_4_MISSING; - else - return CMD_ERR_NO_MATCH; + if (!bw_cfg) { + vty_out(vty, "%% Bandwidth configuration must be specified\n"); + return CMD_ERR_INCOMPLETE; } + if (!strcmp(bw_cfg, "ignore")) + bgp->lb_handling = BGP_LINK_BW_IGNORE_BW; + else if (!strcmp(bw_cfg, "skip-missing")) + bgp->lb_handling = BGP_LINK_BW_SKIP_MISSING; + else if (!strcmp(bw_cfg, "default-weight-for-missing")) + bgp->lb_handling = BGP_LINK_BW_DEFWT_4_MISSING; + else + return CMD_ERR_NO_MATCH; /* This config is used in route install, so redo that. */ FOREACH_AFI_SAFI (afi, safi) { @@ -3027,6 +3022,32 @@ DEFPY (bgp_bestpath_bw, return CMD_SUCCESS; } +DEFPY (no_bgp_bestpath_bw, + no_bgp_bestpath_bw_cmd, + "no bgp bestpath bandwidth [<ignore|skip-missing|default-weight-for-missing>$bw_cfg]", + NO_STR + "BGP specific commands\n" + "Change the default bestpath selection\n" + "Link Bandwidth attribute\n" + "Ignore link bandwidth (i.e., do regular ECMP, not weighted)\n" + "Ignore paths without link bandwidth for ECMP (if other paths have it)\n" + "Assign a low default weight (value 1) to paths not having link bandwidth\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + afi_t afi; + safi_t safi; + + bgp->lb_handling = BGP_LINK_BW_ECMP; + + /* This config is used in route install, so redo that. */ + FOREACH_AFI_SAFI (afi, safi) { + if (!bgp_fibupd_safi(safi)) + continue; + bgp_zebra_announce_table(bgp, afi, safi); + } + return CMD_SUCCESS; +} + /* "no bgp default ipv4-unicast". */ DEFUN (no_bgp_default_ipv4_unicast, no_bgp_default_ipv4_unicast_cmd, @@ -15757,6 +15778,7 @@ void bgp_vty_init(void) /* "bgp bestpath bandwidth" commands */ install_element(BGP_NODE, &bgp_bestpath_bw_cmd); + install_element(BGP_NODE, &no_bgp_bestpath_bw_cmd); /* "no bgp default ipv4-unicast" commands. */ install_element(BGP_NODE, &no_bgp_default_ipv4_unicast_cmd); diff --git a/grpc/subdir.am b/grpc/subdir.am index 048e12a024..045848aee7 100644 --- a/grpc/subdir.am +++ b/grpc/subdir.am @@ -26,6 +26,8 @@ am__v_PROTOC_ = $(am__v_PROTOC_$(AM_DEFAULT_VERBOSITY)) am__v_PROTOC_0 = @echo " PROTOC" $@; am__v_PROTOC_1 = +SUFFIXES += .pb.h .pb.cc .grpc.pb.cc + .proto.pb.cc: $(AM_V_PROTOC)$(PROTOC) -I$(top_srcdir) --cpp_out=$(top_srcdir) $(top_srcdir)/$^ .proto.grpc.pb.cc: diff --git a/ldpd/neighbor.c b/ldpd/neighbor.c index ae51490c07..1aa53151e6 100644 --- a/ldpd/neighbor.c +++ b/ldpd/neighbor.c @@ -619,6 +619,16 @@ nbr_establish_connection(struct nbr *nbr) #endif } + if (nbr->af == AF_INET) { + if (sock_set_ipv4_tos(nbr->fd, IPTOS_PREC_INTERNETCONTROL) == -1) + log_warn("%s: lsr-id %s, sock_set_ipv4_tos error", + __func__, inet_ntoa(nbr->id)); + } else if (nbr->af == AF_INET6) { + if (sock_set_ipv6_dscp(nbr->fd, IPTOS_PREC_INTERNETCONTROL) == -1) + log_warn("%s: lsr-id %s, sock_set_ipv6_dscp error", + __func__, inet_ntoa(nbr->id)); + } + addr2sa(nbr->af, &nbr->laddr, 0, &local_su); addr2sa(nbr->af, &nbr->raddr, LDP_PORT, &remote_su); if (nbr->af == AF_INET6 && nbr->raddr_scope) diff --git a/ldpd/subdir.am b/ldpd/subdir.am index 09936e5c28..0b38c37872 100644 --- a/ldpd/subdir.am +++ b/ldpd/subdir.am @@ -28,7 +28,6 @@ ldpd_libldp_a_SOURCES = \ ldpd/ldp_vty_conf.c \ ldpd/ldp_vty_exec.c \ ldpd/ldp_zebra.c \ - ldpd/ldpd.c \ ldpd/ldpe.c \ ldpd/log.c \ ldpd/logmsg.c \ diff --git a/lib/hook.h b/lib/hook.h index 3823cebe6a..bef5351e90 100644 --- a/lib/hook.h +++ b/lib/hook.h @@ -145,7 +145,7 @@ extern void _hook_register(struct hook *hook, struct hookent *stackent, */ #define _hook_reg_svar(hook, funcptr, arg, has_arg, module, funcname, prio) \ do { \ - static struct hookent stack_hookent = { .ent_on_heap = 0, }; \ + static struct hookent stack_hookent = {}; \ _hook_register(hook, &stack_hookent, funcptr, arg, has_arg, \ module, funcname, prio); \ } while (0) diff --git a/lib/subdir.am b/lib/subdir.am index 2f8cbe5d52..b2f3e7c5de 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -415,7 +415,7 @@ am__v_CLIPPY_1 = CLIPPY_DEPS = $(CLIPPY) $(top_srcdir)/python/clidef.py -SUFFIXES = _clippy.c .proto .pb-c.c .pb-c.h .pb.h .pb.cc .grpc.pb.cc +SUFFIXES += _clippy.c .c_clippy.c: $(AM_V_CLIPPY) $(CLIPPY) $(top_srcdir)/python/clidef.py -o $@ $< diff --git a/python/callgraph-dot.py b/python/callgraph-dot.py new file mode 100644 index 0000000000..4faf1dae16 --- /dev/null +++ b/python/callgraph-dot.py @@ -0,0 +1,476 @@ +# callgraph json to graphviz generator for FRR +# +# Copyright (C) 2020 David Lamparter for NetDEF, Inc. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by 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 + +import re +import sys +import json + +class FunctionNode(object): + funcs = {} + + def __init__(self, name): + super().__init__() + FunctionNode.funcs[name] = self + + self.name = name + self.out = [] + self.inb = [] + self.rank = None + self.defined = False + self.defs = [] + + def __repr__(self): + return '<"%s()" rank=%r>' % (self.name, self.rank) + + def define(self, attrs): + self.defined = True + self.defs.append((attrs['filename'], attrs['line'])) + return self + + def add_call(self, called, attrs): + return CallEdge(self, called, attrs) + + def calls(self): + for e in self.out: + yield e.o + + def calld(self): + for e in self.inb: + yield e.i + + def unlink(self, other): + self.out = list([edge for edge in self.out if edge.o != other]) + other.inb = list([edge for edge in other.inb if edge.i != other]) + + @classmethod + def get(cls, name): + if name in cls.funcs: + return cls.funcs[name] + return FunctionNode(name) + +class CallEdge(object): + def __init__(self, i, o, attrs): + self.i = i + self.o = o + self.is_external = attrs['is_external'] + self.attrs = attrs + + i.out.append(self) + o.inb.append(self) + + def __repr__(self): + return '<"%s()" -> "%s()">' % (self.i.name, self.o.name) + +def nameclean(n): + if '.' in n: + return n.split('.', 1)[0] + return n + +def calc_rank(queue, direction): + nextq = queue + + if direction == 1: + aggr = max + elem = lambda x: x.calls() + else: + aggr = min + elem = lambda x: x.calld() + + currank = direction + cont = True + + while len(nextq) > 0 and cont: + queue = nextq + nextq = [] + + #sys.stderr.write('rank %d\n' % currank) + + cont = False + + for node in queue: + if not node.defined: + node.rank = 0 + continue + + rank = direction + for other in elem(node): + if other is node: + continue + if other.rank is None: + nextq.append(node) + break + rank = aggr(rank, other.rank + direction) + else: + cont = True + node.rank = rank + + currank += direction + + return nextq + +class Graph(dict): + class Subgraph(set): + def __init__(self): + super().__init__() + + class NodeGroup(set): + def __init__(self, members): + super().__init__(members) + + class Node(object): + def __init__(self, graph, fn): + super().__init__() + self._fn = fn + self._fns = [fn] + self._graph = graph + self._calls = set() + self._calld = set() + self._group = None + + def __repr__(self): + return '<Graph.Node "%s()"/%d>' % (self._fn.name, len(self._fns)) + + def __hash__(self): + return hash(self._fn.name) + + def _finalize(self): + for called in self._fn.calls(): + if called.name == self._fn.name: + continue + if called.name in self._graph: + self._calls.add(self._graph[called.name]) + self._graph[called.name]._calld.add(self) + + def unlink(self, other): + self._calls.remove(other) + other._calld.remove(self) + + @property + def name(self): + return self._fn.name + + def calls(self): + return self._calls + def calld(self): + return self._calld + + def group(self, members): + assert self in members + + pregroups = [] + for g in [m._group for m in members]: + if g is None: + continue + if g in pregroups: + continue + + assert g <= members + pregroups.append(g) + + if len(pregroups) == 0: + group = self._graph.NodeGroup(members) + self._graph._groups.append(group) + elif len(pregroups) == 1: + group = pregroups[0] + group |= members + else: + for g in pregroups: + self._graph._groups.remove(g) + group = self._graph.NodeGroup(members) + self._graph._groups.append(group) + + for m in members: + m._group = group + return group + + def merge(self, other): + self._fns.extend(other._fns) + self._calls = (self._calls | other._calls) - {self, other} + self._calld = (self._calld | other._calld) - {self, other} + for c in other._calls: + if c == self: + continue + c._calld.remove(other) + c._calld.add(self) + for c in other._calld: + if c == self: + continue + c._calls.remove(other) + c._calls.add(self) + del self._graph[other._fn.name] + + def __init__(self, funcs): + super().__init__() + self._funcs = funcs + for fn in funcs: + self[fn.name] = self.Node(self, fn) + for node in self.values(): + node._finalize() + self._groups = [] + + def automerge(self): + nodes = list(self.values()) + + while len(nodes): + node = nodes.pop(0) + + candidates = {node} + evalset = set(node.calls()) + prevevalset = None + + while prevevalset != evalset: + prevevalset = evalset + evalset = set() + + for evnode in prevevalset: + inbound = set(evnode.calld()) + if inbound <= candidates: + candidates.add(evnode) + evalset |= set(evnode.calls()) - candidates + else: + evalset.add(evnode) + + #if len(candidates) > 1: + # for candidate in candidates: + # if candidate != node: + # #node.merge(candidate) + # if candidate in nodes: + # nodes.remove(candidate) + node.group(candidates) + + for candidate in candidates: + if candidate in nodes: + nodes.remove(candidate) + + def calc_subgraphs(self): + nodes = list(self.values()) + self._subgraphs = [] + up = {} + down = {} + + self._linear_nodes = [] + + while len(nodes): + sys.stderr.write('%d\n' % len(nodes)) + node = nodes.pop(0) + + down[node] = set() + queue = [node] + while len(queue): + now = queue.pop() + down[node].add(now) + for calls in now.calls(): + if calls in down[node]: + continue + queue.append(calls) + + up[node] = set() + queue = [node] + while len(queue): + now = queue.pop() + up[node].add(now) + for calld in now.calld(): + if calld in up[node]: + continue + queue.append(calld) + + common = up[node] & down[node] + + if len(common) == 1: + self._linear_nodes.append(node) + else: + sg = self.Subgraph() + sg |= common + self._subgraphs.append(sg) + for n in common: + if n != node: + nodes.remove(n) + + return self._subgraphs, self._linear_nodes + + +with open(sys.argv[1], 'r') as fd: + data = json.load(fd) + +extra_info = { + # zebra - LSP WQ + ('lsp_processq_add', 'work_queue_add'): [ + 'lsp_process', + 'lsp_processq_del', + 'lsp_processq_complete', + ], + # zebra - main WQ + ('mq_add_handler', 'work_queue_add'): [ + 'meta_queue_process', + ], + ('meta_queue_process', 'work_queue_add'): [ + 'meta_queue_process', + ], + # bgpd - label pool WQ + ('bgp_lp_get', 'work_queue_add'): [ + 'lp_cbq_docallback', + ], + ('bgp_lp_event_chunk', 'work_queue_add'): [ + 'lp_cbq_docallback', + ], + ('bgp_lp_event_zebra_up', 'work_queue_add'): [ + 'lp_cbq_docallback', + ], + # bgpd - main WQ + ('bgp_process', 'work_queue_add'): [ + 'bgp_process_wq', + 'bgp_processq_del', + ], + ('bgp_add_eoiu_mark', 'work_queue_add'): [ + 'bgp_process_wq', + 'bgp_processq_del', + ], + # clear node WQ + ('bgp_clear_route_table', 'work_queue_add'): [ + 'bgp_clear_route_node', + 'bgp_clear_node_queue_del', + 'bgp_clear_node_complete', + ], + # rfapi WQs + ('rfapi_close', 'work_queue_add'): [ + 'rfapi_deferred_close_workfunc', + ], + ('rfapiRibUpdatePendingNode', 'work_queue_add'): [ + 'rfapiRibDoQueuedCallback', + 'rfapiRibQueueItemDelete', + ], +} + + +for func, fdata in data['functions'].items(): + func = nameclean(func) + fnode = FunctionNode.get(func).define(fdata) + + for call in fdata['calls']: + if call.get('type') in [None, 'unnamed', 'thread_sched']: + if call.get('target') is None: + continue + tgt = nameclean(call['target']) + fnode.add_call(FunctionNode.get(tgt), call) + for fptr in call.get('funcptrs', []): + fnode.add_call(FunctionNode.get(nameclean(fptr)), call) + if tgt == 'work_queue_add': + if (func, tgt) not in extra_info: + sys.stderr.write('%s:%d:%s(): work_queue_add() not handled\n' % ( + call['filename'], call['line'], func)) + else: + attrs = dict(call) + attrs.update({'is_external': False, 'type': 'workqueue'}) + for dst in extra_info[func, tgt]: + fnode.add_call(FunctionNode.get(dst), call) + elif call['type'] == 'install_element': + vty_node = FunctionNode.get('VTY_NODE_%d' % call['vty_node']) + vty_node.add_call(FunctionNode.get(nameclean(call['target'])), call) + elif call['type'] == 'hook': + # TODO: edges for hooks from data['hooks'] + pass + +n = FunctionNode.funcs + +# fix some very low end functions cycling back very far to the top +if 'peer_free' in n: + n['peer_free'].unlink(n['bgp_timer_set']) + n['peer_free'].unlink(n['bgp_addpath_set_peer_type']) +if 'bgp_path_info_extra_free' in n: + n['bgp_path_info_extra_free'].rank = 0 + +if 'zlog_ref' in n: + n['zlog_ref'].rank = 0 +if 'mt_checkalloc' in n: + n['mt_checkalloc'].rank = 0 + +queue = list(FunctionNode.funcs.values()) +queue = calc_rank(queue, 1) +queue = calc_rank(queue, -1) + +sys.stderr.write('%d functions in cyclic set\n' % len(queue)) + +graph = Graph(queue) +graph.automerge() + +gv_nodes = [] +gv_edges = [] + +sys.stderr.write('%d groups after automerge\n' % len(graph._groups)) + +def is_vnc(n): + return n.startswith('rfapi') or n.startswith('vnc') or ('_vnc_' in n) + +_vncstyle = ',fillcolor="#ffffcc",style=filled' +cyclic_set_names = set([fn.name for fn in graph.values()]) + +for i, group in enumerate(graph._groups): + if len(group) > 1: + group.num = i + gv_nodes.append('\tsubgraph cluster_%d {' % i) + gv_nodes.append('\t\tcolor=blue;') + for gn in group: + has_cycle_callers = set(gn.calld()) - group + has_ext_callers = set([edge.i.name for edge in gn._fn.inb]) - cyclic_set_names + + style = '' + etext = '' + if is_vnc(gn.name): + style += _vncstyle + if has_cycle_callers: + style += ',color=blue,penwidth=3' + if has_ext_callers: + style += ',fillcolor="#ffeebb",style=filled' + etext += '<br/><font point-size="10">(%d other callers)</font>' % (len(has_ext_callers)) + + gv_nodes.append('\t\t"%s" [shape=box,label=<%s%s>%s];' % (gn.name, '<br/>'.join([fn.name for fn in gn._fns]), etext, style)) + gv_nodes.append('\t}') + else: + for gn in group: + has_ext_callers = set([edge.i.name for edge in gn._fn.inb]) - cyclic_set_names + + style = '' + etext = '' + if is_vnc(gn.name): + style += _vncstyle + if has_ext_callers: + style += ',fillcolor="#ffeebb",style=filled' + etext += '<br/><font point-size="10">(%d other callers)</font>' % (len(has_ext_callers)) + gv_nodes.append('\t"%s" [shape=box,label=<%s%s>%s];' % (gn.name, '<br/>'.join([fn.name for fn in gn._fns]), etext, style)) + +edges = set() +for gn in graph.values(): + for calls in gn.calls(): + if gn._group == calls._group: + gv_edges.append('\t"%s" -> "%s" [color="#55aa55",style=dashed];' % (gn.name, calls.name)) + else: + def xname(nn): + if len(nn._group) > 1: + return 'cluster_%d' % nn._group.num + else: + return nn.name + tup = xname(gn), calls.name + if tup[0] != tup[1] and tup not in edges: + gv_edges.append('\t"%s" -> "%s" [weight=0.0,w=0.0,color=blue];' % tup) + edges.add(tup) + +with open(sys.argv[2], 'w') as fd: + fd.write('''digraph { + node [fontsize=13,fontname="Fira Sans"]; +%s +}''' % '\n'.join(gv_nodes + [''] + gv_edges)) diff --git a/python/makefile.py b/python/makefile.py index 9af397d373..948d3f7391 100644 --- a/python/makefile.py +++ b/python/makefile.py @@ -11,6 +11,7 @@ import subprocess import re import argparse from string import Template +from makevars import MakeReVars argp = argparse.ArgumentParser(description = 'FRR Makefile extensions') argp.add_argument('--dev-build', action = 'store_const', const = True, @@ -20,13 +21,9 @@ args = argp.parse_args() with open('Makefile', 'r') as fd: before = fd.read() -nolinecont = before.replace('\\\n', '') -m = re.search('^clippy_scan\s*=([^#]*)(?:#.*)?$', nolinecont, flags=re.MULTILINE) -if m is None: - sys.stderr.write('failed to parse Makefile.in\n') - sys.exit(2) +mv = MakeReVars(before) -clippy_scan = m.group(1).strip().split() +clippy_scan = mv['clippy_scan'].strip().split() for clippy_file in clippy_scan: assert clippy_file.endswith('.c') @@ -57,6 +54,7 @@ ${target}: ${clippybase}_clippy.c lines = before.splitlines() autoderp = '#AUTODERP# ' out_lines = [] +bcdeps = [] make_rule_re = re.compile('^([^:\s]+):\s*([^:\s]+)\s*($|\n)') while lines: @@ -80,6 +78,12 @@ while lines: out_lines.append(line) continue + target, dep = m.group(1), m.group(2) + + if target.endswith('.lo') or target.endswith('.o'): + if not dep.endswith('.h'): + bcdeps.append('%s.bc: %s' % (target, target)) + bcdeps.append('\t$(AM_V_LLVM_BC)$(COMPILE) -emit-llvm -c -o $@ %s' % (dep)) if m.group(2) in clippy_scan: out_lines.append(clippyauxdep.substitute(target=m.group(1), clippybase=m.group(2)[:-2])) @@ -88,6 +92,24 @@ while lines: out_lines.append('# clippy{\n# main clippy targets') for clippy_file in clippy_scan: out_lines.append(clippydep.substitute(clippybase = clippy_file[:-2])) + +out_lines.append('') +out_lines.extend(bcdeps) +out_lines.append('') +bc_targets = [] +for varname in ['bin_PROGRAMS', 'sbin_PROGRAMS', 'lib_LTLIBRARIES', 'module_LTLIBRARIES', 'noinst_LIBRARIES']: + bc_targets.extend(mv[varname].strip().split()) +for target in bc_targets: + amtgt = target.replace('/', '_').replace('.', '_').replace('-', '_') + objs = mv[amtgt + '_OBJECTS'].strip().split() + objs = [obj + '.bc' for obj in objs] + deps = mv.get(amtgt + '_DEPENDENCIES', '').strip().split() + deps = [d + '.bc' for d in deps if d.endswith('.a')] + objs.extend(deps) + out_lines.append('%s.bc: %s' % (target, ' '.join(objs))) + out_lines.append('\t$(AM_V_LLVM_LD)$(LLVM_LINK) -o $@ $^') + out_lines.append('') + out_lines.append('# }clippy') out_lines.append('') diff --git a/python/makevars.py b/python/makevars.py index e0e2031a0d..1a85fbd6f5 100644 --- a/python/makevars.py +++ b/python/makevars.py @@ -4,14 +4,33 @@ import os import subprocess +import re -class MakeVars(object): +class MakeVarsBase(object): ''' - makevars['FOO_CFLAGS'] gets you "FOO_CFLAGS" from Makefile + common code between MakeVars and MakeReVars ''' def __init__(self): self._data = dict() + def __getitem__(self, k): + if k not in self._data: + self.getvars([k]) + return self._data[k] + + def get(self, k, defval = None): + if k not in self._data: + self.getvars([k]) + return self._data.get(k) or defval + +class MakeVars(MakeVarsBase): + ''' + makevars['FOO_CFLAGS'] gets you "FOO_CFLAGS" from Makefile + + This variant works by invoking make as a subprocess, i.e. Makefile must + be valid and working. (This is sometimes a problem if depfiles have not + been generated.) + ''' def getvars(self, varlist): ''' get a batch list of variables from make. faster than individual calls. @@ -39,12 +58,33 @@ class MakeVars(object): v = v[1:-1] self._data[k] = v - def __getitem__(self, k): - if k not in self._data: - self.getvars([k]) - return self._data[k] +class MakeReVars(MakeVarsBase): + ''' + makevars['FOO_CFLAGS'] gets you "FOO_CFLAGS" from Makefile - def get(self, k, defval = None): - if k not in self._data: - self.getvars([k]) - return self._data[k] or defval + This variant works by regexing through Makefile. This means the Makefile + does not need to be fully working, but on the other hand it doesn't support + fancy complicated make expressions. + ''' + var_re = re.compile(r'^([^=#\n\s]+)[ \t]*=[ \t]*([^#\n]*)(?:#.*)?$', flags=re.MULTILINE) + repl_re = re.compile(r'\$(?:([A-Za-z])|\(([^\)]+)\))') + + def __init__(self, maketext): + super().__init__() + self._vars = dict(self.var_re.findall(maketext.replace('\\\n', ''))) + + def replacevar(self, match): + varname = match.group(1) or match.group(2) + return self._vars.get(varname, '') + + def getvars(self, varlist): + for varname in varlist: + if varname not in self._vars: + continue + + val, prevval = self._vars[varname], None + while val != prevval: + prevval = val + val = self.repl_re.sub(self.replacevar, val) + + self._data[varname] = val diff --git a/qpb/subdir.am b/qpb/subdir.am index 1864ba7369..80f8f3aca9 100644 --- a/qpb/subdir.am +++ b/qpb/subdir.am @@ -29,6 +29,7 @@ CLEANFILES += \ # end EXTRA_DIST += qpb/qpb.proto +SUFFIXES += .proto .pb-c.c .pb-c.h if HAVE_PROTOBUF diff --git a/tests/topotests/ldp-topo1/r3/how_mpls_table.ref b/tests/topotests/ldp-topo1/r3/how_mpls_table.ref deleted file mode 100644 index 18f7df0ee4..0000000000 --- a/tests/topotests/ldp-topo1/r3/how_mpls_table.ref +++ /dev/null @@ -1,10 +0,0 @@ - Inbound Outbound - Label Type Nexthop Label --------- ------- --------------- -------- - 16 LDP 10.0.2.2 3 - 16 LDP 10.0.3.2 3 - 17 LDP 10.0.2.2 3 - 17 LDP 10.0.3.2 3 - 18 LDP 10.0.2.2 17 - 18 LDP 10.0.3.2 17 - 19 LDP 10.0.2.4 3 diff --git a/tests/topotests/ldp-topo1/r4/how_mpls_table.ref b/tests/topotests/ldp-topo1/r4/how_mpls_table.ref deleted file mode 100644 index 40efab8b5b..0000000000 --- a/tests/topotests/ldp-topo1/r4/how_mpls_table.ref +++ /dev/null @@ -1,9 +0,0 @@ - Inbound Outbound - Label Type Nexthop Label --------- ------- --------------- -------- - 16 LDP 10.0.2.2 17 - 17 LDP 10.0.2.2 3 - 18 LDP 10.0.2.3 3 - 19 LDP 10.0.2.2 3 - 20 LDP 10.0.2.3 3 - 20 LDP 10.0.2.2 3 diff --git a/tools/frr-llvm-cg.c b/tools/frr-llvm-cg.c new file mode 100644 index 0000000000..84a756a376 --- /dev/null +++ b/tools/frr-llvm-cg.c @@ -0,0 +1,649 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// For more information, please refer to <http://unlicense.org/> + +/* based on example code: https://github.com/sheredom/llvm_bc_parsing_example + * which came under the above (un-)license. does not depend on any FRR + * pieces, so no reason to change the license. + * + * please note that while included in the FRR sources, this tool is in no way + * supported or maintained by the FRR community. it is provided as a + * "convenience"; while it worked at some point (using LLVM 8 / 9), it may + * easily break with a future LLVM version or any other factors. + * + * 2020-05-04, David Lamparter + */ + +#include <string.h> +#include <strings.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <assert.h> + +#include <llvm-c/BitReader.h> +#include <llvm-c/BitWriter.h> +#include <llvm-c/Core.h> + +#include <json-c/json.h> + +/* if you want to use this without the special FRRouting defines, + * remove the following #define + */ +#define FRR_SPECIFIC + +static void dbgloc_add(struct json_object *jsobj, LLVMValueRef obj) +{ + unsigned file_len = 0; + const char *file = LLVMGetDebugLocFilename(obj, &file_len); + unsigned line = LLVMGetDebugLocLine(obj); + + if (!file) + file = "???", file_len = 3; + else if (file[0] == '.' && file[1] == '/') + file += 2, file_len -= 2; + + json_object_object_add(jsobj, "filename", + json_object_new_string_len(file, file_len)); + json_object_object_add(jsobj, "line", json_object_new_int64(line)); +} + +static struct json_object *js_get_or_make(struct json_object *parent, + const char *key, + struct json_object *(*maker)(void)) +{ + struct json_object *ret; + + ret = json_object_object_get(parent, key); + if (ret) + return ret; + ret = maker(); + json_object_object_add(parent, key, ret); + return ret; +} + +static bool details_fptr_vars = false; +static bool details_fptr_consts = true; + +enum called_fn { + FN_GENERIC = 0, + FN_NONAME, + FN_INSTALL_ELEMENT, + FN_THREAD_ADD, +}; + +static void walk_const_fptrs(struct json_object *js_call, LLVMValueRef value, + const char *prefix, bool *hdr_written) +{ + LLVMTypeRef type; + LLVMValueKind kind; + + if (LLVMIsAGlobalVariable(value)) { + type = LLVMGlobalGetValueType(value); + value = LLVMGetInitializer(value); + } else { + type = LLVMTypeOf(value); + } + + if (LLVMIsAFunction(value)) { + struct json_object *js_fptrs; + + js_fptrs = js_get_or_make(js_call, "funcptrs", + json_object_new_array); + + size_t fn_len; + const char *fn_name = LLVMGetValueName2(value, &fn_len); + + size_t curlen = json_object_array_length(js_fptrs); + struct json_object *jsobj; + const char *s; + + for (size_t i = 0; i < curlen; i++) { + jsobj = json_object_array_get_idx(js_fptrs, i); + s = json_object_get_string(jsobj); + + if (s && !strcmp(s, fn_name)) + return; + } + + if (details_fptr_consts && !*hdr_written) { + fprintf(stderr, + "%s: calls function pointer from constant or global data\n", + prefix); + *hdr_written = true; + } + if (details_fptr_consts) + fprintf(stderr, "%s- constant: %.*s()\n", + prefix, (int)fn_len, fn_name); + + json_object_array_add(js_fptrs, + json_object_new_string_len(fn_name, + fn_len)); + return; + } + + kind = LLVMGetValueKind(value); + + unsigned len; + char *dump; + + switch (kind) { + case LLVMUndefValueValueKind: + case LLVMConstantAggregateZeroValueKind: + case LLVMConstantPointerNullValueKind: + /* null pointer / array - ignore */ + break; + + case LLVMConstantIntValueKind: + /* integer - ignore */ + break; + + case LLVMConstantStructValueKind: + len = LLVMCountStructElementTypes(type); + for (unsigned i = 0; i < len; i++) + walk_const_fptrs(js_call, LLVMGetOperand(value, i), + prefix, hdr_written); + break; + + case LLVMConstantArrayValueKind: + len = LLVMGetArrayLength(type); + for (unsigned i = 0; i < len; i++) + walk_const_fptrs(js_call, LLVMGetOperand(value, i), + prefix, hdr_written); + return; + + default: + /* to help the user / development */ + if (!*hdr_written) { + fprintf(stderr, + "%s: calls function pointer from constant or global data\n", + prefix); + *hdr_written = true; + } + dump = LLVMPrintValueToString(value); + fprintf(stderr, + "%s- value could not be processed:\n" + "%s- [kind=%d] %s\n", + prefix, prefix, kind, dump); + LLVMDisposeMessage(dump); + return; + } + return; +} + +#ifdef FRR_SPECIFIC +static bool is_thread_sched(const char *name, size_t len) +{ +#define thread_prefix "funcname_" + static const char *const names[] = { + thread_prefix "thread_add_read_write", + thread_prefix "thread_add_timer", + thread_prefix "thread_add_timer_msec", + thread_prefix "thread_add_timer_tv", + thread_prefix "thread_add_event", + thread_prefix "thread_execute", + }; + size_t i; + + for (i = 0; i < sizeof(names) / sizeof(names[0]); i++) { + if (strlen(names[i]) != len) + continue; + if (!memcmp(names[i], name, len)) + return true; + } + return false; +} +#endif + +static void process_call(struct json_object *js_calls, + struct json_object *js_special, + LLVMValueRef instr, + LLVMValueRef function) +{ + struct json_object *js_call, *js_fptrs = NULL; + + LLVMValueRef called = LLVMGetCalledValue(instr); + + if (LLVMIsAConstantExpr(called)) { + LLVMOpcode opcode = LLVMGetConstOpcode(called); + + if (opcode == LLVMBitCast) { + LLVMValueRef op0 = LLVMGetOperand(called, 0); + + if (LLVMIsAFunction(op0)) + called = op0; + } + } + + size_t called_len = 0; + const char *called_name = LLVMGetValueName2(called, &called_len); + unsigned n_args = LLVMGetNumArgOperands(instr); + + bool is_external = LLVMIsDeclaration(called); + enum called_fn called_type = FN_GENERIC; + + js_call = json_object_new_object(); + json_object_array_add(js_calls, js_call); + dbgloc_add(js_call, instr); + json_object_object_add(js_call, "is_external", + json_object_new_boolean(is_external)); + + if (!called_name || called_len == 0) { + called_type = FN_NONAME; + json_object_object_add(js_call, "type", + json_object_new_string("indirect")); + + LLVMValueRef last = called; + + size_t name_len = 0; + const char *name_c = LLVMGetValueName2(function, &name_len); + +#ifdef FRR_SPECIFIC + /* information for FRR hooks is dumped for the registration + * in _hook_typecheck; we can safely ignore the funcptr here + */ + if (strncmp(name_c, "hook_call_", 10) == 0) + return; +#endif + + unsigned file_len = 0; + const char *file = LLVMGetDebugLocFilename(instr, &file_len); + unsigned line = LLVMGetDebugLocLine(instr); + + char prefix[256]; + snprintf(prefix, sizeof(prefix), "%.*s:%d:%.*s()", + (int)file_len, file, line, (int)name_len, name_c); + + while (LLVMIsALoadInst(last) || LLVMIsAGetElementPtrInst(last)) + /* skipping over details for GEP here, but meh. */ + last = LLVMGetOperand(last, 0); + + if (LLVMIsAAllocaInst(last)) { + /* "alloca" is just generically all variables on the + * stack, this does not refer to C alloca() calls + * + * looking at the control flow in the function can + * give better results here, it's just not implemented + * (yet?) + */ + fprintf(stderr, + "%s: call to a function pointer variable\n", + prefix); + + if (details_fptr_vars) { + char *dump = LLVMPrintValueToString(called); + printf("%s- %s\n", prefix, dump); + LLVMDisposeMessage(dump); + } + + json_object_object_add( + js_call, "type", + json_object_new_string("stack_fptr")); + } else if (LLVMIsACallInst(last)) { + /* calling the a function pointer returned from + * another function. + */ + struct json_object *js_indirect; + + js_indirect = js_get_or_make(js_call, "return_of", + json_object_new_array); + + process_call(js_indirect, js_special, last, function); + } else if (LLVMIsAConstant(last)) { + /* function pointer is a constant (includes loading + * from complicated constants like structs or arrays.) + */ + bool hdr_written = false; + walk_const_fptrs(js_call, last, prefix, &hdr_written); + if (details_fptr_consts && !hdr_written) + fprintf(stderr, + "%s: calls function pointer from constant or global data, but no non-NULL function pointers found\n", + prefix); + } else { + char *dump = LLVMPrintValueToString(called); + printf("\t%s\n", dump); + LLVMDisposeMessage(dump); + } + return; +#ifdef FRR_SPECIFIC + } else if (!strcmp(called_name, "install_element")) { + called_type = FN_INSTALL_ELEMENT; + + LLVMValueRef param0 = LLVMGetOperand(instr, 0); + if (!LLVMIsAConstantInt(param0)) + goto out_nonconst; + + long long vty_node = LLVMConstIntGetSExtValue(param0); + json_object_object_add(js_call, "vty_node", + json_object_new_int64(vty_node)); + + LLVMValueRef param1 = LLVMGetOperand(instr, 1); + if (!LLVMIsAGlobalVariable(param1)) + goto out_nonconst; + + LLVMValueRef intlz = LLVMGetInitializer(param1); + assert(intlz && LLVMIsConstant(intlz)); + + LLVMValueKind intlzkind = LLVMGetValueKind(intlz); + assert(intlzkind == LLVMConstantStructValueKind); + + LLVMValueRef funcptr = LLVMGetOperand(intlz, 4); + assert(LLVMIsAFunction(funcptr)); + + size_t target_len = 0; + const char *target; + target = LLVMGetValueName2(funcptr, &target_len); + + json_object_object_add( + js_call, "type", + json_object_new_string("install_element")); + json_object_object_add( + js_call, "target", + json_object_new_string_len(target, target_len)); + return; + + out_nonconst: + json_object_object_add( + js_call, "target", + json_object_new_string("install_element")); + return; + } else if (is_thread_sched(called_name, called_len)) { + called_type = FN_THREAD_ADD; + + json_object_object_add(js_call, "type", + json_object_new_string("thread_sched")); + json_object_object_add( + js_call, "subtype", + json_object_new_string_len(called_name, called_len)); + + LLVMValueRef fparam; + if (strstr(called_name, "_read_")) + fparam = LLVMGetOperand(instr, 2); + else + fparam = LLVMGetOperand(instr, 1); + assert(fparam); + + size_t target_len = 0; + const char *target; + target = LLVMGetValueName2(fparam, &target_len); + + json_object_object_add(js_call, "target", + !target_len ? NULL : + json_object_new_string_len(target, target_len)); + if (!LLVMIsAFunction(fparam)) + json_object_object_add(js_call, "target_unresolved", + json_object_new_boolean(true)); + return; + } else if (!strncmp(called_name, "_hook_typecheck_", + strlen("_hook_typecheck_"))) { + struct json_object *js_hook, *js_this; + const char *hook_name; + + hook_name = called_name + strlen("_hook_typecheck_"); + + json_object_object_add(js_call, "type", + json_object_new_string("hook")); + + LLVMValueRef param0 = LLVMGetOperand(instr, 0); + if (!LLVMIsAFunction(param0)) + return; + + size_t target_len = 0; + const char *target; + target = LLVMGetValueName2(param0, &target_len); + + js_hook = js_get_or_make(js_special, "hooks", + json_object_new_object); + js_hook = js_get_or_make(js_hook, hook_name, + json_object_new_array); + + js_this = json_object_new_object(); + json_object_array_add(js_hook, js_this); + + dbgloc_add(js_this, instr); + json_object_object_add( + js_this, "target", + json_object_new_string_len(target, target_len)); + return; + + /* TODO (FRR specifics): + * - workqueues - not sure we can do much there + * - zclient->* ? + */ +#endif /* FRR_SPECIFIC */ + } else { + json_object_object_add( + js_call, "target", + json_object_new_string_len(called_name, called_len)); + } + + for (unsigned argno = 0; argno < n_args; argno++) { + LLVMValueRef param = LLVMGetOperand(instr, argno); + size_t target_len; + const char *target_name; + + if (LLVMIsAFunction(param)) { + js_fptrs = js_get_or_make(js_call, "funcptrs", + json_object_new_array); + + target_name = LLVMGetValueName2(param, &target_len); + + json_object_array_add(js_fptrs, + json_object_new_string_len( + target_name, target_len)); + } + } +} + +static void process_fn(struct json_object *funcs, + struct json_object *js_special, + LLVMValueRef function) +{ + struct json_object *js_func, *js_calls; + + size_t name_len = 0; + const char *name_c = LLVMGetValueName2(function, &name_len); + char *name; + + name = strndup(name_c, name_len); + + js_func = json_object_object_get(funcs, name); + if (js_func) { + unsigned file_len = 0; + const char *file = LLVMGetDebugLocFilename(function, &file_len); + unsigned line = LLVMGetDebugLocLine(function); + + fprintf(stderr, "%.*s:%d:%s(): duplicate definition!\n", + (int)file_len, file, line, name); + free(name); + return; + } + + js_func = json_object_new_object(); + json_object_object_add(funcs, name, js_func); + free(name); + + js_calls = json_object_new_array(); + json_object_object_add(js_func, "calls", js_calls); + + dbgloc_add(js_func, function); + + for (LLVMBasicBlockRef basicBlock = LLVMGetFirstBasicBlock(function); + basicBlock; basicBlock = LLVMGetNextBasicBlock(basicBlock)) { + + for (LLVMValueRef instr = LLVMGetFirstInstruction(basicBlock); + instr; instr = LLVMGetNextInstruction(instr)) { + + if (LLVMIsAIntrinsicInst(instr)) + continue; + + if (LLVMIsACallInst(instr) || LLVMIsAInvokeInst(instr)) + process_call(js_calls, js_special, instr, + function); + } + } +} + +static void help(int retcode) +{ + fprintf(stderr, + "FRR LLVM bitcode to callgraph analyzer\n" + "\n" + "usage: frr-llvm-cg [-q|-v] [-o <JSONOUTPUT>] BITCODEINPUT\n" + "\n" + "\t-o FILENAME\twrite JSON output to file instead of stdout\n" + "\t-v\t\tbe more verbose\n" + "\t-q\t\tbe quiet\n" + "\n" + "BITCODEINPUT must be a LLVM binary bitcode file (not text\n" + "representation.) Use - to read from stdin.\n" + "\n" + "Note it may be necessary to build this binary tool against\n" + "the specific LLVM version that created the bitcode file.\n"); + exit(retcode); +} + +int main(int argc, char **argv) +{ + int opt; + const char *out = NULL; + const char *inp = NULL; + char v_or_q = '\0'; + + while ((opt = getopt(argc, argv, "hvqo:")) != -1) { + switch (opt) { + case 'o': + if (out) + help(1); + out = optarg; + break; + case 'v': + if (v_or_q && v_or_q != 'v') + help(1); + details_fptr_vars = true; + details_fptr_consts = true; + v_or_q = 'v'; + break; + case 'q': + if (v_or_q && v_or_q != 'q') + help(1); + details_fptr_vars = false; + details_fptr_consts = false; + v_or_q = 'q'; + break; + case 'h': + help(0); + return 0; + default: + help(1); + } + } + + if (optind != argc - 1) + help(1); + + inp = argv[optind]; + + LLVMMemoryBufferRef memoryBuffer; + char *message; + int ret; + + // check if we are to read our input file from stdin + if (!strcmp(inp, "-")) { + inp = "<stdin>"; + ret = LLVMCreateMemoryBufferWithSTDIN(&memoryBuffer, &message); + } else { + ret = LLVMCreateMemoryBufferWithContentsOfFile( + inp, &memoryBuffer, &message); + } + + if (ret) { + fprintf(stderr, "failed to open %s: %s\n", inp, message); + free(message); + return 1; + } + + // now create our module using the memorybuffer + LLVMModuleRef module; + if (LLVMParseBitcode2(memoryBuffer, &module)) { + fprintf(stderr, "%s: invalid bitcode\n", inp); + LLVMDisposeMemoryBuffer(memoryBuffer); + return 1; + } + + // done with the memory buffer now, so dispose of it + LLVMDisposeMemoryBuffer(memoryBuffer); + + struct json_object *js_root, *js_funcs, *js_special; + + js_root = json_object_new_object(); + js_funcs = json_object_new_object(); + json_object_object_add(js_root, "functions", js_funcs); + js_special = json_object_new_object(); + json_object_object_add(js_root, "special", js_special); + + // loop through all the functions in the module + for (LLVMValueRef function = LLVMGetFirstFunction(module); function; + function = LLVMGetNextFunction(function)) { + if (LLVMIsDeclaration(function)) + continue; + + process_fn(js_funcs, js_special, function); + } + + if (out) { + char tmpout[strlen(out) + 5]; + + snprintf(tmpout, sizeof(tmpout), "%s.tmp", out); + ret = json_object_to_file_ext(tmpout, js_root, + JSON_C_TO_STRING_PRETTY | + JSON_C_TO_STRING_PRETTY_TAB | + JSON_C_TO_STRING_NOSLASHESCAPE); + if (ret < 0) { + fprintf(stderr, "could not write JSON to file\n"); + return 1; + } + if (rename(tmpout, out)) { + fprintf(stderr, "could not rename JSON output: %s\n", + strerror(errno)); + unlink(tmpout); + return 1; + } + } else { + ret = json_object_to_fd(1, js_root, + JSON_C_TO_STRING_PRETTY | + JSON_C_TO_STRING_PRETTY_TAB | + JSON_C_TO_STRING_NOSLASHESCAPE); + if (ret < 0) { + fprintf(stderr, "could not write JSON to stdout\n"); + return 1; + } + } + + LLVMDisposeModule(module); + + return 0; +} diff --git a/tools/subdir.am b/tools/subdir.am index c637db6eb1..723a87d100 100644 --- a/tools/subdir.am +++ b/tools/subdir.am @@ -8,6 +8,10 @@ noinst_PROGRAMS += \ tools/gen_yang_deviations \ # end +EXTRA_PROGRAMS += \ + tools/frr-llvm-cg \ + # end + sbin_PROGRAMS += tools/ssd sbin_SCRIPTS += \ tools/frr-reload \ @@ -31,6 +35,14 @@ tools_gen_yang_deviations_LDADD = lib/libfrr.la $(LIBYANG_LIBS) tools_ssd_SOURCES = tools/start-stop-daemon.c +# don't bother autoconf'ing these for a simple optional tool +llvm_version = $(shell echo __clang_major__ | $(CC) -xc -P -E -) +tools_frr_llvm_cg_CFLAGS = $(AM_CFLAGS) `llvm-config-$(llvm_version) --cflags` +tools_frr_llvm_cg_LDFLAGS = `llvm-config-$(llvm_version) --ldflags --libs` +tools_frr_llvm_cg_SOURCES = \ + tools/frr-llvm-cg.c \ + # end + EXTRA_DIST += \ tools/etc \ tools/frr-reload \ diff --git a/zebra/rib.h b/zebra/rib.h index 3717a12814..1667f17909 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -41,7 +41,7 @@ extern "C" { #endif -typedef enum { RNH_NEXTHOP_TYPE, RNH_IMPORT_CHECK_TYPE } rnh_type_t; +enum rnh_type { RNH_NEXTHOP_TYPE, RNH_IMPORT_CHECK_TYPE }; PREDECL_LIST(rnh_list) @@ -58,7 +58,7 @@ struct rnh { afi_t afi; - rnh_type_t type; + enum rnh_type type; uint32_t seqno; @@ -276,7 +276,7 @@ struct rtadv { * Structure that is hung off of a route_table that holds information about * the table. */ -typedef struct rib_table_info_t_ { +struct rib_table_info { /* * Back pointer to zebra_vrf. @@ -284,14 +284,13 @@ typedef struct rib_table_info_t_ { struct zebra_vrf *zvrf; afi_t afi; safi_t safi; +}; -} rib_table_info_t; - -typedef enum { +enum rib_tables_iter_state { RIB_TABLES_ITER_S_INIT, RIB_TABLES_ITER_S_ITERATING, RIB_TABLES_ITER_S_DONE -} rib_tables_iter_state_t; +}; /* * Structure that holds state for iterating over all tables in the @@ -301,16 +300,16 @@ typedef struct rib_tables_iter_t_ { vrf_id_t vrf_id; int afi_safi_ix; - rib_tables_iter_state_t state; + enum rib_tables_iter_state state; } rib_tables_iter_t; /* Events/reasons triggering a RIB update. */ -typedef enum { +enum rib_update_event { RIB_UPDATE_KERNEL, RIB_UPDATE_RMAP_CHANGE, RIB_UPDATE_OTHER, RIB_UPDATE_MAX -} rib_update_event_t; +}; extern void route_entry_copy_nexthops(struct route_entry *re, struct nexthop *nh); @@ -374,10 +373,10 @@ extern struct route_entry *rib_match_ipv4_multicast(vrf_id_t vrf_id, extern struct route_entry *rib_lookup_ipv4(struct prefix_ipv4 *p, vrf_id_t vrf_id); -extern void rib_update(rib_update_event_t event); -extern void rib_update_vrf(vrf_id_t vrf_id, rib_update_event_t event); +extern void rib_update(enum rib_update_event event); +extern void rib_update_vrf(vrf_id_t vrf_id, enum rib_update_event event); extern void rib_update_table(struct route_table *table, - rib_update_event_t event); + enum rib_update_event event); extern int rib_sweep_route(struct thread *t); extern void rib_sweep_table(struct route_table *table); extern void rib_close_table(struct route_table *table); @@ -412,9 +411,9 @@ extern void zebra_rib_evaluate_rn_nexthops(struct route_node *rn, uint32_t seq); /* * rib_table_info */ -static inline rib_table_info_t *rib_table_info(struct route_table *table) +static inline struct rib_table_info *rib_table_info(struct route_table *table) { - return (rib_table_info_t *)route_table_get_info(table); + return (struct rib_table_info *)route_table_get_info(table); } /* diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 882babec81..466e985494 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -90,6 +90,18 @@ struct gw_family_t { static const char ipv4_ll_buf[16] = "169.254.0.1"; static struct in_addr ipv4_ll; +/* Is this a ipv4 over ipv6 route? */ +static bool is_route_v4_over_v6(unsigned char rtm_family, + enum nexthop_types_t nexthop_type) +{ + if (rtm_family == AF_INET + && (nexthop_type == NEXTHOP_TYPE_IPV6 + || nexthop_type == NEXTHOP_TYPE_IPV6_IFINDEX)) + return true; + + return false; +} + /* Helper to control use of kernel-level nexthop ids */ static bool kernel_nexthops_supported(void) { @@ -1165,9 +1177,7 @@ static void _netlink_route_build_singlepath(const struct prefix *p, if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) rtmsg->rtm_flags |= RTNH_F_ONLINK; - if (rtmsg->rtm_family == AF_INET - && (nexthop->type == NEXTHOP_TYPE_IPV6 - || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)) { + if (is_route_v4_over_v6(rtmsg->rtm_family, nexthop->type)) { rtmsg->rtm_flags |= RTNH_F_ONLINK; addattr_l(nlmsg, req_size, RTA_GATEWAY, &ipv4_ll, 4); addattr32(nlmsg, req_size, RTA_OIF, nexthop->ifindex); @@ -1342,9 +1352,7 @@ _netlink_route_build_multipath(const struct prefix *p, const char *routedesc, if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) rtnh->rtnh_flags |= RTNH_F_ONLINK; - if (rtmsg->rtm_family == AF_INET - && (nexthop->type == NEXTHOP_TYPE_IPV6 - || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)) { + if (is_route_v4_over_v6(rtmsg->rtm_family, nexthop->type)) { bytelen = 4; rtnh->rtnh_flags |= RTNH_F_ONLINK; rta_addattr_l(rta, NL_PKT_BUF_SIZE, RTA_GATEWAY, &ipv4_ll, diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 6909bcb137..64fd7fa491 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -180,6 +180,7 @@ static int kernel_rtm(int cmd, const struct prefix *p, switch (p->family) { case AF_INET: { struct in_addr loopback; + loopback.s_addr = htonl(INADDR_LOOPBACK); sin_gate.sin.sin_addr = loopback; #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN @@ -187,11 +188,21 @@ static int kernel_rtm(int cmd, const struct prefix *p, sizeof(struct sockaddr_in); #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ gate = true; - } - break; - case AF_INET6: - zlog_warn("v6 blackhole routes have not been programmed yet"); - break; + } break; + case AF_INET6: { + struct in6_addr loopback; + + inet_pton(AF_INET6, "::1", &loopback); + + sin_gate.sin6.sin6_addr = loopback; + sin_gate.sin6.sin6_family = AF_INET6; + +#ifdef HAVE_STRUCTSOCKADDR_SA_LEN + sin_gate.sin6.sin6_len = + sizeof(struct sockaddr_in6); +#endif /* HAVE_STRUCTSOCKADDR_SA_LEN */ + gate = true; + } break; } } diff --git a/zebra/rtadv.c b/zebra/rtadv.c index 0fb3390410..013eb5819c 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -171,7 +171,7 @@ static int rtadv_recv_packet(struct zebra_vrf *zvrf, int sock, uint8_t *buf, /* Send router advertisement packet. */ static void rtadv_send_packet(int sock, struct interface *ifp, - ipv6_nd_suppress_ra_status stop) + enum ipv6_nd_suppress_ra_status stop) { struct msghdr msg; struct iovec iov; @@ -1003,7 +1003,7 @@ void rtadv_delete_prefix(struct zebra_if *zif, const struct prefix *p) } static void ipv6_nd_suppress_ra_set(struct interface *ifp, - ipv6_nd_suppress_ra_status status) + enum ipv6_nd_suppress_ra_status status) { struct zebra_if *zif; struct zebra_vrf *zvrf; diff --git a/zebra/rtadv.h b/zebra/rtadv.h index 68a5bbcdbe..d7a1ccfb29 100644 --- a/zebra/rtadv.h +++ b/zebra/rtadv.h @@ -147,10 +147,10 @@ enum ipv6_nd_prefix_source { PREFIX_SRC_BOTH, }; -typedef enum { +enum ipv6_nd_suppress_ra_status { RA_ENABLE = 0, RA_SUPPRESS, -} ipv6_nd_suppress_ra_status; +}; extern void rtadv_init(struct zebra_vrf *zvrf); extern void rtadv_vrf_terminate(struct zebra_vrf *zvrf); diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 143354b166..cc8cab1ff5 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -1530,7 +1530,7 @@ int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, { int ret = EINVAL; const struct route_table *table = NULL; - const rib_table_info_t *info; + const struct rib_table_info *info; const struct prefix *p, *src_p; struct zebra_ns *zns; struct zebra_vrf *zvrf; diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c index 8f97c8cf47..47b4965396 100644 --- a/zebra/zebra_fpm.c +++ b/zebra/zebra_fpm.c @@ -76,15 +76,15 @@ static void zfpm_iterate_rmac_table(struct hash_bucket *backet, void *args); * Structure that holds state for iterating over all route_node * structures that are candidates for being communicated to the FPM. */ -typedef struct zfpm_rnodes_iter_t_ { +struct zfpm_rnodes_iter { rib_tables_iter_t tables_iter; route_table_iter_t iter; -} zfpm_rnodes_iter_t; +}; /* * Statistics. */ -typedef struct zfpm_stats_t_ { +struct zfpm_stats { unsigned long connect_calls; unsigned long connect_no_sock; @@ -115,13 +115,12 @@ typedef struct zfpm_stats_t_ { unsigned long t_conn_up_yields; unsigned long t_conn_up_aborts; unsigned long t_conn_up_finishes; - -} zfpm_stats_t; +}; /* * States for the FPM state machine. */ -typedef enum { +enum zfpm_state { /* * In this state we are not yet ready to connect to the FPM. This @@ -147,20 +146,21 @@ typedef enum { */ ZFPM_STATE_ESTABLISHED -} zfpm_state_t; +}; /* * Message format to be used to communicate with the FPM. */ -typedef enum { +enum zfpm_msg_format { ZFPM_MSG_FORMAT_NONE, ZFPM_MSG_FORMAT_NETLINK, ZFPM_MSG_FORMAT_PROTOBUF, -} zfpm_msg_format_e; +}; + /* * Globals. */ -typedef struct zfpm_glob_t_ { +struct zfpm_glob { /* * True if the FPM module has been enabled. @@ -170,11 +170,11 @@ typedef struct zfpm_glob_t_ { /* * Message format to be used to communicate with the fpm. */ - zfpm_msg_format_e message_format; + enum zfpm_msg_format message_format; struct thread_master *master; - zfpm_state_t state; + enum zfpm_state state; in_addr_t fpm_server; /* @@ -231,7 +231,7 @@ typedef struct zfpm_glob_t_ { struct thread *t_conn_down; struct { - zfpm_rnodes_iter_t iter; + struct zfpm_rnodes_iter iter; } t_conn_down_state; /* @@ -241,7 +241,7 @@ typedef struct zfpm_glob_t_ { struct thread *t_conn_up; struct { - zfpm_rnodes_iter_t iter; + struct zfpm_rnodes_iter iter; } t_conn_up_state; unsigned long connect_calls; @@ -251,18 +251,18 @@ typedef struct zfpm_glob_t_ { * Stats from the start of the current statistics interval up to * now. These are the counters we typically update in the code. */ - zfpm_stats_t stats; + struct zfpm_stats stats; /* * Statistics that were gathered in the last collection interval. */ - zfpm_stats_t last_ivl_stats; + struct zfpm_stats last_ivl_stats; /* * Cumulative stats from the last clear to the start of the current * statistics interval. */ - zfpm_stats_t cumulative_stats; + struct zfpm_stats cumulative_stats; /* * Stats interval timer. @@ -273,18 +273,17 @@ typedef struct zfpm_glob_t_ { * If non-zero, the last time when statistics were cleared. */ time_t last_stats_clear_time; +}; -} zfpm_glob_t; - -static zfpm_glob_t zfpm_glob_space; -static zfpm_glob_t *zfpm_g = &zfpm_glob_space; +static struct zfpm_glob zfpm_glob_space; +static struct zfpm_glob *zfpm_g = &zfpm_glob_space; static int zfpm_trigger_update(struct route_node *rn, const char *reason); static int zfpm_read_cb(struct thread *thread); static int zfpm_write_cb(struct thread *thread); -static void zfpm_set_state(zfpm_state_t state, const char *reason); +static void zfpm_set_state(enum zfpm_state state, const char *reason); static void zfpm_start_connect_timer(const char *reason); static void zfpm_start_stats_timer(void); static void zfpm_mac_info_del(struct fpm_mac_info_t *fpm_mac); @@ -300,7 +299,7 @@ static inline int zfpm_thread_should_yield(struct thread *t) /* * zfpm_state_to_str */ -static const char *zfpm_state_to_str(zfpm_state_t state) +static const char *zfpm_state_to_str(enum zfpm_state state) { switch (state) { @@ -343,7 +342,7 @@ static time_t zfpm_get_elapsed_time(time_t reference) /* * zfpm_rnodes_iter_init */ -static inline void zfpm_rnodes_iter_init(zfpm_rnodes_iter_t *iter) +static inline void zfpm_rnodes_iter_init(struct zfpm_rnodes_iter *iter) { memset(iter, 0, sizeof(*iter)); rib_tables_iter_init(&iter->tables_iter); @@ -360,7 +359,8 @@ static inline void zfpm_rnodes_iter_init(zfpm_rnodes_iter_t *iter) /* * zfpm_rnodes_iter_next */ -static inline struct route_node *zfpm_rnodes_iter_next(zfpm_rnodes_iter_t *iter) +static inline struct route_node * +zfpm_rnodes_iter_next(struct zfpm_rnodes_iter *iter) { struct route_node *rn; struct route_table *table; @@ -389,7 +389,7 @@ static inline struct route_node *zfpm_rnodes_iter_next(zfpm_rnodes_iter_t *iter) /* * zfpm_rnodes_iter_pause */ -static inline void zfpm_rnodes_iter_pause(zfpm_rnodes_iter_t *iter) +static inline void zfpm_rnodes_iter_pause(struct zfpm_rnodes_iter *iter) { route_table_iter_pause(&iter->iter); } @@ -397,7 +397,7 @@ static inline void zfpm_rnodes_iter_pause(zfpm_rnodes_iter_t *iter) /* * zfpm_rnodes_iter_cleanup */ -static inline void zfpm_rnodes_iter_cleanup(zfpm_rnodes_iter_t *iter) +static inline void zfpm_rnodes_iter_cleanup(struct zfpm_rnodes_iter *iter) { route_table_iter_cleanup(&iter->iter); rib_tables_iter_cleanup(&iter->tables_iter); @@ -408,7 +408,7 @@ static inline void zfpm_rnodes_iter_cleanup(zfpm_rnodes_iter_t *iter) * * Initialize a statistics block. */ -static inline void zfpm_stats_init(zfpm_stats_t *stats) +static inline void zfpm_stats_init(struct zfpm_stats *stats) { memset(stats, 0, sizeof(*stats)); } @@ -416,7 +416,7 @@ static inline void zfpm_stats_init(zfpm_stats_t *stats) /* * zfpm_stats_reset */ -static inline void zfpm_stats_reset(zfpm_stats_t *stats) +static inline void zfpm_stats_reset(struct zfpm_stats *stats) { zfpm_stats_init(stats); } @@ -424,7 +424,8 @@ static inline void zfpm_stats_reset(zfpm_stats_t *stats) /* * zfpm_stats_copy */ -static inline void zfpm_stats_copy(const zfpm_stats_t *src, zfpm_stats_t *dest) +static inline void zfpm_stats_copy(const struct zfpm_stats *src, + struct zfpm_stats *dest) { memcpy(dest, src, sizeof(*dest)); } @@ -440,8 +441,9 @@ static inline void zfpm_stats_copy(const zfpm_stats_t *src, zfpm_stats_t *dest) * structure is composed entirely of counters. This can easily be * changed when necessary. */ -static void zfpm_stats_compose(const zfpm_stats_t *s1, const zfpm_stats_t *s2, - zfpm_stats_t *result) +static void zfpm_stats_compose(const struct zfpm_stats *s1, + const struct zfpm_stats *s2, + struct zfpm_stats *result) { const unsigned long *p1, *p2; unsigned long *result_p; @@ -451,7 +453,7 @@ static void zfpm_stats_compose(const zfpm_stats_t *s1, const zfpm_stats_t *s2, p2 = (const unsigned long *)s2; result_p = (unsigned long *)result; - num_counters = (sizeof(zfpm_stats_t) / sizeof(unsigned long)); + num_counters = (sizeof(struct zfpm_stats) / sizeof(unsigned long)); for (i = 0; i < num_counters; i++) { result_p[i] = p1[i] + p2[i]; @@ -512,7 +514,7 @@ static inline void zfpm_connect_off(void) static int zfpm_conn_up_thread_cb(struct thread *thread) { struct route_node *rnode; - zfpm_rnodes_iter_t *iter; + struct zfpm_rnodes_iter *iter; rib_dest_t *dest; zfpm_g->t_conn_up = NULL; @@ -626,7 +628,7 @@ static void zfpm_connect_check(void) static int zfpm_conn_down_thread_cb(struct thread *thread) { struct route_node *rnode; - zfpm_rnodes_iter_t *iter; + struct zfpm_rnodes_iter *iter; rib_dest_t *dest; struct fpm_mac_info_t *mac = NULL; @@ -1308,9 +1310,9 @@ static int zfpm_connect_cb(struct thread *t) * * Move state machine into the given state. */ -static void zfpm_set_state(zfpm_state_t state, const char *reason) +static void zfpm_set_state(enum zfpm_state state, const char *reason) { - zfpm_state_t cur_state = zfpm_g->state; + enum zfpm_state cur_state = zfpm_g->state; if (!reason) reason = "Unknown"; @@ -1649,7 +1651,7 @@ static void zfpm_iterate_rmac_table(struct hash_bucket *backet, void *args) } /* - * zfpm_stats_timer_cb + * struct zfpm_statsimer_cb */ static int zfpm_stats_timer_cb(struct thread *t) { @@ -1714,7 +1716,7 @@ void zfpm_start_stats_timer(void) */ static void zfpm_show_stats(struct vty *vty) { - zfpm_stats_t total_stats; + struct zfpm_stats total_stats; time_t elapsed; vty_out(vty, "\n%-40s %10s Last %2d secs\n\n", "Counter", "Total", diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index 00909df1db..c580fe40d5 100644 --- a/zebra/zebra_fpm_netlink.c +++ b/zebra/zebra_fpm_netlink.c @@ -143,13 +143,13 @@ struct fpm_nh_encap_info_t { }; /* - * netlink_nh_info_t + * netlink_nh_info * * Holds information about a single nexthop for netlink. These info * structures are transient and may contain pointers into rib * data structures for convenience. */ -typedef struct netlink_nh_info_t_ { +struct netlink_nh_info { uint32_t if_index; union g_addr *gateway; @@ -160,14 +160,14 @@ typedef struct netlink_nh_info_t_ { int recursive; enum nexthop_types_t type; struct fpm_nh_encap_info_t encap_info; -} netlink_nh_info_t; +}; /* - * netlink_route_info_t + * netlink_route_info * * A structure for holding information for a netlink route message. */ -typedef struct netlink_route_info_t_ { +struct netlink_route_info { uint16_t nlmsg_type; uint8_t rtm_type; uint32_t rtm_table; @@ -180,9 +180,9 @@ typedef struct netlink_route_info_t_ { /* * Nexthop structures */ - netlink_nh_info_t nhs[MULTIPATH_NUM]; + struct netlink_nh_info nhs[MULTIPATH_NUM]; union g_addr *pref_src; -} netlink_route_info_t; +}; /* * netlink_route_info_add_nh @@ -192,11 +192,11 @@ typedef struct netlink_route_info_t_ { * * Returns true if a nexthop was added, false otherwise. */ -static int netlink_route_info_add_nh(netlink_route_info_t *ri, +static int netlink_route_info_add_nh(struct netlink_route_info *ri, struct nexthop *nexthop, struct route_entry *re) { - netlink_nh_info_t nhi; + struct netlink_nh_info nhi; union g_addr *src; zebra_l3vni_t *zl3vni = NULL; @@ -275,7 +275,7 @@ static uint8_t netlink_proto_from_route_type(int type) * * Returns true on success and false on failure. */ -static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd, +static int netlink_route_info_fill(struct netlink_route_info *ri, int cmd, rib_dest_t *dest, struct route_entry *re) { struct nexthop *nexthop; @@ -353,13 +353,13 @@ static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd, * Returns the number of bytes written to the buffer. 0 or a negative * value indicates an error. */ -static int netlink_route_info_encode(netlink_route_info_t *ri, char *in_buf, - size_t in_buf_len) +static int netlink_route_info_encode(struct netlink_route_info *ri, + char *in_buf, size_t in_buf_len) { size_t bytelen; unsigned int nexthop_num = 0; size_t buf_offset; - netlink_nh_info_t *nhi; + struct netlink_nh_info *nhi; enum fpm_nh_encap_type_t encap; struct rtattr *nest; struct vxlan_encap_info_t *vxlan; @@ -520,9 +520,10 @@ done: * * Helper function to log the information in a route_info structure. */ -static void zfpm_log_route_info(netlink_route_info_t *ri, const char *label) +static void zfpm_log_route_info(struct netlink_route_info *ri, + const char *label) { - netlink_nh_info_t *nhi; + struct netlink_nh_info *nhi; unsigned int i; zfpm_debug("%s : %s %s/%d, Proto: %s, Metric: %u", label, @@ -554,7 +555,7 @@ static void zfpm_log_route_info(netlink_route_info_t *ri, const char *label) int zfpm_netlink_encode_route(int cmd, rib_dest_t *dest, struct route_entry *re, char *in_buf, size_t in_buf_len) { - netlink_route_info_t ri_space, *ri; + struct netlink_route_info ri_space, *ri; ri = &ri_space; diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index de044c0ea0..f24552c80b 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -1793,23 +1793,16 @@ static int nexthop_active(afi_t afi, struct route_entry *re, nexthop->vrf_id); return 0; } - if (connected_is_unnumbered(ifp)) { - if (if_is_operative(ifp)) - return 1; + if (if_is_operative(ifp)) + return 1; + else { if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug( " %s: Onlink and interface %s is not operative", __func__, ifp->name); return 0; } - if (!if_is_operative(ifp)) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug( - " %s: Interface %s is not unnumbered", - __func__, ifp->name); - return 0; - } } if ((top->p.family == AF_INET && top->p.prefixlen == 32 @@ -2099,7 +2092,7 @@ static unsigned nexthop_active_check(struct route_node *rn, * in every case. */ if (!family) { - rib_table_info_t *info; + struct rib_table_info *info; info = srcdest_rnode_table_info(rn); family = info->afi; diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index f89656201c..ae730499ab 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -125,7 +125,7 @@ _rnode_zlog(const char *_func, vrf_id_t vrf_id, struct route_node *rn, va_end(ap); if (rn) { - rib_table_info_t *info = srcdest_rnode_table_info(rn); + struct rib_table_info *info = srcdest_rnode_table_info(rn); srcdest_rnode2str(rn, buf, sizeof(buf)); if (info->safi == SAFI_MULTICAST) @@ -420,7 +420,7 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re, struct route_entry *old) { struct nexthop *nexthop; - rib_table_info_t *info = srcdest_rnode_table_info(rn); + struct rib_table_info *info = srcdest_rnode_table_info(rn); struct zebra_vrf *zvrf = vrf_info_lookup(re->vrf_id); const struct prefix *p, *src_p; enum zebra_dplane_result ret; @@ -503,7 +503,7 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re, void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re) { struct nexthop *nexthop; - rib_table_info_t *info = srcdest_rnode_table_info(rn); + struct rib_table_info *info = srcdest_rnode_table_info(rn); struct zebra_vrf *zvrf = vrf_info_lookup(re->vrf_id); if (info->safi != SAFI_UNICAST) { @@ -546,7 +546,7 @@ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re) /* Uninstall the route from kernel. */ static void rib_uninstall(struct route_node *rn, struct route_entry *re) { - rib_table_info_t *info = srcdest_rnode_table_info(rn); + struct rib_table_info *info = srcdest_rnode_table_info(rn); rib_dest_t *dest = rib_dest_from_rnode(rn); struct nexthop *nexthop; @@ -3085,7 +3085,7 @@ int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, return rib_add_multipath(afi, safi, p, src_p, re, ng); } -static const char *rib_update_event2str(rib_update_event_t event) +static const char *rib_update_event2str(enum rib_update_event event) { const char *ret = "UNKNOWN"; @@ -3125,7 +3125,7 @@ static void rib_update_route_node(struct route_node *rn, int type) } /* Schedule routes of a particular table (address-family) based on event. */ -void rib_update_table(struct route_table *table, rib_update_event_t event) +void rib_update_table(struct route_table *table, enum rib_update_event event) { struct route_node *rn; @@ -3133,13 +3133,14 @@ void rib_update_table(struct route_table *table, rib_update_event_t event) struct zebra_vrf *zvrf; struct vrf *vrf; - zvrf = table->info ? ((rib_table_info_t *)table->info)->zvrf - : NULL; + zvrf = table->info + ? ((struct rib_table_info *)table->info)->zvrf + : NULL; vrf = zvrf ? zvrf->vrf : NULL; zlog_debug("%s: %s VRF %s Table %u event %s", __func__, table->info ? afi2str( - ((rib_table_info_t *)table->info)->afi) + ((struct rib_table_info *)table->info)->afi) : "Unknown", VRF_LOGNAME(vrf), zvrf ? zvrf->table_id : 0, rib_update_event2str(event)); @@ -3173,7 +3174,7 @@ void rib_update_table(struct route_table *table, rib_update_event_t event) } } -static void rib_update_handle_vrf(vrf_id_t vrf_id, rib_update_event_t event) +static void rib_update_handle_vrf(vrf_id_t vrf_id, enum rib_update_event event) { struct route_table *table; @@ -3191,7 +3192,7 @@ static void rib_update_handle_vrf(vrf_id_t vrf_id, rib_update_event_t event) rib_update_table(table, event); } -static void rib_update_handle_vrf_all(rib_update_event_t event) +static void rib_update_handle_vrf_all(enum rib_update_event event) { struct zebra_router_table *zrt; @@ -3205,13 +3206,13 @@ static void rib_update_handle_vrf_all(rib_update_event_t event) } struct rib_update_ctx { - rib_update_event_t event; + enum rib_update_event event; bool vrf_all; vrf_id_t vrf_id; }; static struct rib_update_ctx *rib_update_ctx_init(vrf_id_t vrf_id, - rib_update_event_t event) + enum rib_update_event event) { struct rib_update_ctx *ctx; @@ -3251,7 +3252,7 @@ static int rib_update_handler(struct thread *thread) static struct thread *t_rib_update_threads[RIB_UPDATE_MAX]; /* Schedule a RIB update event for specific vrf */ -void rib_update_vrf(vrf_id_t vrf_id, rib_update_event_t event) +void rib_update_vrf(vrf_id_t vrf_id, enum rib_update_event event) { struct rib_update_ctx *ctx; @@ -3271,7 +3272,7 @@ void rib_update_vrf(vrf_id_t vrf_id, rib_update_event_t event) } /* Schedule a RIB update event for all vrfs */ -void rib_update(rib_update_event_t event) +void rib_update(enum rib_update_event event) { struct rib_update_ctx *ctx; @@ -3425,7 +3426,7 @@ unsigned long rib_score_proto(uint8_t proto, unsigned short instance) void rib_close_table(struct route_table *table) { struct route_node *rn; - rib_table_info_t *info; + struct rib_table_info *info; rib_dest_t *dest; if (!table) diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index f9c74c7462..ad2e00b1ec 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -57,8 +57,8 @@ static void free_state(vrf_id_t vrf_id, struct route_entry *re, static void copy_state(struct rnh *rnh, const struct route_entry *re, struct route_node *rn); static int compare_state(struct route_entry *r1, struct route_entry *r2); -static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type, - vrf_id_t vrf_id); +static int send_client(struct rnh *rnh, struct zserv *client, + enum rnh_type type, vrf_id_t vrf_id); static void print_rnh(struct route_node *rn, struct vty *vty); static int zebra_client_cleanup_rnh(struct zserv *client); @@ -68,7 +68,7 @@ void zebra_rnh_init(void) } static inline struct route_table *get_rnh_table(vrf_id_t vrfid, afi_t afi, - rnh_type_t type) + enum rnh_type type) { struct zebra_vrf *zvrf; struct route_table *t = NULL; @@ -148,7 +148,7 @@ static void zebra_rnh_store_in_routing_table(struct rnh *rnh) route_unlock_node(rn); } -struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid, rnh_type_t type, +struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid, enum rnh_type type, bool *exists) { struct route_table *table; @@ -207,7 +207,8 @@ struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid, rnh_type_t type, return (rn->info); } -struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid, rnh_type_t type) +struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid, + enum rnh_type type) { struct route_table *table; struct route_node *rn; @@ -258,7 +259,7 @@ void zebra_free_rnh(struct rnh *rnh) XFREE(MTYPE_RNH, rnh); } -static void zebra_delete_rnh(struct rnh *rnh, rnh_type_t type) +static void zebra_delete_rnh(struct rnh *rnh, enum rnh_type type) { struct route_node *rn; @@ -289,7 +290,7 @@ static void zebra_delete_rnh(struct rnh *rnh, rnh_type_t type) * and as such it will have a resolved rnh. */ void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client, - rnh_type_t type, vrf_id_t vrf_id) + enum rnh_type type, vrf_id_t vrf_id) { if (IS_ZEBRA_DEBUG_NHT) { char buf[PREFIX2STR_BUFFER]; @@ -308,7 +309,7 @@ void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client, } void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client, - rnh_type_t type) + enum rnh_type type) { if (IS_ZEBRA_DEBUG_NHT) { char buf[PREFIX2STR_BUFFER]; @@ -804,7 +805,7 @@ static void zebra_rnh_eval_nexthop_entry(struct zebra_vrf *zvrf, afi_t afi, /* Evaluate one tracked entry */ static void zebra_rnh_evaluate_entry(struct zebra_vrf *zvrf, afi_t afi, - int force, rnh_type_t type, + int force, enum rnh_type type, struct route_node *nrn) { struct rnh *rnh; @@ -851,7 +852,7 @@ static void zebra_rnh_evaluate_entry(struct zebra_vrf *zvrf, afi_t afi, * covers multiple nexthops we are interested in. */ static void zebra_rnh_clear_nhc_flag(struct zebra_vrf *zvrf, afi_t afi, - rnh_type_t type, struct route_node *nrn) + enum rnh_type type, struct route_node *nrn) { struct rnh *rnh; struct route_entry *re; @@ -875,7 +876,7 @@ static void zebra_rnh_clear_nhc_flag(struct zebra_vrf *zvrf, afi_t afi, * of a particular VRF and address-family or a specific prefix. */ void zebra_evaluate_rnh(struct zebra_vrf *zvrf, afi_t afi, int force, - rnh_type_t type, struct prefix *p) + enum rnh_type type, struct prefix *p) { struct route_table *rnh_table; struct route_node *nrn; @@ -911,7 +912,7 @@ void zebra_evaluate_rnh(struct zebra_vrf *zvrf, afi_t afi, int force, } void zebra_print_rnh_table(vrf_id_t vrfid, afi_t afi, struct vty *vty, - rnh_type_t type, struct prefix *p) + enum rnh_type type, struct prefix *p) { struct route_table *table; struct route_node *rn; @@ -997,8 +998,8 @@ static int compare_state(struct route_entry *r1, struct route_entry *r2) return 0; } -static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type, - vrf_id_t vrf_id) +static int send_client(struct rnh *rnh, struct zserv *client, + enum rnh_type type, vrf_id_t vrf_id) { struct stream *s; struct route_entry *re; @@ -1134,7 +1135,7 @@ static void print_rnh(struct route_node *rn, struct vty *vty) } static int zebra_cleanup_rnh_client(vrf_id_t vrf_id, afi_t afi, - struct zserv *client, rnh_type_t type) + struct zserv *client, enum rnh_type type) { struct route_table *ntable; struct route_node *nrn; diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h index 6e2dab8d9f..f07e5bc791 100644 --- a/zebra/zebra_rnh.h +++ b/zebra/zebra_rnh.h @@ -31,7 +31,7 @@ extern "C" { extern void zebra_rnh_init(void); -static inline const char *rnh_type2str(rnh_type_t type) +static inline const char *rnh_type2str(enum rnh_type type) { switch (type) { case RNH_NEXTHOP_TYPE: @@ -44,20 +44,20 @@ static inline const char *rnh_type2str(rnh_type_t type) } extern struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid, - rnh_type_t type, bool *exists); + enum rnh_type type, bool *exists); extern struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid, - rnh_type_t type); + enum rnh_type type); extern void zebra_free_rnh(struct rnh *rnh); extern void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client, - rnh_type_t type, vrf_id_t vrfid); + enum rnh_type type, vrf_id_t vrfid); extern void zebra_register_rnh_pseudowire(vrf_id_t, struct zebra_pw *); extern void zebra_deregister_rnh_pseudowire(vrf_id_t, struct zebra_pw *); extern void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client, - rnh_type_t type); + enum rnh_type type); extern void zebra_evaluate_rnh(struct zebra_vrf *zvrf, afi_t afi, int force, - rnh_type_t type, struct prefix *p); + enum rnh_type type, struct prefix *p); extern void zebra_print_rnh_table(vrf_id_t vrfid, afi_t afi, struct vty *vty, - rnh_type_t type, struct prefix *p); + enum rnh_type type, struct prefix *p); extern char *rnh_str(struct rnh *rnh, char *buf, int size); extern int rnh_resolve_via_default(struct zebra_vrf *zvrf, int family); diff --git a/zebra/zebra_router.c b/zebra/zebra_router.c index ea2b6752b3..ab426ae603 100644 --- a/zebra/zebra_router.c +++ b/zebra/zebra_router.c @@ -93,7 +93,7 @@ struct route_table *zebra_router_get_table(struct zebra_vrf *zvrf, { struct zebra_router_table finder; struct zebra_router_table *zrt; - rib_table_info_t *info; + struct rib_table_info *info; memset(&finder, 0, sizeof(finder)); finder.afi = afi; @@ -133,7 +133,7 @@ void zebra_router_show_table_summary(struct vty *vty) vty_out(vty, "---------------------------------------------------------------------------\n"); RB_FOREACH (zrt, zebra_router_table_head, &zrouter.tables) { - rib_table_info_t *info = route_table_get_info(zrt->table); + struct rib_table_info *info = route_table_get_info(zrt->table); vty_out(vty, "%-16s%5d %9d %7s %15s %8d %10lu\n", info->zvrf->vrf->name, zrt->ns_id, info->zvrf->vrf->vrf_id, diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index 268ee12a65..d262faa070 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -33,10 +33,10 @@ extern "C" { #endif /* MPLS (Segment Routing) global block */ -typedef struct mpls_srgb_t_ { +struct mpls_srgb { uint32_t start_label; uint32_t end_label; -} mpls_srgb_t; +}; struct zebra_rmap { char *name; @@ -111,7 +111,7 @@ struct zebra_vrf { struct route_table *fec_table[AFI_MAX]; /* MPLS Segment Routing Global block */ - mpls_srgb_t mpls_srgb; + struct mpls_srgb mpls_srgb; /* Pseudowires. */ struct zebra_pw_head pseudowires; diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 8024db4ca7..7a0329a774 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -357,7 +357,8 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, const char *mcast_info = ""; if (mcast) { - rib_table_info_t *info = srcdest_rnode_table_info(rn); + struct rib_table_info *info = + srcdest_rnode_table_info(rn); mcast_info = (info->safi == SAFI_MULTICAST) ? " using Multicast RIB" : " using Unicast RIB"; @@ -978,7 +979,7 @@ static void do_show_ip_route_all(struct vty *vty, struct zebra_vrf *zvrf, unsigned short ospf_instance_id) { struct zebra_router_table *zrt; - rib_table_info_t *info; + struct rib_table_info *info; RB_FOREACH (zrt, zebra_router_table_head, &zrouter.tables) { @@ -1059,7 +1060,7 @@ DEFPY (show_ip_nht, afi_t afi = ipv4 ? AFI_IP : AFI_IP6; vrf_id_t vrf_id = VRF_DEFAULT; struct prefix prefix, *p = NULL; - rnh_type_t rtype; + enum rnh_type rtype; if (strcmp(type, "nht") == 0) rtype = RNH_NEXTHOP_TYPE; @@ -1832,8 +1833,8 @@ static void vty_show_ip_route_summary(struct vty *vty, if (!use_json) vty_out(vty, "%-20s %-20s %s (vrf %s)\n", "Route Source", "Routes", "FIB", - zvrf_name(((rib_table_info_t *)route_table_get_info( - table)) + zvrf_name(((struct rib_table_info *) + route_table_get_info(table)) ->zvrf)); for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { @@ -1980,8 +1981,8 @@ static void vty_show_ip_route_summary_prefix(struct vty *vty, if (!use_json) vty_out(vty, "%-20s %-20s %s (vrf %s)\n", "Route Source", "Prefix Routes", "FIB", - zvrf_name(((rib_table_info_t *)route_table_get_info( - table)) + zvrf_name(((struct rib_table_info *) + route_table_get_info(table)) ->zvrf)); for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { |
