Files
Agrant 6c60002696 版权符号改成小写
Signed-off-by: Agrant <luojuheng@huawei.com>
2026-01-19 20:20:35 +08:00

329 lines
10 KiB
C

/*
* Copyright (c) 2021-2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nstackx_smartgenius.h"
#include <errno.h>
#include <string.h>
#include <securec.h>
#include <sys/types.h>
#ifdef SUPPORT_SMARTGENIUS
#include <sys/socket.h>
#include <unistd.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <arpa/inet.h>
#endif /* SUPPORT_SMARTGENIUS */
#include <time.h>
#include "nstackx_dfinder_log.h"
#include "nstackx_error.h"
#include "nstackx_util.h"
#include "nstackx_epoll.h"
#include "nstackx_device.h"
#include "nstackx_timer.h"
#include "coap_discover/coap_discover.h"
#include "nstackx_statistics.h"
#include "nstackx_device_local.h"
#include "nstackx_device_remote.h"
#define TAG "nStackXDFinder"
#ifdef SUPPORT_SMARTGENIUS
#define BUFLEN 256
#define NSTACKX_POSTPONE_DELAY_MS 500
#define NSTACKX_NETLINK_RECOVER_MS 300000 // 5min
#define NSTACKX_ERROR_INTERVAL_S 5 // 5s
typedef enum {
NETLINK_SOCKET_READ_EVENT = 0,
NETLINK_SOCKET_WRITE_EVENT,
NETLINK_SOCKET_ERROR_EVENT,
NETLINK_SOCKET_EVENT
} NetLinkSocketEventType;
static uint64_t g_netlinkSocketEventNum[NETLINK_SOCKET_EVENT];
static EpollTask g_netlinkTask;
static uint8_t g_smartGeniusInit = NSTACKX_FALSE;
static Timer *g_recoverTimer;
static long g_lastErrorTimeS = -1;
static void ParseRTattr(struct rtattr **tb, uint32_t max, struct rtattr *attr, uint32_t len)
{
/*
* Use macro RTA_OK() and RTA_NEXT() to iterate attribute list, and fill table "tb" with attribute whose type is not
* greater than "max".
*/
for (; RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {
if (attr->rta_type <= max) {
tb[attr->rta_type] = attr;
}
}
}
static int CreateNetLinkSocketFd()
{
struct sockaddr_nl local = {0};
local.nl_family = AF_NETLINK;
local.nl_groups = RTMGRP_NOTIFY | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE | RTMGRP_LINK;
local.nl_pid = getpid();
socklen_t len = sizeof(local);
int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (fd < 0) {
DFINDER_LOGE(TAG, "unable to create netlink socket: %d", errno);
return NSTACKX_EFAILED;
}
if (bind(fd, (struct sockaddr *)&local, len) < 0) {
DFINDER_LOGE(TAG, "bind for netlink socket failed: %d", errno);
close(fd);
return NSTACKX_EFAILED;
}
if (getsockname(fd, (struct sockaddr *)&local, &len)) {
DFINDER_LOGE(TAG, "getsockname failed: %d", errno);
close(fd);
return NSTACKX_EFAILED;
}
return fd;
}
static void IfAddrMsgHandle(struct nlmsghdr *msgHdr)
{
struct rtattr *tb[IFA_MAX + 1] = {0}; /* Table to store rtnetlink attribute pointers */
struct ifaddrmsg *ifAddr = NLMSG_DATA(msgHdr); /* Get IP address information from message */
if (msgHdr->nlmsg_len < NLMSG_SPACE(sizeof(struct ifaddrmsg))) {
return;
}
uint32_t len = msgHdr->nlmsg_len - NLMSG_SPACE(sizeof(struct ifaddrmsg));
NetworkInterfaceInfo interfaceInfo;
(void)memset_s(&interfaceInfo, sizeof(interfaceInfo), 0, sizeof(interfaceInfo));
/* Parse attribute in "ifAddr", and store attribute pointers in "tb" */
ParseRTattr(tb, IFA_MAX, IFA_RTA(ifAddr), len);
if (tb[IFA_LABEL] == NULL || tb[IFA_ADDRESS] == NULL) {
return;
}
if (ifAddr->ifa_family != AF_INET) {
return;
}
if (strcpy_s(interfaceInfo.name, sizeof(interfaceInfo.name), (char *)RTA_DATA(tb[IFA_LABEL])) != EOK) {
return;
}
UpdateAllNetworkInterfaceNameIfNeed(&interfaceInfo);
/* Use macro RTA_DATA() to get network insterface name from attribute "IFA_LABEL". */
uint8_t ifaceType = GetIfaceType((char *)RTA_DATA(tb[IFA_LABEL]));
if (ifaceType >= IFACE_TYPE_UNKNOWN) {
DFINDER_LOGE(TAG, "unknown iface type %s", (char *)RTA_DATA(tb[IFA_LABEL]));
return;
}
if (msgHdr->nlmsg_type == RTM_NEWADDR) {
if (memcpy_s(&interfaceInfo.ip, sizeof(interfaceInfo.ip),
RTA_DATA(tb[IFA_ADDRESS]), sizeof(struct in_addr)) != EOK) {
return;
}
DFINDER_LOGD(TAG, "Interface %s got new address.", interfaceInfo.name);
AddLocalIface(interfaceInfo.name, AF_INET, &interfaceInfo.ip);
} else {
DFINDER_LOGD(TAG, "Interface %s delete address.", interfaceInfo.name);
RemoveLocalIface(AF_INET, interfaceInfo.name);
}
}
static void SmartGeniusCallback(void *arg)
{
g_netlinkSocketEventNum[NETLINK_SOCKET_READ_EVENT]++;
struct nlmsghdr *innerNlmsghdr = NULL;
struct nlmsgerr *nlmErr = NULL;
char innerBuf[BUFLEN] = {0};
struct sockaddr_nl peer = {AF_NETLINK, 0, 0, 0};
int len;
socklen_t socklen;
EpollTask *task = arg;
socklen = sizeof(struct sockaddr_nl);
len = recvfrom(task->taskfd, innerBuf, BUFLEN, 0, (struct sockaddr *)&peer, &socklen);
if (len <= 0) {
IncStatistics(STATS_SOCKET_ERROR);
DFINDER_LOGE(TAG, "recvfrom error %d", errno);
return;
}
innerNlmsghdr = (struct nlmsghdr *)innerBuf;
switch (innerNlmsghdr->nlmsg_type) {
case RTM_NEWADDR:
case RTM_DELADDR: {
IfAddrMsgHandle(innerNlmsghdr);
break;
}
case NLMSG_ERROR: {
nlmErr = NLMSG_DATA(innerNlmsghdr);
if (nlmErr->error == 0) {
DFINDER_LOGD(TAG, "NLMSG_ACK");
} else {
DFINDER_LOGE(TAG, "NLMSG_ERROR");
}
break;
}
default:
break;
}
return;
}
static void NetlinkWriteHandle(void *data)
{
(void)data;
g_netlinkSocketEventNum[NETLINK_SOCKET_WRITE_EVENT]++;
}
static void NetlinkErrorHandle(void *data UNUSED)
{
g_netlinkSocketEventNum[NETLINK_SOCKET_ERROR_EVENT]++;
DFINDER_LOGE(TAG, "Netlink Socket ErrorHandle");
struct timespec errorTime;
if (clock_gettime(CLOCK_MONOTONIC, &errorTime) != 0) {
DFINDER_LOGE(TAG, "Get current time fail");
NotifyDFinderMsgRecver(DFINDER_ON_INNER_ERROR);
return;
}
long currErrorTimeS = errorTime.tv_sec;
if (g_lastErrorTimeS > 0 && (currErrorTimeS - g_lastErrorTimeS < NSTACKX_ERROR_INTERVAL_S)) {
// if exception triggered more than twice within 5s: close socket and restart it after 5min
(void)DeRegisterEpollTask(&g_netlinkTask);
close(g_netlinkTask.taskfd);
g_netlinkTask.taskfd = -1;
if (TimerSetTimeout(g_recoverTimer, NSTACKX_NETLINK_RECOVER_MS, NSTACKX_FALSE) != NSTACKX_EOK) {
DFINDER_LOGE(TAG, "Timer setting timer fail");
g_lastErrorTimeS = currErrorTimeS;
NotifyDFinderMsgRecver(DFINDER_ON_INNER_ERROR);
return;
}
g_lastErrorTimeS = -1;
return;
}
g_lastErrorTimeS = currErrorTimeS;
}
static void RecoverNetLinkTask()
{
if (!g_smartGeniusInit) {
return;
}
g_netlinkTask.taskfd = CreateNetLinkSocketFd();
if (g_netlinkTask.taskfd < 0) {
DFINDER_LOGE(TAG, "unable to create netlink socket: %d", errno);
NotifyDFinderMsgRecver(DFINDER_ON_INNER_ERROR);
return;
}
if (RegisterEpollTask(&g_netlinkTask, EPOLLIN) != NSTACKX_EOK) {
DFINDER_LOGE(TAG, "RegisterEpollTask fail");
close(g_netlinkTask.taskfd);
g_netlinkTask.taskfd = -1;
NotifyDFinderMsgRecver(DFINDER_ON_INNER_ERROR);
return;
}
DFINDER_LOGI(TAG, "Recover netlink task success");
}
int32_t SmartGeniusInit(EpollDesc epollfd)
{
if (g_smartGeniusInit) {
return NSTACKX_EOK;
}
int fd = CreateNetLinkSocketFd();
if (fd < 0) {
DFINDER_LOGE(TAG, "unable to create netlink socket");
return NSTACKX_EFAILED;
}
g_netlinkTask.taskfd = fd;
g_netlinkTask.epollfd = epollfd;
g_netlinkTask.readHandle = SmartGeniusCallback;
g_netlinkTask.writeHandle = NetlinkWriteHandle;
g_netlinkTask.errorHandle = NetlinkErrorHandle;
g_netlinkTask.count = 0;
if (RegisterEpollTask(&g_netlinkTask, EPOLLIN) != NSTACKX_EOK) {
DFINDER_LOGE(TAG, "RegisterEpollTask fail");
goto L_CLOSE;
}
g_recoverTimer = TimerStart(epollfd, 0, NSTACKX_FALSE, RecoverNetLinkTask, NULL);
if (g_recoverTimer == NULL) {
DFINDER_LOGE(TAG, "Create recover timer fail");
goto L_ERR;
}
g_lastErrorTimeS = -1;
g_smartGeniusInit = NSTACKX_TRUE;
return NSTACKX_EOK;
L_ERR:
(void)DeRegisterEpollTask(&g_netlinkTask);
L_CLOSE:
close(g_netlinkTask.taskfd);
g_netlinkTask.taskfd = -1;
return NSTACKX_EFAILED;
}
void SmartGeniusClean(void)
{
if (!g_smartGeniusInit) {
return;
}
TimerDelete(g_recoverTimer);
g_recoverTimer = NULL;
g_lastErrorTimeS = -1;
if (g_netlinkTask.taskfd != -1) {
(void)DeRegisterEpollTask(&g_netlinkTask);
close(g_netlinkTask.taskfd);
g_netlinkTask.taskfd = -1;
}
g_smartGeniusInit = NSTACKX_FALSE;
}
void ResetSmartGeniusTaskCount(uint8_t isBusy)
{
if (isBusy) {
DFINDER_LOGI(TAG, "in this busy interval: g_netlinkTask count %llu", g_netlinkTask.count);
}
g_netlinkTask.count = 0;
if (isBusy) {
DFINDER_LOGI(TAG, "SmartGeniusCallback has been called %lu times",
g_netlinkSocketEventNum[NETLINK_SOCKET_READ_EVENT]);
DFINDER_LOGI(TAG, "NetlinkWriteHandle has been called %lu times",
g_netlinkSocketEventNum[NETLINK_SOCKET_WRITE_EVENT]);
DFINDER_LOGI(TAG, "NetlinkErrorHandle has benn called %lu times",
g_netlinkSocketEventNum[NETLINK_SOCKET_ERROR_EVENT]);
}
(void)memset_s(g_netlinkSocketEventNum, sizeof(g_netlinkSocketEventNum), 0, sizeof(g_netlinkSocketEventNum));
}
#else
int32_t SmartGeniusInit(EpollDesc epollfd)
{
(void)epollfd;
return NSTACKX_EOK;
}
void SmartGeniusClean(void)
{
}
void ResetSmartGeniusTaskCount(uint8_t isBusy)
{
(void)isBusy;
}
#endif /* SUPPORT_SMARTGENIUS */