1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <stdbool.h> 5 #include <err.h> 6 #include <errno.h> 7 #include <netdb.h> 8 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> 15 #include <sys/time.h> 16 #include <sys/types.h> 17 18 #include <netinet/in.h> 19 #include <arpa/inet.h> 20 21 #include <net/ethernet.h> 22 #include <net/if.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> 31 32 #include <libxo/xo.h> 33 #include "arp.h" 34 35 #define RTF_ANNOUNCE RTF_PROTO2 36 37 static void 38 nl_init_socket(struct snl_state *ss) 39 { 40 if (snl_init(ss, NETLINK_ROUTE)) 41 return; 42 43 if (modfind("netlink") == -1 && errno == ENOENT) { 44 /* Try to load */ 45 if (kldload("netlink") == -1) 46 err(1, "netlink is not loaded and load attempt failed"); 47 if (snl_init(ss, NETLINK_ROUTE)) 48 return; 49 } 50 51 err(1, "unable to open netlink socket"); 52 } 53 54 static bool 55 get_link_info(struct snl_state *ss, uint32_t ifindex, 56 struct snl_parsed_link_simple *link) 57 { 58 struct snl_writer nw; 59 60 snl_init_writer(ss, &nw); 61 62 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK); 63 struct ifinfomsg *ifmsg = snl_reserve_msg_object(&nw, struct ifinfomsg); 64 if (ifmsg != NULL) 65 ifmsg->ifi_index = ifindex; 66 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) 67 return (false); 68 69 hdr = snl_read_reply(ss, hdr->nlmsg_seq); 70 71 if (hdr == NULL || hdr->nlmsg_type != RTM_NEWLINK) 72 return (false); 73 74 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, link)) 75 return (false); 76 77 return (true); 78 } 79 80 81 82 static bool 83 has_l2(struct snl_state *ss, uint32_t ifindex) 84 { 85 struct snl_parsed_link_simple link = {}; 86 87 if (!get_link_info(ss, ifindex, &link)) 88 return (false); 89 90 return (valid_type(link.ifi_type) != 0); 91 } 92 93 static uint32_t 94 get_myfib(void) 95 { 96 uint32_t fibnum = 0; 97 size_t len = sizeof(fibnum); 98 99 sysctlbyname("net.my_fibnum", (void *)&fibnum, &len, NULL, 0); 100 101 return (fibnum); 102 } 103 104 static int 105 guess_ifindex(struct snl_state *ss, uint32_t fibnum, struct in_addr addr) 106 { 107 struct snl_writer nw; 108 109 snl_init_writer(ss, &nw); 110 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; 114 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); 118 119 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) 120 return (0); 121 122 hdr = snl_read_reply(ss, hdr->nlmsg_seq); 123 124 if (hdr->nlmsg_type != NL_RTM_NEWROUTE) { 125 /* No route found, unable to guess ifindex */ 126 return (0); 127 } 128 129 struct snl_parsed_route r = {}; 130 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r)) 131 return (0); 132 133 if (r.rta_multipath.num_nhops > 0 || (r.rta_rtflags & RTF_GATEWAY)) 134 return (0); 135 136 /* Check if the interface is of supported type */ 137 if (has_l2(ss, r.rta_oif)) 138 return (r.rta_oif); 139 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); 144 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); 150 151 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) 152 return (0); 153 154 hdr = snl_read_reply(ss, hdr->nlmsg_seq); 155 156 if (hdr->nlmsg_type != NL_RTM_NEWNEXTHOP) { 157 /* No nexthop found, unable to guess ifindex */ 158 return (0); 159 } 160 161 struct snl_parsed_nhop nh = {}; 162 if (!snl_parse_nlmsg(ss, hdr, &snl_nhmsg_parser, &nh)) 163 return (0); 164 165 return (nh.nhaf_aif); 166 } 167 168 static uint32_t 169 fix_ifindex(struct snl_state *ss, uint32_t ifindex, struct in_addr addr) 170 { 171 if (ifindex == 0) 172 ifindex = guess_ifindex(ss, get_myfib(), addr); 173 return (ifindex); 174 } 175 176 static void 177 print_entry(struct snl_parsed_neigh *neigh, struct snl_parsed_link_simple *link) 178 { 179 const char *host; 180 struct hostent *hp; 181 struct sockaddr_in *addr = (struct sockaddr_in *)neigh->nda_dst; 182 183 xo_open_instance("arp-cache"); 184 185 if (!opts.nflag) 186 hp = gethostbyaddr((caddr_t)&(addr->sin_addr), 187 sizeof(addr->sin_addr), AF_INET); 188 else 189 hp = 0; 190 if (hp) 191 host = hp->h_name; 192 else { 193 host = "?"; 194 if (h_errno == TRY_AGAIN) 195 opts.nflag = true; 196 } 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), 205 }; 206 memcpy(sdl.sdl_data, NLA_DATA(neigh->nda_lladdr), sdl.sdl_alen); 207 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))); 214 else { 215 216 xo_emit("{:mac-address/%s}", link_ntoa(&sdl)); 217 } 218 } else 219 xo_emit("{d:/(incomplete)}{en:incomplete/true}"); 220 xo_emit(" on {:interface/%s}", link->ifla_ifname); 221 222 if (neigh->ndaf_next_ts == 0) 223 xo_emit("{d:/ permanent}{en:permanent/true}"); 224 else { 225 time_t expire_time; 226 struct timeval now; 227 228 gettimeofday(&now, 0); 229 if ((expire_time = neigh->ndaf_next_ts - now.tv_sec) > 0) 230 xo_emit(" expires in {:expires/%d} seconds", 231 (int)expire_time); 232 else 233 xo_emit("{d:/ expired}{en:expired/true}"); 234 } 235 236 if (neigh->ndm_flags & NTF_PROXY) 237 xo_emit("{d:/ published}{en:published/true}"); 238 239 switch(link->ifi_type) { 240 case IFT_ETHER: 241 xo_emit(" [{:type/ethernet}]"); 242 break; 243 case IFT_FDDI: 244 xo_emit(" [{:type/fddi}]"); 245 break; 246 case IFT_ATM: 247 xo_emit(" [{:type/atm}]"); 248 break; 249 case IFT_L2VLAN: 250 xo_emit(" [{:type/vlan}]"); 251 break; 252 case IFT_IEEE1394: 253 xo_emit(" [{:type/firewire}]"); 254 break; 255 case IFT_BRIDGE: 256 xo_emit(" [{:type/bridge}]"); 257 break; 258 case IFT_INFINIBAND: 259 xo_emit(" [{:type/infiniband}]"); 260 break; 261 default: 262 break; 263 } 264 265 xo_emit("\n"); 266 267 xo_close_instance("arp-cache"); 268 } 269 270 int 271 print_entries_nl(uint32_t ifindex, struct in_addr addr) 272 { 273 struct snl_state ss_req = {}, ss_cmd = {}; 274 struct snl_parsed_link_simple link = {}; 275 struct snl_writer nw; 276 277 nl_init_socket(&ss_req); 278 snl_init_writer(&ss_req, &nw); 279 280 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETNEIGH); 281 struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg); 282 if (ndmsg != NULL) { 283 ndmsg->ndm_family = AF_INET; 284 /* let kernel filter results by interface if provided */ 285 ndmsg->ndm_ifindex = ifindex; 286 } 287 288 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss_req, hdr)) { 289 snl_free(&ss_req); 290 return (0); 291 } 292 293 uint32_t nlmsg_seq = hdr->nlmsg_seq; 294 struct snl_errmsg_data e = {}; 295 int count = 0; 296 nl_init_socket(&ss_cmd); 297 298 while ((hdr = snl_read_reply_multi(&ss_req, nlmsg_seq, &e)) != NULL) { 299 struct snl_parsed_neigh neigh = {}; 300 struct sockaddr_in *neighaddr; 301 302 if (!snl_parse_nlmsg(&ss_req, hdr, &snl_rtm_neigh_parser, &neigh)) 303 continue; 304 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)) 309 continue; 310 } 311 312 /* filter results based on host if provided */ 313 neighaddr = (struct sockaddr_in *)neigh.nda_dst; 314 if (addr.s_addr && 315 (addr.s_addr != neighaddr->sin_addr.s_addr)) 316 continue; 317 318 print_entry(&neigh, &link); 319 count++; 320 snl_clear_lb(&ss_req); 321 } 322 323 snl_free(&ss_req); 324 snl_free(&ss_cmd); 325 326 return (count); 327 } 328 329 int 330 delete_nl(uint32_t ifindex, char *host) 331 { 332 struct snl_state ss = {}; 333 struct snl_writer nw; 334 struct sockaddr_in *dst; 335 336 dst = getaddr(host); 337 if (dst == NULL) 338 return (1); 339 340 nl_init_socket(&ss); 341 342 ifindex = fix_ifindex(&ss, ifindex, dst->sin_addr); 343 if (ifindex == 0) { 344 xo_warnx("delete: cannot locate %s", host); 345 snl_free(&ss); 346 return (0); 347 } 348 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); 352 if (ndmsg != NULL) { 353 ndmsg->ndm_family = AF_INET; 354 ndmsg->ndm_ifindex = ifindex; 355 } 356 snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)dst); 357 358 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) { 359 snl_free(&ss); 360 return (1); 361 } 362 363 struct snl_errmsg_data e = {}; 364 snl_read_reply_code(&ss, hdr->nlmsg_seq, &e); 365 if (e.error != 0) { 366 if (e.error_str != NULL) 367 xo_warnx("delete %s: %s (%s)", host, strerror(e.error), e.error_str); 368 else 369 xo_warnx("delete %s: %s", host, strerror(e.error)); 370 } else 371 printf("%s (%s) deleted\n", host, inet_ntoa(dst->sin_addr)); 372 373 snl_free(&ss); 374 375 return (e.error != 0); 376 } 377 378 int 379 set_nl(uint32_t ifindex, struct sockaddr_in *dst, struct sockaddr_dl *sdl, char *host) 380 { 381 struct snl_state ss = {}; 382 struct snl_writer nw; 383 384 nl_init_socket(&ss); 385 386 ifindex = fix_ifindex(&ss, ifindex, dst->sin_addr); 387 if (ifindex == 0) { 388 xo_warnx("delete: cannot locate %s", host); 389 snl_free(&ss); 390 return (0); 391 } 392 393 if (opts.expire_time != 0) 394 opts.flags &= ~RTF_STATIC; 395 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); 400 if (ndmsg != NULL) { 401 uint8_t nl_flags = 0; 402 403 ndmsg->ndm_family = AF_INET; 404 ndmsg->ndm_ifindex = ifindex; 405 ndmsg->ndm_state = (opts.flags & RTF_STATIC) ? NUD_PERMANENT : NUD_NONE; 406 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; 412 } 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)); 415 416 if (opts.expire_time != 0) { 417 struct timeval now; 418 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); 423 } 424 425 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) { 426 snl_free(&ss); 427 return (1); 428 } 429 430 struct snl_errmsg_data e = {}; 431 snl_read_reply_code(&ss, hdr->nlmsg_seq, &e); 432 if (e.error != 0) { 433 if (e.error_str != NULL) 434 xo_warnx("set: %s: %s (%s)", host, strerror(e.error), e.error_str); 435 else 436 xo_warnx("set %s: %s", host, strerror(e.error)); 437 } 438 snl_free(&ss); 439 440 return (e.error != 0); 441 } 442 443