walnux/net/utils/net_snoop.c
hujun5 a567148888 sched: add up_this_task and up_change_task macro stub
reason:
We can utilize percpu storage to hold information about the
current running task. If we intend to implement this feature, we would
need to define two macros that help us manage this percpu information
effectively.

up_this_task: This macro is designed to read the contents of the percpu
              register to retrieve information about the current
              running task.This allows us to quickly access
              task-specific data without having to disable interrupts,
              access global variables and obtain the current cpu index.

up_update_task: This macro is responsible for updating the contents of
                the percpu register.It is typically called during
                initialization or when a context switch occurs to ensure
                that the percpu register reflects the information of the
                newly running task.

Configuring NuttX and compile:
$ ./tools/configure.sh -l qemu-armv8a:nsh_smp
$ make
Running with qemu
$ qemu-system-aarch64 -cpu cortex-a53 -smp 4 -nographic \
   -machine virt,virtualization=on,gic-version=3 \
   -net none -chardev stdio,id=con,mux=on -serial chardev:con \
   -mon chardev=con,mode=readline -kernel ./nuttx

Signed-off-by: hujun5 <hujun5@xiaomi.com>
2024-10-15 15:25:55 +08:00

540 lines
15 KiB
C

/****************************************************************************
* net/utils/net_snoop.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <assert.h>
#include <endian.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/time.h>
#include <string.h>
#include <sys/param.h>
#include <nuttx/arch.h>
#include <nuttx/net/snoop.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define SNOOP_VERSION_1 1
#define SNOOP_VERSION_2 2
/* microseconds since midnight, January 1st, 0 AD nominal Gregorian. */
#define SNOOP_EPOCH_USEC(tv) (((tv).tv_sec - 0x386d4380ll) * 1000000ll \
+ (tv).tv_usec + 0x00e03ab44a676000ll)
/****************************************************************************
* Private Type Definitions
****************************************************************************/
/* The availability of tools to capture, display and interpret packets
* traversing a network has proven extremely useful in debugging
* networking problems. The ability to capture packets and store them
* for later analysis allows one to de-couple the tasks of collecting
* information about a network problem and analysing that information.
*
* More info about snoop datalink type, please refer to
* https://www.rfc-editor.org/rfc/rfc1761.txt and
* https://fte.com/webhelpii/hsu/Content/Technical_Information/
* BT_Snoop_File_Format.htm
*/
/* The snoop packet capture file is an array of octets structured as
* follows:
*
* +------------------------+
* | |
* | File Header |
* | |
* +------------------------+
* | |
* | Packet Record |
* ~ Number 1 ~
* | |
* +------------------------+
* . .
* . .
* . .
* +------------------------+
* | |
* | Packet Record |
* ~ Number N ~
* | |
* +------------------------+
*/
/* snoop_file_header_s
*
* The structure of the File Header is as follows:
*
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | |
* + Identification Pattern +
* | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Version Number |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Datalink Type |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
begin_packed_struct struct snoop_file_header_s
{
uint8_t magic[8]; /* Identification Pattern */
uint32_t version; /* Version Number */
uint32_t datalink; /* Datalink Type */
} end_packed_struct;
/* snoop_packet_header_s
*
* The structure of the packet record is as follows:
*
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Original Length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Included Length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Packet Record Length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Cumulative Drops |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Timestamp Seconds |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Timestamp Microseconds |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | |
* . .
* . Packet Data .
* . .
* + +- - - - - - - -+
* | | Pad |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
begin_packed_struct struct snoop_packet_header_s
{
uint32_t orig_len; /* actual length of packet */
uint32_t incl_len; /* number of octets captured in file */
union
{
uint32_t flags; /* Packet Flags: 1 hci cmd , eg: btsnoop */
uint32_t rec_len; /* length of record */
};
uint32_t cum_drops; /* cumulative number of dropped packets */
union
{
uint64_t ts_usec; /* timestamp microseconds, eg: btsnoop */
struct
{
uint32_t ts_sec; /* timestamp seconds */
uint32_t ts_usec; /* timestamp microseconds */
} ts;
};
} end_packed_struct;
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: snoop_fill_packet_header
*
* Description:
* This function fill snoop packet header info.
*
****************************************************************************/
static void snoop_fill_packet_header(FAR struct snoop_s *snoop,
uint32_t bytes, uint32_t drops,
uint32_t flags, FAR struct
snoop_packet_header_s *header)
{
struct timeval tv;
switch (snoop->datalink)
{
case SNOOP_DATALINK_HCI_UNENCAP:
case SNOOP_DATALINK_HCI_UART:
case SNOOP_DATALINK_HCI_BSCP:
case SNOOP_DATALINK_HCI_SERIAL:
gettimeofday(&tv, NULL);
header->ts_usec = htobe64(SNOOP_EPOCH_USEC(tv));
header->flags = htobe32(flags);
break;
case SNOOP_DATALINK_TYPE_TOKENBUS:
case SNOOP_DATALINK_TYPE_TOKERING:
case SNOOP_DATALINK_TYPE_METRONET:
case SNOOP_DATALINK_TYPE_ETHERNET:
case SNOOP_DATALINK_TYPE_HDLC:
case SNOOP_DATALINK_TYPE_CHARSYNC:
case SNOOP_DATALINK_TYPE_IBMC2C:
case SNOOP_DATALINK_TYPE_FDDI:
case SNOOP_DATALINK_TYPE_OTHER:
gettimeofday(&tv, NULL);
header->ts.ts_sec = htobe32(tv.tv_sec);
header->ts.ts_usec = htobe32(tv.tv_usec);
header->rec_len = htobe32(flags);
break;
default:
DEBUGASSERT(false);
}
header->orig_len = htobe32(bytes);
header->incl_len = htobe32(bytes);
header->cum_drops = htobe32(drops);
}
/****************************************************************************
* Name: snoop_flush
*
* Description:
* This function could flush snoop buf into file.
*
****************************************************************************/
static int snoop_flush(FAR struct snoop_s *snoop)
{
ssize_t ret;
if (snoop->next == 0)
{
return 0;
}
do
{
ret = file_write(&snoop->filep, snoop->buf, snoop->next);
if (ret < 0)
{
break;
}
snoop->next -= ret;
memmove(snoop->buf, snoop->buf + ret, snoop->next);
}
while (snoop->next > 0);
#ifndef CONFIG_DISABLE_MOUNTPOINT
if (snoop->autosync)
{
ret = file_fsync(&snoop->filep);
}
#endif
return ret;
}
/****************************************************************************
* Name: snoop_flush_lock
*
* Description:
* Snoop flush atomic
*
****************************************************************************/
static int snoop_flush_lock(FAR struct snoop_s *snoop)
{
irqstate_t flags;
int ret;
flags = enter_critical_section();
nxmutex_lock(&snoop->mutex);
ret = snoop_flush(snoop);
nxmutex_unlock(&snoop->mutex);
leave_critical_section(flags);
return ret;
}
/****************************************************************************
* Name: snoop_flush_work
*
* Description:
* Do snoop flush work.
*
****************************************************************************/
static void snoop_flush_work(FAR void *arg)
{
snoop_flush_lock((FAR struct snoop_s *)arg);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: snoop_open
*
* Description:
* This function open snoop file by datalink.
*
* Input Parameters:
* snoop The snoop driver struct
* filename Snoop file name
* datalink Snoop datalink type, such as SNOOP_DATALINK_TYPE_XX
* autosync whether do file_sync when snoop_dump
*
* Returned Value:
* OK on success; Negated errno on failure.
*
* Assumptions:
*
****************************************************************************/
int snoop_open(FAR struct snoop_s *snoop, FAR const char *filename,
uint32_t datalink, bool autosync)
{
struct snoop_file_header_s header;
int ret;
if (!snoop)
{
return -EINVAL;
}
switch (datalink)
{
case SNOOP_DATALINK_TYPE_TOKENBUS:
case SNOOP_DATALINK_TYPE_TOKERING:
case SNOOP_DATALINK_TYPE_METRONET:
case SNOOP_DATALINK_TYPE_ETHERNET:
case SNOOP_DATALINK_TYPE_HDLC:
case SNOOP_DATALINK_TYPE_CHARSYNC:
case SNOOP_DATALINK_TYPE_IBMC2C:
case SNOOP_DATALINK_TYPE_FDDI:
case SNOOP_DATALINK_TYPE_OTHER:
{
static const uint8_t snoop_magic[] =
{
's', 'n', 'o', 'o', 'p', '\0', '\0', '\0'
};
memcpy(header.magic, snoop_magic, nitems(snoop_magic));
header.version = htobe32(SNOOP_VERSION_2);
break;
};
case SNOOP_DATALINK_HCI_UNENCAP:
case SNOOP_DATALINK_HCI_UART:
case SNOOP_DATALINK_HCI_BSCP:
case SNOOP_DATALINK_HCI_SERIAL:
{
static const uint8_t btsnoop_magic[] =
{
'b', 't', 's', 'n', 'o', 'o', 'p', '\0'
};
memcpy(header.magic, btsnoop_magic, nitems(btsnoop_magic));
header.version = htobe32(SNOOP_VERSION_1);
break;
}
default:
{
return -EINVAL;
}
}
ret = file_open(&snoop->filep, filename, O_RDWR | O_CREAT | O_CLOEXEC,
0666);
if (ret < 0)
{
return ret;
}
snoop->datalink = datalink;
snoop->autosync = autosync;
snoop->next = 0;
header.datalink = htobe32(datalink);
ret = file_write(&snoop->filep, &header, sizeof(header));
if (ret != sizeof(header))
{
ret = ret < 0 ? ret : -EINVAL;
goto error;
}
nxmutex_init(&snoop->mutex);
return OK;
error:
snoop_close(snoop);
return ret;
}
/****************************************************************************
* Name: snoop_dump
*
* Description:
* This function dump nbytes buf data into snoop file.
*
* Input Parameters:
* snoop The snoop driver struct
* buf Snoop buffer
* nbytes Snoop buffer size
* drops cumulative number of dropped packets
* flags Packet Flags: 1 hci cmd , eg: btsnoop
*
* Returned Value:
* OK on success; Negated errno on failure.
*
* Assumptions:
*
****************************************************************************/
int snoop_dump(FAR struct snoop_s *snoop, FAR const void *buf,
uint32_t nbytes, uint32_t drops, uint32_t flags)
{
struct snoop_packet_header_s header;
irqstate_t irqflags;
int ret = 0;
if (!snoop)
{
return -EINVAL;
}
snoop_fill_packet_header(snoop, nbytes, drops, flags, &header);
irqflags = enter_critical_section();
if (up_interrupt_context())
{
if (sizeof(snoop->buf) - snoop->next <
nbytes + sizeof(struct snoop_packet_header_s))
{
ret = -ENOMEM;
goto out_leave;
}
memcpy(snoop->buf + snoop->next, &header, sizeof(header));
snoop->next += sizeof(header);
memcpy(snoop->buf + snoop->next, buf, nbytes);
snoop->next += nbytes;
if (work_available(&snoop->work))
{
work_queue(HPWORK, &snoop->work, snoop_flush_work, snoop, 0);
}
goto out_leave;
}
else
{
nxmutex_lock(&snoop->mutex);
ret = snoop_flush(snoop);
if (ret < 0)
{
goto out_unlock;
}
ret = file_write(&snoop->filep, &header, sizeof(header));
if (ret < 0)
{
goto out_unlock;
}
else if (ret != sizeof(header))
{
ret = -EINVAL;
goto out_unlock;
}
ret = file_write(&snoop->filep, buf, nbytes);
if (ret < 0)
{
goto out_unlock;
}
else if (ret != nbytes)
{
ret = -EINVAL;
goto out_unlock;
}
}
out_unlock:
nxmutex_unlock(&snoop->mutex);
out_leave:
leave_critical_section(irqflags);
return ret;
}
/****************************************************************************
* Name: snoop_sync
*
* Description:
* This function sync snoop buffer.
*
* Input Parameters:
* snoop The snoop driver struct
*
* Returned Value:
* OK on success; Negated errno on failure.
*
* Assumptions:
*
****************************************************************************/
int snoop_sync(FAR struct snoop_s *snoop)
{
if (!snoop)
{
return -EINVAL;
}
return snoop_flush_lock(snoop);
}
/****************************************************************************
* Name: snoop_close
*
* Description:
* This function close snoop file.
*
* Input Parameters:
* snoop The snoop driver struct
*
* Returned Value:
* OK on success; Negated errno on failure.
*
* Assumptions:
*
****************************************************************************/
int snoop_close(FAR struct snoop_s *snoop)
{
if (!snoop)
{
return -EINVAL;
}
nxmutex_destroy(&snoop->mutex);
return file_close(&snoop->filep);
}