arp: fix adding proxy entries for P2P interfaces

The old rtsock implementation used in-kernel workaround to do so.
When route to specified destination address used P2P interface, the
kernel did the search with ifa_ifwithnet() for most suitable network
and then add proxy entry to this interface.

Use similar approach with netlink implementation. We already have
get_ether_addr() function that does almost the same thing as
ifa_ifwithnet(). Use it when we find that destination route uses
P2P interface and then try to guess suitable interface. This should
fix the use of netlink-based arp(8) in mpd5.

Rename get_ether_addr() to get_ifinfo(), since now it is used to find
only ifindex in case when hwaddr is specified by user.
Also make set_nl() and delete_nl() prototype similar to rtsock.
And allow '-i' to be used with '-S', since we already allow the same
for '-s'.

PR:		290221
Reported by:	eugen
Reviewed by:	eugen
MFC after:	1 week
Differential Revision:	https://reviews.freebsd.org/D53113
This commit is contained in:
Andrey V. Elsukov 2025-10-17 10:53:35 +03:00
parent 86db3c735d
commit c26d6bf9da
3 changed files with 40 additions and 21 deletions

View file

@ -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,14 @@ 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;
if (!get_ifinfo(dst->sin_addr.s_addr, ea, &ifindex)) {
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;
sdl_m.sdl_alen = ETHER_ADDR_LEN;
} else {
struct ether_addr *ea1 = ether_aton(eaddr);
@ -375,7 +377,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 +524,7 @@ delete(char *host)
#ifdef WITHOUT_NETLINK
return (delete_rtsock(host));
#else
return (delete_nl(0, host));
return (delete_nl(host));
#endif
}
@ -819,11 +821,11 @@ doit:
}
/*
* get_ether_addr - get the hardware address of an interface on the
* same subnet as ipaddr.
* get_ifinfo - get the hardware address and if_index of an interface
* on the same subnet as ipaddr.
*/
static int
get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr)
int
get_ifinfo(in_addr_t ipaddr, struct ether_addr *hwaddr, uint32_t *pifindex)
{
struct ifaddrs *ifa, *ifd, *ifas = NULL;
in_addr_t ina, mask;
@ -862,7 +864,13 @@ 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 (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.

View file

@ -2,8 +2,8 @@
#define _USR_SBIN_ARP_ARP_H_
int valid_type(int type);
int get_ifinfo(in_addr_t ipaddr, struct ether_addr *hwaddr, uint32_t *pifindex);
struct sockaddr_in *getaddr(char *host);
int print_entries_nl(uint32_t ifindex, struct in_addr addr);
struct arp_opts {
bool aflag;
@ -11,13 +11,12 @@ struct arp_opts {
time_t expire_time;
int flags;
char *rifname;
unsigned int rifindex;
uint32_t rifindex;
};
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

View file

@ -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 P2P interface */
if (ifflags & IFF_POINTOPOINT) {
/* Guess interface by dst prefix */
if (get_ifinfo(addr.s_addr, NULL, &ifindex))
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);