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