Merge branch 'master' into evpn-bug-fixes

This commit is contained in:
Mitesh Kanjariya 2018-02-27 02:47:36 -08:00 committed by GitHub
commit 00cbfad6de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 2732 additions and 223 deletions

View File

@ -666,10 +666,11 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,
for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom))
attr->ecommunity = ecommunity_merge(attr->ecommunity, ecom);
/* Add the export RTs for L3VNI - currently only supported for IPV4 host
* routes
/*
* only attach l3-vni export rts for ipv4 address family and if we are
* advertising both the labels in type-2 routes
*/
if (afi == AFI_IP) {
if (afi == AFI_IP && CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) {
vrf_export_rtl = bgpevpn_get_vrf_export_rtl(vpn);
if (vrf_export_rtl && !list_isempty(vrf_export_rtl)) {
for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode,
@ -690,7 +691,12 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,
ecommunity_merge(attr->ecommunity, &ecom_sticky);
}
if (afi == AFI_IP && !is_zero_mac(&attr->rmac)) {
/*
* only attach l3-vni rmac for ipv4 address family and if we are
* advertising both the labels in type-2 routes
*/
if (afi == AFI_IP && !is_zero_mac(&attr->rmac) &&
CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) {
memset(&ecom_rmac, 0, sizeof(ecom_rmac));
encode_rmac_extcomm(&eval_rmac, &attr->rmac);
ecom_rmac.size = 1;
@ -1189,8 +1195,13 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
/* The VNI goes into the 'label' field of the route */
vni2label(vpn->vni, &label[0]);
/* Type-2 routes may carry a second VNI - the L3-VNI */
if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) {
/* Type-2 routes may carry a second VNI - the L3-VNI.
* Only attach second label if we are advertising two labels for
* type-2 routes.
*/
if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE &&
CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) {
vni_t l3vni;
l3vni = bgpevpn_get_l3vni(vpn);
@ -1214,7 +1225,8 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
* be advertised with right labels.
*/
vni2label(vpn->vni, &label[0]);
if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) {
if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE &&
CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) {
vni_t l3vni;
l3vni = bgpevpn_get_l3vni(vpn);
@ -4252,7 +4264,8 @@ static void link_l2vni_hash_to_l3vni(struct hash_backet *backet,
int bgp_evpn_local_l3vni_add(vni_t l3vni,
vrf_id_t vrf_id,
struct ethaddr *rmac,
struct in_addr originator_ip)
struct in_addr originator_ip,
int filter)
{
struct bgp *bgp_vrf = NULL; /* bgp VRF instance */
struct bgp *bgp_def = NULL; /* default bgp instance */
@ -4304,6 +4317,10 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni,
/* set the originator ip */
bgp_vrf->originator_ip = originator_ip;
/* set the right filter - are we using l3vni only for prefix routes? */
if (filter)
SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY);
/* auto derive RD/RT */
if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD))
evpn_auto_rt_import_add_for_vrf(bgp_vrf);
@ -4317,9 +4334,12 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni,
link_l2vni_hash_to_l3vni,
bgp_vrf);
/* updates all corresponding local mac-ip routes */
for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn))
update_routes_for_vni(bgp_def, vpn);
/* Only update all corresponding type-2 routes if we are advertising two
* labels along with type-2 routes
*/
if (!filter)
for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn))
update_routes_for_vni(bgp_def, vpn);
/* advertise type-5 routes if needed */
update_advertise_vrf_routes(bgp_vrf);
@ -4378,9 +4398,12 @@ int bgp_evpn_local_l3vni_del(vni_t l3vni,
}
/* update all corresponding local mac-ip routes */
for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn))
update_routes_for_vni(bgp_def, vpn);
if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY)) {
for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn)) {
UNSET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS);
update_routes_for_vni(bgp_def, vpn);
}
}
/* Delete the instance if it was autocreated */
if (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO))

View File

@ -91,7 +91,8 @@ extern int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni,
u_char flags);
extern int bgp_evpn_local_l3vni_add(vni_t vni, vrf_id_t vrf_id,
struct ethaddr *rmac,
struct in_addr originator_ip);
struct in_addr originator_ip,
int filter);
extern int bgp_evpn_local_l3vni_del(vni_t vni, vrf_id_t vrf_id);
extern int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni);
extern int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,

View File

@ -61,6 +61,8 @@ struct bgpevpn {
#define VNI_FLAG_RD_CFGD 0x4 /* RD is user configured. */
#define VNI_FLAG_IMPRT_CFGD 0x8 /* Import RT is user configured */
#define VNI_FLAG_EXPRT_CFGD 0x10 /* Export RT is user configured */
#define VNI_FLAG_USE_TWO_LABELS 0x20 /* Attach both L2-VNI and L3-VNI if
needed for this VPN */
/* Flag to indicate if we are advertising the g/w mac ip for this VNI*/
u_int8_t advertise_gw_macip;
@ -179,9 +181,13 @@ static inline void bgpevpn_unlink_from_l3vni(struct bgpevpn *vpn)
struct bgp *bgp_vrf = NULL;
bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
if (!bgp_vrf || !bgp_vrf->l2vnis)
if (!bgp_vrf)
return;
listnode_delete(bgp_vrf->l2vnis, vpn);
UNSET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS);
if (bgp_vrf->l2vnis)
listnode_delete(bgp_vrf->l2vnis, vpn);
}
static inline void bgpevpn_link_to_l3vni(struct bgpevpn *vpn)
@ -189,9 +195,16 @@ static inline void bgpevpn_link_to_l3vni(struct bgpevpn *vpn)
struct bgp *bgp_vrf = NULL;
bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
if (!bgp_vrf || !bgp_vrf->l2vnis)
if (!bgp_vrf)
return;
listnode_add_sort(bgp_vrf->l2vnis, vpn);
/* check if we are advertising two labels for this vpn */
if (!CHECK_FLAG(bgp_vrf->vrf_flags,
BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY))
SET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS);
if (bgp_vrf->l2vnis)
listnode_add_sort(bgp_vrf->l2vnis, vpn);
}
static inline int is_vni_configured(struct bgpevpn *vpn)

View File

@ -3941,6 +3941,10 @@ DEFUN (show_bgp_vrf_l3vni_info,
vty_out(vty, " L3-VNI: %u\n", bgp->l3vni);
vty_out(vty, " Rmac: %s\n",
prefix_mac2str(&bgp->rmac, buf, sizeof(buf)));
vty_out(vty, " VNI Filter: %s\n",
CHECK_FLAG(bgp->vrf_flags,
BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY) ?
"prefix-routes-only" : "none");
vty_out(vty, " L2-VNI List:\n");
vty_out(vty, " ");
for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn))
@ -3966,6 +3970,10 @@ DEFUN (show_bgp_vrf_l3vni_info,
json_object_string_add(json, "rmac",
prefix_mac2str(&bgp->rmac, buf,
sizeof(buf)));
json_object_string_add(json, "vniFilter",
CHECK_FLAG(bgp->vrf_flags,
BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY)
? "prefix-routes-only" : "none");
/* list of l2vnis */
for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn))
json_object_array_add(json_vnis,

View File

@ -1726,6 +1726,7 @@ static void bgp_zebra_connected(struct zclient *zclient)
static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
int filter = 0;
char buf[ETHER_ADDR_STRLEN];
vni_t l3vni = 0;
struct ethaddr rmac;
@ -1739,16 +1740,20 @@ static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient,
if (cmd == ZEBRA_L3VNI_ADD) {
stream_get(&rmac, s, sizeof(struct ethaddr));
originator_ip.s_addr = stream_get_ipv4(s);
stream_get(&filter, s, sizeof(int));
}
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("Rx L3-VNI %s VRF %s VNI %u RMAC %s",
zlog_debug("Rx L3-VNI %s VRF %s VNI %u RMAC %s filter %s",
(cmd == ZEBRA_L3VNI_ADD) ? "add" : "del",
vrf_id_to_name(vrf_id), l3vni,
prefix_mac2str(&rmac, buf, sizeof(buf)));
vrf_id_to_name(vrf_id),
l3vni,
prefix_mac2str(&rmac, buf, sizeof(buf)),
filter ? "prefix-routes-only" : "none");
if (cmd == ZEBRA_L3VNI_ADD)
bgp_evpn_local_l3vni_add(l3vni, vrf_id, &rmac, originator_ip);
bgp_evpn_local_l3vni_add(l3vni, vrf_id, &rmac, originator_ip,
filter);
else
bgp_evpn_local_l3vni_del(l3vni, vrf_id);

View File

@ -436,6 +436,7 @@ struct bgp {
#define BGP_VRF_IMPORT_RT_CFGD (1 << 3)
#define BGP_VRF_EXPORT_RT_CFGD (1 << 4)
#define BGP_VRF_RD_CFGD (1 << 5)
#define BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY (1 << 6)
/* unique ID for auto derivation of RD for this vrf */
uint16_t vrf_rd_id;

View File

@ -1886,6 +1886,7 @@ AC_CONFIG_FILES([Makefile
doc/eigrpd.8
doc/ripngd.8
doc/pimd.8
doc/mtracebis.8
doc/nhrpd.8
doc/vtysh.1
doc/watchfrr.8

View File

@ -1,5 +1,6 @@
etc/frr/
usr/bin/vtysh
usr/bin/mtracebis
usr/include/frr/
usr/lib/
tools/frr etc/init.d/
@ -15,6 +16,7 @@ usr/share/man/man8/ripngd.8
usr/share/man/man8/zebra.8
usr/share/man/man8/isisd.8
usr/share/man/man8/watchfrr.8
usr/share/man/man8/mtracebis.8
usr/share/snmp/mibs/
tools/etc/* etc/
tools/*.service lib/systemd/system

View File

@ -1,5 +1,6 @@
etc/frr/
usr/bin/vtysh
usr/bin/mtracebis
usr/include/frr/
usr/lib/
tools/frr usr/lib/frr
@ -16,6 +17,7 @@ usr/share/man/man8/zebra.8
usr/share/man/man8/isisd.8
usr/share/man/man8/watchfrr.8
usr/share/man/man8/frr-args.8
usr/share/man/man8/mtracebis.8
usr/share/snmp/mibs/
tools/etc/* etc/
tools/*.service lib/systemd/system

View File

@ -104,6 +104,7 @@ man_MANS = frr.1 frr-args.8
if PIMD
man_MANS += pimd.8
man_MANS += mtracebis.8
endif
if BGPD
@ -169,6 +170,7 @@ EXTRA_DIST = BGP-TypeCode draft-zebra-00.ms draft-zebra-00.txt \
ripd.8.in \
ripngd.8.in \
pimd.8.in \
mtracebis.8.in \
nhrpd.8.in \
vtysh.1.in \
watchfrr.8.in \

25
doc/mtracebis.8.in Normal file
View File

@ -0,0 +1,25 @@
.\" This file was originally generated by help2man 1.44.1.
.TH MTRACEBIS "8" "February 2018" "mtracebis 0.1" "System Administration Utilities"
.SH NAME
mtracebis \- a program to initiate multicast traceroute "mtrace" queries
.SH SYNOPSIS
mtracebis
<multicast source>
.SH DESCRIPTION
.B mtracebis
is a program used to test multicast connectivity in a multicast and multicast
traceroute enabled IP network.
.PP
Initial version of the program requires multicast source IP address and
initiates a weak traceroute across the network. This tests whether the
interfaces towards the source are multicast enabled. First query sent is a
full query, capable of crossing the network all the way to the source. If this
fails, hop-by-hop queries are initiated.
.PP
Hop-by-hop queries start by requesting only a response from the nearest router.
Following that, next query is extended to the next two routers, and so on...
until a set of routers is tested for connectivity.
.SH SEE ALSO
See the project homepage at <@PACKAGE_URL@>.
.SH AUTHORS
Copyright 2018 Mladen Sablic

View File

@ -306,8 +306,11 @@ if_reset(struct iface *iface, int af)
ia = iface_af_get(iface, af);
if_stop_hello_timer(ia);
while ((adj = RB_ROOT(ia_adj_head, &ia->adj_tree)) != NULL)
while (!RB_EMPTY(ia_adj_head, &ia->adj_tree)) {
adj = RB_ROOT(ia_adj_head, &ia->adj_tree);
adj_del(adj, S_SHUTDOWN);
}
/* try to cleanup */
switch (af) {

View File

@ -76,16 +76,21 @@ l2vpn_del(struct l2vpn *l2vpn)
struct l2vpn_if *lif;
struct l2vpn_pw *pw;
while ((lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree)) != NULL) {
while (!RB_EMPTY(l2vpn_if_head, &l2vpn->if_tree)) {
lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree);
RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
free(lif);
}
while ((pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree)) != NULL) {
while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_tree)) {
pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree);
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
free(pw);
}
while ((pw = RB_ROOT(l2vpn_pw_head,
&l2vpn->pw_inactive_tree)) != NULL) {
while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_inactive_tree)) {
pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_inactive_tree);
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
free(pw);
}

View File

@ -1324,8 +1324,11 @@ lde_nbr_clear(void)
{
struct lde_nbr *ln;
while ((ln = RB_ROOT(nbr_tree, &lde_nbrs)) != NULL)
while (!RB_EMPTY(nbr_tree, &lde_nbrs)) {
ln = RB_ROOT(nbr_tree, &lde_nbrs);
lde_nbr_del(ln);
}
}
static void

View File

@ -129,7 +129,9 @@ fec_clear(struct fec_tree *fh, void (*free_cb)(void *))
{
struct fec *f;
while ((f = RB_ROOT(fec_tree, fh)) != NULL) {
while (!RB_EMPTY(fec_tree, fh)) {
f = RB_ROOT(fec_tree, fh);
fec_remove(fh, f);
free_cb(f);
}

View File

@ -1475,18 +1475,23 @@ l2vpn_del_api(struct ldpd_conf *conf, struct l2vpn *l2vpn)
struct l2vpn_if *lif;
struct l2vpn_pw *pw;
while ((lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree)) != NULL) {
while (!RB_EMPTY(l2vpn_if_head, &l2vpn->if_tree)) {
lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree);
QOBJ_UNREG(lif);
RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
free(lif);
}
while ((pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree)) != NULL) {
while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_tree)) {
pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree);
QOBJ_UNREG(pw);
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
free(pw);
}
while ((pw = RB_ROOT(l2vpn_pw_head,
&l2vpn->pw_inactive_tree)) != NULL) {
while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_inactive_tree)) {
pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_inactive_tree);
QOBJ_UNREG(pw);
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
free(pw);

View File

