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, uint32_t *pflags) 83 { 84 struct snl_parsed_link_simple link = {}; 85 86 *pflags = 0; 87 if (!get_link_info(ss, ifindex, &link)) 88 return (false); 89 90 *pflags = link.ifi_flags; 91 return (valid_type(link.ifi_type) != 0); 92 } 93 94 static uint32_t 95 get_myfib(void) 96 { 97 uint32_t fibnum = 0; 98 size_t len = sizeof(fibnum); 99 100 sysctlbyname("net.my_fibnum", (void *)&fibnum, &len, NULL, 0); 101 102 return (fibnum); 103 } 104 105 static int 106 guess_ifindex(struct snl_state *ss, uint32_t fibnum, struct in_addr addr) 107 { 108 struct snl_writer nw; 109 uint32_t ifindex, ifflags; 110 111 snl_init_writer(ss, &nw); 112 113 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETROUTE); 114 struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg); 115 rtm->rtm_family = AF_INET; 116 117 struct sockaddr_in dst = { .sin_family = AF_INET, .sin_addr = addr }; 118 snl_add_msg_attr_ip(&nw, RTA_DST, (struct sockaddr *)&dst); 119 snl_add_msg_attr_u32(&nw, RTA_TABLE, fibnum); 120 121 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) 122 return (0); 123 124 hdr = snl_read_reply(ss, hdr->nlmsg_seq); 125 126 if (hdr->nlmsg_type != NL_RTM_NEWROUTE) { 127 /* No route found, unable to guess ifindex */ 128 return (0); 129 } 130 131 struct snl_parsed_route r = {}; 132 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r)) 133 return (0); 134 135 if (r.rta_multipath.num_nhops > 0 || (r.rta_rtflags & RTF_GATEWAY)) 136 return (0); 137 138 /* Check if the interface is of supported type */ 139 if (has_l2(ss, r.rta_oif, &ifflags)) 140 return (r.rta_oif); 141 142 /* Check if we are doing proxy arp for P2P interface */ 143 if (ifflags & IFF_POINTOPOINT) { 144 /* Guess interface by dst prefix */ 145 if (get_ifinfo(addr.s_addr, NULL, &ifindex)) 146 return (ifindex); 147 } 148 149 /* Check the case when we matched the loopback route for P2P */ 150 snl_init_writer(ss, &nw); 151 hdr = snl_create_msg_request(&nw, RTM_GETNEXTHOP); 152 snl_reserve_msg_object(&nw, struct nhmsg); 153 154 int off = snl_add_msg_attr_nested(&nw, NHA_FREEBSD); 155 snl_add_msg_attr_u32(&nw, NHAF_KID, r.rta_knh_id); 156 snl_add_msg_attr_u8(&nw, NHAF_FAMILY, AF_INET); 157 snl_add_msg_attr_u32(&nw, NHAF_TABLE, fibnum); 158 snl_end_attr_nested(&nw, off); 159 160 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) 161 return (0); 162 163 hdr = snl_read_reply(ss, hdr->nlmsg_seq); 164 165 if (hdr->nlmsg_type != NL_RTM_NEWNEXTHOP) { 166 /* No nexthop found, unable to guess ifindex */ 167 return (0); 168 } 169 170 struct snl_parsed_nhop nh = {}; 171 if (!snl_parse_nlmsg(ss, hdr, &snl_nhmsg_parser, &nh)) 172 return (0); 173 174 return (nh.nhaf_aif); 175 } 176 177 static uint32_t 178 fix_ifindex(struct snl_state *ss, uint32_t ifindex, struct in_addr addr) 179 { 180 if (ifindex == 0) 181 ifindex = guess_ifindex(ss, get_myfib(), addr); 182 return (ifindex); 183 } 184 185 static void 186 print_entry(struct snl_parsed_neigh *neigh, struct snl_parsed_link_simple *link) 187 { 188 const char *host; 189 struct hostent *hp; 190 struct sockaddr_in *addr = (struct sockaddr_in *)neigh->nda_dst; 191 192 xo_open_instance("arp-cache"); 193 194 if (!opts.nflag) 195 hp = gethostbyaddr((caddr_t)&(addr->sin_addr), 196 sizeof(addr->sin_addr), AF_INET); 197 else 198 hp = 0; 199 if (hp) 200 host = hp->h_name; 201 else { 202 host = "?"; 203 if (h_errno == TRY_AGAIN) 204 opts.nflag = true; 205 } 206 xo_emit("{:hostname/%s} ({:ip-address/%s}) at ", host, 207 inet_ntoa(addr->sin_addr)); 208 if (neigh->nda_lladdr != NULL) { 209 struct sockaddr_dl sdl = { 210 .sdl_family = AF_LINK, 211 .sdl_type = link->ifi_type, 212 .sdl_len = sizeof(struct sockaddr_dl), 213 .sdl_alen = NLA_DATA_LEN(neigh->nda_lladdr), 214 }; 215 memcpy(sdl.sdl_data, NLA_DATA(neigh->nda_lladdr), sdl.sdl_alen); 216 217 if ((sdl.sdl_type == IFT_ETHER || 218 sdl.sdl_type == IFT_L2VLAN || 219 sdl.sdl_type == IFT_BRIDGE) && 220 sdl.sdl_alen == ETHER_ADDR_LEN) 221 xo_emit("{:mac-address/%s}", 222 ether_ntoa((struct ether_addr *)LLADDR(&sdl))); 223 else { 224 225 xo_emit("{:mac-address/%s}", link_ntoa(&sdl)); 226 } 227 } else 228 xo_emit("{d:/(incomplete)}{en:incomplete/true}"); 229 xo_emit(" on {:interface/%s}", link->ifla_ifname); 230 231 if (neigh->ndaf_next_ts == 0) 232 xo_emit("{d:/ permanent}{en:permanent/true}"); 233 else { 234 time_t expire_time; 235 struct timeval now; 236 237 gettimeofday(&now, 0); 238 if ((expire_time = neigh->ndaf_next_ts - now.tv_sec) > 0) 239 xo_emit(" expires in {:expires/%d} seconds", 240 (int)expire_time); 241 else 242 xo_emit("{d:/ expired}{en:expired/true}"); 243 } 244 245 if (neigh->ndm_flags & NTF_PROXY) 246 xo_emit("{d:/ published}{en:published/true}"); 247 248 switch(link->ifi_type) { 249 case IFT_ETHER: 250 xo_emit(" [{:type/ethernet}]"); 251 break; 252 case IFT_FDDI: 253 xo_emit(" [{:type/fddi}]"); 254 break; 255 case IFT_ATM: 256 xo_emit(" [{:type/atm}]"); 257 break; 258 case IFT_L2VLAN: 259 xo_emit(" [{:type/vlan}]"); 260 break; 261 case IFT_IEEE1394: 262 xo_emit(" [{:type/firewire}]"); 263 break; 264 case IFT_BRIDGE: 265 xo_emit(" [{:type/bridge}]"); 266 break; 267 case IFT_INFINIBAND: 268 xo_emit(" [{:type/infiniband}]"); 269 break; 270 default: 271 break; 272 } 273 274 xo_emit("\n"); 275 276 xo_close_instance("arp-cache"); 277 } 278 279 int 280 print_entries_nl(uint32_t ifindex, struct in_addr addr) 281 { 282 struct snl_state ss_req = {}, ss_cmd = {}; 283 struct snl_parsed_link_simple link = {}; 284 struct snl_writer nw; 285 286 nl_init_socket(&ss_req); 287 snl_init_writer(&ss_req, &nw); 288 289 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETNEIGH); 290 struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg); 291 if (ndmsg != NULL) { 292 ndmsg->ndm_family = AF_INET; 293 /* let kernel filter results by interface if provided */ 294 ndmsg->ndm_ifindex = ifindex; 295 } 296 297 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss_req, hdr)) { 298 snl_free(&ss_req); 299 return (0); 300 } 301 302 uint32_t nlmsg_seq = hdr->nlmsg_seq; 303 struct snl_errmsg_data e = {}; 304 int count = 0; 305 nl_init_socket(&ss_cmd); 306 307 while ((hdr = snl_read_reply_multi(&ss_req, nlmsg_seq, &e)) != NULL) { 308 struct snl_parsed_neigh neigh = {}; 309 struct sockaddr_in *neighaddr; 310 311 if (!snl_parse_nlmsg(&ss_req, hdr, &snl_rtm_neigh_parser, &neigh)) 312 continue; 313 314 if (neigh.nda_ifindex != link.ifi_index) { 315 snl_clear_lb(&ss_cmd); 316 memset(&link, 0, sizeof(link)); 317 if (!get_link_info(&ss_cmd, neigh.nda_ifindex, &link)) 318 continue; 319 } 320 321 /* filter results based on host if provided */ 322 neighaddr = (struct sockaddr_in *)neigh.nda_dst; 323 if (addr.s_addr && 324 (addr.s_addr != neighaddr->sin_addr.s_addr)) 325 continue; 326 327 print_entry(&neigh, &link); 328 count++; 329 snl_clear_lb(&ss_req); 330 } 331 332 snl_free(&ss_req); 333 snl_free(&ss_cmd); 334 335 return (count); 336 } 337 338 int 339 delete_nl(char *host) 340 { 341 struct snl_state ss = {}; 342 struct snl_writer nw; 343 struct sockaddr_in *dst; 344 uint32_t ifindex = opts.rifindex; 345 346 dst = getaddr(host); 347 if (dst == NULL) 348 return (1); 349 350 nl_init_socket(&ss); 351 352 ifindex = fix_ifindex(&ss, ifindex, dst->sin_addr); 353 if (ifindex == 0) { 354 xo_warnx("delete: cannot locate %s", host); 355 snl_free(&ss); 356 return (0); 357 } 358 359 snl_init_writer(&ss, &nw); 360 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_DELNEIGH); 361 struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg); 362 if (ndmsg != NULL) { 363 ndmsg->ndm_family = AF_INET; 364 ndmsg->ndm_ifindex = ifindex; 365 } 366 snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)dst); 367 368 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) { 369 snl_free(&ss); 370 return (1); 371 } 372 373 struct snl_errmsg_data e = {}; 374 snl_read_reply_code(&ss, hdr->nlmsg_seq, &e); 375 if (e.error != 0) { 376 if (e.error_str != NULL) 377 xo_warnx("delete %s: %s (%s)", host, strerror(e.error), e.error_str); 378 else 379 xo_warnx("delete %s: %s", host, strerror(e.error)); 380 } else 381 printf("%s (%s) deleted\n", host, inet_ntoa(dst->sin_addr)); 382 383 snl_free(&ss); 384 385 return (e.error != 0); 386 } 387 388 int 389 set_nl(struct sockaddr_in *dst, struct sockaddr_dl *sdl, char *host) 390 { 391 struct snl_state ss = {}; 392 struct snl_writer nw; 393 uint32_t ifindex = opts.rifindex; 394 395 nl_init_socket(&ss); 396 397 ifindex = fix_ifindex(&ss, ifindex, dst->sin_addr); 398 if (ifindex == 0) { 399 xo_warnx("set: cannot locate %s", host); 400 snl_free(&ss); 401 return (0); 402 } 403 404 snl_init_writer(&ss, &nw); 405 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_NEWNEIGH); 406 hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE; 407 struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg); 408 if (ndmsg != NULL) { 409 uint8_t nl_flags = 0; 410 411 ndmsg->ndm_family = AF_INET; 412 ndmsg->ndm_ifindex = ifindex; 413 ndmsg->ndm_state = (opts.expire_time == 0) ? \ 414 NUD_PERMANENT : NUD_NONE; 415 416 if (opts.flags & RTF_ANNOUNCE) 417 nl_flags |= NTF_PROXY; 418 if (opts.expire_time == 0) 419 nl_flags |= NTF_STICKY; 420 ndmsg->ndm_flags = nl_flags; 421 } 422 snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)dst); 423 snl_add_msg_attr(&nw, NDA_LLADDR, sdl->sdl_alen, LLADDR(sdl)); 424 425 if (opts.expire_time != 0) { 426 struct timeval now; 427 428 gettimeofday(&now, 0); 429 int off = snl_add_msg_attr_nested(&nw, NDA_FREEBSD); 430 snl_add_msg_attr_u32(&nw, NDAF_NEXT_STATE_TS, now.tv_sec + opts.expire_time); 431 snl_end_attr_nested(&nw, off); 432 } 433 434 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) { 435 snl_free(&ss); 436 return (1); 437 } 438 439 struct snl_errmsg_data e = {}; 440 snl_read_reply_code(&ss, hdr->nlmsg_seq, &e); 441 if (e.error != 0) { 442 if (e.error_str != NULL) 443 xo_warnx("set: %s: %s (%s)", host, strerror(e.error), e.error_str); 444 else 445 xo_warnx("set %s: %s", host, strerror(e.error)); 446 } 447 snl_free(&ss); 448 449 return (e.error != 0); 450 } 451 452