9 #include <sys/bitcount.h>
10 #include <sys/param.h>
11 #include <sys/linker.h>
12 #include <sys/module.h>
13 #include <sys/socket.h>
14 #include <sys/sysctl.h>
16 #include <sys/types.h>
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
21 #include <net/ethernet.h>
23 #include <net/if_dl.h>
24 #include <net/if_types.h>
25 #include <netlink/netlink.h>
26 #include <netlink/netlink_route.h>
27 #include <netlink/netlink_snl.h>
28 #include <netlink/netlink_snl_route.h>
29 #include <netlink/netlink_snl_route_compat.h>
30 #include <netlink/netlink_snl_route_parsers.h>
35 #define RTF_ANNOUNCE RTF_PROTO2
38 nl_init_socket(struct snl_state *ss)
40 if (snl_init(ss, NETLINK_ROUTE))
43 if (modfind("netlink") == -1 && errno == ENOENT) {
45 if (kldload("netlink") == -1)
46 err(1, "netlink is not loaded and load attempt failed");
47 if (snl_init(ss, NETLINK_ROUTE))
51 err(1, "unable to open netlink socket");
55 get_link_info(struct snl_state *ss, uint32_t ifindex,
56 struct snl_parsed_link_simple *link)
60 snl_init_writer(ss, &nw);
62 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK);
63 struct ifinfomsg *ifmsg = snl_reserve_msg_object(&nw, struct ifinfomsg);
65 ifmsg->ifi_index = ifindex;
66 if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
69 hdr = snl_read_reply(ss, hdr->nlmsg_seq);
71 if (hdr == NULL || hdr->nlmsg_type != RTM_NEWLINK)
74 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, link))
83 has_l2(struct snl_state *ss, uint32_t ifindex)
85 struct snl_parsed_link_simple link = {};
87 if (!get_link_info(ss, ifindex, &link))
90 return (valid_type(link.ifi_type) != 0);
97 size_t len = sizeof(fibnum);
99 sysctlbyname("net.my_fibnum", (void *)&fibnum, &len, NULL, 0);
105 guess_ifindex(struct snl_state *ss, uint32_t fibnum, struct in_addr addr)
107 struct snl_writer nw;
109 snl_init_writer(ss, &nw);
111 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETROUTE);
112 struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg);
113 rtm->rtm_family = AF_INET;
115 struct sockaddr_in dst = { .sin_family = AF_INET, .sin_addr = addr };
116 snl_add_msg_attr_ip(&nw, RTA_DST, (struct sockaddr *)&dst);
117 snl_add_msg_attr_u32(&nw, RTA_TABLE, fibnum);
119 if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
122 hdr = snl_read_reply(ss, hdr->nlmsg_seq);
124 if (hdr->nlmsg_type != NL_RTM_NEWROUTE) {
125 /* No route found, unable to guess ifindex */
129 struct snl_parsed_route r = {};
130 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r))
133 if (r.rta_multipath.num_nhops > 0 || (r.rta_rtflags & RTF_GATEWAY))
136 /* Check if the interface is of supported type */
137 if (has_l2(ss, r.rta_oif))
140 /* Check the case when we matched the loopback route for P2P */
141 snl_init_writer(ss, &nw);
142 hdr = snl_create_msg_request(&nw, RTM_GETNEXTHOP);
143 snl_reserve_msg_object(&nw, struct nhmsg);
145 int off = snl_add_msg_attr_nested(&nw, NHA_FREEBSD);
146 snl_add_msg_attr_u32(&nw, NHAF_KID, r.rta_knh_id);
147 snl_add_msg_attr_u8(&nw, NHAF_FAMILY, AF_INET);
148 snl_add_msg_attr_u32(&nw, NHAF_TABLE, fibnum);
149 snl_end_attr_nested(&nw, off);
151 if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
154 hdr = snl_read_reply(ss, hdr->nlmsg_seq);
156 if (hdr->nlmsg_type != NL_RTM_NEWNEXTHOP) {
157 /* No nexthop found, unable to guess ifindex */
161 struct snl_parsed_nhop nh = {};
162 if (!snl_parse_nlmsg(ss, hdr, &snl_nhmsg_parser, &nh))
165 return (nh.nhaf_aif);
169 fix_ifindex(struct snl_state *ss, uint32_t ifindex, struct in_addr addr)
172 ifindex = guess_ifindex(ss, get_myfib(), addr);
177 print_entry(struct snl_parsed_neigh *neigh, struct snl_parsed_link_simple *link)
181 struct sockaddr_in *addr = (struct sockaddr_in *)neigh->nda_dst;
183 xo_open_instance("arp-cache");
186 hp = gethostbyaddr((caddr_t)&(addr->sin_addr),
187 sizeof(addr->sin_addr), AF_INET);
194 if (h_errno == TRY_AGAIN)
197 xo_emit("{:hostname/%s} ({:ip-address/%s}) at ", host,
198 inet_ntoa(addr->sin_addr));
199 if (neigh->nda_lladdr != NULL) {
200 struct sockaddr_dl sdl = {
201 .sdl_family = AF_LINK,
202 .sdl_type = link->ifi_type,
203 .sdl_len = sizeof(struct sockaddr_dl),
204 .sdl_alen = NLA_DATA_LEN(neigh->nda_lladdr),
206 memcpy(sdl.sdl_data, NLA_DATA(neigh->nda_lladdr), sdl.sdl_alen);
208 if ((sdl.sdl_type == IFT_ETHER ||
209 sdl.sdl_type == IFT_L2VLAN ||
210 sdl.sdl_type == IFT_BRIDGE) &&
211 sdl.sdl_alen == ETHER_ADDR_LEN)
212 xo_emit("{:mac-address/%s}",
213 ether_ntoa((struct ether_addr *)LLADDR(&sdl)));
216 xo_emit("{:mac-address/%s}", link_ntoa(&sdl));
219 xo_emit("{d:/(incomplete)}{en:incomplete/true}");
220 xo_emit(" on {:interface/%s}", link->ifla_ifname);
222 if (neigh->ndaf_next_ts == 0)
223 xo_emit("{d:/ permanent}{en:permanent/true}");
228 gettimeofday(&now, 0);
229 if ((expire_time = neigh->ndaf_next_ts - now.tv_sec) > 0)
230 xo_emit(" expires in {:expires/%d} seconds",
233 xo_emit("{d:/ expired}{en:expired/true}");
236 if (neigh->ndm_flags & NTF_PROXY)
237 xo_emit("{d:/ published}{en:published/true}");
239 switch(link->ifi_type) {
241 xo_emit(" [{:type/ethernet}]");
244 xo_emit(" [{:type/fddi}]");
247 xo_emit(" [{:type/atm}]");
250 xo_emit(" [{:type/vlan}]");
253 xo_emit(" [{:type/firewire}]");
256 xo_emit(" [{:type/bridge}]");
259 xo_emit(" [{:type/infiniband}]");
267 xo_close_instance("arp-cache");
271 print_entries_nl(uint32_t ifindex, struct in_addr addr)
273 struct snl_state ss_req = {}, ss_cmd = {};
274 struct snl_parsed_link_simple link = {};
275 struct snl_writer nw;
277 nl_init_socket(&ss_req);
278 snl_init_writer(&ss_req, &nw);
280 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETNEIGH);
281 struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
283 ndmsg->ndm_family = AF_INET;
284 /* let kernel filter results by interface if provided */
285 ndmsg->ndm_ifindex = ifindex;
288 if (!snl_finalize_msg(&nw) || !snl_send_message(&ss_req, hdr)) {
293 uint32_t nlmsg_seq = hdr->nlmsg_seq;
294 struct snl_errmsg_data e = {};
296 nl_init_socket(&ss_cmd);
298 while ((hdr = snl_read_reply_multi(&ss_req, nlmsg_seq, &e)) != NULL) {
299 struct snl_parsed_neigh neigh = {};
300 struct sockaddr_in *neighaddr;
302 if (!snl_parse_nlmsg(&ss_req, hdr, &snl_rtm_neigh_parser, &neigh))
305 if (neigh.nda_ifindex != link.ifi_index) {
306 snl_clear_lb(&ss_cmd);
307 memset(&link, 0, sizeof(link));
308 if (!get_link_info(&ss_cmd, neigh.nda_ifindex, &link))
312 /* filter results based on host if provided */
313 neighaddr = (struct sockaddr_in *)neigh.nda_dst;
315 (addr.s_addr != neighaddr->sin_addr.s_addr))
318 print_entry(&neigh, &link);
320 snl_clear_lb(&ss_req);
330 delete_nl(uint32_t ifindex, char *host)
332 struct snl_state ss = {};
333 struct snl_writer nw;
334 struct sockaddr_in *dst;
342 ifindex = fix_ifindex(&ss, ifindex, dst->sin_addr);
344 xo_warnx("delete: cannot locate %s", host);
349 snl_init_writer(&ss, &nw);
350 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_DELNEIGH);
351 struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
353 ndmsg->ndm_family = AF_INET;
354 ndmsg->ndm_ifindex = ifindex;
356 snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)dst);
358 if (!snl_finalize_msg(&nw) || !snl_send_message(&ss, hdr)) {
363 struct snl_errmsg_data e = {};
364 snl_read_reply_code(&ss, hdr->nlmsg_seq, &e);
366 if (e.error_str != NULL)
367 xo_warnx("delete %s: %s (%s)", host, strerror(e.error), e.error_str);
369 xo_warnx("delete %s: %s", host, strerror(e.error));
371 printf("%s (%s) deleted\n", host, inet_ntoa(dst->sin_addr));
375 return (e.error != 0);
379 set_nl(uint32_t ifindex, struct sockaddr_in *dst, struct sockaddr_dl *sdl, char *host)
381 struct snl_state ss = {};
382 struct snl_writer nw;
386 ifindex = fix_ifindex(&ss, ifindex, dst->sin_addr);
388 xo_warnx("delete: cannot locate %s", host);
393 if (opts.expire_time != 0)
394 opts.flags &= ~RTF_STATIC;
396 snl_init_writer(&ss, &nw);
397 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_NEWNEIGH);
398 hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
399 struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
401 uint8_t nl_flags = 0;
403 ndmsg->ndm_family = AF_INET;
404 ndmsg->ndm_ifindex = ifindex;
405 ndmsg->ndm_state = (opts.flags & RTF_STATIC) ? NUD_PERMANENT : NUD_NONE;
407 if (opts.flags & RTF_ANNOUNCE)
408 nl_flags |= NTF_PROXY;
409 if (opts.flags & RTF_STATIC)
410 nl_flags |= NTF_STICKY;
411 ndmsg->ndm_flags = nl_flags;
413 snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)dst);
414 snl_add_msg_attr(&nw, NDA_LLADDR, sdl->sdl_alen, LLADDR(sdl));
416 if (opts.expire_time != 0) {
419 gettimeofday(&now, 0);
420 int off = snl_add_msg_attr_nested(&nw, NDA_FREEBSD);
421 snl_add_msg_attr_u32(&nw, NDAF_NEXT_STATE_TS, now.tv_sec + opts.expire_time);
422 snl_end_attr_nested(&nw, off);
425 if (!snl_finalize_msg(&nw) || !snl_send_message(&ss, hdr)) {
430 struct snl_errmsg_data e = {};
431 snl_read_reply_code(&ss, hdr->nlmsg_seq, &e);
433 if (e.error_str != NULL)
434 xo_warnx("set: %s: %s (%s)", host, strerror(e.error), e.error_str);
436 xo_warnx("set %s: %s", host, strerror(e.error));
440 return (e.error != 0);