@ -1066,13 +1066,17 @@ ldp_config_reset_main(struct ldpd_conf *conf)
struct iface *iface;
struct nbr_params *nbrp;
while ((iface = RB_ROOT(iface_head, &conf->iface_tree)) != NULL) {
while (!RB_EMPTY(iface_head, &conf->iface_tree)) {
iface = RB_ROOT(iface_head, &conf->iface_tree);
QOBJ_UNREG(iface);
RB_REMOVE(iface_head, &conf->iface_tree, iface);
free(iface);
}
while ((nbrp = RB_ROOT(nbrp_head, &conf->nbrp_tree)) != NULL) {
while (!RB_EMPTY(nbrp_head, &conf->nbrp_tree)) {
nbrp = RB_ROOT(nbrp_head, &conf->nbrp_tree);
QOBJ_UNREG(nbrp);
RB_REMOVE(nbrp_head, &conf->nbrp_tree, nbrp);
free(nbrp);
@ -1128,20 +1132,25 @@ ldp_config_reset_l2vpns(struct ldpd_conf *conf)
struct l2vpn_if *lif;
struct l2vpn_pw *pw;
while ((l2vpn = RB_ROOT(l2vpn_head, &conf->l2vpn_tree)) != NULL) {
while ((lif = RB_ROOT(l2vpn_if_head,
&l2vpn->if_tree)) != NULL) {
while (!RB_EMPTY(l2vpn_head, &conf->l2vpn_tree)) {
l2vpn = RB_ROOT(l2vpn_head, &conf->l2vpn_tree);
while (!RB_EMPTY(l2vpn_if_head, &l2vpn->if_tree)) {
lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree);
QOBJ_UNREG(lif);
RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
free(lif);
}
while ((pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree)) != NULL) {
while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_tree)) {
pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree);
QOBJ_UNREG(pw);
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
free(pw);
}
while ((pw = RB_ROOT(l2vpn_pw_head,
&l2vpn->pw_inactive_tree)) != NULL) {
while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_inactive_tree)) {
pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_inactive_tree);
QOBJ_UNREG(pw);
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
free(pw);
@ -1160,19 +1169,27 @@ ldp_clear_config(struct ldpd_conf *xconf)
struct nbr_params *nbrp;
struct l2vpn *l2vpn;
while ((iface = RB_ROOT(iface_head, &xconf->iface_tree)) != NULL) {
while (!RB_EMPTY(iface_head, &xconf->iface_tree)) {
iface = RB_ROOT(iface_head, &xconf->iface_tree);
RB_REMOVE(iface_head, &xconf->iface_tree, iface);
free(iface);
}
while ((tnbr = RB_ROOT(tnbr_head, &xconf->tnbr_tree)) != NULL) {
while (!RB_EMPTY(tnbr_head, &xconf->tnbr_tree)) {
tnbr = RB_ROOT(tnbr_head, &xconf->tnbr_tree);
RB_REMOVE(tnbr_head, &xconf->tnbr_tree, tnbr);
free(tnbr);
}
while ((nbrp = RB_ROOT(nbrp_head, &xconf->nbrp_tree)) != NULL) {
while (!RB_EMPTY(nbrp_head, &xconf->nbrp_tree)) {
nbrp = RB_ROOT(nbrp_head, &xconf->nbrp_tree);
RB_REMOVE(nbrp_head, &xconf->nbrp_tree, nbrp);
free(nbrp);
}
while ((l2vpn = RB_ROOT(l2vpn_head, &xconf->l2vpn_tree)) != NULL) {
while (!RB_EMPTY(l2vpn_head, &xconf->l2vpn_tree)) {
l2vpn = RB_ROOT(l2vpn_head, &xconf->l2vpn_tree);
RB_REMOVE(l2vpn_head, &xconf->l2vpn_tree, l2vpn);
l2vpn_del(l2vpn);
}

View File

@ -219,8 +219,11 @@ ldpe_shutdown(void)
assert(if_addr != LIST_FIRST(&global.addr_list));
free(if_addr);
}
while ((adj = RB_ROOT(global_adj_head, &global.adj_tree)) != NULL)
while (!RB_EMPTY(global_adj_head, &global.adj_tree)) {
adj = RB_ROOT(global_adj_head, &global.adj_tree);
adj_del(adj, S_SHUTDOWN);
}
/* clean up */
if (iev_lde)

View File

@ -316,7 +316,9 @@ nbr_del(struct nbr *nbr)
mapping_list_clr(&nbr->release_list);
mapping_list_clr(&nbr->abortreq_list);
while ((adj = RB_ROOT(nbr_adj_head, &nbr->adj_tree)) != NULL) {
while (!RB_EMPTY(nbr_adj_head, &nbr->adj_tree)) {
adj = RB_ROOT(nbr_adj_head, &nbr->adj_tree);
adj->nbr = NULL;
RB_REMOVE(nbr_adj_head, &nbr->adj_tree, adj);
}

View File

@ -1078,7 +1078,9 @@ void if_terminate(struct vrf *vrf)
{
struct interface *ifp;
while ((ifp = RB_ROOT(if_name_head, &vrf->ifaces_by_name)) != NULL) {
while (!RB_EMPTY(if_name_head, &vrf->ifaces_by_name)) {
ifp = RB_ROOT(if_name_head, &vrf->ifaces_by_name);
if (ifp->node) {
ifp->node->info = NULL;
route_unlock_node(ifp->node);

View File

@ -424,8 +424,11 @@ void ns_terminate(void)
{
struct ns *ns;
while ((ns = RB_ROOT(ns_head, &ns_tree)) != NULL)
while (!RB_EMPTY(ns_head, &ns_tree)) {
ns = RB_ROOT(ns_head, &ns_tree);
ns_delete(ns);
}
}
/* Create a socket for the NS. */

View File

@ -239,6 +239,7 @@ static inline void ipv4_addr_copy(struct in_addr *dst,
#define IPV4_NET127(a) ((((u_int32_t) (a)) & 0xff000000) == 0x7f000000)
#define IPV4_LINKLOCAL(a) ((((u_int32_t) (a)) & 0xffff0000) == 0xa9fe0000)
#define IPV4_CLASS_DE(a) ((((u_int32_t) (a)) & 0xe0000000) == 0xe0000000)
#define IPV4_MC_LINKLOCAL(a) ((((u_int32_t) (a)) & 0xffffff00) == 0xe0000000)
/* Max bit/byte length of IPv6 address. */
#define IPV6_MAX_BYTELEN 16

View File

@ -419,12 +419,17 @@ void vrf_terminate(void)
zlog_debug("%s: Shutting down vrf subsystem",
__PRETTY_FUNCTION__);
while ((vrf = RB_ROOT(vrf_id_head, &vrfs_by_id)) != NULL) {
while (!RB_EMPTY(vrf_id_head, &vrfs_by_id)) {
vrf = RB_ROOT(vrf_id_head, &vrfs_by_id);
/* Clear configured flag and invoke delete. */
UNSET_FLAG(vrf->status, VRF_CONFIGURED);
vrf_delete(vrf);
}
while ((vrf = RB_ROOT(vrf_name_head, &vrfs_by_name)) != NULL) {
while (!RB_EMPTY(vrf_name_head, &vrfs_by_name)) {
vrf = RB_ROOT(vrf_name_head, &vrfs_by_name);
/* Clear configured flag and invoke delete. */
UNSET_FLAG(vrf->status, VRF_CONFIGURED);
vrf_delete(vrf);

View File

@ -3693,7 +3693,7 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf,
{
struct interface *ifp;
struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id);
json_object *json_vrf = NULL, *json_intf_array = NULL;
json_object *json_vrf = NULL;
json_object *json_interface_sub = NULL, *json_interface = NULL;
if (use_json) {
@ -3701,7 +3701,7 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf,
json_vrf = json_object_new_object();
else
json_vrf = json;
json_intf_array = json_object_new_array();
json_interface = json_object_new_object();
}
if (ospf->instance) {
@ -3715,15 +3715,10 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf,
ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf);
if (intf_name == NULL) {
if (use_json)
json_object_object_add(json_vrf, "interfaces",
json_intf_array);
/* Show All Interfaces.*/
FOR_ALL_INTERFACES (vrf, ifp) {
if (ospf_oi_count(ifp)) {
if (use_json) {
json_interface =
json_object_new_object();
json_interface_sub =
json_object_new_object();
}
@ -3732,14 +3727,15 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf,
use_json);
if (use_json) {
json_object_array_add(json_intf_array,
json_interface);
json_object_object_add(
json_interface, ifp->name,
json_interface_sub);
}
}
}
if (use_json)
json_object_object_add(json_vrf, "interfaces",
json_interface);
} else {
/* Interface name is specified. */
ifp = if_lookup_by_name(intf_name, ospf->vrf_id);
@ -3753,19 +3749,17 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf,
if (use_json) {
json_interface_sub = json_object_new_object();
json_interface = json_object_new_object();
json_object_object_add(json_vrf, "interfaces",
json_intf_array);
}
show_ip_ospf_interface_sub(
vty, ospf, ifp, json_interface_sub, use_json);
if (use_json) {
json_object_array_add(json_intf_array,
json_interface);
json_object_object_add(json_interface,
ifp->name,
json_interface_sub);
json_object_object_add(json_vrf, "interfaces",
json_interface);
}
}
}
@ -4300,7 +4294,7 @@ static int show_ip_ospf_neighbor_common(struct vty *vty, struct ospf *ospf,
{
struct ospf_interface *oi;
struct listnode *node;
json_object *json_vrf = NULL, *json_nbr_array = NULL;
json_object *json_vrf = NULL;
json_object *json_nbr_sub = NULL;
if (use_json) {
@ -4308,7 +4302,7 @@ static int show_ip_ospf_neighbor_common(struct vty *vty, struct ospf *ospf,
json_vrf = json_object_new_object();
else
json_vrf = json;
json_nbr_array = json_object_new_array();
json_nbr_sub = json_object_new_object();
}
if (ospf->instance) {
@ -4322,21 +4316,16 @@ static int show_ip_ospf_neighbor_common(struct vty *vty, struct ospf *ospf,
ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf);
if (!use_json)
show_ip_ospf_neighbour_header(vty);
else
json_object_object_add(json_vrf, "neighbors",
json_nbr_array);
for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) {
if (ospf_interface_neighbor_count(oi) == 0)
continue;
if (use_json) {
json_nbr_sub = json_object_new_object();
json_object_array_add(json_nbr_array, json_nbr_sub);
}
show_ip_ospf_neighbor_sub(vty, oi, json_nbr_sub, use_json);
}
if (use_json) {
json_object_object_add(json_vrf, "neighbors",
json_nbr_sub);
if (use_vrf) {
if (ospf->vrf_id == VRF_DEFAULT)
json_object_object_add(json, "default",

1
pimd/.gitignore vendored
View File

@ -2,6 +2,7 @@
Makefile.in
libpim.a
pimd
mtracebis
test_igmpv3_join
tags
TAGS

View File

@ -59,6 +59,7 @@ debug commands:
clear ip pim interfaces Reset PIM interfaces
clear ip pim oil Rescan PIM OIL (output interface list)
debug igmp IGMP protocol activity
debug mtrace Mtrace protocol activity
debug mroute PIM interaction with kernel MFC cache
debug pim PIM protocol activity
debug pim zebra ZEBRA protocol activity
@ -76,4 +77,6 @@ statistics commands:
pimd:
show memory pim PIM memory statistics
vtysh:
mtrace Multicast traceroute
-x-

373
pimd/mtracebis.c Normal file
View File

@ -0,0 +1,373 @@
/*
* Multicast Traceroute for FRRouting
* Copyright (C) 2018 Mladen Sablic
*
* 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
*/
#ifdef __linux__
#include "pim_igmp_mtrace.h"
#include "checksum.h"
#include "mtracebis_routeget.h"
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <net/if.h>
#include <unistd.h>
#include <getopt.h>
#define MTRACEBIS_VERSION "0.1"
#define MTRACE_TIMEOUT (5)
#define IP_HDR_LEN (sizeof(struct ip))
#define IP_RA_LEN (4)
#define MTRACE_BUF_LEN (MTRACE_HDR_SIZE + (MTRACE_MAX_HOPS * MTRACE_RSP_SIZE))
#define IP_AND_MTRACE_BUF_LEN (IP_HDR_LEN + IP_RA_LEN + MTRACE_BUF_LEN)
static const char *progname;
static void usage(void)
{
fprintf(stderr, "Usage : %s <multicast source>\n", progname);
}
static void version(void)
{
fprintf(stderr, "%s %s\n", progname, MTRACEBIS_VERSION);
}
static int send_query(int fd, struct in_addr to_addr,
struct igmp_mtrace *mtrace)
{
struct sockaddr_in to;
socklen_t tolen;
int sent;
memset(&to, 0, sizeof(to));
to.sin_family = AF_INET;
to.sin_addr = to_addr;
tolen = sizeof(to);
sent = sendto(fd, (char *)mtrace, sizeof(*mtrace), MSG_DONTWAIT,
(struct sockaddr *)&to, tolen);
if (sent < 1)
return -1;
return 0;
}
static void print_query(struct igmp_mtrace *mtrace)
{
char src_str[INET_ADDRSTRLEN];
char dst_str[INET_ADDRSTRLEN];
char grp_str[INET_ADDRSTRLEN];
printf("* Mtrace from %s to %s via group %s\n",
inet_ntop(AF_INET, &mtrace->src_addr, src_str, sizeof(src_str)),
inet_ntop(AF_INET, &mtrace->dst_addr, dst_str, sizeof(dst_str)),
inet_ntop(AF_INET, &mtrace->grp_addr, grp_str, sizeof(grp_str)));
}
static int recv_response(int fd, long msec, int *hops)
{
int recvd;
char mtrace_buf[IP_AND_MTRACE_BUF_LEN];
struct ip *ip;
struct igmp_mtrace *mtrace;
int mtrace_len;
int responses;
int i;
u_short sum;
recvd = recvfrom(fd, mtrace_buf, IP_AND_MTRACE_BUF_LEN, 0, NULL, 0);
if (recvd < 1) {
fprintf(stderr, "recvfrom error: %s\n", strerror(errno));
return -1;
}
if (recvd < (int)sizeof(struct ip)) {
fprintf(stderr, "no ip header\n");
return -1;
}
ip = (struct ip *)mtrace_buf;
if (ip->ip_v != 4) {
fprintf(stderr, "IP not version 4\n");
return -1;
}
sum = ip->ip_sum;
ip->ip_sum = 0;
if (sum != in_cksum(ip, ip->ip_hl * 4))
return -1;
mtrace = (struct igmp_mtrace *)(mtrace_buf + (4 * ip->ip_hl));
mtrace_len = ntohs(ip->ip_len) - ip->ip_hl * 4;
if (mtrace_len < (int)MTRACE_HDR_SIZE)
return -1;
sum = mtrace->checksum;
mtrace->checksum = 0;
if (sum != in_cksum(mtrace, mtrace_len)) {
fprintf(stderr, "mtrace checksum wrong\n");
return -1;
}
if (mtrace->type != PIM_IGMP_MTRACE_RESPONSE)
return -1;
responses = mtrace_len - sizeof(struct igmp_mtrace);
responses /= sizeof(struct igmp_mtrace_rsp);
printf("%ld ms received responses from %d hops.\n", msec, responses);
if (hops)
*hops = responses;
for (i = 0; i < responses; i++) {
struct igmp_mtrace_rsp *rsp = &mtrace->rsp[i];
if (rsp->fwd_code != 0)
printf("-%d fwd. code 0x%2x.\n", i, rsp->fwd_code);
}
return 0;
}
static int wait_for_response(int fd, int *hops)
{
fd_set readfds;
struct timeval timeout;
int ret = -1;
long msec, rmsec, tmsec;
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
memset(&timeout, 0, sizeof(timeout));
timeout.tv_sec = MTRACE_TIMEOUT;
tmsec = timeout.tv_sec * 1000 + timeout.tv_usec / 1000;
do {
ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
if (ret <= 0)
return ret;
rmsec = timeout.tv_sec * 1000 + timeout.tv_usec / 1000;
msec = tmsec - rmsec;
} while (recv_response(fd, msec, hops) != 0);
return ret;
}
int main(int argc, char *const argv[])
{
struct in_addr mc_source;
struct in_addr iface_addr;
struct in_addr gw_addr;
struct in_addr mtrace_addr;
struct igmp_mtrace mtrace;
int hops = 255;
int rhops;
int maxhops = 255;
int perhop = 3;
int ifindex;
int unicast = 1;
int ttl = 64;
int fd = -1;
int ret = -1;
int c;
int i, j;
char ifname[IF_NAMESIZE];
char ip_str[INET_ADDRSTRLEN];
mtrace_addr.s_addr = inet_addr("224.0.1.32");
uid_t uid = getuid();
if (uid != 0) {
printf("must run as root\n");
exit(EXIT_FAILURE);
}
if (argc <= 0)
progname = "mtracebis";
else
progname = argv[0];
if (argc != 2) {
usage();
exit(EXIT_FAILURE);
}
while (1) {
static struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'v'},
{0, 0, 0, 0} };
int option_index = 0;
c = getopt_long(argc, argv, "vh", long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'h':
usage();
exit(0);
case 'v':
version();
exit(0);
default:
usage();
exit(EXIT_FAILURE);
}
}
if (inet_pton(AF_INET, argv[1], &mc_source) != 1) {
usage();
fprintf(stderr, "%s: %s not a valid IPv4 address\n", argv[0],
argv[1]);
exit(EXIT_FAILURE);
}
ifindex = routeget(mc_source, &iface_addr, &gw_addr);
if (ifindex < 0) {
fprintf(stderr, "%s: failed to get route to source %s\n",
argv[0], argv[1]);
exit(EXIT_FAILURE);
}
if (if_indextoname(ifindex, ifname) == NULL) {
fprintf(stderr, "%s: if_indextoname error: %s\n", argv[0],
strerror(errno));
exit(EXIT_FAILURE);
}
/* zero mtrace struct */
memset((char *)&mtrace, 0, sizeof(mtrace));
/* set up query */
mtrace.type = PIM_IGMP_MTRACE_QUERY_REQUEST;
mtrace.hops = hops;
mtrace.checksum = 0;
mtrace.grp_addr.s_addr = 0;
mtrace.src_addr = mc_source;
mtrace.dst_addr = iface_addr;
mtrace.rsp_addr = unicast ? iface_addr : mtrace_addr;
mtrace.rsp_ttl = ttl;
mtrace.qry_id = 0xffffff & time(NULL);
mtrace.checksum = in_cksum(&mtrace, sizeof(mtrace));
fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
if (fd < 1) {
fprintf(stderr, "%s: socket error: %s\n", argv[0],
strerror(errno));
exit(EXIT_FAILURE);
}
ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname,
strlen(ifname));
if (ret < 0) {
fprintf(stderr, "%s: setsockopt error: %s\n", argv[0],
strerror(errno));
ret = EXIT_FAILURE;
goto close_fd;
}
print_query(&mtrace);
if (send_query(fd, gw_addr, &mtrace) < 0) {
fprintf(stderr, "%s: sendto error: %s\n", argv[0],
strerror(errno));
ret = EXIT_FAILURE;
goto close_fd;
}
printf("Querying full reverse path...\n");
ret = wait_for_response(fd, NULL);
if (ret > 0) {
ret = 0;
goto close_fd;
}
if (ret < 0) {
fprintf(stderr, "%s: select error: %s\n", argv[0],
strerror(errno));
ret = EXIT_FAILURE;
goto close_fd;
}
printf(" * ");
printf("switching to hop-by-hop:\n");
printf("%3d ? (%s)\n", 0,
inet_ntop(AF_INET, &mtrace.dst_addr, ip_str, sizeof(ip_str)));
for (i = 1; i < maxhops; i++) {
printf("%3d ", -i);
mtrace.hops = i;
for (j = 0; j < perhop; j++) {
mtrace.qry_id++;
mtrace.checksum = 0;
mtrace.checksum = in_cksum(&mtrace, sizeof(mtrace));
if (send_query(fd, gw_addr, &mtrace) < 0) {
fprintf(stderr, "%s: sendto error: %s\n",
argv[0], strerror(errno));
ret = EXIT_FAILURE;
goto close_fd;
}
ret = wait_for_response(fd, &rhops);
if (ret > 0) {
if (i > rhops) {
ret = 0;
goto close_fd;
}
break;
}
printf(" *");
}
if (ret <= 0)
printf("\n");
}
ret = 0;
close_fd:
close(fd);
exit(ret);
}
#else /* __linux__ */
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
printf("%s implemented only for GNU/Linux\n", argv[0]);
exit(0);
}
#endif /* __linux__ */

700
pimd/mtracebis_netlink.c Normal file
View File

@ -0,0 +1,700 @@
/*
* libnetlink.c RTnetlink service routines.
*
* 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.
*
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
*
*/
#ifdef __linux__
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <fcntl.h>
#include <net/if_arp.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/uio.h>
#include <assert.h>
#include "mtracebis_netlink.h"
int rcvbuf = 1024 * 1024;
void rtnl_close(struct rtnl_handle *rth)
{
if (rth->fd >= 0) {
close(rth->fd);
rth->fd = -1;
}
}
int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions,
int protocol)
{
socklen_t addr_len;
int sndbuf = 32768;
memset(rth, 0, sizeof(*rth));
rth->fd = socket(AF_NETLINK, SOCK_RAW, protocol);
if (rth->fd < 0) {
perror("Cannot open netlink socket");
return -1;
}
if (setsockopt(rth->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) {
perror("SO_SNDBUF");
return -1;
}
if (setsockopt(rth->fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf)) < 0) {
perror("SO_RCVBUF");
return -1;
}
memset(&rth->local, 0, sizeof(rth->local));
rth->local.nl_family = AF_NETLINK;
rth->local.nl_groups = subscriptions;
if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
perror("Cannot bind netlink socket");
return -1;
}
addr_len = sizeof(rth->local);
if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
perror("Cannot getsockname");
return -1;
}
if (addr_len != sizeof(rth->local)) {
fprintf(stderr, "Wrong address length %d\n", addr_len);
return -1;
}
if (rth->local.nl_family != AF_NETLINK) {
fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family);
return -1;
}
rth->seq = time(NULL);
return 0;
}
int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
{
return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);
}
int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
{
struct {
struct nlmsghdr nlh;
struct rtgenmsg g;
} req;
memset(&req, 0, sizeof(req));
req.nlh.nlmsg_len = sizeof(req);
req.nlh.nlmsg_type = type;
req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
req.nlh.nlmsg_pid = 0;
req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
req.g.rtgen_family = family;
return send(rth->fd, (void*)&req, sizeof(req), 0);
}
int rtnl_send(struct rtnl_handle *rth, const char *buf, int len)
{
return send(rth->fd, buf, len, 0);
}
int rtnl_send_check(struct rtnl_handle *rth, const char *buf, int len)
{
struct nlmsghdr *h;
int status;
char resp[1024];
status = send(rth->fd, buf, len, 0);
if (status < 0)
return status;
/* Check for immediate errors */
status = recv(rth->fd, resp, sizeof(resp), MSG_DONTWAIT|MSG_PEEK);
if (status < 0) {
if (errno == EAGAIN)
return 0;
return -1;
}
for (h = (struct nlmsghdr *)resp; NLMSG_OK(h, (uint32_t)status);
h = NLMSG_NEXT(h, status)) {
if (h->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
fprintf(stderr, "ERROR truncated\n");
else
errno = -err->error;
return -1;
}
}
return 0;
}
int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
{
struct nlmsghdr nlh;
struct sockaddr_nl nladdr;
struct iovec iov[2] = {
{ .iov_base = &nlh, .iov_len = sizeof(nlh) },
{ .iov_base = req, .iov_len = len }
};
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = iov,
.msg_iovlen = 2,
};
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nlh.nlmsg_len = NLMSG_LENGTH(len);
nlh.nlmsg_type = type;
nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
nlh.nlmsg_pid = 0;
nlh.nlmsg_seq = rth->dump = ++rth->seq;
return sendmsg(rth->fd, &msg, 0);
}
int rtnl_dump_filter_l(struct rtnl_handle *rth,
const struct rtnl_dump_filter_arg *arg)
{
struct sockaddr_nl nladdr;
struct iovec iov;
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
char buf[16384];
iov.iov_base = buf;
while (1) {
int status;
const struct rtnl_dump_filter_arg *a;
int found_done = 0;
int msglen = 0;
iov.iov_len = sizeof(buf);
status = recvmsg(rth->fd, &msg, 0);
if (status < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
fprintf(stderr, "netlink receive error %s (%d)\n",
strerror(errno), errno);
return -1;
}
if (status == 0) {
fprintf(stderr, "EOF on netlink\n");
return -1;
}
for (a = arg; a->filter; a++) {
struct nlmsghdr *h = (struct nlmsghdr*)buf;
msglen = status;
while (NLMSG_OK(h, (uint32_t)msglen)) {
int err;
if (nladdr.nl_pid != 0 ||
h->nlmsg_pid != rth->local.nl_pid ||
h->nlmsg_seq != rth->dump) {
if (a->junk) {
err = a->junk(&nladdr, h,
a->arg2);
if (err < 0)
return err;
}
goto skip_it;
}
if (h->nlmsg_type == NLMSG_DONE) {
found_done = 1;
break; /* process next filter */
}
if (h->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
fprintf(stderr,
"ERROR truncated\n");
} else {
errno = -err->error;
perror("RTNETLINK answers");
}
return -1;
}
err = a->filter(&nladdr, h, a->arg1);
if (err < 0)
return err;
skip_it:
h = NLMSG_NEXT(h, msglen);
}
}
if (found_done)
return 0;
if (msg.msg_flags & MSG_TRUNC) {
fprintf(stderr, "Message truncated\n");
continue;
}
if (msglen) {
fprintf(stderr, "!!!Remnant of size %d\n", msglen);
exit(1);
}
}
}
int rtnl_dump_filter(struct rtnl_handle *rth,
rtnl_filter_t filter,
void *arg1,
rtnl_filter_t junk,
void *arg2)
{
const struct rtnl_dump_filter_arg a[2] = {
{ .filter = filter, .arg1 = arg1, .junk = junk, .arg2 = arg2 },
{ .filter = NULL, .arg1 = NULL, .junk = NULL, .arg2 = NULL }
};
return rtnl_dump_filter_l(rth, a);
}
int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
unsigned groups, struct nlmsghdr *answer,
rtnl_filter_t junk,
void *jarg)
{
int status;
unsigned seq;
struct nlmsghdr *h;
struct sockaddr_nl nladdr;
struct iovec iov = {
.iov_base = (void*) n,
.iov_len = n->nlmsg_len
};
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
char buf[16384];
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = peer;
nladdr.nl_groups = groups;
n->nlmsg_seq = seq = ++rtnl->seq;
if (answer == NULL)
n->nlmsg_flags |= NLM_F_ACK;
status = sendmsg(rtnl->fd, &msg, 0);
if (status < 0) {
perror("Cannot talk to rtnetlink");
return -1;
}
memset(buf,0,sizeof(buf));
iov.iov_base = buf;
while (1) {
iov.iov_len = sizeof(buf);
status = recvmsg(rtnl->fd, &msg, 0);
if (status < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
fprintf(stderr, "netlink receive error %s (%d)\n",
strerror(errno), errno);
return -1;
}
if (status == 0) {
fprintf(stderr, "EOF on netlink\n");
return -1;
}
if (msg.msg_namelen != sizeof(nladdr)) {
fprintf(stderr, "sender address length == %d\n", msg.msg_namelen);
exit(1);
}
for (h = (struct nlmsghdr*)buf; status >= (int)sizeof(*h); ) {
int err;
int len = h->nlmsg_len;
int l = len - sizeof(*h);
if (l<0 || len>status) {
if (msg.msg_flags & MSG_TRUNC) {
fprintf(stderr, "Truncated message\n");
return -1;
}
fprintf(stderr, "!!!malformed message: len=%d\n", len);
exit(1);
}
if ((int)nladdr.nl_pid != peer ||
h->nlmsg_pid != rtnl->local.nl_pid ||
h->nlmsg_seq != seq) {
if (junk) {
err = junk(&nladdr, h, jarg);
if (err < 0)
return err;
}
/* Don't forget to skip that message. */
status -= NLMSG_ALIGN(len);
h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
continue;
}
if (h->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
if (l < (int)sizeof(struct nlmsgerr)) {
fprintf(stderr, "ERROR truncated\n");
} else {
errno = -err->error;
if (errno == 0) {
if (answer)
memcpy(answer, h, h->nlmsg_len);
return 0;
}
perror("RTNETLINK answers");
}
return -1;
}
if (answer) {
memcpy(answer, h, h->nlmsg_len);
return 0;
}
fprintf(stderr, "Unexpected reply!!!\n");
status -= NLMSG_ALIGN(len);
h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
}
if (msg.msg_flags & MSG_TRUNC) {
fprintf(stderr, "Message truncated\n");
continue;
}
if (status) {
fprintf(stderr, "!!!Remnant of size %d\n", status);
exit(1);
}
}
}
int rtnl_listen(struct rtnl_handle *rtnl,
rtnl_filter_t handler,
void *jarg)
{
int status;
struct nlmsghdr *h;
struct sockaddr_nl nladdr;
struct iovec iov;
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
char buf[8192];
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = 0;
nladdr.nl_groups = 0;
iov.iov_base = buf;
while (1) {
iov.iov_len = sizeof(buf);
status = recvmsg(rtnl->fd, &msg, 0);
if (status < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
fprintf(stderr, "netlink receive error %s (%d)\n",
strerror(errno), errno);
if (errno == ENOBUFS)
continue;
return -1;
}
if (status == 0) {
fprintf(stderr, "EOF on netlink\n");
return -1;
}
if (msg.msg_namelen != sizeof(nladdr)) {
fprintf(stderr, "Sender address length == %d\n", msg.msg_namelen);
exit(1);
}
for (h =(struct nlmsghdr*)buf; status >= (int)sizeof(*h); ) {
int err;
int len = h->nlmsg_len;
int l = len - sizeof(*h);
if (l<0 || len>status) {
if (msg.msg_flags & MSG_TRUNC) {
fprintf(stderr, "Truncated message\n");
return -1;
}
fprintf(stderr, "!!!malformed message: len=%d\n", len);
exit(1);
}
err = handler(&nladdr, h, jarg);
if (err < 0)
return err;
status -= NLMSG_ALIGN(len);
h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
}
if (msg.msg_flags & MSG_TRUNC) {
fprintf(stderr, "Message truncated\n");
continue;
}
if (status) {
fprintf(stderr, "!!!Remnant of size %d\n", status);
exit(1);
}
}
}
int rtnl_from_file(FILE *rtnl, rtnl_filter_t handler,
void *jarg)
{
int status;
struct sockaddr_nl nladdr;
char buf[8192];
struct nlmsghdr *h = (void*)buf;
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = 0;
nladdr.nl_groups = 0;
while (1) {
int err, len;
int l;
status = fread(&buf, 1, sizeof(*h), rtnl);
if (status < 0) {
if (errno == EINTR)
continue;
perror("rtnl_from_file: fread");
return -1;
}
if (status == 0)
return 0;
len = h->nlmsg_len;
l = len - sizeof(*h);
if (l<0 || len>(int)sizeof(buf)) {
fprintf(stderr, "!!!malformed message: len=%d @%lu\n",
len, ftell(rtnl));
return -1;
}
status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl);
if (status < 0) {
perror("rtnl_from_file: fread");
return -1;
}
if (status < l) {
fprintf(stderr, "rtnl-from_file: truncated message\n");
return -1;
}
err = handler(&nladdr, h, jarg);
if (err < 0)
return err;
}
}
int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
{
int len = RTA_LENGTH(4);
struct rtattr *rta;
if ((int)(NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen) {
fprintf(stderr,"addattr32: Error! max allowed bound %d exceeded\n",maxlen);
return -1;
}
rta = NLMSG_TAIL(n);
rta->rta_type = type;
rta->rta_len = len;
memcpy(RTA_DATA(rta), &data, 4);
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
return 0;
}
int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data,
int alen)
{
int len = RTA_LENGTH(alen);
struct rtattr *rta;
if ((int)(NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len)) > maxlen) {
fprintf(stderr, "addattr_l ERROR: message exceeded bound of %d\n",maxlen);
return -1;
}
rta = NLMSG_TAIL(n);
rta->rta_type = type;
rta->rta_len = len;
if (data)
memcpy(RTA_DATA(rta), data, alen);
else
assert(alen == 0);
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
return 0;
}
int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len)
{
if ((int)(NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len)) > maxlen) {
fprintf(stderr, "addraw_l ERROR: message exceeded bound of %d\n",maxlen);
return -1;
}
memcpy(NLMSG_TAIL(n), data, len);
memset((uint8_t *) NLMSG_TAIL(n) + len, 0, NLMSG_ALIGN(len) - len);
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len);
return 0;
}
struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type)
{
struct rtattr *nest = NLMSG_TAIL(n);
addattr_l(n, maxlen, type, NULL, 0);
return nest;
}
int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest)
{
nest->rta_len = (uint8_t *)NLMSG_TAIL(n) - (uint8_t *)nest;
return n->nlmsg_len;
}
struct rtattr *addattr_nest_compat(struct nlmsghdr *n, int maxlen, int type,
const void *data, int len)
{
struct rtattr *start = NLMSG_TAIL(n);
addattr_l(n, maxlen, type, data, len);
addattr_nest(n, maxlen, type);
return start;
}
int addattr_nest_compat_end(struct nlmsghdr *n, struct rtattr *start)
{
struct rtattr *nest = start + NLMSG_ALIGN(start->rta_len);
start->rta_len = (uint8_t *)NLMSG_TAIL(n) - (uint8_t *)start;
addattr_nest_end(n, nest);
return n->nlmsg_len;
}
int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data)
{
int len = RTA_LENGTH(4);
struct rtattr *subrta;
if ((int)(RTA_ALIGN(rta->rta_len) + len) > maxlen) {
fprintf(stderr,"rta_addattr32: Error! max allowed bound %d exceeded\n",maxlen);
return -1;
}
subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
subrta->rta_type = type;
subrta->rta_len = len;
memcpy(RTA_DATA(subrta), &data, 4);
rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
return 0;
}
int rta_addattr_l(struct rtattr *rta, int maxlen, int type,
const void *data, int alen)
{
struct rtattr *subrta;
int len = RTA_LENGTH(alen);
if ((int)(RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len)) > maxlen) {
fprintf(stderr,"rta_addattr_l: Error! max allowed bound %d exceeded\n",maxlen);
return -1;
}
subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
subrta->rta_type = type;
subrta->rta_len = len;
memcpy(RTA_DATA(subrta), data, alen);
rta->rta_len = NLMSG_ALIGN(rta->rta_len) + RTA_ALIGN(len);
return 0;
}
int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
{
memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
while (RTA_OK(rta, len)) {
if ((rta->rta_type <= max) && (!tb[rta->rta_type]))
tb[rta->rta_type] = rta;
rta = RTA_NEXT(rta,len);
}
if (len)
fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
return 0;
}
int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len)
{
int i = 0;
memset(tb, 0, sizeof(struct rtattr *) * max);
while (RTA_OK(rta, len)) {
if (rta->rta_type <= max && i < max)
tb[i++] = rta;
rta = RTA_NEXT(rta,len);
}
if (len)
fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
return i;
}
int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta,
int len)
{
if ((int)RTA_PAYLOAD(rta) < len)
return -1;
if (RTA_PAYLOAD(rta) >= RTA_ALIGN(len) + sizeof(struct rtattr)) {
rta = (struct rtattr *)(uint8_t *)RTA_DATA(rta)+RTA_ALIGN(len);
return parse_rtattr_nested(tb, max, rta);
}
memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
return 0;
}
#endif /* __linux__ */

