Skip to content

Commit 7c0c899

Browse files
rlubosjukkar
authored andcommitted
[nrf fromtree] net: arp: Add support for gratuitous ARP transmission
Add support for gratuitous ARP transmission by Zephyr network stack. This allows to prematurely fill the peer ARP table, so there's no need to send an explicit request when peer needs to send an actual packet. The gratuitous ARP is send when the network interface is brought up (joins the network) or a new IP address is added. The gratuitous ARP request is also sent periodically, with a configurable interval time. The gratuitous ARP should also be sent whenever MAC address of the interface is changed, but as Zephyr only allows to do this when interface is down, this is already covered by the first case (interface brought up). Signed-off-by: Robert Lubos <[email protected]> (cherry picked from commit 6551e6f)
1 parent f92b43c commit 7c0c899

File tree

2 files changed

+170
-0
lines changed

2 files changed

+170
-0
lines changed

subsys/net/l2/ethernet/Kconfig

+13
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,19 @@ config NET_ARP_GRATUITOUS
7373
ff:ff:ff:ff:ff:ff. Ordinarily, no reply packet will occur.
7474
A gratuitous ARP reply is a reply to which no request has been made.
7575

76+
config NET_ARP_GRATUITOUS_TRANSMISSION
77+
bool "Transmit gratuitous ARP requests"
78+
depends on NET_ARP_GRATUITOUS
79+
depends on NET_MGMT_EVENT
80+
depends on NET_MGMT_EVENT_INFO
81+
help
82+
Transmit gratuitous ARP requests, as defined in RFC 5227.
83+
84+
config NET_ARP_GRATUITOUS_INTERVAL
85+
int "Time interval (in seconds) between sending gratuitous ARP requests"
86+
depends on NET_ARP_GRATUITOUS_TRANSMISSION
87+
default 60
88+
7689
if NET_ARP
7790
module = NET_ARP
7891
module-dep = NET_LOG

subsys/net/l2/ethernet/arp.c

+157
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ LOG_MODULE_REGISTER(net_arp, CONFIG_NET_ARP_LOG_LEVEL);
1616
#include <zephyr/net/net_pkt.h>
1717
#include <zephyr/net/net_if.h>
1818
#include <zephyr/net/net_stats.h>
19+
#include <zephyr/net/net_mgmt.h>
1920

2021
#include "arp.h"
2122
#include "net_private.h"
@@ -34,6 +35,12 @@ static struct k_work_delayable arp_request_timer;
3435

3536
static struct k_mutex arp_mutex;
3637

38+
#if defined(CONFIG_NET_ARP_GRATUITOUS_TRANSMISSION)
39+
static struct net_mgmt_event_callback iface_event_cb;
40+
static struct net_mgmt_event_callback ipv4_event_cb;
41+
static struct k_work_delayable arp_gratuitous_work;
42+
#endif /* defined(CONFIG_NET_ARP_GRATUITOUS_TRANSMISSION) */
43+
3744
static void arp_entry_cleanup(struct arp_entry *entry, bool pending)
3845
{
3946
NET_DBG("entry %p", entry);
@@ -466,6 +473,141 @@ static void arp_gratuitous(struct net_if *iface,
466473
}
467474
}
468475

