diff --git a/usr.sbin/arp/arp.c b/usr.sbin/arp/arp.c index ee4236b5299b..2597226d18ea 100644 --- a/usr.sbin/arp/arp.c +++ b/usr.sbin/arp/arp.c @@ -81,7 +81,6 @@ static int get(char *host); static int file(char *name); static struct rt_msghdr *rtmsg(int cmd, struct sockaddr_in *dst, struct sockaddr_dl *sdl); -static int get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr); static int set_rtsock(struct sockaddr_in *dst, struct sockaddr_dl *sdl_m, char *host); @@ -143,7 +142,8 @@ main(int argc, char *argv[]) if (!func) func = F_GET; if (opts.rifname) { - if (func != F_GET && func != F_SET && !(func == F_DELETE && opts.aflag)) + if (func != F_GET && func != F_SET && func != F_REPLACE && + !(func == F_DELETE && opts.aflag)) xo_errx(1, "-i not applicable to this operation"); if ((opts.rifindex = if_nametoindex(opts.rifname)) == 0) { if (errno == ENXIO) @@ -273,7 +273,6 @@ getaddr(char *host) return (&reply); } -int valid_type(int type); /* * Returns true if the type is a valid one for ARP. */ @@ -357,11 +356,17 @@ set(int argc, char **argv) } ea = (struct ether_addr *)LLADDR(&sdl_m); if ((opts.flags & RTF_ANNOUNCE) && !strcmp(eaddr, "auto")) { - if (!get_ether_addr(dst->sin_addr.s_addr, ea)) { + uint32_t ifindex, ifflags; + if (!get_ether_addr(dst->sin_addr.s_addr, ea, + &ifindex, &ifflags)) { xo_warnx("no interface found for %s", - inet_ntoa(dst->sin_addr)); + inet_ntoa(dst->sin_addr)); return (1); } + if (opts.rifindex == 0) { + opts.rifindex = ifindex; + opts.rifflags = ifflags; + } sdl_m.sdl_alen = ETHER_ADDR_LEN; } else { struct ether_addr *ea1 = ether_aton(eaddr); @@ -375,7 +380,7 @@ set(int argc, char **argv) } } #ifndef WITHOUT_NETLINK - return (set_nl(opts.rifindex, dst, &sdl_m, host)); + return (set_nl(dst, &sdl_m, host)); #else return (set_rtsock(dst, &sdl_m, host)); #endif @@ -522,7 +527,7 @@ delete(char *host) #ifdef WITHOUT_NETLINK return (delete_rtsock(host)); #else - return (delete_nl(0, host)); + return (delete_nl(host)); #endif } @@ -822,8 +827,9 @@ rtmsg(int cmd, struct sockaddr_in *dst, struct sockaddr_dl *sdl) * get_ether_addr - get the hardware address of an interface on the * same subnet as ipaddr. */ -static int -get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr) +int +get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr, + uint32_t *pifindex, uint32_t *pifflags) { struct ifaddrs *ifa, *ifd, *ifas = NULL; in_addr_t ina, mask; @@ -862,7 +868,15 @@ get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr) } if (ifa == NULL) goto done; - + if (pifindex != NULL) + *pifindex = if_nametoindex(ifa->ifa_name); + if (pifflags != NULL) + *pifflags = ifa->ifa_flags; + if (hwaddr == NULL) { + /* ether addr is not required */ + retval = ETHER_ADDR_LEN; + goto done; + } /* * Now scan through again looking for a link-level address * for this interface. diff --git a/usr.sbin/arp/arp.h b/usr.sbin/arp/arp.h index 487863be43e7..e9302af7a217 100644 --- a/usr.sbin/arp/arp.h +++ b/usr.sbin/arp/arp.h @@ -2,8 +2,9 @@ #define _USR_SBIN_ARP_ARP_H_ int valid_type(int type); +int get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr, + uint32_t *pifindex, uint32_t *pifflags); struct sockaddr_in *getaddr(char *host); -int print_entries_nl(uint32_t ifindex, struct in_addr addr); struct arp_opts { bool aflag; @@ -11,13 +12,13 @@ struct arp_opts { time_t expire_time; int flags; char *rifname; - unsigned int rifindex; + uint32_t rifindex; + uint32_t rifflags; }; extern struct arp_opts opts; int print_entries_nl(uint32_t ifindex, struct in_addr addr); -int delete_nl(uint32_t ifindex, char *host); -int set_nl(uint32_t ifindex, struct sockaddr_in *dst, struct sockaddr_dl *sdl, - char *host); +int delete_nl(char *host); +int set_nl(struct sockaddr_in *dst, struct sockaddr_dl *sdl, char *host); #endif diff --git a/usr.sbin/arp/arp_netlink.c b/usr.sbin/arp/arp_netlink.c index db1ef775dea2..78a88740ab6a 100644 --- a/usr.sbin/arp/arp_netlink.c +++ b/usr.sbin/arp/arp_netlink.c @@ -79,13 +79,15 @@ get_link_info(struct snl_state *ss, uint32_t ifindex, static bool -has_l2(struct snl_state *ss, uint32_t ifindex) +has_l2(struct snl_state *ss, uint32_t ifindex, uint32_t *pflags) { struct snl_parsed_link_simple link = {}; + *pflags = 0; if (!get_link_info(ss, ifindex, &link)) return (false); + *pflags = link.ifi_flags; return (valid_type(link.ifi_type) != 0); } @@ -104,6 +106,7 @@ static int guess_ifindex(struct snl_state *ss, uint32_t fibnum, struct in_addr addr) { struct snl_writer nw; + uint32_t ifindex, ifflags; snl_init_writer(ss, &nw); @@ -133,9 +136,16 @@ guess_ifindex(struct snl_state *ss, uint32_t fibnum, struct in_addr addr) return (0); /* Check if the interface is of supported type */ - if (has_l2(ss, r.rta_oif)) + if (has_l2(ss, r.rta_oif, &ifflags)) return (r.rta_oif); + /* Check if we are doing proxy arp for PTP interface */ + if (ifflags & IFF_POINTOPOINT) { + /* Guess interface by dst prefix */ + if (get_ether_addr(addr.s_addr, NULL, &ifindex, &ifflags)) + return (ifindex); + } + /* Check the case when we matched the loopback route for P2P */ snl_init_writer(ss, &nw); hdr = snl_create_msg_request(&nw, RTM_GETNEXTHOP); @@ -326,11 +336,12 @@ print_entries_nl(uint32_t ifindex, struct in_addr addr) } int -delete_nl(uint32_t ifindex, char *host) +delete_nl(char *host) { struct snl_state ss = {}; struct snl_writer nw; struct sockaddr_in *dst; + uint32_t ifindex = opts.rifindex; dst = getaddr(host); if (dst == NULL) @@ -375,10 +386,11 @@ delete_nl(uint32_t ifindex, char *host) } int -set_nl(uint32_t ifindex, struct sockaddr_in *dst, struct sockaddr_dl *sdl, char *host) +set_nl(struct sockaddr_in *dst, struct sockaddr_dl *sdl, char *host) { struct snl_state ss = {}; struct snl_writer nw; + uint32_t ifindex = opts.rifindex; nl_init_socket(&ss);