This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 8eaefd24246ab707b3cf831bd530537550bbfba0 Author: meijian <[email protected]> AuthorDate: Tue Apr 16 17:19:33 2024 +0800 net/netlink: Add RTM neigh notify support Signed-off-by: meijian <[email protected]> --- net/arp/arp_table.c | 89 +++++++++++++--- net/neighbor/neighbor_add.c | 25 +++++ net/netlink/netlink.h | 20 ++++ net/netlink/netlink_route.c | 255 ++++++++++++++++++++++++++++++-------------- 4 files changed, 295 insertions(+), 94 deletions(-) diff --git a/net/arp/arp_table.c b/net/arp/arp_table.c index cfba3d2d67..5b4c691f3c 100644 --- a/net/arp/arp_table.c +++ b/net/arp/arp_table.c @@ -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++; } } diff --git a/net/neighbor/neighbor_add.c b/net/neighbor/neighbor_add.c index 49801c5541..6257f6abb2 100644 --- a/net/neighbor/neighbor_add.c +++ b/net/neighbor/neighbor_add.c @@ -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]); diff --git a/net/netlink/netlink.h b/net/netlink/netlink.h index 474a898c16..fd1f81e4b3 100644 --- a/net/netlink/netlink.h +++ b/net/netlink/netlink.h @@ -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 * diff --git a/net/netlink/netlink_route.c b/net/netlink/netlink_route.c index e91241a1d0..24ee4cddda 100644 --- a/net/netlink/netlink_route.c +++ b/net/netlink/netlink_route.c @@ -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,45 +497,20 @@ 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; - /* 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); - rspsize = SIZEOF_NLROUTE_RECVFROM_RESPONSE_S(tabsize); - allocsize = SIZEOF_NLROUTE_RECVFROM_RSPLIST_S(tabsize); - - entry = kmm_zalloc(allocsize); - if (entry == NULL) - { - nerr("ERROR: Failed to allocate response buffer.\n"); - return -ENOMEM; - } - - /* Populate the entry */ - - 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 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, + ncopied = arp_snapshot((FAR struct arpreq *)(*entry)->payload.data, CONFIG_NET_ARPTAB_SIZE); net_unlock(); @@ -543,7 +518,7 @@ static int netlink_get_arptable(NETLINK_HANDLE handle, * we can trim the allocation. */ - if (ncopied < CONFIG_NET_ARPTAB_SIZE) + if (ncopied > 0) { FAR struct getneigh_recvfrom_rsplist_s *newentry; @@ -551,27 +526,23 @@ static int netlink_get_arptable(NETLINK_HANDLE handle, 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); + newentry = kmm_realloc(*entry, allocsize); if (newentry != NULL) { - entry = newentry; + *entry = newentry; } - entry->payload.hdr.nlmsg_len = rspsize; - entry->payload.attr.rta_len = RTA_LENGTH(tabsize); + (*entry)->payload.hdr.nlmsg_len = rspsize; + (*entry)->payload.attr.rta_len = RTA_LENGTH(tabsize); } - /* Finally, add the data to the list of pending responses */ - - netlink_add_response(handle, (FAR struct netlink_response_s *)entry); - return OK; + return ncopied; } #endif /**************************************************************************** - * Name: netlink_get_nbtable() + * Name: netlink_fill_nbtable() * * Description: * Return the entire IPv6 neighbor table. @@ -579,55 +550,29 @@ static int netlink_get_arptable(NETLINK_HANDLE handle, ****************************************************************************/ #if defined(CONFIG_NET_IPv6) && !defined(CONFIG_NETLINK_DISABLE_GETNEIGH) -static int netlink_get_nbtable(NETLINK_HANDLE handle, - FAR const struct nlroute_sendto_request_s *req) +static size_t netlink_fill_nbtable( + 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; - /* 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) - { - nerr("ERROR: Failed to allocate response buffer.\n"); - return -ENOMEM; - } - - /* Populate the entry */ - - 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); + (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) + if (ncopied > 0) { FAR struct getneigh_recvfrom_rsplist_s *newentry; @@ -635,25 +580,147 @@ static int netlink_get_nbtable(NETLINK_HANDLE handle, 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); + newentry = kmm_realloc(*entry, allocsize); if (newentry != NULL) { - entry = newentry; + *entry = newentry; } - entry->payload.hdr.nlmsg_len = rspsize; - entry->payload.attr.rta_len = RTA_LENGTH(tabsize); + (*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 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. + */ + +#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); + + /* Allocate the response buffer */ + + alloc = kmm_zalloc(allocsize); + if (alloc == NULL) + { + nerr("ERROR: Failed to allocate response buffer.\n"); + return NULL; + } + + /* Initialize the response buffer */ + + 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; + + resp->msg.ndm_family = domain; + resp->attr.rta_len = RTA_LENGTH(tabsize); + + /* Copy neigh or arp entries into resp data */ + + if (req == NULL) + { + if (neigh == NULL) + { + return NULL; + } + + /* 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); + } +#endif + + /* If no entry in table, just free alloc */ + + 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 netlink_response_s *resp; + + resp = netlink_get_neighbor(NULL, domain, RTM_GETNEIGH, req); + if (resp == NULL) + { + return -ENOENT; + } + + netlink_add_response(handle, resp); + + return netlink_add_terminator(handle, &req->hdr, 0); +} +#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 */