130
pimd/mtracebis_netlink.h Normal file
View File

@ -0,0 +1,130 @@
/*
* libnetlink.c RTnetlink service routines.
*
* 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.
*
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
*
*/
#ifdef __linux__
#ifndef __LIBNETLINK_H__
#define __LIBNETLINK_H__ 1
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/if_link.h>
#include <linux/if_addr.h>
#include <linux/neighbour.h>
struct rtnl_handle
{
int fd;
struct sockaddr_nl local;
struct sockaddr_nl peer;
__u32 seq;
__u32 dump;
};
extern int rcvbuf;
extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions);
extern int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, int protocol);
extern void rtnl_close(struct rtnl_handle *rth);
extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type);
extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len);
typedef int (*rtnl_filter_t)(const struct sockaddr_nl *,
struct nlmsghdr *n, void *);
struct rtnl_dump_filter_arg
{
rtnl_filter_t filter;
void *arg1;
rtnl_filter_t junk;
void *arg2;
};
extern int rtnl_dump_filter_l(struct rtnl_handle *rth,
const struct rtnl_dump_filter_arg *arg);
extern int rtnl_dump_filter(struct rtnl_handle *rth, rtnl_filter_t filter,
void *arg1,
rtnl_filter_t junk,
void *arg2);
extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
unsigned groups, struct nlmsghdr *answer,
rtnl_filter_t junk,
void *jarg);
extern int rtnl_send(struct rtnl_handle *rth, const char *buf, int);
extern int rtnl_send_check(struct rtnl_handle *rth, const char *buf, int);
extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data);
extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen);
extern int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len);
extern struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type);
extern int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest);
extern struct rtattr *addattr_nest_compat(struct nlmsghdr *n, int maxlen, int type, const void *data, int len);
extern int addattr_nest_compat_end(struct nlmsghdr *n, struct rtattr *nest);
extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data);
extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, const void *data, int alen);
extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len);
extern int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len);
extern int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta, int len);
#define parse_rtattr_nested(tb, max, rta) \
(parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta)))
#define parse_rtattr_nested_compat(tb, max, rta, data, len) \
({ data = RTA_PAYLOAD(rta) >= len ? RTA_DATA(rta) : NULL; \
__parse_rtattr_nested_compat(tb, max, rta, len); })
extern int rtnl_listen(struct rtnl_handle *, rtnl_filter_t handler,
void *jarg);
extern int rtnl_from_file(FILE *, rtnl_filter_t handler,
void *jarg);
#define NLMSG_TAIL(nmsg) \
((struct rtattr *) (((uint8_t *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
#ifndef IFA_RTA
#define IFA_RTA(r) \
((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
#endif
#ifndef IFA_PAYLOAD
#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg))
#endif
#ifndef IFLA_RTA
#define IFLA_RTA(r) \
((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
#endif
#ifndef IFLA_PAYLOAD
#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg))
#endif
#ifndef NDA_RTA
#define NDA_RTA(r) \
((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
#endif
#ifndef NDA_PAYLOAD
#define NDA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndmsg))
#endif
#ifndef NDTA_RTA
#define NDTA_RTA(r) \
((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndtmsg))))
#endif
#ifndef NDTA_PAYLOAD
#define NDTA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndtmsg))
#endif
#endif /* __LIBNETLINK_H__ */
#endif /* __linux__ */

98
pimd/mtracebis_routeget.c Normal file
View File

@ -0,0 +1,98 @@
/*
* Multicast Traceroute for FRRouting
* Copyright (C) 2018 Mladen Sablic
*
* 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
*/
#ifdef __linux__
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <sys/types.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <string.h>
#include "mtracebis_netlink.h"
#include "mtracebis_routeget.h"
static int find_dst(struct nlmsghdr *n, struct in_addr *src, struct in_addr *gw)
{
struct rtmsg *r = NLMSG_DATA(n);
int len = n->nlmsg_len;
struct rtattr *tb[RTA_MAX + 1];
len -= NLMSG_LENGTH(sizeof(*r));
if (len < 0) {
fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
return -1;
}
parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
if (tb[RTA_PREFSRC])
src->s_addr = *(uint32_t *)RTA_DATA(tb[RTA_PREFSRC]);
if (tb[RTA_GATEWAY])
gw->s_addr = *(uint32_t *)RTA_DATA(tb[RTA_GATEWAY]);
if (tb[RTA_OIF])
return *(int *)RTA_DATA(tb[RTA_OIF]);
return 0;
}
int routeget(struct in_addr dst, struct in_addr *src, struct in_addr *gw)
{
struct {
struct nlmsghdr n;
struct rtmsg r;
char buf[1024];
} req;
int ret;
struct rtnl_handle rth = {.fd = -1};
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
req.n.nlmsg_flags = NLM_F_REQUEST;
req.n.nlmsg_type = RTM_GETROUTE;
req.r.rtm_family = AF_INET;
req.r.rtm_table = 0;
req.r.rtm_protocol = 0;
req.r.rtm_scope = 0;
req.r.rtm_type = 0;
req.r.rtm_src_len = 0;
req.r.rtm_dst_len = 0;
req.r.rtm_tos = 0;
addattr_l(&req.n, sizeof(req), RTA_DST, &dst.s_addr, 4);
req.r.rtm_dst_len = 32;
ret = rtnl_open(&rth, 0);
if (ret < 0)
return ret;
if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
ret = -1;
goto close_rth;
}
ret = find_dst(&req.n, src, gw);
close_rth:
rtnl_close(&rth);
return ret;
}
#endif /* __linux__ */

31
pimd/mtracebis_routeget.h Normal file
View File

@ -0,0 +1,31 @@
/*
* Multicast Traceroute for FRRouting
* Copyright (C) 2018 Mladen Sablic
*
* 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
*/
#ifdef __linux__
#ifndef ROUTEGET_H
#define ROUTEGET_H
#include <netinet/in.h>
int routeget(struct in_addr dst, struct in_addr *src, struct in_addr *gw);
#endif /* ROUTEGET */
#endif /* __linux__ */

View File

@ -7311,6 +7311,27 @@ DEFUN (no_debug_msdp_packets,
ALIAS(no_debug_msdp_packets, undebug_msdp_packets_cmd, "undebug msdp packets",
UNDEBUG_STR DEBUG_MSDP_STR DEBUG_MSDP_PACKETS_STR)
DEFUN (debug_mtrace,
debug_mtrace_cmd,
"debug mtrace",
DEBUG_STR
DEBUG_MTRACE_STR)
{
PIM_DO_DEBUG_MTRACE;
return CMD_SUCCESS;
}
DEFUN (no_debug_mtrace,
no_debug_mtrace_cmd,
"no debug mtrace",
NO_STR
DEBUG_STR
DEBUG_MTRACE_STR)
{
PIM_DONT_DEBUG_MTRACE;
return CMD_SUCCESS;
}
DEFUN_NOSH (show_debugging_pim,
show_debugging_pim_cmd,
"show debugging [pim]",
@ -8721,6 +8742,8 @@ void pim_cmd_init(void)
install_element(ENABLE_NODE, &debug_msdp_packets_cmd);
install_element(ENABLE_NODE, &no_debug_msdp_packets_cmd);
install_element(ENABLE_NODE, &undebug_msdp_packets_cmd);
install_element(ENABLE_NODE, &debug_mtrace_cmd);
install_element(ENABLE_NODE, &no_debug_mtrace_cmd);
install_element(CONFIG_NODE, &debug_igmp_cmd);
install_element(CONFIG_NODE, &no_debug_igmp_cmd);
@ -8763,6 +8786,8 @@ void pim_cmd_init(void)
install_element(CONFIG_NODE, &debug_msdp_packets_cmd);
install_element(CONFIG_NODE, &no_debug_msdp_packets_cmd);
install_element(CONFIG_NODE, &undebug_msdp_packets_cmd);
install_element(CONFIG_NODE, &debug_mtrace_cmd);
install_element(CONFIG_NODE, &no_debug_mtrace_cmd);
install_element(CONFIG_NODE, &ip_msdp_mesh_group_member_cmd);
install_element(VRF_NODE, &ip_msdp_mesh_group_member_cmd);

View File

@ -63,6 +63,7 @@
#define DEBUG_MSDP_EVENTS_STR "MSDP protocol events\n"
#define DEBUG_MSDP_INTERNAL_STR "MSDP protocol internal\n"
#define DEBUG_MSDP_PACKETS_STR "MSDP protocol packets\n"
#define DEBUG_MTRACE_STR "Mtrace protocol activity\n"
void pim_cmd_init(void);

View File

@ -85,9 +85,11 @@ static void *if_list_clean(struct pim_interface *pim_ifp)
if (pim_ifp->sec_addr_list)
list_delete_and_null(&pim_ifp->sec_addr_list);
while ((ch = RB_ROOT(pim_ifchannel_rb,
&pim_ifp->ifchannel_rb)) != NULL)
while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) {
ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb);
pim_ifchannel_delete(ch);
}
XFREE(MTYPE_PIM_INTERFACE, pim_ifp);
@ -250,9 +252,11 @@ void pim_if_delete(struct interface *ifp)
if (pim_ifp->boundary_oil_plist)
XFREE(MTYPE_PIM_INTERFACE, pim_ifp->boundary_oil_plist);
while ((ch = RB_ROOT(pim_ifchannel_rb,
&pim_ifp->ifchannel_rb)) != NULL)
while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) {
ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb);
pim_ifchannel_delete(ch);
}
XFREE(MTYPE_PIM_INTERFACE, pim_ifp);

View File

@ -211,8 +211,9 @@ void pim_ifchannel_delete_all(struct interface *ifp)
if (!pim_ifp)
return;
while ((ch = RB_ROOT(pim_ifchannel_rb,
&pim_ifp->ifchannel_rb)) != NULL) {
while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) {
ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb);
pim_ifchannel_delete(ch);
}
}

View File

@ -29,6 +29,7 @@
#include "pim_igmp.h"
#include "pim_igmpv2.h"
#include "pim_igmpv3.h"
#include "pim_igmp_mtrace.h"
#include "pim_iface.h"
#include "pim_sock.h"
#include "pim_mroute.h"
@ -504,6 +505,16 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
case PIM_IGMP_V2_LEAVE_GROUP:
return igmp_v2_recv_leave(igmp, ip_hdr->ip_src, from_str,
igmp_msg, igmp_msg_len);
case PIM_IGMP_MTRACE_RESPONSE:
return igmp_mtrace_recv_response(igmp, ip_hdr, ip_hdr->ip_src,
from_str, igmp_msg,
igmp_msg_len);
break;
case PIM_IGMP_MTRACE_QUERY_REQUEST:
return igmp_mtrace_recv_qry_req(igmp, ip_hdr, ip_hdr->ip_src,
from_str, igmp_msg,
igmp_msg_len);
}
zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);

View File

@ -37,6 +37,8 @@
#define PIM_IGMP_V1_MEMBERSHIP_REPORT (0x12)
#define PIM_IGMP_V2_MEMBERSHIP_REPORT (0x16)
#define PIM_IGMP_V2_LEAVE_GROUP (0x17)
#define PIM_IGMP_MTRACE_RESPONSE (0x1E)
#define PIM_IGMP_MTRACE_QUERY_REQUEST (0x1F)
#define PIM_IGMP_V3_MEMBERSHIP_REPORT (0x22)
#define IGMP_V3_REPORT_HEADER_SIZE (8)

727
pimd/pim_igmp_mtrace.c Normal file
View File

@ -0,0 +1,727 @@
/*
* Multicast traceroute for FRRouting
* Copyright (C) 2017 Mladen Sablic
*
* 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 "pimd.h"
#include "pim_util.h"
#include "pim_sock.h"
#include "pim_rp.h"
#include "pim_oil.h"
#include "pim_ifchannel.h"
#include "pim_macro.h"
#include "pim_igmp_mtrace.h"
static void mtrace_rsp_init(struct igmp_mtrace_rsp *mtrace_rspp)
{
mtrace_rspp->arrival = 0;
mtrace_rspp->incoming.s_addr = 0;
mtrace_rspp->outgoing.s_addr = 0;
mtrace_rspp->prev_hop.s_addr = 0;
mtrace_rspp->in_count = MTRACE_UNKNOWN_COUNT;
mtrace_rspp->out_count = MTRACE_UNKNOWN_COUNT;
mtrace_rspp->total = MTRACE_UNKNOWN_COUNT;
mtrace_rspp->rtg_proto = 0;
mtrace_rspp->fwd_ttl = 0;
mtrace_rspp->mbz = 0;
mtrace_rspp->s = 0;
mtrace_rspp->src_mask = 0;
mtrace_rspp->fwd_code = MTRACE_FWD_CODE_NO_ERROR;
}
static void mtrace_rsp_debug(uint32_t qry_id, int rsp,
struct igmp_mtrace_rsp *mrspp)
{
char inc_str[INET_ADDRSTRLEN];
char out_str[INET_ADDRSTRLEN];
char prv_str[INET_ADDRSTRLEN];
zlog_debug(
"Rx mt(%d) qid=%ud arr=%x in=%s out=%s prev=%s proto=%d fwd=%d",
rsp, ntohl(qry_id), mrspp->arrival,
inet_ntop(AF_INET, &(mrspp->incoming), inc_str,
sizeof(inc_str)),
inet_ntop(AF_INET, &(mrspp->outgoing), out_str,
sizeof(out_str)),
inet_ntop(AF_INET, &(mrspp->prev_hop), prv_str,
sizeof(prv_str)),
mrspp->rtg_proto, mrspp->fwd_code);
}
static void mtrace_debug(struct pim_interface *pim_ifp,
struct igmp_mtrace *mtracep, int mtrace_len)
{
char inc_str[INET_ADDRSTRLEN];
char grp_str[INET_ADDRSTRLEN];
char src_str[INET_ADDRSTRLEN];
char dst_str[INET_ADDRSTRLEN];
char rsp_str[INET_ADDRSTRLEN];
zlog_debug(
"Rx mtrace packet incoming on %s: "
"hops=%d type=%d size=%d, grp=%s, src=%s,"
" dst=%s rsp=%s ttl=%d qid=%ud",
inet_ntop(AF_INET, &(pim_ifp->primary_address), inc_str,
sizeof(inc_str)),
mtracep->hops, mtracep->type, mtrace_len,
inet_ntop(AF_INET, &(mtracep->grp_addr), grp_str,
sizeof(grp_str)),
inet_ntop(AF_INET, &(mtracep->src_addr), src_str,
sizeof(src_str)),
inet_ntop(AF_INET, &(mtracep->dst_addr), dst_str,
sizeof(dst_str)),
inet_ntop(AF_INET, &(mtracep->rsp_addr), rsp_str,
sizeof(rsp_str)),
mtracep->rsp_ttl, ntohl(mtracep->qry_id));
if (mtrace_len > (int)sizeof(struct igmp_mtrace)) {
int i;
int responses = mtrace_len - sizeof(struct igmp_mtrace);
if ((responses % sizeof(struct igmp_mtrace_rsp)) != 0)
if (PIM_DEBUG_MTRACE)
zlog_debug(
"Mtrace response block of wrong"
" length");
responses = responses / sizeof(struct igmp_mtrace_rsp);
for (i = 0; i < responses; i++)
mtrace_rsp_debug(mtracep->qry_id, i, &mtracep->rsp[i]);
}
}
/* 5.1 Query Arrival Time */
static uint32_t query_arrival_time(void)
{
struct timeval tv;
uint32_t qat;
char m_qat[] = "Query arrival time lookup failed: errno=%d: %s";
if (gettimeofday(&tv, NULL) < 0) {
if (PIM_DEBUG_MTRACE)
zlog_warn(m_qat, errno, safe_strerror(errno));
return 0;
}
/* not sure second offset correct, as I get different value */
qat = ((tv.tv_sec + 32384) << 16) + ((tv.tv_usec << 10) / 15625);
return qat;
}
static int mtrace_send_packet(struct interface *ifp,
struct igmp_mtrace *mtracep,
size_t mtrace_buf_len, struct in_addr dst_addr,
struct in_addr group_addr)
{
struct sockaddr_in to;
struct pim_interface *pim_ifp;
socklen_t tolen;
ssize_t sent;
int ret;
int fd;
char pim_str[INET_ADDRSTRLEN];
char rsp_str[INET_ADDRSTRLEN];
u_char ttl;
pim_ifp = ifp->info;
memset(&to, 0, sizeof(to));
to.sin_family = AF_INET;
to.sin_addr = dst_addr;
tolen = sizeof(to);
if (PIM_DEBUG_MTRACE)
zlog_debug("Sending mtrace packet to %s on %s",
inet_ntop(AF_INET, &mtracep->rsp_addr, rsp_str,
sizeof(rsp_str)),
inet_ntop(AF_INET, &pim_ifp->primary_address,
pim_str, sizeof(pim_str)));
fd = pim_socket_raw(IPPROTO_IGMP);
if (fd < 0)
return -1;
ret = pim_socket_bind(fd, ifp);
if (ret < 0) {
ret = -1;
goto close_fd;
}
if (IPV4_CLASS_DE(ntohl(dst_addr.s_addr))) {
if (IPV4_MC_LINKLOCAL(ntohl(dst_addr.s_addr))) {
ttl = 1;
} else {
if (mtracep->type == PIM_IGMP_MTRACE_RESPONSE)
ttl = mtracep->rsp_ttl;
else
ttl = 64;
}
ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
sizeof(ttl));
if (ret < 0) {
if (PIM_DEBUG_MTRACE)
zlog_warn("Failed to set socket multicast TTL");
ret = -1;
goto close_fd;
}
}
sent = sendto(fd, (char *)mtracep, mtrace_buf_len, MSG_DONTWAIT,
(struct sockaddr *)&to, tolen);
if (sent != (ssize_t)mtrace_buf_len) {
char dst_str[INET_ADDRSTRLEN];
char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
pim_inet4_dump("<group?>", group_addr, group_str,
sizeof(group_str));
if (sent < 0) {
if (PIM_DEBUG_MTRACE)
zlog_warn(
"Send mtrace request failed for %s on"
"%s: group=%s msg_size=%zd: errno=%d: "
" %s",
dst_str, ifp->name, group_str,
mtrace_buf_len, errno,
safe_strerror(errno));
} else {
if (PIM_DEBUG_MTRACE)
zlog_warn(
"Send mtrace request failed for %s on"
" %s: group=%s msg_size=%zd: sent=%zd",
dst_str, ifp->name, group_str,
mtrace_buf_len, sent);
}
ret = -1;
goto close_fd;
}
ret = 0;
close_fd:
close(fd);
return ret;
}
static int mtrace_un_forward_packet(struct pim_instance *pim, struct ip *ip_hdr,
struct interface *interface)
{
struct pim_nexthop nexthop;
struct sockaddr_in to;
struct interface *if_out;
socklen_t tolen;
int ret;
int fd;
int sent;
uint16_t checksum;
checksum = ip_hdr->ip_sum;
ip_hdr->ip_sum = 0;
if (checksum != in_cksum(ip_hdr, ip_hdr->ip_hl * 4))
return -1;
if (ip_hdr->ip_ttl-- <= 1)
return -1;
ip_hdr->ip_sum = in_cksum(ip_hdr, ip_hdr->ip_hl * 4);
fd = pim_socket_raw(IPPROTO_RAW);
if (fd < 0)
return -1;
pim_socket_ip_hdr(fd);
if (interface == NULL) {
ret = pim_nexthop_lookup(pim, &nexthop, ip_hdr->ip_dst, 0);
if (ret != 0) {
if (PIM_DEBUG_MTRACE)
zlog_warn(
"Dropping mtrace packet, "
"no route to destination");
return -1;
}
if_out = nexthop.interface;
} else {
if_out = interface;
}
ret = pim_socket_bind(fd, if_out);
if (ret < 0) {
close(fd);
return -1;
}
memset(&to, 0, sizeof(to));
to.sin_family = AF_INET;
to.sin_addr = ip_hdr->ip_dst;
tolen = sizeof(to);
sent = sendto(fd, ip_hdr, ntohs(ip_hdr->ip_len), 0,
(struct sockaddr *)&to, tolen);
close(fd);
if (sent < 0) {
if (PIM_DEBUG_MTRACE)
zlog_warn(
"Failed to forward mtrace packet:"
" sendto errno=%d, %s",
errno, safe_strerror(errno));
return -1;
}
if (PIM_DEBUG_MTRACE) {
zlog_debug("Fwd mtrace packet len=%u to %s ttl=%u",
ntohs(ip_hdr->ip_len), inet_ntoa(ip_hdr->ip_dst),
ip_hdr->ip_ttl);
}
return 0;
}
static int mtrace_mc_forward_packet(struct pim_instance *pim, struct ip *ip_hdr)
{
struct prefix_sg sg;
struct channel_oil *c_oil;
struct listnode *chnode;
struct listnode *chnextnode;
struct pim_ifchannel *ch = NULL;
int ret = -1;
memset(&sg, 0, sizeof(struct prefix_sg));
sg.grp = ip_hdr->ip_dst;
c_oil = pim_find_channel_oil(pim, &sg);
if (c_oil == NULL) {
if (PIM_DEBUG_MTRACE) {
zlog_debug(
"Dropping mtrace multicast packet "
"len=%u to %s ttl=%u",
ntohs(ip_hdr->ip_len),
inet_ntoa(ip_hdr->ip_dst), ip_hdr->ip_ttl);
}
return -1;
}
if (c_oil->up == NULL)
return -1;
if (c_oil->up->ifchannels == NULL)
return -1;
for (ALL_LIST_ELEMENTS(c_oil->up->ifchannels, chnode, chnextnode, ch)) {
if (pim_macro_chisin_oiflist(ch)) {
int r;
r = mtrace_un_forward_packet(pim, ip_hdr,
ch->interface);
if (r == 0)
ret = 0;
}
}
return ret;
}
static int mtrace_forward_packet(struct pim_instance *pim, struct ip *ip_hdr)
{
if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr)))
return mtrace_mc_forward_packet(pim, ip_hdr);
else
return mtrace_un_forward_packet(pim, ip_hdr, NULL);
}
/* 6.5 Sending Traceroute Responses */
static int mtrace_send_mc_response(struct pim_instance *pim,
struct igmp_mtrace *mtracep,
size_t mtrace_len)
{
struct prefix_sg sg;
struct channel_oil *c_oil;
struct listnode *chnode;
struct listnode *chnextnode;
struct pim_ifchannel *ch = NULL;
int ret = -1;
memset(&sg, 0, sizeof(struct prefix_sg));
sg.grp = mtracep->rsp_addr;
c_oil = pim_find_channel_oil(pim, &sg);
if (c_oil == NULL) {
if (PIM_DEBUG_MTRACE) {
zlog_debug(
"Dropping mtrace multicast response packet "
"len=%u to %s",
(unsigned int)mtrace_len,
inet_ntoa(mtracep->rsp_addr));
}
return -1;
}
if (c_oil->up == NULL)
return -1;
if (c_oil->up->ifchannels == NULL)
return -1;
for (ALL_LIST_ELEMENTS(c_oil->up->ifchannels, chnode, chnextnode, ch)) {
if (pim_macro_chisin_oiflist(ch)) {
int r;
r = mtrace_send_packet(ch->interface, mtracep,
mtrace_len, mtracep->rsp_addr,
mtracep->grp_addr);
if (r == 0)
ret = 0;
}
}
return ret;
}
static int mtrace_send_response(struct pim_instance *pim,
struct igmp_mtrace *mtracep, size_t mtrace_len)
{
struct pim_nexthop nexthop;
int ret;
mtracep->type = PIM_IGMP_MTRACE_RESPONSE;
mtracep->checksum = 0;
mtracep->checksum = in_cksum((char *)mtracep, mtrace_len);
if (IPV4_CLASS_DE(ntohl(mtracep->rsp_addr.s_addr))) {
struct pim_rpf *p_rpf;
char grp_str[INET_ADDRSTRLEN];
if (pim_rp_i_am_rp(pim, mtracep->rsp_addr))
return mtrace_send_mc_response(pim, mtracep,
mtrace_len);
p_rpf = pim_rp_g(pim, mtracep->rsp_addr);
if (p_rpf == NULL) {
if (PIM_DEBUG_MTRACE)
zlog_warn("mtrace no RP for %s",
inet_ntop(AF_INET,
&(mtracep->rsp_addr),
grp_str, sizeof(grp_str)));
return -1;
}
nexthop = p_rpf->source_nexthop;
if (PIM_DEBUG_MTRACE)
zlog_debug("mtrace response to RP");
} else {
/* TODO: should use unicast rib lookup */
ret = pim_nexthop_lookup(pim, &nexthop, mtracep->rsp_addr, 1);
if (ret != 0) {
if (PIM_DEBUG_MTRACE)
zlog_warn(
"Dropped response qid=%ud, no route to "
"response address",
mtracep->qry_id);
return -1;
}
}
return mtrace_send_packet(nexthop.interface, mtracep, mtrace_len,
mtracep->rsp_addr, mtracep->grp_addr);
}
int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr,
struct in_addr from, const char *from_str,
char *igmp_msg, int igmp_msg_len)
{
static uint32_t qry_id, qry_src;
char mtrace_buf[MTRACE_HDR_SIZE + MTRACE_MAX_HOPS * MTRACE_RSP_SIZE];
struct pim_nexthop nexthop;
struct interface *ifp;
struct interface *out_ifp;
struct pim_interface *pim_ifp;
struct pim_interface *pim_out_ifp;
struct pim_instance *pim;
struct igmp_mtrace *mtracep;
struct igmp_mtrace_rsp *rspp;
struct in_addr nh_addr;
enum mtrace_fwd_code fwd_code = MTRACE_FWD_CODE_NO_ERROR;
int ret;
size_t r_len;
int last_rsp_ind = 0;
size_t mtrace_len;
uint16_t recv_checksum;
uint16_t checksum;
ifp = igmp->interface;
pim_ifp = ifp->info;
pim = pim_ifp->pim;
/*
* 6. Router Behaviour
* Check if mtrace packet is addressed elsewhere and forward,
* if applicable
*/
if (!IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr)))
if (!if_lookup_exact_address(&ip_hdr->ip_dst, AF_INET,
pim->vrf_id))
return mtrace_forward_packet(pim, ip_hdr);
if (igmp_msg_len < (int)sizeof(struct igmp_mtrace)) {
if (PIM_DEBUG_MTRACE)
zlog_warn(
"Recv mtrace packet from %s on %s: too short,"
" len=%d, min=%zu",
from_str, ifp->name, igmp_msg_len,
sizeof(struct igmp_mtrace));
return -1;
}
mtracep = (struct igmp_mtrace *)igmp_msg;
recv_checksum = mtracep->checksum;
mtracep->checksum = 0;
checksum = in_cksum(igmp_msg, igmp_msg_len);
if (recv_checksum != checksum) {
if (PIM_DEBUG_MTRACE)
zlog_warn(
"Recv mtrace packet from %s on %s: checksum"
" mismatch: received=%x computed=%x",
from_str, ifp->name, recv_checksum, checksum);
return -1;
}
if (PIM_DEBUG_MTRACE)
mtrace_debug(pim_ifp, mtracep, igmp_msg_len);
/* subtract header from message length */
r_len = igmp_msg_len - sizeof(struct igmp_mtrace);
/* Classify mtrace packet, check if it is a query */
if (!r_len) {
if (PIM_DEBUG_MTRACE)
zlog_debug("Received IGMP multicast traceroute query");
/* 6.1.1 Packet verification */
if (!pim_if_connected_to_source(ifp, mtracep->dst_addr)) {
if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr))) {
if (PIM_DEBUG_MTRACE)
zlog_debug(
"Dropping multicast query "
"on wrong interface");
return -1;
}
/* Unicast query on wrong interface */
fwd_code = MTRACE_FWD_CODE_WRONG_IF;
}
if (qry_id == mtracep->qry_id && qry_src == from.s_addr) {
if (PIM_DEBUG_MTRACE)
zlog_debug(
"Dropping multicast query with "
"duplicate source and id");
return -1;
}
qry_id = mtracep->qry_id;
qry_src = from.s_addr;
}
/* if response fields length is equal to a whole number of responses */
else if ((r_len % sizeof(struct igmp_mtrace_rsp)) == 0) {
r_len = igmp_msg_len - sizeof(struct igmp_mtrace);
if (r_len != 0)
last_rsp_ind = r_len / sizeof(struct igmp_mtrace_rsp);
if (last_rsp_ind > MTRACE_MAX_HOPS) {
if (PIM_DEBUG_MTRACE)
zlog_warn("Mtrace request of excessive size");
return -1;
}
} else {
if (PIM_DEBUG_MTRACE)
zlog_warn(
"Recv mtrace packet from %s on %s: "
"invalid length %d",
from_str, ifp->name, igmp_msg_len);
return -1;
}
/* 6.2.1 Packet Verification - drop not link-local multicast */
if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr))
&& !IPV4_MC_LINKLOCAL(ntohl(ip_hdr->ip_dst.s_addr))) {
if (PIM_DEBUG_MTRACE)
zlog_warn(
"Recv mtrace packet from %s on %s:"
" not link-local multicast %s",
from_str, ifp->name, inet_ntoa(ip_hdr->ip_dst));
return -1;
}
/* 6.2.2. Normal Processing */
/* 6.2.2. 1. */
if (last_rsp_ind == MTRACE_MAX_HOPS) {
mtracep->rsp[MTRACE_MAX_HOPS - 1].fwd_code =
MTRACE_FWD_CODE_NO_SPACE;
return mtrace_send_response(pim_ifp->pim, mtracep,
igmp_msg_len);
}
/* calculate new mtrace mtrace lenght with extra response */
mtrace_len = igmp_msg_len + sizeof(struct igmp_mtrace_rsp);
/* copy received query/request */
memcpy(mtrace_buf, igmp_msg, igmp_msg_len);
/* repoint mtracep pointer to copy */
mtracep = (struct igmp_mtrace *)mtrace_buf;
/* pointer for extra response field to be filled in */
rspp = &mtracep->rsp[last_rsp_ind];
/* initialize extra response field */
mtrace_rsp_init(rspp);
rspp->arrival = htonl(query_arrival_time());
rspp->outgoing = pim_ifp->primary_address;
rspp->out_count = htonl(MTRACE_UNKNOWN_COUNT);
/* 6.2.2. 2. Attempt to determine forwarding information */
nh_addr.s_addr = 0;
ret = pim_nexthop_lookup(pim, &nexthop, mtracep->src_addr, 1);
if (ret == 0) {
char nexthop_str[INET_ADDRSTRLEN];
if (PIM_DEBUG_MTRACE)
zlog_debug("mtrace pim_nexthop_lookup OK");
if (PIM_DEBUG_MTRACE)
zlog_warn("mtrace next_hop=%s",
inet_ntop(nexthop.mrib_nexthop_addr.family,
&nexthop.mrib_nexthop_addr.u.prefix,
nexthop_str, sizeof(nexthop_str)));
if (nexthop.mrib_nexthop_addr.family == AF_INET)
nh_addr = nexthop.mrib_nexthop_addr.u.prefix4;
}
/* 6.4 Forwarding Traceroute Requests: ... Otherwise, ... */
else {
if (PIM_DEBUG_MTRACE)
zlog_debug("mtrace not found neighbor");
if (!fwd_code)
rspp->fwd_code = MTRACE_FWD_CODE_NO_ROUTE;
else
rspp->fwd_code = fwd_code;
/* 6.5 Sending Traceroute Responses */
return mtrace_send_response(pim, mtracep, mtrace_len);
}
out_ifp = nexthop.interface;
pim_out_ifp = out_ifp->info;
rspp->incoming = pim_out_ifp->primary_address;
rspp->prev_hop = nh_addr;
rspp->in_count = htonl(MTRACE_UNKNOWN_COUNT);
rspp->total = htonl(MTRACE_UNKNOWN_COUNT);
rspp->rtg_proto = MTRACE_RTG_PROTO_PIM;
rspp->s = 1;
rspp->src_mask = 32;
if (nh_addr.s_addr == 0) {
/* reached source? */
if (pim_if_connected_to_source(out_ifp, mtracep->src_addr))
return mtrace_send_response(pim, mtracep, mtrace_len);
/*
* 6.4 Forwarding Traceroute Requests:
* Previous-hop router not known
*/
inet_aton(MCAST_ALL_ROUTERS, &nh_addr);
}
if (mtracep->hops <= (last_rsp_ind + 1))
return mtrace_send_response(pim, mtracep, mtrace_len);
mtracep->checksum = 0;
mtracep->checksum = in_cksum(mtrace_buf, mtrace_len);
return mtrace_send_packet(out_ifp, mtracep, mtrace_len, nh_addr,
mtracep->grp_addr);
}
int igmp_mtrace_recv_response(struct igmp_sock *igmp, struct ip *ip_hdr,
struct in_addr from, const char *from_str,
char *igmp_msg, int igmp_msg_len)
{
static uint32_t qry_id, rsp_dst;
struct interface *ifp;
struct pim_interface *pim_ifp;
struct pim_instance *pim;
struct igmp_mtrace *mtracep;
uint16_t recv_checksum;
uint16_t checksum;
ifp = igmp->interface;
pim_ifp = ifp->info;
pim = pim_ifp->pim;
mtracep = (struct igmp_mtrace *)igmp_msg;
recv_checksum = mtracep->checksum;
mtracep->checksum = 0;
checksum = in_cksum(igmp_msg, igmp_msg_len);
if (recv_checksum != checksum) {
if (PIM_DEBUG_MTRACE)
zlog_warn(
"Recv mtrace response from %s on %s: checksum"
" mismatch: received=%x computed=%x",
from_str, ifp->name, recv_checksum, checksum);
return -1;
}
mtracep->checksum = checksum;
if (PIM_DEBUG_MTRACE)
mtrace_debug(pim_ifp, mtracep, igmp_msg_len);
/* Drop duplicate packets */
if (qry_id == mtracep->qry_id && rsp_dst == ip_hdr->ip_dst.s_addr) {
if (PIM_DEBUG_MTRACE)
zlog_debug("duplicate mtrace response packet dropped");
return -1;
}
qry_id = mtracep->qry_id;
rsp_dst = ip_hdr->ip_dst.s_addr;
return mtrace_forward_packet(pim, ip_hdr);
}

