122ce4affSfengbojiang /*-
222ce4affSfengbojiang * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
322ce4affSfengbojiang *
422ce4affSfengbojiang * Copyright (c) 2020 Alexander V. Chernikov
522ce4affSfengbojiang *
622ce4affSfengbojiang * Redistribution and use in source and binary forms, with or without
722ce4affSfengbojiang * modification, are permitted provided that the following conditions
822ce4affSfengbojiang * are met:
922ce4affSfengbojiang * 1. Redistributions of source code must retain the above copyright
1022ce4affSfengbojiang * notice, this list of conditions and the following disclaimer.
1122ce4affSfengbojiang * 2. Redistributions in binary form must reproduce the above copyright
1222ce4affSfengbojiang * notice, this list of conditions and the following disclaimer in the
1322ce4affSfengbojiang * documentation and/or other materials provided with the distribution.
1422ce4affSfengbojiang *
1522ce4affSfengbojiang * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1622ce4affSfengbojiang * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1722ce4affSfengbojiang * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1822ce4affSfengbojiang * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1922ce4affSfengbojiang * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2022ce4affSfengbojiang * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2122ce4affSfengbojiang * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2222ce4affSfengbojiang * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2322ce4affSfengbojiang * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2422ce4affSfengbojiang * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2522ce4affSfengbojiang * SUCH DAMAGE.
2622ce4affSfengbojiang */
2722ce4affSfengbojiang
2822ce4affSfengbojiang #include <sys/cdefs.h>
2922ce4affSfengbojiang __FBSDID("$FreeBSD$");
3022ce4affSfengbojiang #include <sys/param.h>
3122ce4affSfengbojiang #include <sys/socket.h>
3222ce4affSfengbojiang #include <sys/sysctl.h>
3322ce4affSfengbojiang
3422ce4affSfengbojiang #include <net/if.h>
3522ce4affSfengbojiang #include <net/if_dl.h>
3622ce4affSfengbojiang #include <net/if_types.h>
3722ce4affSfengbojiang #include <net/route.h>
3822ce4affSfengbojiang #include <net/route/nhop.h>
3922ce4affSfengbojiang
4022ce4affSfengbojiang #include <netinet/in.h>
4122ce4affSfengbojiang
4222ce4affSfengbojiang #include <arpa/inet.h>
4322ce4affSfengbojiang #include <libutil.h>
4422ce4affSfengbojiang #include <stdio.h>
4522ce4affSfengbojiang #include <stdlib.h>
4622ce4affSfengbojiang #include <stdbool.h>
4722ce4affSfengbojiang #include <string.h>
4822ce4affSfengbojiang #include <sysexits.h>
4922ce4affSfengbojiang #include <unistd.h>
5022ce4affSfengbojiang #include <err.h>
5122ce4affSfengbojiang #include <libxo/xo.h>
5222ce4affSfengbojiang #include "netstat.h"
5322ce4affSfengbojiang #include "common.h"
5422ce4affSfengbojiang
5522ce4affSfengbojiang #define WID_GW_DEFAULT(af) (((af) == AF_INET6) ? 40 : 18)
5622ce4affSfengbojiang
5722ce4affSfengbojiang static int wid_gw;
5822ce4affSfengbojiang static int wid_if = 10;
5922ce4affSfengbojiang static int wid_nhidx = 8;
6022ce4affSfengbojiang static int wid_refcnt = 8;
6122ce4affSfengbojiang
6222ce4affSfengbojiang struct nhop_entry {
6322ce4affSfengbojiang char gw[64];
6422ce4affSfengbojiang char ifname[IFNAMSIZ];
6522ce4affSfengbojiang };
6622ce4affSfengbojiang
6722ce4affSfengbojiang struct nhop_map {
6822ce4affSfengbojiang struct nhop_entry *ptr;
6922ce4affSfengbojiang size_t size;
7022ce4affSfengbojiang };
7122ce4affSfengbojiang static struct nhop_map global_nhop_map;
7222ce4affSfengbojiang
7322ce4affSfengbojiang static struct ifmap_entry *ifmap;
7422ce4affSfengbojiang static size_t ifmap_size;
7522ce4affSfengbojiang
7622ce4affSfengbojiang static struct nhop_entry *
nhop_get(struct nhop_map * map,uint32_t idx)7722ce4affSfengbojiang nhop_get(struct nhop_map *map, uint32_t idx)
7822ce4affSfengbojiang {
7922ce4affSfengbojiang
8022ce4affSfengbojiang if (idx >= map->size)
8122ce4affSfengbojiang return (NULL);
8222ce4affSfengbojiang if (*map->ptr[idx].ifname == '\0')
8322ce4affSfengbojiang return (NULL);
8422ce4affSfengbojiang return &map->ptr[idx];
8522ce4affSfengbojiang }
8622ce4affSfengbojiang
8722ce4affSfengbojiang static void
print_nhgroup_header(int af1 __unused)8822ce4affSfengbojiang print_nhgroup_header(int af1 __unused)
8922ce4affSfengbojiang {
9022ce4affSfengbojiang
9122ce4affSfengbojiang xo_emit("{T:/%-*.*s}{T:/%-*.*s}{T:/%*.*s}{T:/%*.*s}{T:/%*.*s}"
9222ce4affSfengbojiang "{T:/%*.*s}{T:/%*s}\n",
9322ce4affSfengbojiang wid_nhidx, wid_nhidx, "GrpIdx",
9422ce4affSfengbojiang wid_nhidx, wid_nhidx, "NhIdx",
9522ce4affSfengbojiang wid_nhidx, wid_nhidx, "Weight",
9622ce4affSfengbojiang wid_nhidx, wid_nhidx, "Slots",
9722ce4affSfengbojiang wid_gw, wid_gw, "Gateway",
9822ce4affSfengbojiang wid_if, wid_if, "Netif",
9922ce4affSfengbojiang wid_refcnt, "Refcnt");
10022ce4affSfengbojiang }
10122ce4affSfengbojiang
10222ce4affSfengbojiang static void
print_padding(char sym,int len)10322ce4affSfengbojiang print_padding(char sym, int len)
10422ce4affSfengbojiang {
10522ce4affSfengbojiang char buffer[56];
10622ce4affSfengbojiang
10722ce4affSfengbojiang memset(buffer, sym, sizeof(buffer));
10822ce4affSfengbojiang buffer[0] = '{';
10922ce4affSfengbojiang buffer[1] = 'P';
11022ce4affSfengbojiang buffer[2] = ':';
11122ce4affSfengbojiang buffer[3] = ' ';
11222ce4affSfengbojiang buffer[len + 3] = '}';
11322ce4affSfengbojiang buffer[len + 4] = '\0';
11422ce4affSfengbojiang xo_emit(buffer);
11522ce4affSfengbojiang }
11622ce4affSfengbojiang
11722ce4affSfengbojiang
11822ce4affSfengbojiang static void
print_nhgroup_entry_sysctl(const char * name,struct rt_msghdr * rtm,struct nhgrp_external * nhge)11922ce4affSfengbojiang print_nhgroup_entry_sysctl(const char *name, struct rt_msghdr *rtm,
12022ce4affSfengbojiang struct nhgrp_external *nhge)
12122ce4affSfengbojiang {
12222ce4affSfengbojiang char buffer[128];
12322ce4affSfengbojiang struct nhop_entry *ne;
12422ce4affSfengbojiang struct nhgrp_nhop_external *ext_cp, *ext_dp;
12522ce4affSfengbojiang struct nhgrp_container *nhg_cp, *nhg_dp;
12622ce4affSfengbojiang
12722ce4affSfengbojiang nhg_cp = (struct nhgrp_container *)(nhge + 1);
12822ce4affSfengbojiang if (nhg_cp->nhgc_type != NHG_C_TYPE_CNHOPS || nhg_cp->nhgc_subtype != 0)
12922ce4affSfengbojiang return;
13022ce4affSfengbojiang ext_cp = (struct nhgrp_nhop_external *)(nhg_cp + 1);
13122ce4affSfengbojiang
13222ce4affSfengbojiang nhg_dp = (struct nhgrp_container *)((char *)nhg_cp + nhg_cp->nhgc_len);
13322ce4affSfengbojiang if (nhg_dp->nhgc_type != NHG_C_TYPE_DNHOPS || nhg_dp->nhgc_subtype != 0)
13422ce4affSfengbojiang return;
13522ce4affSfengbojiang ext_dp = (struct nhgrp_nhop_external *)(nhg_dp + 1);
13622ce4affSfengbojiang
13722ce4affSfengbojiang xo_open_instance(name);
13822ce4affSfengbojiang
13922ce4affSfengbojiang snprintf(buffer, sizeof(buffer), "{[:-%d}{:nhgrp-index/%%lu}{]:} ", wid_nhidx);
14022ce4affSfengbojiang
14122ce4affSfengbojiang xo_emit(buffer, nhge->nhg_idx);
14222ce4affSfengbojiang
14322ce4affSfengbojiang /* nhidx */
14422ce4affSfengbojiang print_padding('-', wid_nhidx);
14522ce4affSfengbojiang /* weight */
14622ce4affSfengbojiang print_padding('-', wid_nhidx);
14722ce4affSfengbojiang /* slots */
14822ce4affSfengbojiang print_padding('-', wid_nhidx);
14922ce4affSfengbojiang print_padding('-', wid_gw);
15022ce4affSfengbojiang print_padding('-', wid_if);
15122ce4affSfengbojiang xo_emit("{t:nhg-refcnt/%*lu}", wid_refcnt, nhge->nhg_refcount);
15222ce4affSfengbojiang xo_emit("\n");
15322ce4affSfengbojiang
15422ce4affSfengbojiang xo_open_list("nhop-weights");
15522ce4affSfengbojiang for (uint32_t i = 0; i < nhg_cp->nhgc_count; i++) {
15622ce4affSfengbojiang /* TODO: optimize slots calculations */
15722ce4affSfengbojiang uint32_t slots = 0;
15822ce4affSfengbojiang for (uint32_t sidx = 0; sidx < nhg_dp->nhgc_count; sidx++) {
15922ce4affSfengbojiang if (ext_dp[sidx].nh_idx == ext_cp[i].nh_idx)
16022ce4affSfengbojiang slots++;
16122ce4affSfengbojiang }
16222ce4affSfengbojiang xo_open_instance("nhop-weight");
16322ce4affSfengbojiang print_padding(' ', wid_nhidx);
16422ce4affSfengbojiang // nh index
16522ce4affSfengbojiang xo_emit("{t:nh-index/%*lu}", wid_nhidx, ext_cp[i].nh_idx);
16622ce4affSfengbojiang xo_emit("{t:nh-weight/%*lu}", wid_nhidx, ext_cp[i].nh_weight);
16722ce4affSfengbojiang xo_emit("{t:nh-slots/%*lu}", wid_nhidx, slots);
16822ce4affSfengbojiang ne = nhop_get(&global_nhop_map, ext_cp[i].nh_idx);
16922ce4affSfengbojiang if (ne != NULL) {
17022ce4affSfengbojiang xo_emit("{t:nh-gw/%*.*s}", wid_gw, wid_gw, ne->gw);
17122ce4affSfengbojiang xo_emit("{t:nh-interface/%*.*s}", wid_if, wid_if, ne->ifname);
17222ce4affSfengbojiang }
17322ce4affSfengbojiang xo_emit("\n");
17422ce4affSfengbojiang xo_close_instance("nhop-weight");
17522ce4affSfengbojiang }
17622ce4affSfengbojiang xo_close_list("nhop-weights");
17722ce4affSfengbojiang xo_close_instance(name);
17822ce4affSfengbojiang }
17922ce4affSfengbojiang
18022ce4affSfengbojiang static int
cmp_nhg_idx(const void * _a,const void * _b)18122ce4affSfengbojiang cmp_nhg_idx(const void *_a, const void *_b)
18222ce4affSfengbojiang {
18322ce4affSfengbojiang const struct nhops_map *a, *b;
18422ce4affSfengbojiang
18522ce4affSfengbojiang a = _a;
18622ce4affSfengbojiang b = _b;
18722ce4affSfengbojiang
18822ce4affSfengbojiang if (a->idx > b->idx)
18922ce4affSfengbojiang return (1);
19022ce4affSfengbojiang else if (a->idx < b->idx)
19122ce4affSfengbojiang return (-1);
19222ce4affSfengbojiang return (0);
19322ce4affSfengbojiang }
19422ce4affSfengbojiang
19522ce4affSfengbojiang static void
dump_nhgrp_sysctl(int fibnum,int af,struct nhops_dump * nd)19622ce4affSfengbojiang dump_nhgrp_sysctl(int fibnum, int af, struct nhops_dump *nd)
19722ce4affSfengbojiang {
19822ce4affSfengbojiang size_t needed;
19922ce4affSfengbojiang int mib[7];
20022ce4affSfengbojiang char *buf, *next, *lim;
20122ce4affSfengbojiang struct rt_msghdr *rtm;
20222ce4affSfengbojiang struct nhgrp_external *nhg;
20322ce4affSfengbojiang struct nhops_map *nhg_map;
20422ce4affSfengbojiang size_t nhg_count, nhg_size;
20522ce4affSfengbojiang
20622ce4affSfengbojiang mib[0] = CTL_NET;
20722ce4affSfengbojiang mib[1] = PF_ROUTE;
20822ce4affSfengbojiang mib[2] = 0;
20922ce4affSfengbojiang mib[3] = af;
21022ce4affSfengbojiang mib[4] = NET_RT_NHGRP;
21122ce4affSfengbojiang mib[5] = 0;
21222ce4affSfengbojiang mib[6] = fibnum;
21322ce4affSfengbojiang if (sysctl(mib, nitems(mib), NULL, &needed, NULL, 0) < 0)
21422ce4affSfengbojiang err(EX_OSERR, "sysctl: net.route.0.%d.nhgrpdump.%d estimate",
21522ce4affSfengbojiang af, fibnum);
21622ce4affSfengbojiang if ((buf = malloc(needed)) == NULL)
21722ce4affSfengbojiang errx(2, "malloc(%lu)", (unsigned long)needed);
21822ce4affSfengbojiang if (sysctl(mib, nitems(mib), buf, &needed, NULL, 0) < 0)
21922ce4affSfengbojiang err(1, "sysctl: net.route.0.%d.nhgrpdump.%d", af, fibnum);
22022ce4affSfengbojiang lim = buf + needed;
22122ce4affSfengbojiang
22222ce4affSfengbojiang /*
22322ce4affSfengbojiang * nexhops groups are received unsorted. Collect everything first,
22422ce4affSfengbojiang * and sort prior displaying.
22522ce4affSfengbojiang */
22622ce4affSfengbojiang nhg_count = 0;
22722ce4affSfengbojiang nhg_size = 16;
22822ce4affSfengbojiang nhg_map = calloc(nhg_size, sizeof(struct nhops_map));
22922ce4affSfengbojiang for (next = buf; next < lim; next += rtm->rtm_msglen) {
23022ce4affSfengbojiang rtm = (struct rt_msghdr *)next;
23122ce4affSfengbojiang if (rtm->rtm_version != RTM_VERSION)
23222ce4affSfengbojiang continue;
23322ce4affSfengbojiang
23422ce4affSfengbojiang if (nhg_count >= nhg_size) {
23522ce4affSfengbojiang nhg_size *= 2;
23622ce4affSfengbojiang nhg_map = realloc(nhg_map, nhg_size * sizeof(struct nhops_map));
23722ce4affSfengbojiang }
23822ce4affSfengbojiang
23922ce4affSfengbojiang nhg = (struct nhgrp_external *)(rtm + 1);
24022ce4affSfengbojiang nhg_map[nhg_count].idx = nhg->nhg_idx;
24122ce4affSfengbojiang nhg_map[nhg_count].rtm = rtm;
24222ce4affSfengbojiang nhg_count++;
24322ce4affSfengbojiang }
24422ce4affSfengbojiang
24522ce4affSfengbojiang if (nhg_count > 0)
24622ce4affSfengbojiang qsort(nhg_map, nhg_count, sizeof(struct nhops_map), cmp_nhg_idx);
24722ce4affSfengbojiang nd->nh_buf = buf;
24822ce4affSfengbojiang nd->nh_count = nhg_count;
24922ce4affSfengbojiang nd->nh_map = nhg_map;
25022ce4affSfengbojiang }
25122ce4affSfengbojiang
25222ce4affSfengbojiang static void
print_nhgrp_sysctl(int fibnum,int af)25322ce4affSfengbojiang print_nhgrp_sysctl(int fibnum, int af)
25422ce4affSfengbojiang {
25522ce4affSfengbojiang struct nhops_dump nd;
25622ce4affSfengbojiang struct nhgrp_external *nhg;
25722ce4affSfengbojiang struct rt_msghdr *rtm;
25822ce4affSfengbojiang
25922ce4affSfengbojiang dump_nhgrp_sysctl(fibnum, af, &nd);
26022ce4affSfengbojiang
26122ce4affSfengbojiang xo_open_container("nhgrp-table");
26222ce4affSfengbojiang xo_open_list("rt-family");
26322ce4affSfengbojiang if (nd.nh_count > 0) {
26422ce4affSfengbojiang wid_gw = WID_GW_DEFAULT(af);
26522ce4affSfengbojiang xo_open_instance("rt-family");
26622ce4affSfengbojiang pr_family(af);
26722ce4affSfengbojiang xo_open_list("nhgrp-entry");
26822ce4affSfengbojiang
26922ce4affSfengbojiang print_nhgroup_header(af);
27022ce4affSfengbojiang
27122ce4affSfengbojiang for (size_t i = 0; i < nd.nh_count; i++) {
27222ce4affSfengbojiang rtm = nd.nh_map[i].rtm;
27322ce4affSfengbojiang nhg = (struct nhgrp_external *)(rtm + 1);
27422ce4affSfengbojiang print_nhgroup_entry_sysctl("nhgrp-entry", rtm, nhg);
27522ce4affSfengbojiang }
27622ce4affSfengbojiang }
27722ce4affSfengbojiang xo_close_list("rt-family");
27822ce4affSfengbojiang xo_close_container("nhgrp-table");
27922ce4affSfengbojiang free(nd.nh_buf);
28022ce4affSfengbojiang }
28122ce4affSfengbojiang
28222ce4affSfengbojiang static void
update_global_map(struct nhop_external * nh)28322ce4affSfengbojiang update_global_map(struct nhop_external *nh)
28422ce4affSfengbojiang {
28522ce4affSfengbojiang char iface_name[128];
286*f6b123a0Sfengbojiang char gw_addr[144];
28722ce4affSfengbojiang struct nhop_addrs *na;
28822ce4affSfengbojiang struct sockaddr *sa_gw;
28922ce4affSfengbojiang
29022ce4affSfengbojiang na = (struct nhop_addrs *)((char *)nh + nh->nh_len);
29122ce4affSfengbojiang sa_gw = (struct sockaddr *)((char *)na + na->gw_sa_off);
29222ce4affSfengbojiang
29322ce4affSfengbojiang memset(iface_name, 0, sizeof(iface_name));
29422ce4affSfengbojiang if (nh->ifindex < (uint32_t)ifmap_size) {
29522ce4affSfengbojiang strlcpy(iface_name, ifmap[nh->ifindex].ifname,
29622ce4affSfengbojiang sizeof(iface_name));
29722ce4affSfengbojiang if (*iface_name == '\0')
29822ce4affSfengbojiang strlcpy(iface_name, "---", sizeof(iface_name));
29922ce4affSfengbojiang }
30022ce4affSfengbojiang
30122ce4affSfengbojiang if (nh->nh_flags & NHF_GATEWAY) {
30222ce4affSfengbojiang const char *cp;
30322ce4affSfengbojiang cp = fmt_sockaddr(sa_gw, NULL, RTF_HOST);
30422ce4affSfengbojiang strlcpy(gw_addr, cp, sizeof(gw_addr));
30522ce4affSfengbojiang } else
30622ce4affSfengbojiang snprintf(gw_addr, sizeof(gw_addr), "%s/resolve", iface_name);
30722ce4affSfengbojiang
30822ce4affSfengbojiang nhop_map_update(&global_nhop_map, nh->nh_idx, gw_addr, iface_name);
30922ce4affSfengbojiang }
31022ce4affSfengbojiang
31122ce4affSfengbojiang static void
prepare_nh_map(int fibnum,int af)31222ce4affSfengbojiang prepare_nh_map(int fibnum, int af)
31322ce4affSfengbojiang {
31422ce4affSfengbojiang struct nhops_dump nd;
31522ce4affSfengbojiang struct nhop_external *nh;
31622ce4affSfengbojiang struct rt_msghdr *rtm;
31722ce4affSfengbojiang
31822ce4affSfengbojiang dump_nhops_sysctl(fibnum, af, &nd);
31922ce4affSfengbojiang
32022ce4affSfengbojiang for (size_t i = 0; i < nd.nh_count; i++) {
32122ce4affSfengbojiang rtm = nd.nh_map[i].rtm;
32222ce4affSfengbojiang nh = (struct nhop_external *)(rtm + 1);
32322ce4affSfengbojiang update_global_map(nh);
32422ce4affSfengbojiang }
32522ce4affSfengbojiang
32622ce4affSfengbojiang free(nd.nh_buf);
32722ce4affSfengbojiang }
32822ce4affSfengbojiang
32922ce4affSfengbojiang void
nhgrp_print(int fibnum,int af)33022ce4affSfengbojiang nhgrp_print(int fibnum, int af)
33122ce4affSfengbojiang {
33222ce4affSfengbojiang size_t intsize;
33322ce4affSfengbojiang int numfibs;
33422ce4affSfengbojiang
33522ce4affSfengbojiang intsize = sizeof(int);
33622ce4affSfengbojiang if (fibnum == -1 &&
33722ce4affSfengbojiang sysctlbyname("net.my_fibnum", &fibnum, &intsize, NULL, 0) == -1)
33822ce4affSfengbojiang fibnum = 0;
33922ce4affSfengbojiang if (sysctlbyname("net.fibs", &numfibs, &intsize, NULL, 0) == -1)
34022ce4affSfengbojiang numfibs = 1;
34122ce4affSfengbojiang if (fibnum < 0 || fibnum > numfibs - 1)
34222ce4affSfengbojiang errx(EX_USAGE, "%d: invalid fib", fibnum);
34322ce4affSfengbojiang
34422ce4affSfengbojiang ifmap = prepare_ifmap(&ifmap_size);
34522ce4affSfengbojiang prepare_nh_map(fibnum, af);
34622ce4affSfengbojiang
34722ce4affSfengbojiang xo_open_container("route-nhgrp-information");
34822ce4affSfengbojiang xo_emit("{T:Nexthop groups data}");
34922ce4affSfengbojiang if (fibnum)
35022ce4affSfengbojiang xo_emit(" ({L:fib}: {:fib/%d})", fibnum);
35122ce4affSfengbojiang xo_emit("\n");
35222ce4affSfengbojiang print_nhgrp_sysctl(fibnum, af);
35322ce4affSfengbojiang xo_close_container("route-nhgrp-information");
35422ce4affSfengbojiang }
35522ce4affSfengbojiang
356