476+
#if defined(CONFIG_NET_ARP_GRATUITOUS_TRANSMISSION)
477+
static void arp_gratuitous_send(struct net_if *iface,
478+
struct in_addr *ipaddr)
479+
{
480+
struct net_arp_hdr *hdr;
481+
struct net_pkt *pkt;
482+
483+
pkt = net_pkt_alloc_with_buffer(iface, sizeof(struct net_arp_hdr),
484+
AF_UNSPEC, 0, NET_BUF_TIMEOUT);
485+
if (!pkt) {
486+
return;
487+
}
488+
489+
net_buf_add(pkt->buffer, sizeof(struct net_arp_hdr));
490+
net_pkt_set_vlan_tag(pkt, net_eth_get_vlan_tag(iface));
491+
492+
hdr = NET_ARP_HDR(pkt);
493+
494+
hdr->hwtype = htons(NET_ARP_HTYPE_ETH);
495+
hdr->protocol = htons(NET_ETH_PTYPE_IP);
496+
hdr->hwlen = sizeof(struct net_eth_addr);
497+
hdr->protolen = sizeof(struct in_addr);
498+
hdr->opcode = htons(NET_ARP_REQUEST);
499+
500+
memcpy(&hdr->dst_hwaddr.addr, net_eth_broadcast_addr(),
501+
sizeof(struct net_eth_addr));
502+
memcpy(&hdr->src_hwaddr.addr, net_if_get_link_addr(iface)->addr,
503+
sizeof(struct net_eth_addr));
504+
505+
net_ipv4_addr_copy_raw(hdr->dst_ipaddr, (uint8_t *)ipaddr);
506+
net_ipv4_addr_copy_raw(hdr->src_ipaddr, (uint8_t *)ipaddr);
507+
508+
net_pkt_lladdr_src(pkt)->addr = net_if_get_link_addr(iface)->addr;
509+
net_pkt_lladdr_src(pkt)->len = sizeof(struct net_eth_addr);
510+
511+
net_pkt_lladdr_dst(pkt)->addr = (uint8_t *)net_eth_broadcast_addr();
512+
net_pkt_lladdr_dst(pkt)->len = sizeof(struct net_eth_addr);
513+
514+
NET_DBG("Sending gratuitous ARP pkt %p", pkt);
515+
516+
if (net_if_send_data(iface, pkt) == NET_DROP) {
517+
net_pkt_unref(pkt);
518+
}
519+
}
520+
521+
static void notify_all_ipv4_addr(struct net_if *iface)
522+
{
523+
struct net_if_ipv4 *ipv4 = iface->config.ip.ipv4;
524+
int i;
525+
526+
if (!ipv4) {
527+
return;
528+
}
529+
530+
for (i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) {
531+
if (ipv4->unicast[i].ipv4.is_used &&
532+
ipv4->unicast[i].ipv4.address.family == AF_INET &&
533+
ipv4->unicast[i].ipv4.addr_state == NET_ADDR_PREFERRED) {
534+
arp_gratuitous_send(iface,
535+
&ipv4->unicast[i].ipv4.address.in_addr);
536+
}
537+
}
538+
}
539+
540+
static void iface_event_handler(struct net_mgmt_event_callback *cb,
541+
uint32_t mgmt_event, struct net_if *iface)
542+
{
543+
ARG_UNUSED(cb);
544+
545+
if (!(net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET) ||
546+
net_eth_is_vlan_interface(iface))) {
547+
return;
548+
}
549+
550+
if (mgmt_event != NET_EVENT_IF_UP) {
551+
return;
552+
}
553+
554+
notify_all_ipv4_addr(iface);
555+
}
556+
557+
static void ipv4_event_handler(struct net_mgmt_event_callback *cb,
558+
uint32_t mgmt_event, struct net_if *iface)
559+
{
560+
struct in_addr *ipaddr;
561+
562+
if (!(net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET) ||
563+
net_eth_is_vlan_interface(iface))) {
564+
return;
565+
}
566+
567+
if (!net_if_is_up(iface)) {
568+
return;
569+
}
570+
571+
if (mgmt_event != NET_EVENT_IPV4_ADDR_ADD) {
572+
return;
573+
}
574+
575+
if (cb->info_length != sizeof(struct in_addr)) {
576+
return;
577+
}
578+
579+
ipaddr = (struct in_addr *)cb->info;
580+
581+
arp_gratuitous_send(iface, ipaddr);
582+
}
583+
584+
static void iface_cb(struct net_if *iface, void *user_data)
585+
{
586+
ARG_UNUSED(user_data);
587+
588+
if (!(net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET) ||
589+
net_eth_is_vlan_interface(iface))) {
590+
return;
591+
}
592+
593+
if (!net_if_is_up(iface)) {
594+
return;
595+
}
596+
597+
notify_all_ipv4_addr(iface);
598+
}
599+
600+
static void arp_gratuitous_work_handler(struct k_work *work)
601+
{
602+
ARG_UNUSED(work);
603+
604+
net_if_foreach(iface_cb, NULL);
605+
606+
k_work_reschedule(&arp_gratuitous_work,
607+
K_SECONDS(CONFIG_NET_ARP_GRATUITOUS_INTERVAL));
608+
}
609+
#endif /* defined(CONFIG_NET_ARP_GRATUITOUS_TRANSMISSION) */
610+
469611
void net_arp_update(struct net_if *iface,
470612
struct in_addr *src,
471613
struct net_eth_addr *hwaddr,
@@ -837,4 +979,19 @@ void net_arp_init(void)
837979
k_mutex_init(&arp_mutex);
838980

839981
arp_cache_initialized = true;
982+
983+
#if defined(CONFIG_NET_ARP_GRATUITOUS_TRANSMISSION)
984+
net_mgmt_init_event_callback(&iface_event_cb, iface_event_handler,
985+
NET_EVENT_IF_UP);
986+
net_mgmt_init_event_callback(&ipv4_event_cb, ipv4_event_handler,
987+
NET_EVENT_IPV4_ADDR_ADD);
988+
989+
net_mgmt_add_event_callback(&iface_event_cb);
990+
net_mgmt_add_event_callback(&ipv4_event_cb);
991+
992+
k_work_init_delayable(&arp_gratuitous_work,
993+
arp_gratuitous_work_handler);
994+
k_work_reschedule(&arp_gratuitous_work,
995+
K_SECONDS(CONFIG_NET_ARP_GRATUITOUS_INTERVAL));
996+
#endif /* defined(CONFIG_NET_ARP_GRATUITOUS_TRANSMISSION) */
840997
}

0 commit comments

Comments
 (0)