103
pimd/pim_igmp_mtrace.h Normal file
View File

@ -0,0 +1,103 @@
/*
* Multicast traceroute for FRRouting
* Copyright (C) 2017 Mladen Sablic
*
* 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 PIM_IGMP_MTRACE_H
#define PIM_IGMP_MTRACE_H
#include <zebra.h>
#include "pim_igmp.h"
#define MTRACE_MAX_HOPS (255)
#define MTRACE_UNKNOWN_COUNT (0xffffffff)
enum mtrace_fwd_code {
MTRACE_FWD_CODE_NO_ERROR = 0x00,
MTRACE_FWD_CODE_WRONG_IF = 0x01,
MTRACE_FWD_CODE_PRUNE_SENT = 0x02,
MTRACE_FWD_CODE_PRUNE_RCVD = 0x03,
MTRACE_FWD_CODE_SCOPED = 0x04,
MTRACE_FWD_CODE_NO_ROUTE = 0x05,
MTRACE_FWD_CODE_WRONG_LAST_HOP = 0x06,
MTRACE_FWD_CODE_NOT_FORWARDING = 0x07,
MTRACE_FWD_CODE_REACHED_RP = 0x08,
MTRACE_FWD_CODE_RPF_IF = 0x09,
MTRACE_FWD_CODE_NO_MULTICAST = 0x0A,
MTRACE_FWD_CODE_INFO_HIDDEN = 0x0B,
MTRACE_FWD_CODE_NO_SPACE = 0x81,
MTRACE_FWD_CODE_OLD_ROUTER = 0x82,
MTRACE_FWD_CODE_ADMIN_PROHIB = 0x83
};
enum mtrace_rtg_proto {
MTRACE_RTG_PROTO_DVMRP = 1,
MTRACE_RTG_PROTO_MOSPF = 2,
MTRACE_RTG_PROTO_PIM = 3,
MTRACE_RTG_PROTO_CBT = 4,
MTRACE_RTG_PROTO_PIM_SPECIAL = 5,
MTRACE_RTG_PROTO_PIM_STATIC = 6,
MTRACE_RTG_PROTO_DVMRP_STATIC = 7,
MTRACE_RTG_PROTO_PIM_MBGP = 8,
MTRACE_RTG_PROTO_CBT_SPECIAL = 9,
MTRACE_RTG_PROTO_CBT_STATIC = 10,
MTRACE_RTG_PROTO_PIM_ASSERT = 11,
};
struct igmp_mtrace_rsp {
uint32_t arrival;
struct in_addr incoming;
struct in_addr outgoing;
struct in_addr prev_hop;
uint32_t in_count;
uint32_t out_count;
uint32_t total;
uint32_t rtg_proto : 8;
uint32_t fwd_ttl : 8;
/* little endian order for next three fields */
uint32_t src_mask : 6;
uint32_t s : 1;
uint32_t mbz : 1;
uint32_t fwd_code : 8;
} __attribute__((packed));
struct igmp_mtrace {
uint8_t type;
uint8_t hops;
uint16_t checksum;
struct in_addr grp_addr;
struct in_addr src_addr;
struct in_addr dst_addr;
struct in_addr rsp_addr;
uint32_t rsp_ttl : 8;
uint32_t qry_id : 24;
struct igmp_mtrace_rsp rsp[0];
} __attribute__((packed));
#define MTRACE_HDR_SIZE (sizeof(struct igmp_mtrace))
#define MTRACE_RSP_SIZE (sizeof(struct igmp_mtrace_rsp))
int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr,
struct in_addr from, const char *from_str,
char *igmp_msg, int igmp_msg_len);
int igmp_mtrace_recv_response(struct igmp_sock *igmp, struct ip *ip_hdr,
struct in_addr from, const char *from_str,
char *igmp_msg, int igmp_msg_len);
#endif /* PIM_IGMP_MTRACE_H */

