mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-16 19:19:59 +00:00
bgpd: Fixup json with RouteDistinguishers
The json option for displaying a bgp table with route Distinguishers in it was not properly working. This code cleans this issue up. Additionally attempt to make the code a bit easier to read and handle. Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
This commit is contained in:
parent
98ce9a06b4
commit
445c24803d
448
bgpd/bgp_route.c
448
bgpd/bgp_route.c
@ -6500,246 +6500,258 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo,
|
|||||||
|
|
||||||
/* Print attribute */
|
/* Print attribute */
|
||||||
attr = binfo->attr;
|
attr = binfo->attr;
|
||||||
if (attr) {
|
if (!attr) {
|
||||||
/*
|
if (json_paths)
|
||||||
* For ENCAP and EVPN routes, nexthop address family is not
|
json_object_array_add(json_paths, json_path);
|
||||||
* neccessarily the same as the prefix address family.
|
else
|
||||||
* Both SAFI_MPLS_VPN and SAFI_ENCAP use the MP nexthop field
|
vty_out(vty, "\n");
|
||||||
* EVPN routes are also exchanged with a MP nexthop. Currently,
|
|
||||||
* this
|
|
||||||
* is only IPv4, the value will be present in either
|
|
||||||
* attr->nexthop or
|
|
||||||
* attr->mp_nexthop_global_in
|
|
||||||
*/
|
|
||||||
if ((safi == SAFI_ENCAP) || (safi == SAFI_MPLS_VPN)) {
|
|
||||||
char buf[BUFSIZ];
|
|
||||||
int af = NEXTHOP_FAMILY(attr->mp_nexthop_len);
|
|
||||||
|
|
||||||
switch (af) {
|
return;
|
||||||
case AF_INET:
|
}
|
||||||
vty_out(vty, "%s",
|
|
||||||
inet_ntop(af,
|
|
||||||
&attr->mp_nexthop_global_in,
|
|
||||||
buf, BUFSIZ));
|
|
||||||
break;
|
|
||||||
case AF_INET6:
|
|
||||||
vty_out(vty, "%s",
|
|
||||||
inet_ntop(af, &attr->mp_nexthop_global,
|
|
||||||
buf, BUFSIZ));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
vty_out(vty, "?");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (safi == SAFI_EVPN) {
|
|
||||||
if (json_paths) {
|
|
||||||
json_nexthop_global = json_object_new_object();
|
|
||||||
|
|
||||||
json_object_string_add(
|
/*
|
||||||
json_nexthop_global, "ip",
|
* For ENCAP and EVPN routes, nexthop address family is not
|
||||||
|
* neccessarily the same as the prefix address family.
|
||||||
|
* Both SAFI_MPLS_VPN and SAFI_ENCAP use the MP nexthop field
|
||||||
|
* EVPN routes are also exchanged with a MP nexthop. Currently,
|
||||||
|
* this
|
||||||
|
* is only IPv4, the value will be present in either
|
||||||
|
* attr->nexthop or
|
||||||
|
* attr->mp_nexthop_global_in
|
||||||
|
*/
|
||||||
|
if ((safi == SAFI_ENCAP) || (safi == SAFI_MPLS_VPN)) {
|
||||||
|
char buf[BUFSIZ];
|
||||||
|
char nexthop[128];
|
||||||
|
int af = NEXTHOP_FAMILY(attr->mp_nexthop_len);
|
||||||
|
|
||||||
|
switch (af) {
|
||||||
|
case AF_INET:
|
||||||
|
sprintf(nexthop, "%s",
|
||||||
|
inet_ntop(af, &attr->mp_nexthop_global_in,
|
||||||
|
buf, BUFSIZ));
|
||||||
|
break;
|
||||||
|
case AF_INET6:
|
||||||
|
sprintf(nexthop, "%s",
|
||||||
|
inet_ntop(af, &attr->mp_nexthop_global,
|
||||||
|
buf, BUFSIZ));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sprintf(nexthop, "?");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json_paths) {
|
||||||
|
json_nexthop_global = json_object_new_object();
|
||||||
|
|
||||||
|
json_object_string_add(json_nexthop_global,
|
||||||
|
"afi",
|
||||||
|
(af == AF_INET) ?
|
||||||
|
"ip" : "ipv6");
|
||||||
|
json_object_string_add(json_nexthop_global,
|
||||||
|
(af == AF_INET) ?
|
||||||
|
"ip" : "ipv6",
|
||||||
|
nexthop);
|
||||||
|
json_object_boolean_true_add(json_nexthop_global,
|
||||||
|
"used");
|
||||||
|
} else
|
||||||
|
vty_out(vty, "%s", nexthop);
|
||||||
|
} else if (safi == SAFI_EVPN) {
|
||||||
|
if (json_paths) {
|
||||||
|
json_nexthop_global = json_object_new_object();
|
||||||
|
|
||||||
|
json_object_string_add(json_nexthop_global, "ip",
|
||||||
|
inet_ntoa(attr->nexthop));
|
||||||
|
json_object_string_add(json_nexthop_global,
|
||||||
|
"afi", "ipv4");
|
||||||
|
json_object_boolean_true_add(json_nexthop_global,
|
||||||
|
"used");
|
||||||
|
} else
|
||||||
|
vty_out(vty, "%-16s", inet_ntoa(attr->nexthop));
|
||||||
|
}
|
||||||
|
/* IPv4 Next Hop */
|
||||||
|
else if (p->family == AF_INET
|
||||||
|
&& !BGP_ATTR_NEXTHOP_AFI_IP6(attr)) {
|
||||||
|
if (json_paths) {
|
||||||
|
json_nexthop_global = json_object_new_object();
|
||||||
|
|
||||||
|
if ((safi == SAFI_MPLS_VPN)
|
||||||
|
|| (safi == SAFI_EVPN))
|
||||||
|
json_object_string_add(json_nexthop_global,
|
||||||
|
"ip",
|
||||||
|
inet_ntoa(attr->mp_nexthop_global_in));
|
||||||
|
else
|
||||||
|
json_object_string_add(json_nexthop_global,
|
||||||
|
"ip",
|
||||||
|
inet_ntoa(attr->nexthop));
|
||||||
|
|
||||||
|
json_object_string_add(json_nexthop_global,
|
||||||
|
"afi", "ipv4");
|
||||||
|
json_object_boolean_true_add(json_nexthop_global,
|
||||||
|
"used");
|
||||||
|
} else {
|
||||||
|
if ((safi == SAFI_MPLS_VPN)
|
||||||
|
|| (safi == SAFI_EVPN))
|
||||||
|
vty_out(vty, "%-16s",
|
||||||
|
inet_ntoa(
|
||||||
|
attr->mp_nexthop_global_in));
|
||||||
|
else
|
||||||
|
vty_out(vty, "%-16s",
|
||||||
inet_ntoa(attr->nexthop));
|
inet_ntoa(attr->nexthop));
|
||||||
json_object_string_add(json_nexthop_global,
|
|
||||||
"afi", "ipv4");
|
|
||||||
json_object_boolean_true_add(
|
|
||||||
json_nexthop_global, "used");
|
|
||||||
} else
|
|
||||||
vty_out(vty, "%-16s", inet_ntoa(attr->nexthop));
|
|
||||||
}
|
}
|
||||||
/* IPv4 Next Hop */
|
}
|
||||||
else if (p->family == AF_INET
|
|
||||||
&& !BGP_ATTR_NEXTHOP_AFI_IP6(attr)) {
|
|
||||||
if (json_paths) {
|
|
||||||
json_nexthop_global = json_object_new_object();
|
|
||||||
|
|
||||||
if ((safi == SAFI_MPLS_VPN)
|
/* IPv6 Next Hop */
|
||||||
|| (safi == SAFI_EVPN))
|
else if (p->family == AF_INET6
|
||||||
json_object_string_add(
|
|| BGP_ATTR_NEXTHOP_AFI_IP6(attr)) {
|
||||||
json_nexthop_global, "ip",
|
int len;
|
||||||
inet_ntoa(
|
char buf[BUFSIZ];
|
||||||
attr->mp_nexthop_global_in));
|
|
||||||
else
|
|
||||||
json_object_string_add(
|
|
||||||
json_nexthop_global, "ip",
|
|
||||||
inet_ntoa(attr->nexthop));
|
|
||||||
|
|
||||||
json_object_string_add(json_nexthop_global,
|
if (json_paths) {
|
||||||
"afi", "ipv4");
|
json_nexthop_global = json_object_new_object();
|
||||||
json_object_boolean_true_add(
|
json_object_string_add(json_nexthop_global, "ip",
|
||||||
json_nexthop_global, "used");
|
inet_ntop(AF_INET6,
|
||||||
} else {
|
&attr->mp_nexthop_global, buf,
|
||||||
if ((safi == SAFI_MPLS_VPN)
|
BUFSIZ));
|
||||||
|| (safi == SAFI_EVPN))
|
json_object_string_add(json_nexthop_global,
|
||||||
vty_out(vty, "%-16s",
|
"afi", "ipv6");
|
||||||
inet_ntoa(
|
json_object_string_add(json_nexthop_global,
|
||||||
attr->mp_nexthop_global_in));
|
"scope", "global");
|
||||||
else
|
|
||||||
vty_out(vty, "%-16s",
|
|
||||||
inet_ntoa(attr->nexthop));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* IPv6 Next Hop */
|
/* We display both LL & GL if both have been
|
||||||
else if (p->family == AF_INET6
|
* received */
|
||||||
|| BGP_ATTR_NEXTHOP_AFI_IP6(attr)) {
|
if ((attr->mp_nexthop_len == 32)
|
||||||
int len;
|
|| (binfo->peer->conf_if)) {
|
||||||
char buf[BUFSIZ];
|
json_nexthop_ll =
|
||||||
|
json_object_new_object();
|
||||||
if (json_paths) {
|
|
||||||
json_nexthop_global = json_object_new_object();
|
|
||||||
json_object_string_add(
|
json_object_string_add(
|
||||||
json_nexthop_global, "ip",
|
json_nexthop_ll, "ip",
|
||||||
inet_ntop(AF_INET6,
|
inet_ntop(
|
||||||
&attr->mp_nexthop_global, buf,
|
AF_INET6,
|
||||||
BUFSIZ));
|
&attr->mp_nexthop_local,
|
||||||
json_object_string_add(json_nexthop_global,
|
buf, BUFSIZ));
|
||||||
|
json_object_string_add(json_nexthop_ll,
|
||||||
"afi", "ipv6");
|
"afi", "ipv6");
|
||||||
json_object_string_add(json_nexthop_global,
|
json_object_string_add(json_nexthop_ll,
|
||||||
"scope", "global");
|
"scope",
|
||||||
|
"link-local");
|
||||||
|
|
||||||
/* We display both LL & GL if both have been
|
if ((IPV6_ADDR_CMP(
|
||||||
* received */
|
&attr->mp_nexthop_global,
|
||||||
if ((attr->mp_nexthop_len == 32)
|
&attr->mp_nexthop_local)
|
||||||
|| (binfo->peer->conf_if)) {
|
!= 0)
|
||||||
json_nexthop_ll =
|
&& !attr->mp_nexthop_prefer_global)
|
||||||
json_object_new_object();
|
|
||||||
json_object_string_add(
|
|
||||||
json_nexthop_ll, "ip",
|
|
||||||
inet_ntop(
|
|
||||||
AF_INET6,
|
|
||||||
&attr->mp_nexthop_local,
|
|
||||||
buf, BUFSIZ));
|
|
||||||
json_object_string_add(json_nexthop_ll,
|
|
||||||
"afi", "ipv6");
|
|
||||||
json_object_string_add(json_nexthop_ll,
|
|
||||||
"scope",
|
|
||||||
"link-local");
|
|
||||||
|
|
||||||
if ((IPV6_ADDR_CMP(
|
|
||||||
&attr->mp_nexthop_global,
|
|
||||||
&attr->mp_nexthop_local)
|
|
||||||
!= 0)
|
|
||||||
&& !attr->mp_nexthop_prefer_global)
|
|
||||||
json_object_boolean_true_add(
|
|
||||||
json_nexthop_ll,
|
|
||||||
"used");
|
|
||||||
else
|
|
||||||
json_object_boolean_true_add(
|
|
||||||
json_nexthop_global,
|
|
||||||
"used");
|
|
||||||
} else
|
|
||||||
json_object_boolean_true_add(
|
json_object_boolean_true_add(
|
||||||
json_nexthop_global, "used");
|
json_nexthop_ll,
|
||||||
} else {
|
"used");
|
||||||
/* Display LL if LL/Global both in table unless
|
else
|
||||||
* prefer-global is set */
|
json_object_boolean_true_add(
|
||||||
if (((attr->mp_nexthop_len == 32)
|
json_nexthop_global,
|
||||||
&& !attr->mp_nexthop_prefer_global)
|
"used");
|
||||||
|| (binfo->peer->conf_if)) {
|
} else
|
||||||
if (binfo->peer->conf_if) {
|
json_object_boolean_true_add(
|
||||||
len = vty_out(
|
json_nexthop_global, "used");
|
||||||
vty, "%s",
|
} else {
|
||||||
binfo->peer->conf_if);
|
/* Display LL if LL/Global both in table unless
|
||||||
len = 16 - len; /* len of IPv6
|
* prefer-global is set */
|
||||||
addr + max
|
if (((attr->mp_nexthop_len == 32)
|
||||||
len of def
|
&& !attr->mp_nexthop_prefer_global)
|
||||||
ifname */
|
|| (binfo->peer->conf_if)) {
|
||||||
|
if (binfo->peer->conf_if) {
|
||||||
|
len = vty_out(
|
||||||
|
vty, "%s",
|
||||||
|
binfo->peer->conf_if);
|
||||||
|
len = 16 - len; /* len of IPv6
|
||||||
|
addr + max
|
||||||
|
len of def
|
||||||
|
ifname */
|
||||||
|
|
||||||
if (len < 1)
|
if (len < 1)
|
||||||
vty_out(vty, "\n%*s",
|
vty_out(vty, "\n%*s",
|
||||||
36, " ");
|
36, " ");
|
||||||
else
|
else
|
||||||
vty_out(vty, "%*s", len,
|
vty_out(vty, "%*s", len,
|
||||||
" ");
|
" ");
|
||||||
} else {
|
|
||||||
len = vty_out(
|
|
||||||
vty, "%s",
|
|
||||||
inet_ntop(
|
|
||||||
AF_INET6,
|
|
||||||
&attr->mp_nexthop_local,
|
|
||||||
buf, BUFSIZ));
|
|
||||||
len = 16 - len;
|
|
||||||
|
|
||||||
if (len < 1)
|
|
||||||
vty_out(vty, "\n%*s",
|
|
||||||
36, " ");
|
|
||||||
else
|
|
||||||
vty_out(vty, "%*s", len,
|
|
||||||
" ");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
len = vty_out(
|
len = vty_out(
|
||||||
vty, "%s",
|
vty, "%s",
|
||||||
inet_ntop(
|
inet_ntop(
|
||||||
AF_INET6,
|
AF_INET6,
|
||||||
&attr->mp_nexthop_global,
|
&attr->mp_nexthop_local,
|
||||||
buf, BUFSIZ));
|
buf, BUFSIZ));
|
||||||
len = 16 - len;
|
len = 16 - len;
|
||||||
|
|
||||||
if (len < 1)
|
if (len < 1)
|
||||||
vty_out(vty, "\n%*s", 36, " ");
|
vty_out(vty, "\n%*s",
|
||||||
|
36, " ");
|
||||||
else
|
else
|
||||||
vty_out(vty, "%*s", len, " ");
|
vty_out(vty, "%*s", len,
|
||||||
|
" ");
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
len = vty_out(vty, "%s",
|
||||||
|
inet_ntop(AF_INET6,
|
||||||
|
&attr->mp_nexthop_global,
|
||||||
|
buf, BUFSIZ));
|
||||||
|
len = 16 - len;
|
||||||
|
|
||||||
|
if (len < 1)
|
||||||
|
vty_out(vty, "\n%*s", 36, " ");
|
||||||
|
else
|
||||||
|
vty_out(vty, "%*s", len, " ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* MED/Metric */
|
|
||||||
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))
|
|
||||||
if (json_paths)
|
|
||||||
json_object_int_add(json_path, "med",
|
|
||||||
attr->med);
|
|
||||||
else
|
|
||||||
vty_out(vty, "%10u", attr->med);
|
|
||||||
else if (!json_paths)
|
|
||||||
vty_out(vty, " ");
|
|
||||||
|
|
||||||
/* Local Pref */
|
|
||||||
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))
|
|
||||||
if (json_paths)
|
|
||||||
json_object_int_add(json_path, "localpref",
|
|
||||||
attr->local_pref);
|
|
||||||
else
|
|
||||||
vty_out(vty, "%7u", attr->local_pref);
|
|
||||||
else if (!json_paths)
|
|
||||||
vty_out(vty, " ");
|
|
||||||
|
|
||||||
if (json_paths)
|
|
||||||
json_object_int_add(json_path, "weight", attr->weight);
|
|
||||||
else
|
|
||||||
vty_out(vty, "%7u ", attr->weight);
|
|
||||||
|
|
||||||
if (json_paths) {
|
|
||||||
char buf[BUFSIZ];
|
|
||||||
json_object_string_add(json_path, "peerId",
|
|
||||||
sockunion2str(&binfo->peer->su,
|
|
||||||
buf,
|
|
||||||
SU_ADDRSTRLEN));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Print aspath */
|
|
||||||
if (attr->aspath) {
|
|
||||||
if (json_paths)
|
|
||||||
json_object_string_add(json_path, "aspath",
|
|
||||||
attr->aspath->str);
|
|
||||||
else
|
|
||||||
aspath_print_vty(vty, "%s", attr->aspath, " ");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Print origin */
|
|
||||||
if (json_paths)
|
|
||||||
json_object_string_add(
|
|
||||||
json_path, "origin",
|
|
||||||
bgp_origin_long_str[attr->origin]);
|
|
||||||
else
|
|
||||||
vty_out(vty, "%s", bgp_origin_str[attr->origin]);
|
|
||||||
} else {
|
|
||||||
if (json_paths)
|
|
||||||
json_object_string_add(json_path, "alert",
|
|
||||||
"No attributes");
|
|
||||||
else
|
|
||||||
vty_out(vty, "No attributes to print\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* MED/Metric */
|
||||||
|
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))
|
||||||
|
if (json_paths)
|
||||||
|
json_object_int_add(json_path, "med",
|
||||||
|
attr->med);
|
||||||
|
else
|
||||||
|
vty_out(vty, "%10u", attr->med);
|
||||||
|
else if (!json_paths)
|
||||||
|
vty_out(vty, " ");
|
||||||
|
|
||||||
|
/* Local Pref */
|
||||||
|
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))
|
||||||
|
if (json_paths)
|
||||||
|
json_object_int_add(json_path, "localpref",
|
||||||
|
attr->local_pref);
|
||||||
|
else
|
||||||
|
vty_out(vty, "%7u", attr->local_pref);
|
||||||
|
else if (!json_paths)
|
||||||
|
vty_out(vty, " ");
|
||||||
|
|
||||||
|
if (json_paths)
|
||||||
|
json_object_int_add(json_path, "weight", attr->weight);
|
||||||
|
else
|
||||||
|
vty_out(vty, "%7u ", attr->weight);
|
||||||
|
|
||||||
|
if (json_paths) {
|
||||||
|
char buf[BUFSIZ];
|
||||||
|
json_object_string_add(json_path, "peerId",
|
||||||
|
sockunion2str(&binfo->peer->su,
|
||||||
|
buf,
|
||||||
|
SU_ADDRSTRLEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print aspath */
|
||||||
|
if (attr->aspath) {
|
||||||
|
if (json_paths)
|
||||||
|
json_object_string_add(json_path, "aspath",
|
||||||
|
attr->aspath->str);
|
||||||
|
else
|
||||||
|
aspath_print_vty(vty, "%s", attr->aspath, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print origin */
|
||||||
|
if (json_paths)
|
||||||
|
json_object_string_add(
|
||||||
|
json_path, "origin",
|
||||||
|
bgp_origin_long_str[attr->origin]);
|
||||||
|
else
|
||||||
|
vty_out(vty, "%s", bgp_origin_str[attr->origin]);
|
||||||
|
|
||||||
if (json_paths) {
|
if (json_paths) {
|
||||||
if (json_nexthop_global || json_nexthop_ll) {
|
if (json_nexthop_global || json_nexthop_ll) {
|
||||||
json_nexthops = json_object_new_array();
|
json_nexthops = json_object_new_array();
|
||||||
@ -8152,9 +8164,15 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
|
|||||||
bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT ? "Default"
|
bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT ? "Default"
|
||||||
: bgp->name,
|
: bgp->name,
|
||||||
table->version, inet_ntoa(bgp->router_id));
|
table->version, inet_ntoa(bgp->router_id));
|
||||||
|
if (rd)
|
||||||
|
vty_out(vty, " \"routeDistinguishers\" : {");
|
||||||
json_paths = json_object_new_object();
|
json_paths = json_object_new_object();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (use_json && rd) {
|
||||||
|
vty_out(vty, " \"%s\" : { ", rd);
|
||||||
|
}
|
||||||
|
|
||||||
/* Start processing of routes. */
|
/* Start processing of routes. */
|
||||||
for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) {
|
for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) {
|
||||||
if (rn->info == NULL)
|
if (rn->info == NULL)
|
||||||
@ -8331,8 +8349,6 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
|
|||||||
vty_out(vty,
|
vty_out(vty,
|
||||||
"Route Distinguisher: %s\n",
|
"Route Distinguisher: %s\n",
|
||||||
rd);
|
rd);
|
||||||
else
|
|
||||||
vty_out(vty, "rd:\"%s\",", rd);
|
|
||||||
}
|
}
|
||||||
if (type == bgp_show_type_dampend_paths
|
if (type == bgp_show_type_dampend_paths
|
||||||
|| type == bgp_show_type_damp_neighbor)
|
|| type == bgp_show_type_damp_neighbor)
|
||||||
@ -8428,6 +8444,8 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi,
|
|||||||
&output_cum, &total_cum);
|
&output_cum, &total_cum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (use_json)
|
||||||
|
vty_out(vty, " } }");
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
static int bgp_show(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
|
static int bgp_show(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
|
||||||
|
Loading…
Reference in New Issue
Block a user