diff --git a/subsys/net/l2/ethernet/arp.c b/subsys/net/l2/ethernet/arp.c index 304994f2e7b5a..fb810f0ea30be 100644 --- a/subsys/net/l2/ethernet/arp.c +++ b/subsys/net/l2/ethernet/arp.c @@ -228,10 +228,19 @@ static void arp_request_timeout(struct k_work *work) k_mutex_unlock(&arp_mutex); } +static inline bool is_same_subnet(uint32_t addr1, + uint32_t addr2, + uint32_t netmask) +{ + return ((addr1 & netmask) == (addr2 & netmask)); +} + static inline struct net_in_addr *if_get_addr(struct net_if *iface, - const uint8_t *addr) + const uint8_t *own_addr, + const struct net_in_addr *dst_addr) { struct net_if_ipv4 *ipv4 = iface->config.ip.ipv4; + struct net_in_addr *fall_back = NULL; if (!ipv4) { return NULL; @@ -241,14 +250,23 @@ static inline struct net_in_addr *if_get_addr(struct net_if *iface, if (ipv4->unicast[i].ipv4.is_used && ipv4->unicast[i].ipv4.address.family == NET_AF_INET && ipv4->unicast[i].ipv4.addr_state == NET_ADDR_PREFERRED && - (!addr || + ((own_addr == NULL) || net_ipv4_addr_cmp_raw( - addr, ipv4->unicast[i].ipv4.address.in_addr.s4_addr))) { - return &ipv4->unicast[i].ipv4.address.in_addr; + own_addr, ipv4->unicast[i].ipv4.address.in_addr.s4_addr))) { + if ((dst_addr == NULL) || + is_same_subnet(dst_addr->s_addr, + ipv4->unicast[i].ipv4.address.in_addr.s_addr, + ipv4->unicast[i].netmask.s_addr)) { + /* Use preferred address on the same subnet as destination */ + return &ipv4->unicast[i].ipv4.address.in_addr; + } else if (fall_back == NULL) { + /* Use address as fallback, if no match for subnet is found */ + fall_back = &ipv4->unicast[i].ipv4.address.in_addr; + } } } - return NULL; + return fall_back; } static inline struct net_pkt *arp_prepare(struct net_if *iface, @@ -339,7 +357,7 @@ static inline struct net_pkt *arp_prepare(struct net_if *iface, } else if (!entry) { my_addr = (struct net_in_addr *)NET_IPV4_HDR(pending)->src; } else { - my_addr = if_get_addr(entry->iface, (const uint8_t *)current_ip); + my_addr = if_get_addr(entry->iface, (const uint8_t *)current_ip, next_addr); } if (my_addr) { @@ -852,7 +870,7 @@ enum net_verdict net_arp_input(struct net_pkt *pkt, } /* Someone wants to know our ll address */ - addr = if_get_addr(net_pkt_iface(pkt), arp_hdr->dst_ipaddr); + addr = if_get_addr(net_pkt_iface(pkt), arp_hdr->dst_ipaddr, NULL); if (!addr) { /* Not for us so drop the packet silently */ return NET_DROP;