net/netlink: Add RTM neigh notify support

Signed-off-by: meijian <meijian@xiaomi.com>
This commit is contained in:
meijian 2024-04-16 17:19:33 +08:00 committed by Alan Carvalho de Assis
parent 34aeeb024c
commit 8eaefd2424
4 changed files with 333 additions and 132 deletions

View file

@ -61,6 +61,7 @@
#include <nuttx/net/ip.h>
#include "netdev/netdev.h"
#include "netlink/netlink.h"
#include "arp/arp.h"
#ifdef CONFIG_NET_ARP
@ -208,6 +209,37 @@ static FAR struct arp_entry_s *arp_lookup(in_addr_t ipaddr,
return NULL;
}
/****************************************************************************
* Name: arp_get_arpreq
*
* Description:
* Translate (struct arp_entry_s) to (struct arpreq) for netlink notify.
*
* Input Parameters:
* output - Location to return the ARP table copy
* input - The arp entry in table
*
****************************************************************************/
#ifdef CONFIG_NETLINK_ROUTE
static void arp_get_arpreq(FAR struct arpreq *output,
FAR struct arp_entry_s *input)
{
FAR struct sockaddr_in *outaddr;
DEBUGASSERT(output != NULL && input != NULL);
outaddr = (FAR struct sockaddr_in *)&output->arp_pa;
outaddr->sin_family = AF_INET;
outaddr->sin_port = 0;
outaddr->sin_addr.s_addr = input->at_ipaddr;
memcpy(output->arp_ha.sa_data, input->at_ethaddr.ether_addr_octet,
sizeof(struct ether_addr));
strlcpy((FAR char *)output->arp_dev, input->at_dev->d_ifname,
sizeof(output->arp_dev));
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
@ -237,6 +269,11 @@ int arp_update(FAR struct net_driver_s *dev, in_addr_t ipaddr,
FAR const uint8_t *ethaddr)
{
FAR struct arp_entry_s *tabptr = &g_arptable[0];
#ifdef CONFIG_NETLINK_ROUTE
struct arpreq arp_notify;
bool found = false;
bool new_entry;
#endif
int i;
/* Walk through the ARP mapping table and try to find an entry to
@ -257,6 +294,9 @@ int arp_update(FAR struct net_driver_s *dev, in_addr_t ipaddr,
/* An old entry found, break. */
tabptr = &g_arptable[i];
#ifdef CONFIG_NETLINK_ROUTE
found = true;
#endif
break;
}
else
@ -267,6 +307,21 @@ int arp_update(FAR struct net_driver_s *dev, in_addr_t ipaddr,
}
}
/* When overwite old entry, notify old entry RTM_DELNEIGH */
#ifdef CONFIG_NETLINK_ROUTE
if (!found && tabptr->at_ipaddr != 0)
{
arp_get_arpreq(&arp_notify, tabptr);
netlink_neigh_notify(&arp_notify, RTM_DELNEIGH, AF_INET);
}
/* Need to notify when entry is not found or changes in table */
new_entry = !found || memcmp(tabptr->at_ethaddr.ether_addr_octet,
ethaddr, ETHER_ADDR_LEN) != 0;
#endif
/* Now, tabptr is the ARP table entry which we will fill with the new
* information.
*/
@ -275,6 +330,17 @@ int arp_update(FAR struct net_driver_s *dev, in_addr_t ipaddr,
memcpy(tabptr->at_ethaddr.ether_addr_octet, ethaddr, ETHER_ADDR_LEN);
tabptr->at_dev = dev;
tabptr->at_time = clock_systime_ticks();
/* Notify the new entry */
#ifdef CONFIG_NETLINK_ROUTE
if (new_entry)
{
arp_get_arpreq(&arp_notify, tabptr);
netlink_neigh_notify(&arp_notify, RTM_NEWNEIGH, AF_INET);
}
#endif
return OK;
}
@ -392,12 +458,21 @@ int arp_find(in_addr_t ipaddr, FAR uint8_t *ethaddr,
int arp_delete(in_addr_t ipaddr, FAR struct net_driver_s *dev)
{
FAR struct arp_entry_s *tabptr;
#ifdef CONFIG_NETLINK_ROUTE
struct arpreq arp_notify;
#endif
/* Check if the IPv4 address is in the ARP table. */
tabptr = arp_lookup(ipaddr, dev);
if (tabptr != NULL)
{
/* Notify to netlink */
#ifdef CONFIG_NETLINK_ROUTE
arp_get_arpreq(&arp_notify, tabptr);
netlink_neigh_notify(&arp_notify, RTM_DELNEIGH, AF_INET);
#endif
/* Yes.. Set the IP address to zero to "delete" it */
tabptr->at_ipaddr = 0;
@ -459,7 +534,6 @@ unsigned int arp_snapshot(FAR struct arpreq *snapshot,
unsigned int nentries)
{
FAR struct arp_entry_s *tabptr;
FAR struct sockaddr_in *outaddr;
clock_t now;
unsigned int ncopied;
int i;
@ -474,16 +548,7 @@ unsigned int arp_snapshot(FAR struct arpreq *snapshot,
if (tabptr->at_ipaddr != 0 &&
now - tabptr->at_time <= ARP_MAXAGE_TICK)
{
outaddr = (FAR struct sockaddr_in *)&snapshot[ncopied].arp_pa;
outaddr->sin_family = AF_INET;
outaddr->sin_port = 0;
outaddr->sin_addr.s_addr = tabptr->at_ipaddr;
memcpy(snapshot[ncopied].arp_ha.sa_data,
tabptr->at_ethaddr.ether_addr_octet,
sizeof(struct ether_addr));
strlcpy((FAR char *)snapshot[ncopied].arp_dev,
tabptr->at_dev->d_ifname,
sizeof(snapshot[ncopied].arp_dev));
arp_get_arpreq(&snapshot[ncopied], tabptr);
ncopied++;
}
}

View file

@ -36,6 +36,7 @@
#include <nuttx/net/neighbor.h>
#include "netdev/netdev.h"
#include "netlink/netlink.h"
#include "neighbor/neighbor.h"
/****************************************************************************
@ -65,6 +66,8 @@ void neighbor_add(FAR struct net_driver_s *dev, FAR net_ipv6addr_t ipaddr,
uint8_t lltype;
clock_t oldest_time;
int oldest_ndx;
bool found = false;
bool new_entry;
int i;
DEBUGASSERT(dev != NULL && addr != NULL);
@ -85,6 +88,7 @@ void neighbor_add(FAR struct net_driver_s *dev, FAR net_ipv6addr_t ipaddr,
net_ipv6addr_cmp(g_neighbors[i].ne_ipaddr, ipaddr))
{
oldest_ndx = i;
found = true;
break;
}
@ -95,6 +99,19 @@ void neighbor_add(FAR struct net_driver_s *dev, FAR net_ipv6addr_t ipaddr,
}
}
/* When overwite old entry, need to notify RTM_DELNEIGH */
if (!found && g_neighbors[oldest_ndx].ne_time != 0)
{
netlink_neigh_notify(&g_neighbors[oldest_ndx], RTM_DELNEIGH,
AF_INET6);
}
/* Need to notify when entry is not found or changes in table */
new_entry = !found || memcmp(&g_neighbors[oldest_ndx].ne_addr.u, addr,
g_neighbors[oldest_ndx].ne_addr.na_llsize) != 0;
/* Use the oldest or first free entry (either pointed to by the
* "oldest_ndx" variable).
*/
@ -109,6 +126,14 @@ void neighbor_add(FAR struct net_driver_s *dev, FAR net_ipv6addr_t ipaddr,
memcpy(&g_neighbors[oldest_ndx].ne_addr.u, addr,
g_neighbors[oldest_ndx].ne_addr.na_llsize);
/* Notify the new entry */
if (new_entry)
{
netlink_neigh_notify(&g_neighbors[oldest_ndx], RTM_NEWNEIGH,
AF_INET6);
}
/* Dump the contents of the new entry */
neighbor_dumpentry("Added entry", &g_neighbors[oldest_ndx]);

View file

@ -47,6 +47,7 @@
# define netlink_device_notify(dev)
# define netlink_device_notify_ipaddr(dev, type, domain, addr, preflen)
# define netlink_route_notify(route, type, domain)
# define netlink_neigh_notify(neigh, type, domain)
#endif
#ifdef CONFIG_NET_NETLINK
@ -532,6 +533,25 @@ void netlink_device_notify_ipaddr(FAR struct net_driver_s *dev,
void netlink_route_notify(FAR const void *route, int type, int domain);
#endif
/****************************************************************************
* Name: netlink_neigh_notify()
*
* Description:
* Perform the neigh broadcast for the NETLINK_ROUTE protocol.
*
* Input Parameters:
* neigh - The ARP entry or neighbour entry
* type - The type of the message, RTM_*NEIGH
* domain - The domain of the message
*
****************************************************************************/
#if defined(CONFIG_NETLINK_DISABLE_GETNEIGH)
# define netlink_neigh_notify(neigh, type, domain)
#else
void netlink_neigh_notify(FAR const void *neigh, int type, int domain);
#endif
/****************************************************************************
* Name: nla_next
*

View file

@ -489,7 +489,7 @@ static int netlink_get_devlist(NETLINK_HANDLE handle,
#endif
/****************************************************************************
* Name: netlink_get_arptable()
* Name: netlink_fill_arptable()
*
* Description:
* Return the entire ARP table.
@ -497,162 +497,229 @@ static int netlink_get_devlist(NETLINK_HANDLE handle,
****************************************************************************/
#if defined(CONFIG_NET_ARP) && !defined(CONFIG_NETLINK_DISABLE_GETNEIGH)
static int netlink_get_arptable(NETLINK_HANDLE handle,
FAR const struct nlroute_sendto_request_s *req)
static size_t netlink_fill_arptable(
FAR struct getneigh_recvfrom_rsplist_s **entry)
{
FAR struct getneigh_recvfrom_rsplist_s *entry;
unsigned int ncopied;
size_t allocsize;
size_t tabsize;
size_t rspsize;
/* Lock the network so that the ARP table will be stable, then copy
* the ARP table into the allocated memory.
*/
net_lock();
ncopied = arp_snapshot((FAR struct arpreq *)(*entry)->payload.data,
CONFIG_NET_ARPTAB_SIZE);
net_unlock();
/* Now we have the real number of valid entries in the ARP table and
* we can trim the allocation.
*/
if (ncopied > 0)
{
FAR struct getneigh_recvfrom_rsplist_s *newentry;
tabsize = ncopied * sizeof(struct arpreq);
rspsize = SIZEOF_NLROUTE_RECVFROM_RESPONSE_S(tabsize);
allocsize = SIZEOF_NLROUTE_RECVFROM_RSPLIST_S(tabsize);
newentry = kmm_realloc(*entry, allocsize);
if (newentry != NULL)
{
*entry = newentry;
}
(*entry)->payload.hdr.nlmsg_len = rspsize;
(*entry)->payload.attr.rta_len = RTA_LENGTH(tabsize);
}
return ncopied;
}
#endif
/****************************************************************************
* Name: netlink_fill_nbtable()
*
* Description:
* Return the entire IPv6 neighbor table.
*
****************************************************************************/
#if defined(CONFIG_NET_IPv6) && !defined(CONFIG_NETLINK_DISABLE_GETNEIGH)
static size_t netlink_fill_nbtable(
FAR struct getneigh_recvfrom_rsplist_s **entry)
{
unsigned int ncopied;
size_t allocsize;
size_t tabsize;
size_t rspsize;
/* Lock the network so that the Neighbor table will be stable, then
* copy the Neighbor table into the allocated memory.
*/
net_lock();
ncopied = neighbor_snapshot(
(FAR struct neighbor_entry_s *)(*entry)->payload.data,
CONFIG_NET_IPv6_NCONF_ENTRIES);
net_unlock();
/* Now we have the real number of valid entries in the Neighbor table
* and we can trim the allocation.
*/
if (ncopied > 0)
{
FAR struct getneigh_recvfrom_rsplist_s *newentry;
tabsize = ncopied * sizeof(struct neighbor_entry_s);
rspsize = SIZEOF_NLROUTE_RECVFROM_RESPONSE_S(tabsize);
allocsize = SIZEOF_NLROUTE_RECVFROM_RSPLIST_S(tabsize);
newentry = kmm_realloc(*entry, allocsize);
if (newentry != NULL)
{
*entry = newentry;
}
(*entry)->payload.hdr.nlmsg_len = rspsize;
(*entry)->payload.attr.rta_len = RTA_LENGTH(tabsize);
}
return ncopied;
}
#endif
/****************************************************************************
* Name: netlink_fill_nbtable()
*
* Description:
* Return the entire IPv6 neighbor table.
*
****************************************************************************/
#if !defined(CONFIG_NETLINK_DISABLE_GETNEIGH)
static FAR struct netlink_response_s *
netlink_get_neighbor(FAR const void *neigh, int domain, int type,
FAR const struct nlroute_sendto_request_s *req)
{
FAR struct getneigh_recvfrom_rsplist_s *alloc;
FAR struct getneigh_recvfrom_response_s *resp;
size_t allocsize;
size_t tabsize;
size_t tabnum;
size_t rspsize;
/* Preallocate memory to hold the maximum sized ARP table
* REVISIT: This is probably excessively large and could cause false
* memory out conditions. A better approach would be to actually count
* the number of valid entries in the ARP table.
*/
tabsize = CONFIG_NET_ARPTAB_SIZE * sizeof(struct arpreq);
#if defined(CONFIG_NET_ARP)
if (domain == AF_INET)
{
tabnum = req ? CONFIG_NET_ARPTAB_SIZE : 1;
tabsize = tabnum * sizeof(struct arpreq);
}
else
#endif
#if defined(CONFIG_NET_IPv6)
if (domain == AF_INET6)
{
tabnum = req ? CONFIG_NET_IPv6_NCONF_ENTRIES : 1;
tabsize = tabnum * sizeof(struct neighbor_entry_s);
}
else
#endif
{
return NULL;
}
rspsize = SIZEOF_NLROUTE_RECVFROM_RESPONSE_S(tabsize);
allocsize = SIZEOF_NLROUTE_RECVFROM_RSPLIST_S(tabsize);
entry = kmm_zalloc(allocsize);
if (entry == NULL)
/* Allocate the response buffer */
alloc = kmm_zalloc(allocsize);
if (alloc == NULL)
{
nerr("ERROR: Failed to allocate response buffer.\n");
return -ENOMEM;
return NULL;
}
/* Populate the entry */
/* Initialize the response buffer */
memcpy(&entry->payload.hdr, &req->hdr, sizeof(struct nlmsghdr));
entry->payload.hdr.nlmsg_len = rspsize;
entry->payload.msg.ndm_family = req->gen.rtgen_family;
entry->payload.attr.rta_len = RTA_LENGTH(tabsize);
resp = &alloc->payload;
resp->hdr.nlmsg_len = rspsize;
resp->hdr.nlmsg_type = type;
resp->hdr.nlmsg_flags = req ? req->hdr.nlmsg_flags : 0;
resp->hdr.nlmsg_seq = req ? req->hdr.nlmsg_seq : 0;
resp->hdr.nlmsg_pid = req ? req->hdr.nlmsg_pid : 0;
/* Lock the network so that the ARP table will be stable, then copy
* the ARP table into the allocated memory.
*/
resp->msg.ndm_family = domain;
resp->attr.rta_len = RTA_LENGTH(tabsize);
net_lock();
ncopied = arp_snapshot((FAR struct arpreq *)entry->payload.data,
CONFIG_NET_ARPTAB_SIZE);
net_unlock();
/* Copy neigh or arp entries into resp data */
/* Now we have the real number of valid entries in the ARP table and
* we can trim the allocation.
*/
if (ncopied < CONFIG_NET_ARPTAB_SIZE)
if (req == NULL)
{
FAR struct getneigh_recvfrom_rsplist_s *newentry;
tabsize = ncopied * sizeof(struct arpreq);
rspsize = SIZEOF_NLROUTE_RECVFROM_RESPONSE_S(tabsize);
allocsize = SIZEOF_NLROUTE_RECVFROM_RSPLIST_S(tabsize);
newentry = (FAR struct getneigh_recvfrom_rsplist_s *)
kmm_realloc(entry, allocsize);
if (newentry != NULL)
if (neigh == NULL)
{
entry = newentry;
return NULL;
}
entry->payload.hdr.nlmsg_len = rspsize;
entry->payload.attr.rta_len = RTA_LENGTH(tabsize);
/* Only one entry need to notify */
memcpy(resp->data, neigh, tabsize);
}
#if defined(CONFIG_NET_ARP)
else if (domain == AF_INET)
{
tabnum = netlink_fill_arptable(&alloc);
}
#endif
#if defined(CONFIG_NET_IPv6)
else if (domain == AF_INET6)
{
tabnum = netlink_fill_nbtable(&alloc);
}
/* Finally, add the data to the list of pending responses */
netlink_add_response(handle, (FAR struct netlink_response_s *)entry);
return OK;
}
#endif
/****************************************************************************
* Name: netlink_get_nbtable()
*
* Description:
* Return the entire IPv6 neighbor table.
*
****************************************************************************/
/* If no entry in table, just free alloc */
#if defined(CONFIG_NET_IPv6) && !defined(CONFIG_NETLINK_DISABLE_GETNEIGH)
static int netlink_get_nbtable(NETLINK_HANDLE handle,
if (tabnum <= 0)
{
kmm_free(alloc);
nwarn("WARNING: Failed to get entry in %s table.\n",
domain == AF_INET ? "ARP" : "neighbor");
return NULL;
}
return (FAR struct netlink_response_s *)alloc;
}
static int netlink_get_neighborlist(NETLINK_HANDLE handle, int domain,
FAR const struct nlroute_sendto_request_s *req)
{
FAR struct getneigh_recvfrom_rsplist_s *entry;
unsigned int ncopied;
size_t allocsize;
size_t tabsize;
size_t rspsize;
FAR struct netlink_response_s *resp;
/* Preallocate memory to hold the maximum sized Neighbor table
* REVISIT: This is probably excessively large and could cause false
* memory out conditions. A better approach would be to actually count
* the number of valid entries in the Neighbor table.
*/
tabsize = CONFIG_NET_IPv6_NCONF_ENTRIES *
sizeof(struct neighbor_entry_s);
rspsize = SIZEOF_NLROUTE_RECVFROM_RESPONSE_S(tabsize);
allocsize = SIZEOF_NLROUTE_RECVFROM_RSPLIST_S(tabsize);
entry = kmm_zalloc(allocsize);
if (entry == NULL)
resp = netlink_get_neighbor(NULL, domain, RTM_GETNEIGH, req);
if (resp == NULL)
{
nerr("ERROR: Failed to allocate response buffer.\n");
return -ENOMEM;
return -ENOENT;
}
/* Populate the entry */
netlink_add_response(handle, resp);
memcpy(&entry->payload.hdr, &req->hdr, sizeof(struct nlmsghdr));
entry->payload.hdr.nlmsg_len = rspsize;
entry->payload.msg.ndm_family = req->gen.rtgen_family;
entry->payload.attr.rta_len = RTA_LENGTH(tabsize);
/* Lock the network so that the Neighbor table will be stable, then
* copy the Neighbor table into the allocated memory.
*/
net_lock();
ncopied = neighbor_snapshot(
(FAR struct neighbor_entry_s *)entry->payload.data,
CONFIG_NET_IPv6_NCONF_ENTRIES);
net_unlock();
/* Now we have the real number of valid entries in the Neighbor table
* and we can trim the allocation.
*/
if (ncopied < CONFIG_NET_IPv6_NCONF_ENTRIES)
{
FAR struct getneigh_recvfrom_rsplist_s *newentry;
tabsize = ncopied * sizeof(struct neighbor_entry_s);
rspsize = SIZEOF_NLROUTE_RECVFROM_RESPONSE_S(tabsize);
allocsize = SIZEOF_NLROUTE_RECVFROM_RSPLIST_S(tabsize);
newentry = (FAR struct getneigh_recvfrom_rsplist_s *)
kmm_realloc(entry, allocsize);
if (newentry != NULL)
{
entry = newentry;
}
entry->payload.hdr.nlmsg_len = rspsize;
entry->payload.attr.rta_len = RTA_LENGTH(tabsize);
}
/* Finally, add the response to the list of pending responses */
netlink_add_response(handle, (FAR struct netlink_response_s *)entry);
return OK;
return netlink_add_terminator(handle, &req->hdr, 0);
}
#endif
#endif /* CONFIG_NETLINK_DISABLE_GETNEIGH */
/****************************************************************************
* Name: netlink_ipv4_route
@ -1234,7 +1301,7 @@ ssize_t netlink_route_sendto(NETLINK_HANDLE handle,
if (req->gen.rtgen_family == AF_INET)
{
ret = netlink_get_arptable(handle, req);
ret = netlink_get_neighborlist(handle, AF_INET, req);
}
else
#endif
@ -1244,7 +1311,7 @@ ssize_t netlink_route_sendto(NETLINK_HANDLE handle,
if (req->gen.rtgen_family == AF_INET6)
{
ret = netlink_get_nbtable(handle, req);
ret = netlink_get_neighborlist(handle, AF_INET6, req);
}
else
#endif
@ -1491,4 +1558,28 @@ void netlink_route_notify(FAR const void *route, int type, int domain)
}
#endif
/****************************************************************************
* Name: netlink_neigh_notify()
*
* Description:
* Perform the neigh broadcast for the NETLINK_ROUTE protocol.
*
****************************************************************************/
#ifndef CONFIG_NETLINK_DISABLE_GETNEIGH
void netlink_neigh_notify(FAR const void *neigh, int type, int domain)
{
FAR struct netlink_response_s *resp;
resp = netlink_get_neighbor(neigh, domain, type, NULL);
if (resp == NULL)
{
return;
}
netlink_add_broadcast(RTNLGRP_NEIGH, resp);
netlink_add_terminator(NULL, NULL, RTNLGRP_NEIGH);
}
#endif
#endif /* CONFIG_NETLINK_ROUTE */