View File

@ -135,8 +135,8 @@ void pim_channel_oil_free(struct channel_oil *c_oil)
XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
}
static struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
struct prefix_sg *sg)
struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
struct prefix_sg *sg)
{
struct channel_oil *c_oil = NULL;
struct channel_oil lookup;

View File

@ -86,6 +86,8 @@ void pim_oil_init(struct pim_instance *pim);
void pim_oil_terminate(struct pim_instance *pim);
void pim_channel_oil_free(struct channel_oil *c_oil);
struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
struct prefix_sg *sg);
struct channel_oil *pim_channel_oil_add(struct pim_instance *pim,
struct prefix_sg *sg,
int input_vif_index);

View File

@ -78,6 +78,11 @@ int pim_debug_config_write(struct vty *vty)
++writes;
}
if (PIM_DEBUG_MTRACE) {
vty_out(vty, "debug mtrace\n");
++writes;
}
if (PIM_DEBUG_MROUTE_DETAIL) {
vty_out(vty, "debug mroute detail\n");
++writes;

View File

@ -112,6 +112,7 @@
#define PIM_MASK_PIM_NHT (1 << 22)
#define PIM_MASK_PIM_NHT_DETAIL (1 << 23)
#define PIM_MASK_PIM_NHT_RP (1 << 24)
#define PIM_MASK_MTRACE (1 << 25)
/* Remember 32 bits!!! */
/* PIM error codes */
@ -188,6 +189,7 @@ extern int32_t qpim_register_probe_time;
#define PIM_DEBUG_PIM_NHT (qpim_debugs & PIM_MASK_PIM_NHT)
#define PIM_DEBUG_PIM_NHT_DETAIL (qpim_debugs & PIM_MASK_PIM_NHT_DETAIL)
#define PIM_DEBUG_PIM_NHT_RP (qpim_debugs & PIM_MASK_PIM_NHT_RP)
#define PIM_DEBUG_MTRACE (qpim_debugs & PIM_MASK_MTRACE)
#define PIM_DEBUG_EVENTS (qpim_debugs & (PIM_MASK_PIM_EVENTS | PIM_MASK_IGMP_EVENTS | PIM_MASK_MSDP_EVENTS))
#define PIM_DEBUG_PACKETS (qpim_debugs & (PIM_MASK_PIM_PACKETS | PIM_MASK_IGMP_PACKETS | PIM_MASK_MSDP_PACKETS))
@ -216,6 +218,7 @@ extern int32_t qpim_register_probe_time;
#define PIM_DO_DEBUG_MSDP_INTERNAL (qpim_debugs |= PIM_MASK_MSDP_INTERNAL)
#define PIM_DO_DEBUG_PIM_NHT (qpim_debugs |= PIM_MASK_PIM_NHT)
#define PIM_DO_DEBUG_PIM_NHT_RP (qpim_debugs |= PIM_MASK_PIM_NHT_RP)
#define PIM_DO_DEBUG_MTRACE (qpim_debugs |= PIM_MASK_MTRACE)
#define PIM_DONT_DEBUG_PIM_EVENTS (qpim_debugs &= ~PIM_MASK_PIM_EVENTS)
#define PIM_DONT_DEBUG_PIM_PACKETS (qpim_debugs &= ~PIM_MASK_PIM_PACKETS)
@ -240,6 +243,7 @@ extern int32_t qpim_register_probe_time;
#define PIM_DONT_DEBUG_MSDP_INTERNAL (qpim_debugs &= ~PIM_MASK_MSDP_INTERNAL)
#define PIM_DONT_DEBUG_PIM_NHT (qpim_debugs &= ~PIM_MASK_PIM_NHT)
#define PIM_DONT_DEBUG_PIM_NHT_RP (qpim_debugs &= ~PIM_MASK_PIM_NHT_RP)
#define PIM_DONT_DEBUG_MTRACE (qpim_debugs &= ~PIM_MASK_MTRACE)
void pim_init(void);
void pim_terminate(void);

View File

@ -5,6 +5,7 @@
if PIMD
noinst_LIBRARIES += pimd/libpim.a
sbin_PROGRAMS += pimd/pimd
bin_PROGRAMS += pimd/mtracebis
noinst_PROGRAMS += pimd/test_igmpv3_join
dist_examples_DATA += pimd/pimd.conf.sample
endif
@ -18,6 +19,7 @@ pimd_libpim_a_SOURCES = \
pimd/pim_iface.c \
pimd/pim_ifchannel.c \
pimd/pim_igmp.c \
pimd/pim_igmp_mtrace.c \
pimd/pim_igmpv2.c \
pimd/pim_igmpv3.c \
pimd/pim_instance.c \
@ -66,6 +68,7 @@ noinst_HEADERS += \
pimd/pim_ifchannel.h \
pimd/pim_igmp.h \
pimd/pim_igmp_join.h \
pimd/pim_igmp_mtrace.h \
pimd/pim_igmpv2.h \
pimd/pim_igmpv3.h \
pimd/pim_instance.h \
@ -101,6 +104,8 @@ noinst_HEADERS += \
pimd/pim_zebra.h \
pimd/pim_zlookup.h \
pimd/pimd.h \
pimd/mtracebis_netlink.h \
pimd/mtracebis_routeget.h \
# end
pimd_pimd_LDADD = pimd/libpim.a lib/libfrr.la @LIBCAP@
@ -108,3 +113,9 @@ pimd_pimd_SOURCES = pimd/pim_main.c
pimd_test_igmpv3_join_LDADD = lib/libfrr.la
pimd_test_igmpv3_join_SOURCES = pimd/test_igmpv3_join.c
pimd_mtracebis_LDADD = lib/libfrr.la
pimd_mtracebis_SOURCES = pimd/mtracebis.c \
pimd/mtracebis_netlink.c \
pimd/mtracebis_routeget.c \
# end

View File

@ -55,7 +55,6 @@ my $min_conf_desc_length = 4;
my $spelling_file = "$D/spelling.txt";
my $codespell = 0;
my $codespellfile = "/usr/share/codespell/dictionary.txt";
my $conststructsfile = "$D/const_structs.checkpatch";
my $typedefsfile = "";
my $color = "auto";
my $allow_c99_comments = 1;
@ -685,10 +684,6 @@ sub read_words {
return 0;
}
my $const_structs = "";
read_words(\$const_structs, $conststructsfile)
or warn "No structs that should be const will be found - file '$conststructsfile': $!\n";
my $typeOtherTypedefs = "";
if (length($typedefsfile)) {
read_words(\$typeOtherTypedefs, $typedefsfile)
@ -2766,18 +2761,6 @@ sub process {
$rpt_cleaners = 1;
}
# Check for FSF mailing addresses.
if ($rawline =~ /\bwrite to the Free/i ||
$rawline =~ /\b675\s+Mass\s+Ave/i ||
$rawline =~ /\b59\s+Temple\s+Pl/i ||
$rawline =~ /\b51\s+Franklin\s+St/i) {
my $herevet = "$here\n" . cat_vet($rawline) . "\n";
my $msg_level = \&ERROR;
$msg_level = \&CHK if ($file);
&{$msg_level}("FSF_MAILING_ADDRESS",
"Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.\n" . $herevet)
}
# check for Kconfig help text having a real description
# Only applies when adding the entry originally, after that we do not have
# sufficient context to determine whether it is indeed long enough.
@ -4058,6 +4041,10 @@ sub process {
# likely a typedef for a function.
} elsif ($ctx =~ /$Type$/) {
# All-uppercase function names are usually macros,
# ignore those
} elsif ($name eq uc $name) {
} else {
if (WARN("SPACING",
"space prohibited between function name and open parenthesis '('\n" . $herecurr) &&
@ -6205,14 +6192,6 @@ sub process {
"please use device_initcall() or more appropriate function instead of __initcall() (see include/linux/init.h)\n" . $herecurr);
}
# check for various structs that are normally const (ops, kgdb, device_tree)
# and avoid what seem like struct definitions 'struct foo {'
if ($line !~ /\bconst\b/ &&
$line =~ /\bstruct\s+($const_structs)\b(?!\s*\{)/) {
WARN("CONST_STRUCT",
"struct $1 should normally be const\n" . $herecurr);
}
# use of NR_CPUS is usually wrong
# ignore definitions of NR_CPUS and usage to define arrays as likely right
if ($line =~ /\bNR_CPUS\b/ &&

View File

@ -45,10 +45,19 @@ echo $mod
if [ -z "$mod" ]; then
echo "There doesn't seem to be any changes."
else
cp $tree/$mod /tmp/f1/
echo "Copying source to temp directory..."
for file in $mod; do
echo "$tree/$file --> /tmp/f1/$file"
cp $tree/$file /tmp/f1/
done
git -C $tree reset --hard
git -C $tree clean -fd
cp $tree/$mod /tmp/f2/
for file in $mod; do
if [ -f $tree/$file ]; then
echo "$tree/$file --> /tmp/f2/$file"
cp $tree/$file /tmp/f2/
fi
done
echo "Running style checks..."
for file in /tmp/f1/*; do
echo "$checkpatch $file > $file _cp"
@ -60,15 +69,20 @@ else
done
echo "Done."
for file in /tmp/f1/*_cp; do
echo "Report for $(basename $file _cp)"
echo "==============================================="
if [ -a /tmp/f2/$(basename $file) ]; then
diff $file /tmp/f2/$(basename $file) | grep -v "normally be const" | grep -A3 "ERROR\|WARNING"
result=$(diff $file /tmp/f2/$(basename $file) | grep -A3 "ERROR\|WARNING" | grep -A2 -B2 '/tmp/f1')
else
cat $file | grep -v "normally be const" | grep -A3 "ERROR\|WARNING"
result=$(cat $file | grep -A3 "ERROR\|WARNING" | grep -A2 -B2 '/tmp/f1')
fi
if [ "$?" -eq "0" ]; then
stat=1
echo "Report for $(basename $file _cp)" 1>&2
echo "===============================================" 1>&2
echo "$result" 1>&2
if echo $result | grep -q "ERROR"; then
stat=2
elif [ "$stat" -eq "0" ]; then
stat=1
fi
fi
done
fi

View File

@ -2628,6 +2628,19 @@ ALIAS(vtysh_traceroute, vtysh_traceroute_ip_cmd, "traceroute ip WORD",
"IP trace\n"
"Trace route to destination address or hostname\n")
DEFUN (vtysh_mtrace,
vtysh_mtrace_cmd,
"mtrace WORD",
"Multicast trace route to multicast source\n"
"Multicast trace route to multicast source address\n")
{
int idx = 1;
argv_find(argv, argc, "WORD", &idx);
execute_command("mtracebis", 1, argv[idx]->arg, NULL);
return CMD_SUCCESS;
}
DEFUN (vtysh_ping6,
vtysh_ping6_cmd,
"ping ipv6 WORD",
@ -3327,6 +3340,7 @@ void vtysh_init_vty(void)
install_element(VIEW_NODE, &vtysh_ping_ip_cmd);
install_element(VIEW_NODE, &vtysh_traceroute_cmd);
install_element(VIEW_NODE, &vtysh_traceroute_ip_cmd);
install_element(VIEW_NODE, &vtysh_mtrace_cmd);
install_element(VIEW_NODE, &vtysh_ping6_cmd);
install_element(VIEW_NODE, &vtysh_traceroute6_cmd);
#if defined(HAVE_SHELL_ACCESS)

View File

@ -527,7 +527,7 @@ int main(int argc, char **argv, char **env)
homedir = vtysh_get_home();
if (homedir) {
snprintf(history_file, sizeof(history_file),
"%s/.history_quagga", homedir);
"%s/.history_frr", homedir);
if (read_history(history_file) != 0) {
int fp;

View File

@ -34,8 +34,25 @@
DEFINE_MTYPE(ZEBRA, ZEBRA_NS, "Zebra Name Space")
static inline int
zebra_ns_table_entry_compare(const struct zebra_ns_table *e1,
const struct zebra_ns_table *e2);
RB_GENERATE(zebra_ns_table_head, zebra_ns_table, zebra_ns_table_entry,
zebra_ns_table_entry_compare);
static struct zebra_ns *dzns;
static inline int
zebra_ns_table_entry_compare(const struct zebra_ns_table *e1,
const struct zebra_ns_table *e2)
{
if (e1->tableid == e2->tableid)
return (e1->afi - e2->afi);
return e1->tableid - e2->tableid;
}
struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id)
{
return dzns;
@ -57,10 +74,79 @@ int zebra_ns_enable(ns_id_t ns_id, void **info)
return 0;
}
struct route_table *zebra_ns_find_table(struct zebra_ns *zns,
uint32_t tableid, afi_t afi)
{
struct zebra_ns_table finder;
struct zebra_ns_table *znst;
memset(&finder, 0, sizeof(finder));
finder.afi = afi;
finder.tableid = tableid;
znst = RB_FIND(zebra_ns_table_head, &zns->ns_tables, &finder);
if (znst)
return znst->table;
else
return NULL;
}
struct route_table *zebra_ns_get_table(struct zebra_ns *zns,
struct zebra_vrf *zvrf, uint32_t tableid,
afi_t afi)
{
struct zebra_ns_table finder;
struct zebra_ns_table *znst;
rib_table_info_t *info;
memset(&finder, 0, sizeof(finder));
finder.afi = afi;
finder.tableid = tableid;
znst = RB_FIND(zebra_ns_table_head, &zns->ns_tables, &finder);
if (znst)
return znst->table;
znst = XCALLOC(MTYPE_ZEBRA_NS, sizeof(*znst));
znst->tableid = tableid;
znst->afi = afi;
znst->table =
(afi == AFI_IP6) ? srcdest_table_init() : route_table_init();
info = XCALLOC(MTYPE_RIB_TABLE_INFO, sizeof(*info));
info->zvrf = zvrf;
info->afi = afi;
info->safi = SAFI_UNICAST;
znst->table->info = info;
znst->table->cleanup = zebra_rtable_node_cleanup;
RB_INSERT(zebra_ns_table_head, &zns->ns_tables, znst);
return znst->table;
}
static void zebra_ns_free_table(struct zebra_ns_table *znst)
{
void *table_info;
rib_close_table(znst->table);
table_info = znst->table->info;
route_table_finish(znst->table);
XFREE(MTYPE_RIB_TABLE_INFO, table_info);
XFREE(MTYPE_ZEBRA_NS, znst);
}
int zebra_ns_disable(ns_id_t ns_id, void **info)
{
struct zebra_ns_table *znst;
struct zebra_ns *zns = (struct zebra_ns *)(*info);
while (!RB_EMPTY(zebra_ns_table_head, &zns->ns_tables)) {
znst = RB_ROOT(zebra_ns_table_head, &zns->ns_tables);
RB_REMOVE(zebra_ns_table_head, &zns->ns_tables, znst);
zebra_ns_free_table(znst);
}
route_table_finish(zns->if_table);
zebra_vxlan_ns_disable(zns);
#if defined(HAVE_RTADV)
@ -72,6 +158,7 @@ int zebra_ns_disable(ns_id_t ns_id, void **info)
return 0;
}
int zebra_ns_init(void)
{
dzns = XCALLOC(MTYPE_ZEBRA_NS, sizeof(struct zebra_ns));

View File

@ -34,6 +34,18 @@ struct nlsock {
};
#endif
struct zebra_ns_table {
RB_ENTRY(zebra_ns_table) zebra_ns_table_entry;
uint32_t tableid;
afi_t afi;
struct route_table *table;
};
RB_HEAD(zebra_ns_table_head, zebra_ns_table);
RB_PROTOTYPE(zebra_ns_table_head, zebra_ns_table, zebra_ns_table_entry,
zebra_ns_table_entry_compare)
struct zebra_ns {
/* net-ns name. */
char name[VRF_NAMSIZ];
@ -55,6 +67,8 @@ struct zebra_ns {
#if defined(HAVE_RTADV)
struct rtadv rtadv;
#endif /* HAVE_RTADV */
struct zebra_ns_table_head ns_tables;
};
struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id);
@ -62,4 +76,10 @@ struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id);
int zebra_ns_init(void);
int zebra_ns_enable(ns_id_t ns_id, void **info);
int zebra_ns_disable(ns_id_t ns_id, void **info);
extern struct route_table *zebra_ns_find_table(struct zebra_ns *zns,
uint32_t tableid, afi_t afi);
extern struct route_table *zebra_ns_get_table(struct zebra_ns *zns,
struct zebra_vrf *zvrf,
uint32_t tableid, afi_t afi);
#endif

View File

@ -294,8 +294,11 @@ void zebra_pw_exit(struct zebra_vrf *zvrf)
{
struct zebra_pw *pw;
while ((pw = RB_ROOT(zebra_pw_head, &zvrf->pseudowires)) != NULL)
while (!RB_EMPTY(zebra_pw_head, &zvrf->pseudowires)) {
pw = RB_ROOT(zebra_pw_head, &zvrf->pseudowires);
zebra_pw_del(zvrf, pw);
}
}
DEFUN_NOSH (pseudowire_if,

View File

@ -1150,7 +1150,8 @@ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re)
*/
hook_call(rib_update, rn, "uninstalling from kernel");
kernel_route_rib(rn, p, src_p, re, NULL);
zvrf->removals++;
if (zvrf)
zvrf->removals++;
return;
}

View File

@ -174,7 +174,6 @@ static int zebra_vrf_disable(struct vrf *vrf)
struct static_route *si;
struct route_table *table;
struct interface *ifp;
u_int32_t table_id;
afi_t afi;
safi_t safi;
unsigned i;
@ -213,12 +212,6 @@ static int zebra_vrf_disable(struct vrf *vrf)
for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++)
rib_close_table(zvrf->table[afi][safi]);
if (vrf->vrf_id == VRF_DEFAULT)
for (table_id = 0; table_id < ZEBRA_KERNEL_TABLE_MAX;
table_id++)
if (zvrf->other_table[afi][table_id])
rib_close_table(zvrf->other_table[afi][table_id]);
}
/* Cleanup Vxlan, MPLS and PW tables. */
@ -236,7 +229,8 @@ static int zebra_vrf_disable(struct vrf *vrf)
struct route_node *rnode;
rib_dest_t *dest;
for (ALL_LIST_ELEMENTS(zebrad.mq->subq[i], lnode, nnode, rnode)) {
for (ALL_LIST_ELEMENTS(zebrad.mq->subq[i],
lnode, nnode, rnode)) {
dest = rib_dest_from_rnode(rnode);
if (dest && rib_dest_vrf(dest) == zvrf) {
route_unlock_node(rnode);
@ -258,17 +252,6 @@ static int zebra_vrf_disable(struct vrf *vrf)
zvrf->table[afi][safi] = NULL;
}
if (vrf->vrf_id == VRF_DEFAULT)
for (table_id = 0; table_id < ZEBRA_KERNEL_TABLE_MAX;
table_id++)
if (zvrf->other_table[afi][table_id]) {
table = zvrf->other_table[afi][table_id];
table_info = table->info;
route_table_finish(table);
XFREE(MTYPE_RIB_TABLE_INFO, table_info);
zvrf->other_table[afi][table_id] = NULL;
}
route_table_finish(zvrf->rnh_table[afi]);
zvrf->rnh_table[afi] = NULL;
route_table_finish(zvrf->import_check_table[afi]);
@ -282,7 +265,6 @@ static int zebra_vrf_delete(struct vrf *vrf)
{
struct zebra_vrf *zvrf = vrf->info;
struct route_table *table;
u_int32_t table_id;
afi_t afi;
safi_t safi;
unsigned i;
@ -328,14 +310,6 @@ static int zebra_vrf_delete(struct vrf *vrf)
route_table_finish(table);
}
for (table_id = 0; table_id < ZEBRA_KERNEL_TABLE_MAX; table_id++)
if (zvrf->other_table[afi][table_id]) {
table = zvrf->other_table[afi][table_id];
table_info = table->info;
route_table_finish(table);
XFREE(MTYPE_RIB_TABLE_INFO, table_info);
}
route_table_finish(zvrf->rnh_table[afi]);
route_table_finish(zvrf->import_check_table[afi]);
}
@ -407,8 +381,8 @@ struct route_table *zebra_vrf_table_with_table_id(afi_t afi, safi_t safi,
return table;
}
static void zebra_rtable_node_cleanup(struct route_table *table,
struct route_node *node)
void zebra_rtable_node_cleanup(struct route_table *table,
struct route_node *node)
{
struct route_entry *re, *next;
@ -545,34 +519,20 @@ struct route_table *zebra_vrf_other_route_table(afi_t afi, u_int32_t table_id,
vrf_id_t vrf_id)
{
struct zebra_vrf *zvrf;
rib_table_info_t *info;
struct route_table *table;
struct zebra_ns *zns;
zvrf = vrf_info_lookup(vrf_id);
if (!zvrf)
return NULL;
if (afi >= AFI_MAX)
return NULL;
zns = zvrf->zns;
if (table_id >= ZEBRA_KERNEL_TABLE_MAX)
if (afi >= AFI_MAX)
return NULL;
if ((vrf_id == VRF_DEFAULT) && (table_id != RT_TABLE_MAIN)
&& (table_id != zebrad.rtm_table_default)) {
if (zvrf->other_table[afi][table_id] == NULL) {
table = (afi == AFI_IP6) ? srcdest_table_init()
: route_table_init();
info = XCALLOC(MTYPE_RIB_TABLE_INFO, sizeof(*info));
info->zvrf = zvrf;
info->afi = afi;
info->safi = SAFI_UNICAST;
table->info = info;
table->cleanup = zebra_rtable_node_cleanup;
zvrf->other_table[afi][table_id] = table;
}
return (zvrf->other_table[afi][table_id]);
return zebra_ns_get_table(zns, zvrf, table_id, afi);
}
return zvrf->table[afi][SAFI_UNICAST];
@ -598,7 +558,10 @@ static int vrf_config_write(struct vty *vty)
if (vrf_is_user_cfged(vrf)) {
vty_out(vty, "vrf %s\n", zvrf_name(zvrf));
if (zvrf->l3vni)
vty_out(vty, " vni %u\n", zvrf->l3vni);
vty_out(vty, " vni %u%s\n",
zvrf->l3vni,
is_l3vni_for_prefix_routes_only(zvrf->l3vni) ?
" prefix-routes-only" :"");
vty_out(vty, "!\n");
}

View File

@ -62,9 +62,6 @@ struct zebra_vrf {
/* Import check table (used mostly by BGP */
struct route_table *import_check_table[AFI_MAX];
/* Routing tables off of main table for redistribute table */
struct route_table *other_table[AFI_MAX][ZEBRA_KERNEL_TABLE_MAX];
/* 2nd pointer type used primarily to quell a warning on
* ALL_LIST_ELEMENTS_RO
*/
@ -154,4 +151,7 @@ extern struct route_table *
zebra_vrf_other_route_table(afi_t afi, u_int32_t table_id, vrf_id_t vrf_id);
extern int zebra_vrf_has_config(struct zebra_vrf *zvrf);
extern void zebra_vrf_init(void);
extern void zebra_rtable_node_cleanup(struct route_table *table,
struct route_node *node);
#endif

View File

@ -1235,46 +1235,21 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
}
}
static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
safi_t safi, bool use_fib, u_char use_json,
route_tag_t tag,
const struct prefix *longer_prefix_p,
bool supernets_only, int type,
u_short ospf_instance_id)
static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf,
struct route_table *table, afi_t afi,
bool use_fib, route_tag_t tag,
const struct prefix *longer_prefix_p,
bool supernets_only, int type,
u_short ospf_instance_id, u_char use_json)
{
struct route_table *table;
rib_dest_t *dest;
struct route_node *rn;
struct route_entry *re;
int first = 1;
struct zebra_vrf *zvrf = NULL;
char buf[BUFSIZ];
rib_dest_t *dest;
json_object *json = NULL;
json_object *json_prefix = NULL;
u_int32_t addr;
if (!(zvrf = zebra_vrf_lookup_by_name(vrf_name))) {
if (use_json)
vty_out(vty, "{}\n");
else
vty_out(vty, "vrf %s not defined\n", vrf_name);
return CMD_SUCCESS;
}
if (zvrf_id(zvrf) == VRF_UNKNOWN) {
if (use_json)
vty_out(vty, "{}\n");
else
vty_out(vty, "vrf %s inactive\n", vrf_name);
return CMD_SUCCESS;
}
table = zebra_vrf_table(afi, safi, zvrf_id(zvrf));
if (!table) {
if (use_json)
vty_out(vty, "{}\n");
return CMD_SUCCESS;
}
uint32_t addr;
char buf[BUFSIZ];
if (use_json)
json = json_object_new_object();
@ -1352,6 +1327,67 @@ static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
json, JSON_C_TO_STRING_PRETTY));
json_object_free(json);
}
}
static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
safi_t safi, bool use_fib, u_char use_json,
route_tag_t tag,
const struct prefix *longer_prefix_p,
bool supernets_only, int type,
u_short ospf_instance_id)
{
struct route_table *table;
struct zebra_vrf *zvrf = NULL;
if (!(zvrf = zebra_vrf_lookup_by_name(vrf_name))) {
if (use_json)
vty_out(vty, "{}\n");
else
vty_out(vty, "vrf %s not defined\n", vrf_name);
return CMD_SUCCESS;
}
if (zvrf_id(zvrf) == VRF_UNKNOWN) {
if (use_json)
vty_out(vty, "{}\n");
else
vty_out(vty, "vrf %s inactive\n", vrf_name);
return CMD_SUCCESS;
}
table = zebra_vrf_table(afi, safi, zvrf_id(zvrf));
if (!table) {
if (use_json)
vty_out(vty, "{}\n");
return CMD_SUCCESS;
}
do_show_route_helper(vty, zvrf, table, afi, use_fib, tag,
longer_prefix_p, supernets_only, type,
ospf_instance_id, use_json);
return CMD_SUCCESS;
}
DEFPY (show_route_table,
show_route_table_cmd,
"show <ip$ipv4|ipv6$ipv6> route table (1-4294967295)$table [json$json]",
SHOW_STR
IP_STR
IP6_STR
"IP routing table\n"
"Table to display\n"
"The table number to display, if available\n"
JSON_STR)
{
afi_t afi = ipv4 ? AFI_IP : AFI_IP6;
struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT);
struct route_table *t;
t = zebra_ns_find_table(zvrf->zns, table, afi);
if (t)
do_show_route_helper(vty, zvrf, t, afi, false, 0, false, false,
0, 0, !!json);
return CMD_SUCCESS;
}
@ -2353,20 +2389,26 @@ DEFUN (show_vrf,
DEFUN (default_vrf_vni_mapping,
default_vrf_vni_mapping_cmd,
"vni " CMD_VNI_RANGE,
"vni " CMD_VNI_RANGE "[prefix-routes-only]",
"VNI corresponding to the DEFAULT VRF\n"
"VNI-ID\n")
"VNI-ID\n"
"Prefix routes only \n")
{
int ret = 0;
char err[ERR_STR_SZ];
struct zebra_vrf *zvrf = NULL;
vni_t vni = strtoul(argv[1]->arg, NULL, 10);
int filter = 0;
zvrf = vrf_info_lookup(VRF_DEFAULT);
if (!zvrf)
return CMD_WARNING;
ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 1);
if (argc == 3)
filter = 1;
ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ,
filter, 1);
if (ret != 0) {
vty_out(vty, "%s\n", err);
return CMD_WARNING;
@ -2391,7 +2433,7 @@ DEFUN (no_default_vrf_vni_mapping,
if (!zvrf)
return CMD_WARNING;
ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0);
ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0, 0);
if (ret != 0) {
vty_out(vty, "%s\n", err);
return CMD_WARNING;
@ -2402,11 +2444,13 @@ DEFUN (no_default_vrf_vni_mapping,
DEFUN (vrf_vni_mapping,
vrf_vni_mapping_cmd,
"vni " CMD_VNI_RANGE,
"vni " CMD_VNI_RANGE "[prefix-routes-only]",
"VNI corresponding to tenant VRF\n"
"VNI-ID\n")
"VNI-ID\n"
"prefix-routes-only\n")
{
int ret = 0;
int filter = 0;
ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
vni_t vni = strtoul(argv[1]->arg, NULL, 10);
@ -2415,9 +2459,13 @@ DEFUN (vrf_vni_mapping,
assert(vrf);
assert(zvrf);
if (argc == 3)
filter = 1;
/* Mark as having FRR configuration */
vrf_set_user_cfged(vrf);
ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 1);
ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ,
filter, 1);
if (ret != 0) {
vty_out(vty, "%s\n", err);
return CMD_WARNING;
@ -2442,7 +2490,7 @@ DEFUN (no_vrf_vni_mapping,
assert(vrf);
assert(zvrf);
ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0);
ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0, 0);
if (ret != 0) {
vty_out(vty, "%s\n", err);
return CMD_WARNING;
@ -3341,6 +3389,7 @@ void zebra_vty_init(void)
install_element(VIEW_NODE, &show_vrf_cmd);
install_element(VIEW_NODE, &show_vrf_vni_cmd);
install_element(VIEW_NODE, &show_route_cmd);
install_element(VIEW_NODE, &show_route_table_cmd);
install_element(VIEW_NODE, &show_route_detail_cmd);
install_element(VIEW_NODE, &show_route_summary_cmd);
install_element(VIEW_NODE, &show_ip_nht_cmd);

View File

@ -928,6 +928,9 @@ static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx)
zl3vni_svi_if_name(zl3vni));
vty_out(vty, " State: %s\n",
zl3vni_state2str(zl3vni));
vty_out(vty, " VNI Filter: %s\n",
CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) ?
"prefix-routes-only" : "none");
vty_out(vty, " Router MAC: %s\n",
zl3vni_rmac2str(zl3vni, buf, sizeof(buf)));
vty_out(vty, " L2 VNIs: ");
@ -951,6 +954,10 @@ static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx)
json_object_string_add(json, "routerMac",
zl3vni_rmac2str(zl3vni, buf,
sizeof(buf)));
json_object_string_add(json, "vniFilter",
CHECK_FLAG(zl3vni->filter,
PREFIX_ROUTES_ONLY) ?
"prefix-routes-only" : "none");
for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni)) {
json_object_array_add(json_vni_list,
json_object_new_int(zvni->vni));
@ -3612,15 +3619,19 @@ static int zl3vni_send_add_to_client(zebra_l3vni_t *zl3vni)
stream_putl(s, zl3vni->vni);
stream_put(s, &rmac, sizeof(struct ethaddr));
stream_put_in_addr(s, &zl3vni->local_vtep_ip);
stream_put(s, &zl3vni->filter, sizeof(int));
/* Write packet size. */
stream_putw_at(s, 0, stream_get_endp(s));
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug("Send L3_VNI_ADD %u VRF %s RMAC %s local-ip %s to %s",
zlog_debug(
"Send L3_VNI_ADD %u VRF %s RMAC %s local-ip %s filter %s to %s",
zl3vni->vni, vrf_id_to_name(zl3vni_vrf_id(zl3vni)),
prefix_mac2str(&rmac, buf, sizeof(buf)),
inet_ntoa(zl3vni->local_vtep_ip),
CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) ?
"prefix-routes-only" : "none",
zebra_route_string(client->proto));
client->l3vniadd_cnt++;
@ -3665,10 +3676,6 @@ static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni)
if (!zl3vni)
return;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug("L3-VNI %u is UP - send add to BGP",
zl3vni->vni);
/* send l3vni add to BGP */
zl3vni_send_add_to_client(zl3vni);
}
@ -3678,10 +3685,6 @@ static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni)
if (!zl3vni)
return;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug("L3-VNI %u is Down - Send del to BGP",
zl3vni->vni);
/* send l3-vni del to BGP*/
zl3vni_send_del_to_client(zl3vni);
}
@ -3834,6 +3837,17 @@ static int zebra_vxlan_readd_remote_rmac(zebra_l3vni_t *zl3vni,
/* Public functions */
int is_l3vni_for_prefix_routes_only(vni_t vni)
{
zebra_l3vni_t *zl3vni = NULL;
zl3vni = zl3vni_lookup(vni);
if (!zl3vni)
return 0;
return CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) ? 1 : 0;
}
/* handle evpn route in vrf table */
void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id,
struct ethaddr *rmac,
@ -6449,7 +6463,7 @@ int zebra_vxlan_if_add(struct interface *ifp)
int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf,
vni_t vni,
char *err, int err_str_sz,
int add)
int filter, int add)
{
zebra_l3vni_t *zl3vni = NULL;
struct zebra_vrf *zvrf_default = NULL;
@ -6494,6 +6508,12 @@ int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf,
/* associate the vrf with vni */
zvrf->l3vni = vni;
/* set the filter in l3vni to denote if we are using l3vni only
* for prefix routes
*/
if (filter)
SET_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY);
/* associate with vxlan-intf;
* we need to associate with the vxlan-intf first
*/

View File

@ -52,6 +52,7 @@ is_evpn_enabled()
#define VNI_STR_LEN 32
extern int is_l3vni_for_prefix_routes_only(vni_t vni);
extern ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id);
extern int zebra_vxlan_vrf_delete(struct zebra_vrf *zvrf);
extern int zebra_vxlan_vrf_enable(struct zebra_vrf *zvrf);
@ -154,7 +155,7 @@ extern int zebra_vxlan_advertise_all_vni(struct zserv *client,
struct zebra_vrf *zvrf);
extern int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni,
char *err,
int err_str_sz, int add);
int err_str_sz, int filter, int add);
extern void zebra_vxlan_init_tables(struct zebra_vrf *zvrf);
extern void zebra_vxlan_close_tables(struct zebra_vrf *);
extern void zebra_vxlan_cleanup_tables(struct zebra_vrf *);

View File

@ -101,6 +101,9 @@ struct zebra_l3vni_t_ {
/* vrf_id */
vrf_id_t vrf_id;
uint32_t filter;
#define PREFIX_ROUTES_ONLY (1 << 0) /* l3-vni used for prefix routes only */
/* Local IP */
struct in_addr local_vtep_ip;