├── .github └── workflows │ └── main.yml ├── .gitignore ├── COPYING ├── LICENSES └── ISC.txt ├── Makefile.am ├── autogen.sh ├── configure.ac ├── examples ├── .gitignore ├── Makefile.am ├── README.examples ├── af_ieee802154_rx.c ├── af_ieee802154_tx.c ├── af_inet6_rx.c ├── af_inet6_tx.c ├── af_packet_rx.c └── af_packet_tx.c ├── src ├── .gitignore ├── Makefile.am ├── event.c ├── info.c ├── interface.c ├── iwpan.c ├── iwpan.h ├── mac.c ├── nl802154.h ├── nl_extras.h ├── phy.c ├── scan.c └── sections.c ├── wpan-hwsim ├── Makefile.am ├── mac802154_hwsim.h └── wpan-hwsim.c └── wpan-ping ├── .gitignore ├── Makefile.am ├── README.wpan-ping └── wpan-ping.c /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2023 Stefan Schmidt 2 | # 3 | # SPDX-License-Identifier: ISC 4 | 5 | name: Basic compile test for wpan-tools 6 | on: push 7 | 8 | jobs: 9 | reuse: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Check out repository 13 | uses: actions/checkout@v4 14 | - name: Install reuse tool 15 | run: pip3 install reuse 16 | - name: Run reuse lint 17 | run: reuse lint 18 | - name: Create SBOM 19 | run: reuse spdx > sbom.txt 20 | compile: 21 | runs-on: ubuntu-latest 22 | env: 23 | CC: ${{ matrix.cc }} 24 | strategy: 25 | matrix: 26 | cc: [gcc, clang] 27 | os: [ubuntu-16.04, ubuntu-18.04, ubuntu-20.04, ubuntu-22.04, ubuntu-latest] 28 | steps: 29 | - name: Check out repository 30 | uses: actions/checkout@v4 31 | - name: Install dependencies 32 | run: sudo apt install libnl-3-dev libnl-genl-3-dev ${{ matrix.cc }} 33 | - name: autogen 34 | run: ./autogen.sh 35 | - name: configure 36 | run: ./configure 37 | - name: make 38 | run: make 39 | - name: distcheck 40 | run: make distcheck 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2014 Alexander Aring 2 | # 3 | # SPDX-License-Identifier: ISC 4 | 5 | *.o 6 | tags 7 | TAGS 8 | Makefile 9 | Makefile.in 10 | aclocal.m4 11 | autom4te.cache 12 | build-aux 13 | config.* 14 | configure 15 | libtool 16 | stamp-h1 17 | m4/ 18 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | LICENSES/ISC.txt -------------------------------------------------------------------------------- /LICENSES/ISC.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Alexander Aring 2 | Copyright (c) 2015 Stefan Schmidt 3 | 4 | Code is based on iw tool. 5 | 6 | Original Authors: 7 | 8 | Copyright (c) 2007, 2008 Johannes Berg 9 | Copyright (c) 2007 Andy Lutomirski 10 | Copyright (c) 2007 Mike Kershaw 11 | Copyright (c) 2008-2009 Luis R. Rodriguez 12 | 13 | Permission to use, copy, modify, and/or distribute this software for any 14 | purpose with or without fee is hereby granted, provided that the above 15 | copyright notice and this permission notice appear in all copies. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 18 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 19 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 20 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 21 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 22 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 23 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 24 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2014 Alexander Aring 2 | # 3 | # SPDX-License-Identifier: ISC 4 | 5 | EXTRA_DIST = 6 | CLEANFILES = 7 | ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} 8 | AM_MAKEFLAGS = --no-print-directory 9 | 10 | AM_CPPFLAGS = \ 11 | -include $(top_builddir)/config.h \ 12 | -DSYSCONFDIR=\""$(sysconfdir)"\" \ 13 | -DLIBEXECDIR=\""$(libexecdir)"\" \ 14 | -I${top_srcdir}/src 15 | 16 | AM_CFLAGS = ${WPAN_TOOLS_CFLAGS} \ 17 | -fvisibility=hidden \ 18 | -ffunction-sections \ 19 | -fdata-sections 20 | 21 | AM_LDFLAGS = \ 22 | -Wl,--gc-sections \ 23 | -Wl,--as-needed 24 | 25 | SUBDIRS = \ 26 | src \ 27 | wpan-ping \ 28 | wpan-hwsim \ 29 | examples 30 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | # SPDX-FileCopyrightText: 2014 Alexander Aring 4 | # 5 | # SPDX-License-Identifier: ISC 6 | 7 | if [ -f .git/hooks/pre-commit.sample -a ! -f .git/hooks/pre-commit ] ; then 8 | cp -p .git/hooks/pre-commit.sample .git/hooks/pre-commit && \ 9 | chmod +x .git/hooks/pre-commit && \ 10 | echo "Activated pre-commit hook." 11 | fi 12 | 13 | autoreconf --install --symlink 14 | 15 | libdir() { 16 | echo $(cd $1/$(gcc -print-multi-os-directory); pwd) 17 | } 18 | 19 | args="--prefix=/usr \ 20 | --sysconfdir=/etc \ 21 | --libdir=$(libdir /usr/lib)" 22 | 23 | echo 24 | echo "----------------------------------------------------------------" 25 | echo "Initialized build system. For a common configuration please run:" 26 | echo "----------------------------------------------------------------" 27 | echo 28 | echo "./configure CFLAGS='-g -O0' $args" 29 | echo 30 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | dnl SPDX-FileCopyrightText: 2014 Alexander Aring 2 | dnl 3 | dnl SPDX-License-Identifier: ISC 4 | 5 | AC_PREREQ(2.60) 6 | AC_INIT([Userspace tools for Linux IEEE 802.15.4 stack], 7 | [0.10], [linux-wpan@vger.kernel.org], [wpan-tools], 8 | [https://github.com/linux-wpan/wpan-tools]) 9 | AC_CONFIG_SRCDIR([src/iwpan.c]) 10 | AC_CONFIG_AUX_DIR([build-aux]) 11 | AM_INIT_AUTOMAKE([ 12 | foreign 13 | 1.11 14 | -Wall 15 | -Werror 16 | -Wno-portability 17 | silent-rules 18 | tar-pax 19 | dist-xz 20 | subdir-objects 21 | ]) 22 | AC_PROG_CC_STDC 23 | AC_USE_SYSTEM_EXTENSIONS 24 | AC_SYS_LARGEFILE 25 | AC_CONFIG_MACRO_DIR([m4]) 26 | AM_SILENT_RULES([yes]) 27 | LT_INIT([ 28 | disable-static 29 | pic-only 30 | ]) 31 | AC_PREFIX_DEFAULT([/usr]) 32 | 33 | AC_PROG_SED 34 | AC_PROG_MKDIR_P 35 | 36 | PKG_CHECK_MODULES(LIBNL3, [libnl-3.0 >= 3.1 libnl-genl-3.0 >= 3.1]) 37 | 38 | AC_CHECK_FUNCS([ \ 39 | __secure_getenv \ 40 | secure_getenv\ 41 | ]) 42 | 43 | WPAN_TOOLS_CFLAGS="\ 44 | -Wall \ 45 | -Wchar-subscripts \ 46 | -Wformat-security \ 47 | -Wmissing-declarations \ 48 | -Wmissing-prototypes \ 49 | -Wnested-externs \ 50 | -Wpointer-arith \ 51 | -Wshadow \ 52 | -Wsign-compare \ 53 | -Wstrict-prototypes \ 54 | -Wtype-limits \ 55 | " 56 | AC_SUBST([WPAN_TOOLS_CFLAGS]) 57 | 58 | AC_CONFIG_HEADERS(config.h) 59 | AC_CONFIG_FILES([ 60 | Makefile 61 | src/Makefile 62 | wpan-ping/Makefile 63 | wpan-hwsim/Makefile 64 | examples/Makefile 65 | ]) 66 | 67 | AC_OUTPUT 68 | AC_MSG_RESULT([ 69 | $PACKAGE $VERSION 70 | ===== 71 | 72 | prefix: ${prefix} 73 | sysconfdir: ${sysconfdir} 74 | 75 | compiler: ${CC} 76 | cflags: ${CFLAGS} 77 | ldflags: ${LDFLAGS} 78 | ]) 79 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2016 Samsung Electronics Co., Ltd. 2 | # 3 | # SPDX-License-Identifier: ISC 4 | 5 | /.deps 6 | /af_ieee802154_rx 7 | /af_ieee802154_tx 8 | /af_packet_rx 9 | /af_packet_tx 10 | /af_inet6_rx 11 | /af_inet6_tx 12 | -------------------------------------------------------------------------------- /examples/Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2016 Samsung Electronics Co., Ltd. 2 | # 3 | # SPDX-License-Identifier: ISC 4 | 5 | noinst_PROGRAMS = af_ieee802154_tx \ 6 | af_ieee802154_rx \ 7 | af_packet_tx \ 8 | af_packet_rx \ 9 | af_inet6_tx \ 10 | af_inet6_rx 11 | 12 | EXTRA_DIST = README.examples 13 | -------------------------------------------------------------------------------- /examples/README.examples: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2016 Samsung Electronics Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: ISC 4 | 5 | This folder contains various examples on how the Linux-wpan stack can be used 6 | from userspace, using the Linux socket interface. 7 | 8 | These examples do not cover all socket programming functionality nor do they 9 | give a real introduction to it. Their aim is to provide some quick examples for 10 | using the Linux-wpan stack for developers familiar with socket programming. 11 | 12 | Keep in mind that the socket interface is used for the data plane. For the 13 | configuration plane you would use netlink, which is heavily demonstrated in 14 | iwpan itself. 15 | 16 | Examples: 17 | --------- 18 | Address family AF_IEEE802154 with type SOCK_DGRAM: 19 | * Direct usage of IEEE 802.15.4 frames, no 6LoWPAN involved 20 | * Short and extended address usage is demonstrated 21 | * af_ieee802154_rx loops and prints each received IEEE 802.15.4 frame on stdout 22 | * af_ieee802154_tx sends a IEEE 802.15.4 frame 23 | 24 | Address family AF_INET6 with type SOCK_DGRAM: 25 | * 6LoWPAN examples, IPv6 over IEEE 802.15.4 26 | * af_inet6_rx binds on any IPv6 address on UDP port 27 | * af_inet6_tx sends an UDP packet to the IPv6 all nodes address 28 | 29 | Address family AF_PACKET with type SOCK_RAW: 30 | * Raw access to IEEE 8021.5.4 frames for rx and tx 31 | * Useful for sniffer, packet generators, network stack in userspace, etc 32 | * af_packet_rx loops and prints each frame as hexdump 33 | * af_packet_tx constructs the raw packet payload manually and sends it 34 | 35 | Some of the example need special privileges to run. If you get a error like: 36 | socket: Operation not permitted 37 | 38 | Make sure your user that the needed privileges or run the example with sudo. 39 | -------------------------------------------------------------------------------- /examples/af_ieee802154_rx.c: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2016 Samsung Electronics Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: ISC 4 | 5 | /* IEEE 802.15.4 socket example */ 6 | /* gcc af_ieee802154_rx.c -o af_ieee802154_rx */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define IEEE802154_ADDR_LEN 8 16 | #define MAX_PACKET_LEN 127 17 | #define EXTENDED 1 18 | 19 | enum { 20 | IEEE802154_ADDR_NONE = 0x0, 21 | IEEE802154_ADDR_SHORT = 0x2, 22 | IEEE802154_ADDR_LONG = 0x3, 23 | }; 24 | 25 | struct ieee802154_addr_sa { 26 | int addr_type; 27 | uint16_t pan_id; 28 | union { 29 | uint8_t hwaddr[IEEE802154_ADDR_LEN]; 30 | uint16_t short_addr; 31 | }; 32 | }; 33 | 34 | struct sockaddr_ieee802154 { 35 | sa_family_t family; 36 | struct ieee802154_addr_sa addr; 37 | }; 38 | 39 | int main(int argc, char *argv[]) { 40 | int ret, sd; 41 | struct sockaddr_ieee802154 src, dst; 42 | unsigned char buf[MAX_PACKET_LEN + 1]; 43 | socklen_t addrlen; 44 | /* IEEE 802.15.4 extended address to receive frames on, adapt to your setup */ 45 | uint8_t long_addr[IEEE802154_ADDR_LEN] = {0xd6, 0x55, 0x2c, 0xd6, 0xe4, 0x1c, 0xeb, 0x57}; 46 | 47 | /* Create IEEE 802.15.4 address family socket for the SOCK_DGRAM type */ 48 | sd = socket(PF_IEEE802154, SOCK_DGRAM, 0); 49 | if (sd < 0) { 50 | perror("socket"); 51 | return 1; 52 | } 53 | 54 | /* Prepare source socket address struct */ 55 | memset(&src, 0, sizeof(src)); 56 | src.family = AF_IEEE802154; 57 | /* Used PAN ID is 0x23 here, adapt to your setup */ 58 | src.addr.pan_id = 0x0023; 59 | 60 | #if EXTENDED /* IEEE 802.15.4 extended address usage */ 61 | src.addr.addr_type = IEEE802154_ADDR_LONG; 62 | memcpy(&src.addr.hwaddr, &long_addr, IEEE802154_ADDR_LEN); 63 | #else 64 | src.addr.addr_type = IEEE802154_ADDR_SHORT; 65 | src.addr.short_addr = 0x0002; 66 | #endif 67 | 68 | /* Bind socket on this side */ 69 | ret = bind(sd, (struct sockaddr *)&src, sizeof(src)); 70 | if (ret) { 71 | perror("bind"); 72 | close(sd); 73 | return 1; 74 | } 75 | 76 | addrlen = sizeof(dst); 77 | 78 | /* Infinite loop receiving 802.15.4 frames and print out */ 79 | while (1) { 80 | ret = recvfrom(sd, buf, MAX_PACKET_LEN, 0, (struct sockaddr *)&dst, &addrlen); 81 | if (ret < 0) { 82 | perror("recvfrom"); 83 | continue; 84 | } 85 | buf[ret] = '\0'; 86 | #if EXTENDED 87 | printf("Received (from %s): %s\n", dst.addr.hwaddr, buf); 88 | #else 89 | printf("Received (from %x): %s\n", dst.addr.short_addr, buf); 90 | #endif 91 | } 92 | 93 | shutdown(sd, SHUT_RDWR); 94 | close(sd); 95 | return 0; 96 | } 97 | -------------------------------------------------------------------------------- /examples/af_ieee802154_tx.c: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2016 Samsung Electronics Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: ISC 4 | 5 | /* IEEE 802.15.4 socket example */ 6 | /* gcc af_ieee802154_tx.c -o af_ieee802154_tx */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define IEEE802154_ADDR_LEN 8 16 | #define MAX_PACKET_LEN 127 17 | #define EXTENDED 1 18 | 19 | enum { 20 | IEEE802154_ADDR_NONE = 0x0, 21 | IEEE802154_ADDR_SHORT = 0x2, 22 | IEEE802154_ADDR_LONG = 0x3, 23 | }; 24 | 25 | struct ieee802154_addr_sa { 26 | int addr_type; 27 | uint16_t pan_id; 28 | union { 29 | uint8_t hwaddr[IEEE802154_ADDR_LEN]; 30 | uint16_t short_addr; 31 | }; 32 | }; 33 | 34 | struct sockaddr_ieee802154 { 35 | sa_family_t family; 36 | struct ieee802154_addr_sa addr; 37 | }; 38 | 39 | int main(int argc, char *argv[]) { 40 | int sd; 41 | ssize_t len; 42 | struct sockaddr_ieee802154 dst; 43 | char buf[MAX_PACKET_LEN + 1]; 44 | /* IEEE 802.15.4 extended send address, adapt to your setup */ 45 | uint8_t long_addr[IEEE802154_ADDR_LEN] = {0xd6, 0x55, 0x2c, 0xd6, 0xe4, 0x1c, 0xeb, 0x57}; 46 | 47 | /* Create IEEE 802.15.4 address family socket for the SOCK_DGRAM type */ 48 | sd = socket(PF_IEEE802154, SOCK_DGRAM, 0); 49 | if (sd < 0) { 50 | perror("socket"); 51 | return 1; 52 | } 53 | 54 | /* Prepare destination socket address struct */ 55 | memset(&dst, 0, sizeof(dst)); 56 | dst.family = AF_IEEE802154; 57 | /* Used PAN ID is 0x23 here, adapt to your setup */ 58 | dst.addr.pan_id = 0x0023; 59 | 60 | #if EXTENDED /* IEEE 802.15.4 extended address usage */ 61 | dst.addr.addr_type = IEEE802154_ADDR_LONG; 62 | memcpy(&dst.addr.hwaddr, long_addr, IEEE802154_ADDR_LEN); 63 | #else 64 | dst.addr.addr_type = IEEE802154_ADDR_SHORT; 65 | dst.addr.short_addr = 0x0002; 66 | #endif 67 | 68 | sprintf(buf, "Hello world from IEEE 802.15.4 socket example!"); 69 | 70 | /* sendto() is used for implicity in this example, bin()/send() would 71 | * be an alternative */ 72 | len = sendto(sd, buf, strlen(buf), 0, (struct sockaddr *)&dst, sizeof(dst)); 73 | if (len < 0) { 74 | perror("sendto"); 75 | } 76 | 77 | shutdown(sd, SHUT_RDWR); 78 | close(sd); 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /examples/af_inet6_rx.c: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2016 Samsung Electronics Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: ISC 4 | 5 | /* IEEE 802.15.4 socket example */ 6 | /* gcc af_inet6_rx.c -o af_inet6_rx */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define MAX_PACKET_LEN 2048 16 | 17 | int main(int argc, char *argv[]) { 18 | int ret, sd; 19 | ssize_t len; 20 | struct sockaddr_in6 src, dst; 21 | unsigned char buf[MAX_PACKET_LEN + 1]; 22 | socklen_t addrlen; 23 | char ipv6[INET6_ADDRSTRLEN]; 24 | 25 | /* Create IPv6 address family socket for the SOCK_DGRAM type */ 26 | sd = socket(PF_INET6, SOCK_DGRAM, 0); 27 | if (sd < 0) { 28 | perror("socket"); 29 | return 1; 30 | } 31 | 32 | /* Prepare source and destination socket address structs */ 33 | memset(&dst, 0, sizeof(dst)); 34 | memset(&src, 0, sizeof(src)); 35 | src.sin6_family = AF_INET6; 36 | src.sin6_addr = in6addr_any; 37 | /* Port within the compressed port range for potential NHC UDP compression */ 38 | src.sin6_port = htons(61617); 39 | 40 | /* Bind socket on this side */ 41 | ret = bind(sd, (struct sockaddr *)&src, sizeof(src)); 42 | if (ret) { 43 | perror("bind"); 44 | close(sd); 45 | return 1; 46 | } 47 | 48 | addrlen = sizeof(dst); 49 | 50 | /* Infinite loop receiving IPv6 packets and print out */ 51 | while (1) { 52 | len = recvfrom(sd, buf, MAX_PACKET_LEN, 0, (struct sockaddr *)&dst, &addrlen); 53 | if (len < 0) { 54 | perror("recvfrom"); 55 | continue; 56 | } 57 | buf[len] = '\0'; 58 | inet_ntop(AF_INET6, &(dst.sin6_addr), ipv6, INET6_ADDRSTRLEN); 59 | printf("Received (from %s): %s\n", ipv6, buf); 60 | } 61 | 62 | shutdown(sd, SHUT_RDWR); 63 | close(sd); 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /examples/af_inet6_tx.c: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2016 Samsung Electronics Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: ISC 4 | 5 | /* IEEE 802.15.4 socket example */ 6 | /* gcc af_inet6_tx.c -o af_inet6_tx */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #define IEEE802154_ADDR_LEN 8 17 | #define MAX_PACKET_LEN 2048 18 | 19 | int main(int argc, char *argv[]) { 20 | int ret, sd; 21 | struct sockaddr_in6 dst; 22 | struct ifreq ifr; 23 | char buf[MAX_PACKET_LEN + 1]; 24 | 25 | /* Create IPv6 address family socket for the SOCK_DGRAM type */ 26 | sd = socket(PF_INET6, SOCK_DGRAM, 0); 27 | if (sd < 0) { 28 | perror("socket"); 29 | return 1; 30 | } 31 | 32 | /* Bind the socket to lowpan0 to make sure we send over it, adapt to your setup */ 33 | memset(&ifr, 0, sizeof(ifr)); 34 | snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "lowpan0"); 35 | ret = setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)); 36 | if (ret < 0) { 37 | perror("setsockopt"); 38 | return 1; 39 | } 40 | 41 | /* Prepare destination socket address struct */ 42 | memset(&dst, 0, sizeof(dst)); 43 | dst.sin6_family = AF_INET6; 44 | /* Port within the compressed port range for potential NHC UDP compression */ 45 | dst.sin6_port = htons(61617); 46 | inet_pton(AF_INET6, "ff02::1", &(dst.sin6_addr)); 47 | 48 | sprintf(buf, "Hello world from AF_INET6 socket example!"); 49 | 50 | /* sendto() is used for implicity in this example, bin()/send() would 51 | * be an alternative */ 52 | ret = sendto(sd, buf, strlen(buf), 0, (struct sockaddr *)&dst, sizeof(dst)); 53 | if (ret < 0) { 54 | perror("sendto"); 55 | } 56 | 57 | shutdown(sd, SHUT_RDWR); 58 | close(sd); 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /examples/af_packet_rx.c: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2016 Samsung Electronics Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: ISC 4 | 5 | /* IEEE 802.15.4 socket example */ 6 | /* gcc af_packet_rx.c -o af_packet_rx */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | #define IEEE802154_ADDR_LEN 8 21 | #define MAX_PACKET_LEN 127 22 | 23 | #ifndef ETH_P_IEEE802154 24 | #define ETH_P_IEEE802154 0x00F6 25 | #endif 26 | 27 | int main(int argc, char *argv[]) { 28 | int ret, sd, i; 29 | ssize_t len; 30 | unsigned char buf[MAX_PACKET_LEN + 1]; 31 | struct sockaddr_ll sll; 32 | struct ifreq ifr; 33 | 34 | /* Create AF_PACKET address family socket for the SOCK_RAW type */ 35 | sd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IEEE802154)); 36 | if (sd < 0) { 37 | perror("socket"); 38 | return 1; 39 | } 40 | 41 | /* Get interface index */ 42 | strncpy(ifr.ifr_name, "monitor0", IFNAMSIZ); 43 | ret = ioctl(sd, SIOCGIFINDEX, &ifr); 44 | if (ret < 0) { 45 | perror("ioctl"); 46 | close(sd); 47 | return 1; 48 | } 49 | 50 | /* Prepare source socket address struct */ 51 | memset(&sll, 0, sizeof(sll)); 52 | sll.sll_family = AF_PACKET; 53 | sll.sll_ifindex = ifr.ifr_ifindex; 54 | sll.sll_protocol = htons(ETH_P_IEEE802154); 55 | 56 | /* Bind socket on this side */ 57 | ret = bind(sd, (struct sockaddr *)&sll, sizeof(sll)); 58 | if (ret < 0) { 59 | perror("bind"); 60 | close(sd); 61 | return 1; 62 | } 63 | 64 | while (1) { 65 | /* Receive and print the whole packet payload, including FCS */ 66 | len = recv(sd, buf, MAX_PACKET_LEN, 0); 67 | if (len < 0) { 68 | perror("recv"); 69 | continue; 70 | } 71 | printf("Received:"); 72 | for (i = 0; i < len; i++) 73 | printf(" %x", buf[i]); 74 | printf("\n"); 75 | } 76 | 77 | shutdown(sd, SHUT_RDWR); 78 | close(sd); 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /examples/af_packet_tx.c: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2016 Samsung Electronics Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: ISC 4 | 5 | /* IEEE 802.15.4 socket example */ 6 | /* gcc af_packet_tx.c -o af_packet_tx */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #ifndef ETH_P_IEEE802154 20 | #define ETH_P_IEEE802154 0x00F6 21 | #endif 22 | 23 | #define MAX_PACKET_LEN 127 24 | 25 | int main(int argc, char *argv[]) { 26 | int ret, sd; 27 | ssize_t len; 28 | struct sockaddr_ll sll; 29 | struct ifreq ifr; 30 | unsigned char buf[MAX_PACKET_LEN + 1]; 31 | 32 | /* Create AF_PACKET address family socket for the SOCK_RAW type */ 33 | sd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IEEE802154)); 34 | if (sd < 0) { 35 | perror("socket"); 36 | return 1; 37 | } 38 | 39 | /* Using a monitor interface here results in a bad FCS and two missing 40 | * bytes from payload, using the normal IEEE 802.15.4 interface here */ 41 | strncpy(ifr.ifr_name, "wpan0", IFNAMSIZ); 42 | ret = ioctl(sd, SIOCGIFINDEX, &ifr); 43 | if (ret < 0) { 44 | perror("ioctl"); 45 | close(sd); 46 | return 1; 47 | } 48 | 49 | /* Prepare destination socket address struct */ 50 | memset(&sll, 0, sizeof(sll)); 51 | sll.sll_family = AF_PACKET; 52 | sll.sll_ifindex = ifr.ifr_ifindex; 53 | sll.sll_protocol = htons(ETH_P_IEEE802154); 54 | 55 | /* Bind socket on this side */ 56 | ret = bind(sd, (struct sockaddr *)&sll, sizeof(sll)); 57 | if (ret < 0) { 58 | perror("bind"); 59 | close(sd); 60 | return 1; 61 | } 62 | 63 | /* Construct raw packet payload, length and FCS gets added in the kernel */ 64 | buf[0] = 0x21; /* Frame Control Field */ 65 | buf[1] = 0xc8; /* Frame Control Field */ 66 | buf[2] = 0x8b; /* Sequence number */ 67 | buf[3] = 0xff; /* Destination PAN ID 0xffff */ 68 | buf[4] = 0xff; /* Destination PAN ID */ 69 | buf[5] = 0x02; /* Destination short address 0x0002 */ 70 | buf[6] = 0x00; /* Destination short address */ 71 | buf[7] = 0x23; /* Source PAN ID 0x0023 */ 72 | buf[8] = 0x00; /* */ 73 | buf[9] = 0x60; /* Source extended address ae:c2:4a:1c:21:16:e2:60 */ 74 | buf[10] = 0xe2; /* */ 75 | buf[11] = 0x16; /* */ 76 | buf[12] = 0x21; /* */ 77 | buf[13] = 0x1c; /* */ 78 | buf[14] = 0x4a; /* */ 79 | buf[15] = 0xc2; /* */ 80 | buf[16] = 0xae; /* */ 81 | buf[17] = 0xAA; /* Payload */ 82 | buf[18] = 0xBB; /* */ 83 | buf[19] = 0xCC; /* */ 84 | 85 | /* Send constructed packet over binded interface */ 86 | len = send(sd, buf, 20, 0); 87 | if (len < 0) { 88 | perror("send"); 89 | } 90 | 91 | shutdown(sd, SHUT_RDWR); 92 | close(sd); 93 | return 0; 94 | } 95 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2014 Alexander Aring 2 | # 3 | # SPDX-License-Identifier: ISC 4 | 5 | .deps 6 | iwpan 7 | *.o 8 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2014 Alexander Aring 2 | # 3 | # SPDX-License-Identifier: ISC 4 | 5 | bin_PROGRAMS = \ 6 | iwpan 7 | 8 | iwpan_SOURCES = \ 9 | iwpan.c \ 10 | iwpan.h \ 11 | sections.c \ 12 | info.c \ 13 | interface.c \ 14 | phy.c \ 15 | mac.c \ 16 | scan.c \ 17 | event.c \ 18 | nl_extras.h \ 19 | nl802154.h 20 | 21 | iwpan_CFLAGS = $(AM_CFLAGS) $(LIBNL3_CFLAGS) 22 | iwpan_LDADD = $(LIBNL3_LIBS) 23 | -------------------------------------------------------------------------------- /src/event.c: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 David Girault 2 | // 3 | // SPDX-License-Identifier: ISC 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "nl802154.h" 18 | #include "nl_extras.h" 19 | #include "iwpan.h" 20 | 21 | struct print_event_args { 22 | struct timeval ts; /* internal */ 23 | bool have_ts; /* must be set false */ 24 | bool frame, time, reltime; 25 | }; 26 | 27 | static int print_event(struct nl_msg *msg, void *arg) 28 | { 29 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); 30 | struct nlattr *tb[NL802154_ATTR_MAX + 1], *nst, *nestedcoord; 31 | struct nlattr *pan[NL802154_COORD_MAX + 1]; 32 | struct print_event_args *args = arg; 33 | char ifname[100]; 34 | static struct nla_policy pan_policy[NL802154_COORD_MAX + 1] = { 35 | [NL802154_COORD_PANID] = { .type = NLA_U16, }, 36 | [NL802154_COORD_ADDR] = { .minlen = 2, .maxlen = 8, }, /* 2 or 8 */ 37 | }; 38 | uint8_t reg_type; 39 | uint32_t wpan_phy_idx = 0; 40 | int rem_nst; 41 | uint16_t status; 42 | int ret; 43 | 44 | if (args->time || args->reltime) { 45 | unsigned long long usecs, previous; 46 | 47 | previous = 1000000ULL * args->ts.tv_sec + args->ts.tv_usec; 48 | gettimeofday(&args->ts, NULL); 49 | usecs = 1000000ULL * args->ts.tv_sec + args->ts.tv_usec; 50 | if (args->reltime) { 51 | if (!args->have_ts) { 52 | usecs = 0; 53 | args->have_ts = true; 54 | } else 55 | usecs -= previous; 56 | } 57 | printf("%llu.%06llu: ", usecs/1000000, usecs % 1000000); 58 | } 59 | 60 | nla_parse(tb, NL802154_ATTR_MAX, genlmsg_attrdata(gnlh, 0), 61 | genlmsg_attrlen(gnlh, 0), NULL); 62 | 63 | if (tb[NL802154_ATTR_IFINDEX] && tb[NL802154_ATTR_WPAN_PHY]) { 64 | if_indextoname(nla_get_u32(tb[NL802154_ATTR_IFINDEX]), ifname); 65 | printf("%s (phy #%d): ", ifname, nla_get_u32(tb[NL802154_ATTR_WPAN_PHY])); 66 | } else if (tb[NL802154_ATTR_WPAN_DEV] && tb[NL802154_ATTR_WPAN_PHY]) { 67 | printf("wdev 0x%llx (phy #%d): ", 68 | (unsigned long long)nla_get_u64(tb[NL802154_ATTR_WPAN_DEV]), 69 | nla_get_u32(tb[NL802154_ATTR_WPAN_PHY])); 70 | } else if (tb[NL802154_ATTR_IFINDEX]) { 71 | if_indextoname(nla_get_u32(tb[NL802154_ATTR_IFINDEX]), ifname); 72 | printf("%s: ", ifname); 73 | } else if (tb[NL802154_ATTR_WPAN_DEV]) { 74 | printf("wdev 0x%llx: ", (unsigned long long)nla_get_u64(tb[NL802154_ATTR_WPAN_DEV])); 75 | } else if (tb[NL802154_ATTR_WPAN_PHY]) { 76 | printf("phy #%d: ", nla_get_u32(tb[NL802154_ATTR_WPAN_PHY])); 77 | } 78 | 79 | switch (gnlh->cmd) { 80 | case NL802154_CMD_NEW_WPAN_PHY: 81 | printf("renamed to %s\n", nla_get_string(tb[NL802154_ATTR_WPAN_PHY_NAME])); 82 | break; 83 | case NL802154_CMD_DEL_WPAN_PHY: 84 | printf("delete wpan_phy\n"); 85 | break; 86 | case NL802154_CMD_TRIGGER_SCAN: 87 | printf("scan started\n"); 88 | break; 89 | case NL802154_CMD_SCAN_DONE: 90 | if (tb[NL802154_ATTR_SCAN_DONE_REASON]) 91 | status = nla_get_u8(tb[NL802154_ATTR_SCAN_DONE_REASON]); 92 | if (status == NL802154_SCAN_DONE_REASON_ABORTED) 93 | printf("scan aborted\n"); 94 | else 95 | printf("scan finished\n"); 96 | break; 97 | case NL802154_CMD_ABORT_SCAN: 98 | printf("scan aborted\n"); 99 | break; 100 | case NL802154_CMD_SCAN_EVENT: 101 | nestedcoord = tb[NL802154_ATTR_COORDINATOR]; 102 | if (!nestedcoord) 103 | break; 104 | ret = nla_parse_nested(pan, NL802154_COORD_MAX, nestedcoord, pan_policy); 105 | if (ret < 0) 106 | break; 107 | if (!pan[NL802154_COORD_PANID]) 108 | break; 109 | printf("beacon received: PAN 0x%04x", 110 | le16toh(nla_get_u16(pan[NL802154_COORD_PANID]))); 111 | if (pan[NL802154_COORD_ADDR]) { 112 | struct nlattr *coord = pan[NL802154_COORD_ADDR]; 113 | if (nla_len(coord) == 2) { 114 | uint16_t addr = nla_get_u16(coord); 115 | printf(", addr 0x%04x\n", le16toh(addr)); 116 | } else { 117 | uint64_t addr = nla_get_u64(coord); 118 | printf(", addr 0x%016" PRIx64 "\n", le64toh(addr)); 119 | } 120 | } 121 | break; 122 | default: 123 | printf("unknown event %d\n", gnlh->cmd); 124 | break; 125 | } 126 | fflush(stdout); 127 | return NL_SKIP; 128 | } 129 | 130 | static int __prepare_listen_events(struct nl802154_state *state) 131 | { 132 | int mcid, ret; 133 | 134 | /* Configuration multicast group */ 135 | mcid = genl_ctrl_resolve_grp(state->nl_sock, NL802154_GENL_NAME, 136 | "config"); 137 | if (mcid < 0) 138 | return mcid; 139 | ret = nl_socket_add_membership(state->nl_sock, mcid); 140 | if (ret) 141 | return ret; 142 | 143 | /* Scan multicast group */ 144 | mcid = genl_ctrl_resolve_grp(state->nl_sock, NL802154_GENL_NAME, 145 | "scan"); 146 | if (mcid >= 0) { 147 | ret = nl_socket_add_membership(state->nl_sock, mcid); 148 | if (ret) 149 | return ret; 150 | } 151 | 152 | /* MLME multicast group */ 153 | mcid = genl_ctrl_resolve_grp(state->nl_sock, NL802154_GENL_NAME, 154 | "mlme"); 155 | if (mcid >= 0) { 156 | ret = nl_socket_add_membership(state->nl_sock, mcid); 157 | if (ret) 158 | return ret; 159 | } 160 | 161 | return 0; 162 | } 163 | 164 | static int __do_listen_events(struct nl802154_state *state, 165 | struct print_event_args *args) 166 | { 167 | struct nl_cb *cb = nl_cb_alloc(iwpan_debug ? NL_CB_DEBUG : NL_CB_DEFAULT); 168 | if (!cb) { 169 | fprintf(stderr, "failed to allocate netlink callbacks\n"); 170 | return -ENOMEM; 171 | } 172 | nl_socket_set_cb(state->nl_sock, cb); 173 | /* No sequence checking for multicast messages */ 174 | nl_socket_disable_seq_check(state->nl_sock); 175 | /* Install print_event message handler */ 176 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_event, args); 177 | 178 | /* Loop waiting until interrupted by signal */ 179 | while (1) { 180 | int ret = nl_recvmsgs(state->nl_sock, cb); 181 | if (ret) { 182 | fprintf(stderr, "nl_recvmsgs return error %d\n", ret); 183 | break; 184 | } 185 | } 186 | /* Free allocated nl_cb structure */ 187 | nl_cb_put(cb); 188 | return 0; 189 | } 190 | 191 | static int print_events(struct nl802154_state *state, 192 | struct nl_cb *cb, 193 | struct nl_msg *msg, 194 | int argc, char **argv, 195 | enum id_input id) 196 | { 197 | struct print_event_args args; 198 | int ret; 199 | 200 | memset(&args, 0, sizeof(args)); 201 | 202 | argc--; 203 | argv++; 204 | 205 | while (argc > 0) { 206 | if (strcmp(argv[0], "-f") == 0) 207 | args.frame = true; 208 | else if (strcmp(argv[0], "-t") == 0) 209 | args.time = true; 210 | else if (strcmp(argv[0], "-r") == 0) 211 | args.reltime = true; 212 | else 213 | return 1; 214 | argc--; 215 | argv++; 216 | } 217 | if (args.time && args.reltime) 218 | return 1; 219 | if (argc) 220 | return 1; 221 | 222 | /* Prepare reception of all multicast messages */ 223 | ret = __prepare_listen_events(state); 224 | if (ret) 225 | return ret; 226 | 227 | /* Read message loop */ 228 | return __do_listen_events(state, &args); 229 | } 230 | TOPLEVEL(monitor, "[-t|-r] [-f]", 0, 0, CIB_NONE, print_events, 231 | "Monitor events from the kernel.\n" 232 | "-t - print timestamp\n" 233 | "-r - print relative timestamp\n" 234 | "-f - print full frame for auth/assoc etc."); 235 | -------------------------------------------------------------------------------- /src/info.c: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2014 Alexander Aring 2 | // 3 | // SPDX-License-Identifier: ISC 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "nl802154.h" 16 | #include "nl_extras.h" 17 | #include "iwpan.h" 18 | 19 | static void print_minmax_handler(int min, int max) 20 | { 21 | int i; 22 | 23 | for (i = min; i <= max; i++) 24 | printf("%d,", i); 25 | 26 | /* TODO */ 27 | printf("\b \n"); 28 | } 29 | 30 | static void print_freq_handler(int channel_page, int channel) 31 | { 32 | float freq = 0; 33 | 34 | switch (channel_page) { 35 | case 0: 36 | if (channel == 0) { 37 | freq = 868.3; 38 | printf("%5.1f", freq); 39 | break; 40 | } else if (channel > 0 && channel < 11) { 41 | freq = 906 + 2 * (channel - 1); 42 | } else { 43 | freq = 2405 + 5 * (channel - 11); 44 | } 45 | printf("%5.0f", freq); 46 | break; 47 | case 1: 48 | if (channel == 0) { 49 | freq = 868.3; 50 | printf("%5.1f", freq); 51 | break; 52 | } else if (channel >= 1 && channel <= 10) { 53 | freq = 906 + 2 * (channel - 1); 54 | } 55 | printf("%5.0f", freq); 56 | break; 57 | case 2: 58 | if (channel == 0) { 59 | freq = 868.3; 60 | printf("%5.1f", freq); 61 | break; 62 | } else if (channel >= 1 && channel <= 10) { 63 | freq = 906 + 2 * (channel - 1); 64 | } 65 | printf("%5.0f", freq); 66 | break; 67 | case 3: 68 | if (channel >= 0 && channel <= 12) { 69 | freq = 2412 + 5 * channel; 70 | } else if (channel == 13) { 71 | freq = 2484; 72 | } 73 | printf("%4.0f", freq); 74 | break; 75 | case 4: 76 | switch (channel) { 77 | case 0: 78 | freq = 499.2; 79 | break; 80 | case 1: 81 | freq = 3494.4; 82 | break; 83 | case 2: 84 | freq = 3993.6; 85 | break; 86 | case 3: 87 | freq = 4492.8; 88 | break; 89 | case 4: 90 | freq = 3993.6; 91 | break; 92 | case 5: 93 | freq = 6489.6; 94 | break; 95 | case 6: 96 | freq = 6988.8; 97 | break; 98 | case 7: 99 | freq = 6489.6; 100 | break; 101 | case 8: 102 | freq = 7488.0; 103 | break; 104 | case 9: 105 | freq = 7987.2; 106 | break; 107 | case 10: 108 | freq = 8486.4; 109 | break; 110 | case 11: 111 | freq = 7987.2; 112 | break; 113 | case 12: 114 | freq = 8985.6; 115 | break; 116 | case 13: 117 | freq = 9484.8; 118 | break; 119 | case 14: 120 | freq = 9984.0; 121 | break; 122 | case 15: 123 | freq = 9484.8; 124 | break; 125 | } 126 | printf("%6.1f", freq); 127 | break; 128 | case 5: 129 | if (channel >= 0 && channel <= 3) { 130 | freq = 780 + 2 * channel; 131 | } else if (channel >= 4 && channel <= 7) { 132 | freq = 780 + 2 * (channel - 4); 133 | } 134 | printf("%3.0f", freq); 135 | break; 136 | case 6: 137 | if (channel >= 0 && channel <= 7) { 138 | freq = 951.2 + 0.6 * channel; 139 | } else if (channel >= 8 && channel <= 9) { 140 | freq = 954.4 + 0.2 * (channel - 8); 141 | } else if (channel >= 10 && channel <= 21) { 142 | freq = 951.1 + 0.4 * (channel - 10); 143 | } 144 | 145 | printf("%5.1f", freq); 146 | break; 147 | default: 148 | printf("Unknown"); 149 | break; 150 | } 151 | } 152 | 153 | static char cca_mode_buf[100]; 154 | 155 | static const char *print_cca_mode_handler(enum nl802154_cca_modes cca_mode, 156 | enum nl802154_cca_opts cca_opt) 157 | { 158 | switch (cca_mode) { 159 | case NL802154_CCA_ENERGY: 160 | sprintf(cca_mode_buf,"(%d) %s", cca_mode, "Energy above threshold"); 161 | break; 162 | case NL802154_CCA_CARRIER: 163 | sprintf(cca_mode_buf,"(%d) %s", cca_mode, "Carrier sense only"); 164 | break; 165 | case NL802154_CCA_ENERGY_CARRIER: 166 | switch (cca_opt) { 167 | case NL802154_CCA_OPT_ENERGY_CARRIER_AND: 168 | sprintf(cca_mode_buf, "(%d, cca_opt: %d) %s", cca_mode, cca_opt, 169 | "Carrier sense with energy above threshold (logical operator is 'and')"); 170 | break; 171 | case NL802154_CCA_OPT_ENERGY_CARRIER_OR: 172 | sprintf(cca_mode_buf, "(%d, cca_opt: %d) %s", cca_mode, cca_opt, 173 | "Carrier sense with energy above threshold (logical operator is 'or')"); 174 | break; 175 | default: 176 | sprintf(cca_mode_buf, "Unknown CCA option (%d) for CCA mode (%d)", 177 | cca_opt, cca_mode); 178 | break; 179 | } 180 | break; 181 | case NL802154_CCA_ALOHA: 182 | sprintf(cca_mode_buf,"(%d) %s", cca_mode, "ALOHA"); 183 | break; 184 | case NL802154_CCA_UWB_SHR: 185 | sprintf(cca_mode_buf,"(%d) %s", cca_mode, 186 | "UWB preamble sense based on the SHR of a frame"); 187 | break; 188 | case NL802154_CCA_UWB_MULTIPLEXED: 189 | sprintf(cca_mode_buf,"(%d) %s", cca_mode, 190 | "UWB preamble sense based on the packet with the multiplexed preamble"); 191 | break; 192 | default: 193 | sprintf(cca_mode_buf, "Unknown CCA mode (%d)", cca_mode); 194 | break; 195 | } 196 | return cca_mode_buf; 197 | } 198 | 199 | static const char *commands[NL802154_CMD_MAX + 1] = { 200 | [NL802154_CMD_UNSPEC] = "unspec", 201 | [NL802154_CMD_GET_WPAN_PHY] = "get_wpan_phy", 202 | [NL802154_CMD_SET_WPAN_PHY] = "set_wpan_phy", 203 | [NL802154_CMD_NEW_WPAN_PHY] = "new_wpan_phy", 204 | [NL802154_CMD_DEL_WPAN_PHY] = "del_wpan_phy", 205 | [NL802154_CMD_GET_INTERFACE] = "get_interface", 206 | [NL802154_CMD_SET_INTERFACE] = "set_interface", 207 | [NL802154_CMD_NEW_INTERFACE] = "new_interface", 208 | [NL802154_CMD_DEL_INTERFACE] = "del_interface", 209 | [NL802154_CMD_SET_CHANNEL] = "set_channel", 210 | [NL802154_CMD_SET_PAN_ID] = "set_pan_id", 211 | [NL802154_CMD_SET_SHORT_ADDR] = "set_short_addr", 212 | [NL802154_CMD_SET_TX_POWER] = "set_tx_power", 213 | [NL802154_CMD_SET_CCA_MODE] = "set_cca_mode", 214 | [NL802154_CMD_SET_CCA_ED_LEVEL] = "set_cca_ed_level", 215 | [NL802154_CMD_SET_MAX_FRAME_RETRIES] = "set_max_frame_retries", 216 | [NL802154_CMD_SET_BACKOFF_EXPONENT] = "set_backoff_exponent", 217 | [NL802154_CMD_SET_MAX_CSMA_BACKOFFS] = "set_max_csma_backoffs", 218 | [NL802154_CMD_SET_LBT_MODE] = "set_lbt_mode", 219 | [NL802154_CMD_SET_ACKREQ_DEFAULT] = "set_ackreq_default", 220 | [NL802154_CMD_LIST_ASSOCIATIONS] = "list_associations", 221 | [NL802154_CMD_SET_MAX_ASSOCIATIONS] = "set_max_associations", 222 | [NL802154_CMD_ASSOCIATE] = "associate", 223 | [NL802154_CMD_DISASSOCIATE] = "disassociate", 224 | }; 225 | 226 | static char cmdbuf[100]; 227 | 228 | static const char *command_name(enum nl802154_commands cmd) 229 | { 230 | if (cmd <= NL802154_CMD_MAX && commands[cmd]) 231 | return commands[cmd]; 232 | 233 | sprintf(cmdbuf, "Unknown command (%d)", cmd); 234 | return cmdbuf; 235 | } 236 | 237 | static int print_phy_handler(struct nl_msg *msg, void *arg) 238 | { 239 | struct nlattr *tb_msg[NL802154_ATTR_MAX + 1]; 240 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); 241 | int rem_page, i, ret; 242 | int64_t phy_id = -1; 243 | bool print_name = true; 244 | struct nlattr *nl_page; 245 | enum nl802154_cca_modes cca_mode; 246 | 247 | nla_parse(tb_msg, NL802154_ATTR_MAX, genlmsg_attrdata(gnlh, 0), 248 | genlmsg_attrlen(gnlh, 0), NULL); 249 | 250 | if (tb_msg[NL802154_ATTR_WPAN_PHY]) { 251 | if (nla_get_u32(tb_msg[NL802154_ATTR_WPAN_PHY]) == phy_id) 252 | print_name = false; 253 | phy_id = nla_get_u32(tb_msg[NL802154_ATTR_WPAN_PHY]); 254 | } 255 | if (print_name && tb_msg[NL802154_ATTR_WPAN_PHY_NAME]) 256 | printf("wpan_phy %s\n", nla_get_string(tb_msg[NL802154_ATTR_WPAN_PHY_NAME])); 257 | 258 | /* TODO remove this handling it's deprecated */ 259 | if (tb_msg[NL802154_ATTR_CHANNELS_SUPPORTED]) { 260 | unsigned char page = 0; 261 | unsigned long channel; 262 | printf("supported channels:\n"); 263 | nla_for_each_nested(nl_page, 264 | tb_msg[NL802154_ATTR_CHANNELS_SUPPORTED], 265 | rem_page) { 266 | channel = nla_get_u32(nl_page); 267 | if (channel) { 268 | printf("\tpage %d: ", page); 269 | for (i = 0; i <= 31; i++) { 270 | if (channel & 0x1) 271 | printf("%d,", i); 272 | channel >>= 1; 273 | } 274 | /* TODO hack use sprintf here */ 275 | printf("\b \b\n"); 276 | } 277 | page++; 278 | } 279 | } 280 | 281 | if (tb_msg[NL802154_ATTR_PAGE]) 282 | printf("current_page: %d\n", nla_get_u8(tb_msg[NL802154_ATTR_PAGE])); 283 | 284 | if (tb_msg[NL802154_ATTR_CHANNEL] && 285 | tb_msg[NL802154_ATTR_PAGE]) { 286 | printf("current_channel: %d, ", nla_get_u8(tb_msg[NL802154_ATTR_CHANNEL])); 287 | print_freq_handler(nla_get_u8(tb_msg[NL802154_ATTR_PAGE]), 288 | nla_get_u8(tb_msg[NL802154_ATTR_CHANNEL])); 289 | printf(" MHz\n"); 290 | } 291 | 292 | if (tb_msg[NL802154_ATTR_CCA_MODE]) { 293 | enum nl802154_cca_opts cca_opt = NL802154_CCA_OPT_ATTR_MAX; 294 | if (tb_msg[NL802154_ATTR_CCA_OPT]) 295 | cca_opt = nla_get_u32(tb_msg[NL802154_ATTR_CCA_OPT]); 296 | 297 | cca_mode = nla_get_u32(tb_msg[NL802154_ATTR_CCA_MODE]); 298 | 299 | printf("cca_mode: %s", print_cca_mode_handler(cca_mode, cca_opt)); 300 | printf("\n"); 301 | } 302 | 303 | if (tb_msg[NL802154_ATTR_CCA_ED_LEVEL]) 304 | printf("cca_ed_level: %.3g\n", MBM_TO_DBM(nla_get_s32(tb_msg[NL802154_ATTR_CCA_ED_LEVEL]))); 305 | 306 | if (tb_msg[NL802154_ATTR_TX_POWER]) 307 | printf("tx_power: %.3g\n", MBM_TO_DBM(nla_get_s32(tb_msg[NL802154_ATTR_TX_POWER]))); 308 | 309 | if (tb_msg[NL802154_ATTR_WPAN_PHY_CAPS]) { 310 | struct nlattr *tb_caps[NL802154_CAP_ATTR_MAX + 1]; 311 | /* TODO fix netlink lib that we can use NLA_S32 here 312 | * see function validate_nla line if (pt->type > NLA_TYPE_MAX) */ 313 | static struct nla_policy caps_policy[NL802154_CAP_ATTR_MAX + 1] = { 314 | [NL802154_CAP_ATTR_CHANNELS] = { .type = NLA_NESTED }, 315 | [NL802154_CAP_ATTR_TX_POWERS] = { .type = NLA_NESTED }, 316 | [NL802154_CAP_ATTR_CCA_ED_LEVELS] = { .type = NLA_NESTED }, 317 | [NL802154_CAP_ATTR_CCA_MODES] = { .type = NLA_NESTED }, 318 | [NL802154_CAP_ATTR_CCA_OPTS] = { .type = NLA_NESTED }, 319 | [NL802154_CAP_ATTR_MIN_MINBE] = { .type = NLA_U8 }, 320 | [NL802154_CAP_ATTR_MAX_MINBE] = { .type = NLA_U8 }, 321 | [NL802154_CAP_ATTR_MIN_MAXBE] = { .type = NLA_U8 }, 322 | [NL802154_CAP_ATTR_MAX_MAXBE] = { .type = NLA_U8 }, 323 | [NL802154_CAP_ATTR_MIN_CSMA_BACKOFFS] = { .type = NLA_U8 }, 324 | [NL802154_CAP_ATTR_MAX_CSMA_BACKOFFS] = { .type = NLA_U8 }, 325 | [NL802154_CAP_ATTR_MIN_FRAME_RETRIES] = { .type = NLA_U8 }, 326 | [NL802154_CAP_ATTR_MAX_FRAME_RETRIES] = { .type = NLA_U8 }, 327 | [NL802154_CAP_ATTR_IFTYPES] = { .type = NLA_NESTED }, 328 | [NL802154_CAP_ATTR_LBT] = { .type = NLA_U32 }, 329 | }; 330 | 331 | printf("capabilities:\n"); 332 | 333 | ret = nla_parse_nested(tb_caps, NL802154_CAP_ATTR_MAX, 334 | tb_msg[NL802154_ATTR_WPAN_PHY_CAPS], 335 | caps_policy); 336 | if (ret) { 337 | printf("failed to parse caps\n"); 338 | return -EIO; 339 | } 340 | 341 | if (tb_caps[NL802154_CAP_ATTR_IFTYPES]) { 342 | struct nlattr *nl_iftypes; 343 | int rem_iftypes; 344 | printf("\tiftypes: "); 345 | nla_for_each_nested(nl_iftypes, 346 | tb_caps[NL802154_CAP_ATTR_IFTYPES], 347 | rem_iftypes) 348 | printf("%s,", iftype_name(nla_type(nl_iftypes))); 349 | /* TODO */ 350 | printf("\b \n"); 351 | } 352 | 353 | if (tb_caps[NL802154_CAP_ATTR_CHANNELS]) { 354 | int counter = 0; 355 | int rem_pages; 356 | struct nlattr *nl_pages; 357 | printf("\tchannels:\n"); 358 | nla_for_each_nested(nl_pages, tb_caps[NL802154_CAP_ATTR_CHANNELS], 359 | rem_pages) { 360 | int rem_channels; 361 | struct nlattr *nl_channels; 362 | counter = 0; 363 | printf("\t\tpage %d: ", nla_type(nl_pages)); 364 | nla_for_each_nested(nl_channels, nl_pages, rem_channels) { 365 | if (counter % 3 == 0) { 366 | printf("\n\t\t\t[%2d] ", nla_type(nl_channels)); 367 | print_freq_handler(nla_type(nl_pages), nla_type(nl_channels)); 368 | printf(" MHz, "); 369 | } else { 370 | printf("[%2d] ", nla_type(nl_channels)); 371 | print_freq_handler(nla_type(nl_pages), nla_type(nl_channels)); 372 | printf(" MHz, "); 373 | } 374 | counter++; 375 | } 376 | /* TODO hack use sprintf here */ 377 | printf("\b\b \b\n"); 378 | } 379 | } 380 | 381 | if (tb_caps[NL802154_CAP_ATTR_TX_POWERS]) { 382 | int rem_pwrs; 383 | int counter = 0; 384 | struct nlattr *nl_pwrs; 385 | 386 | printf("\ttx_powers: "); 387 | nla_for_each_nested(nl_pwrs, tb_caps[NL802154_CAP_ATTR_TX_POWERS], rem_pwrs) { 388 | if (counter % 6 == 0) { 389 | printf("\n\t\t\t%.3g dBm, ", MBM_TO_DBM(nla_get_s32(nl_pwrs))); 390 | } else { 391 | printf("%.3g dBm, ", MBM_TO_DBM(nla_get_s32(nl_pwrs))); 392 | } 393 | counter++; 394 | } 395 | /* TODO */ 396 | printf("\b \n"); 397 | } 398 | 399 | if (tb_caps[NL802154_CAP_ATTR_CCA_ED_LEVELS]) { 400 | int rem_levels; 401 | int counter = 0; 402 | struct nlattr *nl_levels; 403 | 404 | printf("\tcca_ed_levels: "); 405 | nla_for_each_nested(nl_levels, tb_caps[NL802154_CAP_ATTR_CCA_ED_LEVELS], rem_levels) { 406 | if (counter % 6 == 0) { 407 | printf("\n\t\t\t%.3g dBm, ", MBM_TO_DBM(nla_get_s32(nl_levels))); 408 | } else { 409 | printf("%.3g dBm, ", MBM_TO_DBM(nla_get_s32(nl_levels))); 410 | } 411 | counter++; 412 | } 413 | /* TODO */ 414 | printf("\b \n"); 415 | } 416 | 417 | if (tb_caps[NL802154_CAP_ATTR_CCA_MODES]) { 418 | struct nlattr *nl_cca_modes; 419 | int rem_cca_modes; 420 | printf("\tcca_modes: "); 421 | nla_for_each_nested(nl_cca_modes, 422 | tb_caps[NL802154_CAP_ATTR_CCA_MODES], 423 | rem_cca_modes) { 424 | /* Loop through all CCA options only if it is a 425 | * CCA mode that takes CCA options into 426 | * consideration. 427 | */ 428 | if (tb_caps[NL802154_CAP_ATTR_CCA_OPTS] && 429 | nla_type(nl_cca_modes) == NL802154_CCA_ENERGY_CARRIER) { 430 | struct nlattr *nl_cca_opts; 431 | int rem_cca_opts; 432 | 433 | nla_for_each_nested(nl_cca_opts, 434 | tb_caps[NL802154_CAP_ATTR_CCA_OPTS], 435 | rem_cca_opts) { 436 | printf("\n\t\t%s", 437 | print_cca_mode_handler( 438 | nla_type(nl_cca_modes), 439 | nla_type(nl_cca_opts))); 440 | } 441 | } else { 442 | printf("\n\t\t%s", 443 | print_cca_mode_handler( 444 | nla_type(nl_cca_modes), 445 | NL802154_CCA_OPT_ATTR_MAX)); 446 | 447 | } 448 | } 449 | /* TODO */ 450 | printf("\n"); 451 | } 452 | 453 | if (tb_caps[NL802154_CAP_ATTR_MIN_MINBE] && 454 | tb_caps[NL802154_CAP_ATTR_MAX_MINBE] && 455 | tb_caps[NL802154_CAP_ATTR_MIN_MAXBE] && 456 | tb_caps[NL802154_CAP_ATTR_MAX_MAXBE]) { 457 | printf("\tmin_be: "); 458 | print_minmax_handler(nla_get_u8(tb_caps[NL802154_CAP_ATTR_MIN_MINBE]), 459 | nla_get_u8(tb_caps[NL802154_CAP_ATTR_MAX_MINBE])); 460 | printf("\tmax_be: "); 461 | print_minmax_handler(nla_get_u8(tb_caps[NL802154_CAP_ATTR_MIN_MAXBE]), 462 | nla_get_u8(tb_caps[NL802154_CAP_ATTR_MAX_MAXBE])); 463 | } 464 | 465 | if (tb_caps[NL802154_CAP_ATTR_MIN_CSMA_BACKOFFS] && 466 | tb_caps[NL802154_CAP_ATTR_MAX_CSMA_BACKOFFS]) { 467 | printf("\tcsma_backoffs: "); 468 | print_minmax_handler(nla_get_u8(tb_caps[NL802154_CAP_ATTR_MIN_CSMA_BACKOFFS]), 469 | nla_get_u8(tb_caps[NL802154_CAP_ATTR_MAX_CSMA_BACKOFFS])); 470 | } 471 | 472 | if (tb_caps[NL802154_CAP_ATTR_MIN_FRAME_RETRIES] && 473 | tb_caps[NL802154_CAP_ATTR_MAX_FRAME_RETRIES]) { 474 | printf("\tframe_retries: "); 475 | print_minmax_handler(nla_get_s8(tb_caps[NL802154_CAP_ATTR_MIN_FRAME_RETRIES]), 476 | nla_get_s8(tb_caps[NL802154_CAP_ATTR_MAX_FRAME_RETRIES])); 477 | } 478 | 479 | if (tb_caps[NL802154_CAP_ATTR_LBT]) { 480 | printf("\tlbt: "); 481 | switch (nla_get_u32(tb_caps[NL802154_CAP_ATTR_LBT])) { 482 | case NL802154_SUPPORTED_BOOL_FALSE: 483 | printf("false\n"); 484 | break; 485 | case NL802154_SUPPORTED_BOOL_TRUE: 486 | printf("true\n"); 487 | break; 488 | case NL802154_SUPPORTED_BOOL_BOTH: 489 | printf("false,true\n"); 490 | break; 491 | default: 492 | printf("unknown\n"); 493 | break; 494 | } 495 | } 496 | } 497 | return 0; 498 | } 499 | 500 | static int handle_info(struct nl802154_state *state, 501 | struct nl_cb *cb, 502 | struct nl_msg *msg, 503 | int argc, char **argv, 504 | enum id_input id) 505 | { 506 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_phy_handler, NULL); 507 | 508 | return 0; 509 | } 510 | 511 | __COMMAND(NULL, info, "info", NULL, NL802154_CMD_GET_WPAN_PHY, 0, 0, CIB_PHY, handle_info, 512 | "Show capabilities for the specified wireless device.", NULL); 513 | TOPLEVEL(list, NULL, NL802154_CMD_GET_WPAN_PHY, NLM_F_DUMP, CIB_NONE, handle_info, 514 | "List all wireless devices and their capabilities."); 515 | TOPLEVEL(phy, NULL, NL802154_CMD_GET_WPAN_PHY, NLM_F_DUMP, CIB_NONE, handle_info, NULL); 516 | -------------------------------------------------------------------------------- /src/interface.c: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2014 Alexander Aring 2 | // 3 | // SPDX-License-Identifier: ISC 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "nl802154.h" 18 | #include "nl_extras.h" 19 | #include "iwpan.h" 20 | 21 | SECTION(interface); 22 | 23 | static char iftypebuf[100]; 24 | 25 | const char *iftype_name(enum nl802154_iftype iftype) 26 | { 27 | switch (iftype) { 28 | case NL802154_IFTYPE_MONITOR: 29 | return "monitor"; 30 | case NL802154_IFTYPE_NODE: 31 | return "node"; 32 | case NL802154_IFTYPE_COORD: 33 | return "coordinator"; 34 | default: 35 | sprintf(iftypebuf, "Invalid iftype (%d)", iftype); 36 | return iftypebuf; 37 | } 38 | } 39 | 40 | /* for help */ 41 | #define IFACE_TYPES "Valid interface types are: node, monitor, coordinator." 42 | 43 | /* return 0 if ok, internal error otherwise */ 44 | static int get_if_type(int *argc, char ***argv, enum nl802154_iftype *type, 45 | bool need_type) 46 | { 47 | char *tpstr; 48 | 49 | if (*argc < 1 + !!need_type) 50 | return 1; 51 | 52 | if (need_type && strcmp((*argv)[0], "type")) 53 | return 1; 54 | 55 | tpstr = (*argv)[!!need_type]; 56 | *argc -= 1 + !!need_type; 57 | *argv += 1 + !!need_type; 58 | 59 | if (strcmp(tpstr, "node") == 0) { 60 | *type = NL802154_IFTYPE_NODE; 61 | return 0; 62 | } else if (strcmp(tpstr, "monitor") == 0) { 63 | *type = NL802154_IFTYPE_MONITOR; 64 | return 0; 65 | } else if (strcmp(tpstr, "coordinator") == 0) { 66 | *type = NL802154_IFTYPE_COORD; 67 | return 0; 68 | } 69 | 70 | fprintf(stderr, "invalid interface type %s\n", tpstr); 71 | return 2; 72 | } 73 | 74 | #define EUI64_ALEN 8 75 | 76 | static int extendedaddr_a2n(unsigned char *mac_addr, char *arg) 77 | { 78 | int i; 79 | 80 | for (i = 0; i < EUI64_ALEN ; i++) { 81 | int temp; 82 | char *cp = strchr(arg, ':'); 83 | if (cp) { 84 | *cp = 0; 85 | cp++; 86 | } 87 | if (sscanf(arg, "%x", &temp) != 1) 88 | return -1; 89 | if (temp < 0 || temp > 255) 90 | return -1; 91 | 92 | mac_addr[EUI64_ALEN - 1 - i] = temp; 93 | if (!cp) 94 | break; 95 | arg = cp; 96 | } 97 | if (i < EUI64_ALEN - 1) 98 | return -1; 99 | 100 | return 0; 101 | } 102 | 103 | /* return 0 if ok, internal error otherwise */ 104 | static int get_eui64(int *argc, char ***argv, void *eui64) 105 | { 106 | int ret; 107 | 108 | if (*argc < 1) 109 | return 0; 110 | 111 | ret = extendedaddr_a2n(eui64, (*argv)[0]); 112 | if (ret) { 113 | fprintf(stderr, "invalid extended address\n"); 114 | return 2; 115 | } 116 | 117 | 118 | *argc -= 1; 119 | *argv += 1; 120 | 121 | return 0; 122 | } 123 | 124 | static int handle_interface_add(struct nl802154_state *state, 125 | struct nl_cb *cb, 126 | struct nl_msg *msg, 127 | int argc, char **argv, 128 | enum id_input id) 129 | { 130 | char *name; 131 | enum nl802154_iftype type; 132 | uint64_t eui64 = 0; 133 | int tpset; 134 | 135 | if (argc < 1) 136 | return 1; 137 | 138 | name = argv[0]; 139 | argc--; 140 | argv++; 141 | 142 | tpset = get_if_type(&argc, &argv, &type, true); 143 | if (tpset) 144 | return tpset; 145 | 146 | tpset = get_eui64(&argc, &argv, &eui64); 147 | if (tpset) 148 | return tpset; 149 | 150 | if (argc) 151 | return 1; 152 | 153 | NLA_PUT_STRING(msg, NL802154_ATTR_IFNAME, name); 154 | NLA_PUT_U32(msg, NL802154_ATTR_IFTYPE, type); 155 | NLA_PUT_U64(msg, NL802154_ATTR_EXTENDED_ADDR, eui64); 156 | 157 | return 0; 158 | 159 | nla_put_failure: 160 | return -ENOBUFS; 161 | } 162 | COMMAND(interface, add, " type [extended address ]", 163 | NL802154_CMD_NEW_INTERFACE, 0, CIB_PHY, handle_interface_add, 164 | "Add a new virtual interface with the given configuration.\n" 165 | IFACE_TYPES "\n\n"); 166 | COMMAND(interface, add, " type [extended address ]", 167 | NL802154_CMD_NEW_INTERFACE, 0, CIB_NETDEV, handle_interface_add, NULL); 168 | 169 | static int handle_interface_del(struct nl802154_state *state, 170 | struct nl_cb *cb, 171 | struct nl_msg *msg, 172 | int argc, char **argv, 173 | enum id_input id) 174 | { 175 | return 0; 176 | } 177 | TOPLEVEL(del, NULL, NL802154_CMD_DEL_INTERFACE, 0, CIB_NETDEV, handle_interface_del, 178 | "Remove this virtual interface"); 179 | HIDDEN(interface, del, NULL, NL802154_CMD_DEL_INTERFACE, 0, CIB_NETDEV, handle_interface_del); 180 | 181 | static int print_iface_handler(struct nl_msg *msg, void *arg) 182 | { 183 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); 184 | struct nlattr *tb_msg[NL802154_ATTR_MAX + 1]; 185 | unsigned int *wpan_phy = arg; 186 | const char *indent = ""; 187 | 188 | nla_parse(tb_msg, NL802154_ATTR_MAX, genlmsg_attrdata(gnlh, 0), 189 | genlmsg_attrlen(gnlh, 0), NULL); 190 | 191 | if (wpan_phy && tb_msg[NL802154_ATTR_WPAN_PHY]) { 192 | unsigned int thiswpan_phy = nla_get_u32(tb_msg[NL802154_ATTR_WPAN_PHY]); 193 | indent = "\t"; 194 | if (*wpan_phy != thiswpan_phy) 195 | printf("phy#%d\n", thiswpan_phy); 196 | *wpan_phy = thiswpan_phy; 197 | } 198 | 199 | if (tb_msg[NL802154_ATTR_IFNAME]) 200 | printf("%sInterface %s\n", indent, nla_get_string(tb_msg[NL802154_ATTR_IFNAME])); 201 | else 202 | printf("%sUnnamed/non-netdev interface\n", indent); 203 | 204 | if (tb_msg[NL802154_ATTR_IFINDEX]) 205 | printf("%s\tifindex %d\n", indent, nla_get_u32(tb_msg[NL802154_ATTR_IFINDEX])); 206 | if (tb_msg[NL802154_ATTR_WPAN_DEV]) 207 | printf("%s\twpan_dev 0x%llx\n", indent, 208 | (unsigned long long)nla_get_u64(tb_msg[NL802154_ATTR_WPAN_DEV])); 209 | if (tb_msg[NL802154_ATTR_EXTENDED_ADDR]) 210 | printf("%s\textended_addr 0x%016" PRIx64 "\n", indent, 211 | le64toh(nla_get_u64(tb_msg[NL802154_ATTR_EXTENDED_ADDR]))); 212 | if (tb_msg[NL802154_ATTR_SHORT_ADDR]) 213 | printf("%s\tshort_addr 0x%04x\n", indent, 214 | le16toh(nla_get_u16(tb_msg[NL802154_ATTR_SHORT_ADDR]))); 215 | if (tb_msg[NL802154_ATTR_PAN_ID]) 216 | printf("%s\tpan_id 0x%04x\n", indent, 217 | le16toh(nla_get_u16(tb_msg[NL802154_ATTR_PAN_ID]))); 218 | if (tb_msg[NL802154_ATTR_IFTYPE]) 219 | printf("%s\ttype %s\n", indent, iftype_name(nla_get_u32(tb_msg[NL802154_ATTR_IFTYPE]))); 220 | if (tb_msg[NL802154_ATTR_MAX_FRAME_RETRIES]) 221 | printf("%s\tmax_frame_retries %d\n", indent, nla_get_s8(tb_msg[NL802154_ATTR_MAX_FRAME_RETRIES])); 222 | if (tb_msg[NL802154_ATTR_MIN_BE]) 223 | printf("%s\tmin_be %d\n", indent, nla_get_u8(tb_msg[NL802154_ATTR_MIN_BE])); 224 | if (tb_msg[NL802154_ATTR_MAX_BE]) 225 | printf("%s\tmax_be %d\n", indent, nla_get_u8(tb_msg[NL802154_ATTR_MAX_BE])); 226 | if (tb_msg[NL802154_ATTR_MAX_CSMA_BACKOFFS]) 227 | printf("%s\tmax_csma_backoffs %d\n", indent, nla_get_u8(tb_msg[NL802154_ATTR_MAX_CSMA_BACKOFFS])); 228 | if (tb_msg[NL802154_ATTR_LBT_MODE]) 229 | printf("%s\tlbt %d\n", indent, nla_get_u8(tb_msg[NL802154_ATTR_LBT_MODE])); 230 | if (tb_msg[NL802154_ATTR_ACKREQ_DEFAULT]) 231 | printf("%s\tackreq_default %d\n", indent, nla_get_u8(tb_msg[NL802154_ATTR_ACKREQ_DEFAULT])); 232 | 233 | return NL_SKIP; 234 | } 235 | 236 | static int handle_interface_info(struct nl802154_state *state, 237 | struct nl_cb *cb, 238 | struct nl_msg *msg, 239 | int argc, char **argv, 240 | enum id_input id) 241 | { 242 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_iface_handler, NULL); 243 | return 0; 244 | } 245 | TOPLEVEL(info, NULL, NL802154_CMD_GET_INTERFACE, 0, CIB_NETDEV, handle_interface_info, 246 | "Show information for this interface."); 247 | 248 | static unsigned int dev_dump_wpan_phy; 249 | 250 | static int handle_dev_dump(struct nl802154_state *state, 251 | struct nl_cb *cb, 252 | struct nl_msg *msg, 253 | int argc, char **argv, 254 | enum id_input id) 255 | { 256 | dev_dump_wpan_phy = -1; 257 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_iface_handler, &dev_dump_wpan_phy); 258 | return 0; 259 | } 260 | TOPLEVEL(dev, NULL, NL802154_CMD_GET_INTERFACE, NLM_F_DUMP, CIB_NONE, handle_dev_dump, 261 | "List all network interfaces for wireless hardware."); 262 | -------------------------------------------------------------------------------- /src/iwpan.c: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2014 Alexander Aring 2 | // 3 | // SPDX-License-Identifier: ISC 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "nl802154.h" 22 | #include "iwpan.h" 23 | #include "config.h" 24 | 25 | /* TODO libnl 1.x compatibility code */ 26 | 27 | int iwpan_debug = 0; 28 | 29 | static int nl802154_init(struct nl802154_state *state) 30 | { 31 | int err; 32 | 33 | state->nl_sock = nl_socket_alloc(); 34 | if (!state->nl_sock) { 35 | fprintf(stderr, "Failed to allocate netlink socket.\n"); 36 | return -ENOMEM; 37 | } 38 | 39 | nl_socket_set_buffer_size(state->nl_sock, 8192, 8192); 40 | 41 | if (genl_connect(state->nl_sock)) { 42 | fprintf(stderr, "Failed to connect to generic netlink.\n"); 43 | err = -ENOLINK; 44 | goto out_handle_destroy; 45 | } 46 | 47 | state->nl802154_id = genl_ctrl_resolve(state->nl_sock, "nl802154"); 48 | if (state->nl802154_id < 0) { 49 | fprintf(stderr, "nl802154 not found.\n"); 50 | err = -ENOENT; 51 | goto out_handle_destroy; 52 | } 53 | 54 | return 0; 55 | 56 | out_handle_destroy: 57 | nl_socket_free(state->nl_sock); 58 | return err; 59 | } 60 | 61 | static void nl802154_cleanup(struct nl802154_state *state) 62 | { 63 | nl_socket_free(state->nl_sock); 64 | } 65 | 66 | static int cmd_size; 67 | 68 | extern struct cmd __start___cmd; 69 | extern struct cmd __stop___cmd; 70 | 71 | static void __usage_cmd(const struct cmd *cmd, char *indent, bool full) 72 | { 73 | const char *start, *lend, *end; 74 | 75 | printf("%s", indent); 76 | 77 | switch (cmd->idby) { 78 | case CIB_NONE: 79 | break; 80 | case CIB_PHY: 81 | printf("phy "); 82 | break; 83 | case CIB_NETDEV: 84 | printf("dev "); 85 | break; 86 | case CIB_WPAN_DEV: 87 | printf("wdev "); 88 | break; 89 | } 90 | if (cmd->parent && cmd->parent->name) 91 | printf("%s ", cmd->parent->name); 92 | printf("%s", cmd->name); 93 | 94 | if (cmd->args) { 95 | /* print line by line */ 96 | start = cmd->args; 97 | end = strchr(start, '\0'); 98 | printf(" "); 99 | do { 100 | lend = strchr(start, '\n'); 101 | if (!lend) 102 | lend = end; 103 | if (start != cmd->args) { 104 | printf("\t"); 105 | switch (cmd->idby) { 106 | case CIB_NONE: 107 | break; 108 | case CIB_PHY: 109 | printf("phy "); 110 | break; 111 | case CIB_NETDEV: 112 | printf("dev "); 113 | break; 114 | case CIB_WPAN_DEV: 115 | printf("wdev "); 116 | break; 117 | } 118 | if (cmd->parent && cmd->parent->name) 119 | printf("%s ", cmd->parent->name); 120 | printf("%s ", cmd->name); 121 | } 122 | printf("%.*s\n", (int)(lend - start), start); 123 | start = lend + 1; 124 | } while (end != lend); 125 | } else { 126 | printf("\n"); 127 | } 128 | 129 | if (!full || !cmd->help) 130 | return; 131 | 132 | /* hack */ 133 | if (strlen(indent)) 134 | indent = "\t\t"; 135 | else 136 | printf("\n"); 137 | 138 | /* print line by line */ 139 | start = cmd->help; 140 | end = strchr(start, '\0'); 141 | do { 142 | lend = strchr(start, '\n'); 143 | if (!lend) 144 | lend = end; 145 | printf("%s", indent); 146 | printf("%.*s\n", (int)(lend - start), start); 147 | start = lend + 1; 148 | } while (end != lend); 149 | 150 | printf("\n"); 151 | } 152 | 153 | #define for_each_cmd(_cmd) \ 154 | for (_cmd = &__start___cmd; _cmd < &__stop___cmd; \ 155 | _cmd = (const struct cmd *)((char *)_cmd + cmd_size)) 156 | 157 | static void usage_options(void) 158 | { 159 | printf("Options:\n"); 160 | printf("\t--debug\t\tenable netlink debugging\n"); 161 | } 162 | 163 | static const char *argv0; 164 | 165 | static void usage(int argc, char **argv) 166 | { 167 | const struct cmd *section, *cmd; 168 | bool full = argc >= 0; 169 | const char *sect_filt = NULL; 170 | const char *cmd_filt = NULL; 171 | 172 | if (argc > 0) 173 | sect_filt = argv[0]; 174 | 175 | if (argc > 1) 176 | cmd_filt = argv[1]; 177 | 178 | printf("Usage:\t%s [options] command\n", argv0); 179 | usage_options(); 180 | printf("\t--version\tshow version (" PACKAGE_VERSION ")\n"); 181 | printf("Commands:\n"); 182 | for_each_cmd(section) { 183 | if (section->parent) 184 | continue; 185 | 186 | if (sect_filt && strcmp(section->name, sect_filt)) 187 | continue; 188 | 189 | if (section->handler && !section->hidden) 190 | __usage_cmd(section, "\t", full); 191 | 192 | for_each_cmd(cmd) { 193 | if (section != cmd->parent) 194 | continue; 195 | if (!cmd->handler || cmd->hidden) 196 | continue; 197 | if (cmd_filt && strcmp(cmd->name, cmd_filt)) 198 | continue; 199 | __usage_cmd(cmd, "\t", full); 200 | } 201 | } 202 | printf("\nCommands that use the netdev ('dev') can also be given the\n" 203 | "'wdev' instead to identify the device.\n"); 204 | printf("\nYou can omit the 'phy' or 'dev' if " 205 | "the identification is unique,\n" 206 | "e.g. \"iwpan wpan0 info\" or \"iwpan phy0 info\". " 207 | "(Don't when scripting.)\n\n" 208 | "Do NOT screenscrape this tool, we don't " 209 | "consider its output stable.\n\n"); 210 | } 211 | 212 | static void usage_cmd(const struct cmd *cmd) 213 | { 214 | printf("Usage:\t%s [options] ", argv0); 215 | __usage_cmd(cmd, "", true); 216 | usage_options(); 217 | } 218 | 219 | static void version(void) 220 | { 221 | printf("iwpan version " PACKAGE_VERSION "\n"); 222 | } 223 | 224 | static int phy_lookup(char *name) 225 | { 226 | char buf[200]; 227 | int fd, pos; 228 | 229 | snprintf(buf, sizeof(buf), "/sys/class/ieee802154/%s/index", name); 230 | 231 | fd = open(buf, O_RDONLY); 232 | if (fd < 0) 233 | return -1; 234 | pos = read(fd, buf, sizeof(buf) - 1); 235 | if (pos < 0) { 236 | close(fd); 237 | return -1; 238 | } 239 | buf[pos] = '\0'; 240 | close(fd); 241 | return atoi(buf); 242 | } 243 | 244 | static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, 245 | void *arg) 246 | { 247 | int *ret = arg; 248 | *ret = err->error; 249 | return NL_STOP; 250 | } 251 | 252 | static int finish_handler(struct nl_msg *msg, void *arg) 253 | { 254 | int *ret = arg; 255 | *ret = 0; 256 | return NL_SKIP; 257 | } 258 | 259 | static int ack_handler(struct nl_msg *msg, void *arg) 260 | { 261 | int *ret = arg; 262 | *ret = 0; 263 | return NL_STOP; 264 | } 265 | 266 | static int __handle_cmd(struct nl802154_state *state, enum id_input idby, 267 | int argc, char **argv, const struct cmd **cmdout) 268 | { 269 | const struct cmd *cmd, *match = NULL, *sectcmd; 270 | struct nl_cb *cb; 271 | struct nl_cb *s_cb; 272 | struct nl_msg *msg; 273 | signed long long devidx = 0; 274 | int err, o_argc; 275 | const char *command, *section; 276 | char *tmp, **o_argv; 277 | enum command_identify_by command_idby = CIB_NONE; 278 | 279 | if (argc <= 1 && idby != II_NONE) 280 | return 1; 281 | 282 | o_argc = argc; 283 | o_argv = argv; 284 | 285 | switch (idby) { 286 | case II_PHY_IDX: 287 | command_idby = CIB_PHY; 288 | devidx = strtoul(*argv + 4, &tmp, 0); 289 | if (*tmp != '\0') 290 | return 1; 291 | argc--; 292 | argv++; 293 | break; 294 | case II_PHY_NAME: 295 | command_idby = CIB_PHY; 296 | devidx = phy_lookup(*argv); 297 | argc--; 298 | argv++; 299 | break; 300 | case II_NETDEV: 301 | command_idby = CIB_NETDEV; 302 | devidx = if_nametoindex(*argv); 303 | if (devidx == 0) 304 | devidx = -1; 305 | argc--; 306 | argv++; 307 | break; 308 | case II_WPAN_DEV: 309 | command_idby = CIB_WPAN_DEV; 310 | devidx = strtoll(*argv, &tmp, 0); 311 | if (*tmp != '\0') 312 | return 1; 313 | argc--; 314 | argv++; 315 | default: 316 | break; 317 | } 318 | 319 | if (devidx < 0) 320 | return -errno; 321 | 322 | section = *argv; 323 | argc--; 324 | argv++; 325 | 326 | for_each_cmd(sectcmd) { 327 | if (sectcmd->parent) 328 | continue; 329 | /* ok ... bit of a hack for the dupe 'info' section */ 330 | if (match && sectcmd->idby != command_idby) 331 | continue; 332 | if (strcmp(sectcmd->name, section) == 0) 333 | match = sectcmd; 334 | } 335 | 336 | sectcmd = match; 337 | match = NULL; 338 | if (!sectcmd) 339 | return 1; 340 | 341 | if (argc > 0) { 342 | command = *argv; 343 | 344 | for_each_cmd(cmd) { 345 | if (!cmd->handler) 346 | continue; 347 | if (cmd->parent != sectcmd) 348 | continue; 349 | /* 350 | * ignore mismatch id by, but allow WPAN_DEV 351 | * in place of NETDEV 352 | */ 353 | if (cmd->idby != command_idby && 354 | !(cmd->idby == CIB_NETDEV && 355 | command_idby == CIB_WPAN_DEV)) 356 | continue; 357 | if (strcmp(cmd->name, command)) 358 | continue; 359 | if (argc > 1 && !cmd->args) 360 | continue; 361 | match = cmd; 362 | break; 363 | } 364 | 365 | if (match) { 366 | argc--; 367 | argv++; 368 | } 369 | } 370 | 371 | if (match) 372 | cmd = match; 373 | else { 374 | /* Use the section itself, if possible. */ 375 | cmd = sectcmd; 376 | if (argc && !cmd->args) 377 | return 1; 378 | if (cmd->idby != command_idby && 379 | !(cmd->idby == CIB_NETDEV && command_idby == CIB_WPAN_DEV)) 380 | return 1; 381 | if (!cmd->handler) 382 | return 1; 383 | } 384 | 385 | if (cmd->selector) { 386 | cmd = cmd->selector(argc, argv); 387 | if (!cmd) 388 | return 1; 389 | } 390 | 391 | if (cmdout) 392 | *cmdout = cmd; 393 | 394 | if (!cmd->cmd) { 395 | argc = o_argc; 396 | argv = o_argv; 397 | return cmd->handler(state, NULL, NULL, argc, argv, idby); 398 | } 399 | 400 | msg = nlmsg_alloc(); 401 | if (!msg) { 402 | fprintf(stderr, "failed to allocate netlink message\n"); 403 | return 2; 404 | } 405 | 406 | cb = nl_cb_alloc(iwpan_debug ? NL_CB_DEBUG : NL_CB_DEFAULT); 407 | s_cb = nl_cb_alloc(iwpan_debug ? NL_CB_DEBUG : NL_CB_DEFAULT); 408 | if (!cb || !s_cb) { 409 | fprintf(stderr, "failed to allocate netlink callbacks\n"); 410 | err = 2; 411 | goto out_free_msg; 412 | } 413 | 414 | genlmsg_put(msg, 0, 0, state->nl802154_id, 0, 415 | cmd->nl_msg_flags, cmd->cmd, 0); 416 | 417 | switch (command_idby) { 418 | case CIB_PHY: 419 | NLA_PUT_U32(msg, NL802154_ATTR_WPAN_PHY, devidx); 420 | break; 421 | case CIB_NETDEV: 422 | NLA_PUT_U32(msg, NL802154_ATTR_IFINDEX, devidx); 423 | break; 424 | case CIB_WPAN_DEV: 425 | NLA_PUT_U64(msg, NL802154_ATTR_WPAN_DEV, devidx); 426 | break; 427 | default: 428 | break; 429 | } 430 | 431 | err = cmd->handler(state, cb, msg, argc, argv, idby); 432 | if (err) 433 | goto out; 434 | 435 | nl_socket_set_cb(state->nl_sock, s_cb); 436 | 437 | err = nl_send_auto_complete(state->nl_sock, msg); 438 | if (err < 0) 439 | goto out; 440 | 441 | err = 1; 442 | 443 | nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); 444 | nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); 445 | nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); 446 | 447 | while (err > 0) 448 | nl_recvmsgs(state->nl_sock, cb); 449 | out: 450 | nl_cb_put(cb); 451 | out_free_msg: 452 | nlmsg_free(msg); 453 | return err; 454 | nla_put_failure: 455 | fprintf(stderr, "building message failed\n"); 456 | return 2; 457 | } 458 | 459 | int handle_cmd(struct nl802154_state *state, enum id_input idby, 460 | int argc, char **argv) 461 | { 462 | return __handle_cmd(state, idby, argc, argv, NULL); 463 | } 464 | 465 | int main(int argc, char **argv) 466 | { 467 | struct nl802154_state nlstate; 468 | const struct cmd *cmd = NULL; 469 | int err; 470 | 471 | /* calculate command size including padding */ 472 | cmd_size = labs((long)&__section_set - (long)&__section_get); 473 | /* strip off self */ 474 | argc--; 475 | argv0 = *argv++; 476 | 477 | if (argc > 0 && strcmp(*argv, "--debug") == 0) { 478 | iwpan_debug = 1; 479 | argc--; 480 | argv++; 481 | } 482 | 483 | if (argc > 0 && strcmp(*argv, "--version") == 0) { 484 | version(); 485 | return 0; 486 | } 487 | 488 | /* need to treat "help" command specially so it works w/o nl802154 */ 489 | if (argc == 0 || strcmp(*argv, "help") == 0) { 490 | usage(argc - 1, argv + 1); 491 | return 0; 492 | } 493 | 494 | err = nl802154_init(&nlstate); 495 | if (err) 496 | return 1; 497 | 498 | if (strcmp(*argv, "dev") == 0 && argc > 1) { 499 | argc--; 500 | argv++; 501 | err = __handle_cmd(&nlstate, II_NETDEV, argc, argv, &cmd); 502 | } else if (strncmp(*argv, "phy", 3) == 0 && argc > 1) { 503 | if (strlen(*argv) == 3) { 504 | argc--; 505 | argv++; 506 | err = __handle_cmd(&nlstate, II_PHY_NAME, argc, argv, &cmd); 507 | } else if (*(*argv + 3) == '#') 508 | err = __handle_cmd(&nlstate, II_PHY_IDX, argc, argv, &cmd); 509 | else 510 | goto detect; 511 | } else if (strcmp(*argv, "wdev") == 0 && argc > 1) { 512 | argc--; 513 | argv++; 514 | err = __handle_cmd(&nlstate, II_WPAN_DEV, argc, argv, &cmd); 515 | } else { 516 | int idx; 517 | enum id_input idby = II_NONE; 518 | detect: 519 | if ((idx = if_nametoindex(argv[0])) != 0) 520 | idby = II_NETDEV; 521 | else if ((idx = phy_lookup(argv[0])) >= 0) 522 | idby = II_PHY_NAME; 523 | err = __handle_cmd(&nlstate, idby, argc, argv, &cmd); 524 | } 525 | 526 | if (err == 1) { 527 | if (cmd) 528 | usage_cmd(cmd); 529 | else 530 | usage(0, NULL); 531 | } else if (err < 0) 532 | fprintf(stderr, "command failed: %s (%d)\n", strerror(-err), err); 533 | 534 | nl802154_cleanup(&nlstate); 535 | 536 | return err; 537 | } 538 | -------------------------------------------------------------------------------- /src/iwpan.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2014 Alexander Aring 2 | // 3 | // SPDX-License-Identifier: ISC 4 | 5 | #ifndef __IWPAN_H 6 | #define __IWPAN_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "nl802154.h" 16 | 17 | /* TODO libnl1 compatibility */ 18 | //#define nl_sock nl_handle 19 | 20 | struct nl802154_state { 21 | struct nl_sock *nl_sock; 22 | int nl802154_id; 23 | }; 24 | 25 | enum command_identify_by { 26 | CIB_NONE, 27 | CIB_PHY, 28 | CIB_NETDEV, 29 | CIB_WPAN_DEV, 30 | }; 31 | 32 | enum id_input { 33 | II_NONE, 34 | II_NETDEV, 35 | II_PHY_NAME, 36 | II_PHY_IDX, 37 | II_WPAN_DEV, 38 | }; 39 | 40 | struct cmd { 41 | const char *name; 42 | const char *args; 43 | const char *help; 44 | const enum nl802154_commands cmd; 45 | int nl_msg_flags; 46 | int hidden; 47 | const enum command_identify_by idby; 48 | /* The handler should return a negative error code, 49 | * zero on success, 1 if the arguments were wrong 50 | * and the usage message should be displayed, 2 otherwise. 51 | */ 52 | int (*handler)(struct nl802154_state *state, 53 | struct nl_cb *cb, 54 | struct nl_msg *msg, 55 | int argc, char **argv, 56 | enum id_input id); 57 | const struct cmd *(*selector)(int argc, char **argv); 58 | const struct cmd *parent; 59 | }; 60 | 61 | #define __COMMAND(_section, _symname, _name, _args, _nlcmd, _flags, _hidden, _idby, _handler, _help, _sel)\ 62 | static struct cmd \ 63 | __cmd ## _ ## _symname ## _ ## _handler ## _ ## _nlcmd ## _ ## _idby ## _ ## _hidden\ 64 | __attribute__((used)) __attribute__((section("__cmd"))) = { \ 65 | .name = (_name), \ 66 | .args = (_args), \ 67 | .cmd = (_nlcmd), \ 68 | .nl_msg_flags = (_flags), \ 69 | .hidden = (_hidden), \ 70 | .idby = (_idby), \ 71 | .handler = (_handler), \ 72 | .help = (_help), \ 73 | .parent = _section, \ 74 | .selector = (_sel), \ 75 | } 76 | #define __ACMD(_section, _symname, _name, _args, _nlcmd, _flags, _hidden, _idby, _handler, _help, _sel, _alias)\ 77 | __COMMAND(_section, _symname, _name, _args, _nlcmd, _flags, _hidden, _idby, _handler, _help, _sel);\ 78 | static const struct cmd *_alias = &__cmd ## _ ## _symname ## _ ## _handler ## _ ## _nlcmd ## _ ## _idby ## _ ## _hidden 79 | #define COMMAND(section, name, args, cmd, flags, idby, handler, help) \ 80 | __COMMAND(&(__section ## _ ## section), name, #name, args, cmd, flags, 0, idby, handler, help, NULL) 81 | #define COMMAND_ALIAS(section, name, args, cmd, flags, idby, handler, help, selector, alias)\ 82 | __ACMD(&(__section ## _ ## section), name, #name, args, cmd, flags, 0, idby, handler, help, selector, alias) 83 | #define HIDDEN(section, name, args, cmd, flags, idby, handler) \ 84 | __COMMAND(&(__section ## _ ## section), name, #name, args, cmd, flags, 1, idby, handler, NULL, NULL) 85 | #define TOPLEVEL(_name, _args, _nlcmd, _flags, _idby, _handler, _help) \ 86 | struct cmd \ 87 | __section ## _ ## _name \ 88 | __attribute__((used)) __attribute__((section("__cmd"))) = { \ 89 | .name = (#_name), \ 90 | .args = (_args), \ 91 | .cmd = (_nlcmd), \ 92 | .nl_msg_flags = (_flags), \ 93 | .idby = (_idby), \ 94 | .handler = (_handler), \ 95 | .help = (_help), \ 96 | } 97 | 98 | #define SECTION(_name) \ 99 | struct cmd __section ## _ ## _name \ 100 | __attribute__((used)) __attribute__((section("__cmd"))) = { \ 101 | .name = (#_name), \ 102 | .hidden = 1, \ 103 | } 104 | 105 | #define DECLARE_SECTION(_name) \ 106 | extern struct cmd __section ## _ ## _name; 107 | 108 | #define DBM_TO_MBM(gain) \ 109 | ((int)(((float)gain) * 100)) 110 | #define MBM_TO_DBM(gain) \ 111 | ((float)(gain) / 100) 112 | 113 | int handle_cmd(struct nl802154_state *state, enum id_input idby, 114 | int argc, char **argv); 115 | 116 | DECLARE_SECTION(set); 117 | DECLARE_SECTION(get); 118 | 119 | const char *iftype_name(enum nl802154_iftype iftype); 120 | 121 | extern int iwpan_debug; 122 | 123 | #endif /* __IWPAN_H */ 124 | -------------------------------------------------------------------------------- /src/mac.c: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2014 Alexander Aring 2 | // 3 | // SPDX-License-Identifier: ISC 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "nl_extras.h" 17 | #include "nl802154.h" 18 | #include "iwpan.h" 19 | 20 | static int handle_pan_id_set(struct nl802154_state *state, 21 | struct nl_cb *cb, 22 | struct nl_msg *msg, 23 | int argc, char **argv, 24 | enum id_input id) 25 | { 26 | unsigned long pan_id; 27 | char *end; 28 | 29 | if (argc < 1) 30 | return 1; 31 | 32 | /* PAN ID */ 33 | pan_id = strtoul(argv[0], &end, 0); 34 | if (*end != '\0') 35 | return 1; 36 | 37 | if (pan_id > UINT16_MAX) 38 | return 1; 39 | 40 | NLA_PUT_U16(msg, NL802154_ATTR_PAN_ID, htole16(pan_id)); 41 | 42 | return 0; 43 | 44 | nla_put_failure: 45 | return -ENOBUFS; 46 | } 47 | COMMAND(set, pan_id, "", 48 | NL802154_CMD_SET_PAN_ID, 0, CIB_NETDEV, handle_pan_id_set, NULL); 49 | 50 | static int handle_short_addr_set(struct nl802154_state *state, 51 | struct nl_cb *cb, 52 | struct nl_msg *msg, 53 | int argc, char **argv, 54 | enum id_input id) 55 | { 56 | unsigned long short_addr; 57 | char *end; 58 | 59 | if (argc < 1) 60 | return 1; 61 | 62 | /* SHORT ADDR */ 63 | short_addr = strtoul(argv[0], &end, 0); 64 | if (*end != '\0') 65 | return 1; 66 | 67 | if (short_addr > UINT16_MAX) 68 | return 1; 69 | 70 | NLA_PUT_U16(msg, NL802154_ATTR_SHORT_ADDR, htole16(short_addr)); 71 | 72 | return 0; 73 | 74 | nla_put_failure: 75 | return -ENOBUFS; 76 | } 77 | COMMAND(set, short_addr, "", 78 | NL802154_CMD_SET_SHORT_ADDR, 0, CIB_NETDEV, handle_short_addr_set, NULL); 79 | 80 | static int handle_max_frame_retries_set(struct nl802154_state *state, 81 | struct nl_cb *cb, 82 | struct nl_msg *msg, 83 | int argc, char **argv, 84 | enum id_input id) 85 | { 86 | long retries; 87 | char *end; 88 | 89 | if (argc < 1) 90 | return 1; 91 | 92 | /* RETRIES */ 93 | retries = strtol(argv[0], &end, 0); 94 | if (*end != '\0') 95 | return 1; 96 | 97 | if (retries > INT8_MAX) 98 | return 1; 99 | 100 | NLA_PUT_S8(msg, NL802154_ATTR_MAX_FRAME_RETRIES, retries); 101 | 102 | return 0; 103 | 104 | nla_put_failure: 105 | return -ENOBUFS; 106 | } 107 | COMMAND(set, max_frame_retries, "", 108 | NL802154_CMD_SET_MAX_FRAME_RETRIES, 0, CIB_NETDEV, 109 | handle_max_frame_retries_set, NULL); 110 | 111 | static int handle_backoff_exponent(struct nl802154_state *state, 112 | struct nl_cb *cb, 113 | struct nl_msg *msg, 114 | int argc, char **argv, 115 | enum id_input id) 116 | { 117 | unsigned long max_be; 118 | unsigned long min_be; 119 | char *end; 120 | 121 | if (argc < 2) 122 | return 1; 123 | 124 | /* MIN_BE */ 125 | min_be = strtoul(argv[0], &end, 0); 126 | if (*end != '\0') 127 | return 1; 128 | 129 | /* MAX_BE */ 130 | max_be = strtoul(argv[1], &end, 0); 131 | if (*end != '\0') 132 | return 1; 133 | 134 | if (min_be > UINT8_MAX || max_be > UINT8_MAX) 135 | return 1; 136 | 137 | NLA_PUT_U8(msg, NL802154_ATTR_MIN_BE, min_be); 138 | NLA_PUT_U8(msg, NL802154_ATTR_MAX_BE, max_be); 139 | 140 | return 0; 141 | 142 | nla_put_failure: 143 | return -ENOBUFS; 144 | } 145 | COMMAND(set, backoff_exponents, " ", 146 | NL802154_CMD_SET_BACKOFF_EXPONENT, 0, CIB_NETDEV, 147 | handle_backoff_exponent, NULL); 148 | 149 | static int handle_max_csma_backoffs(struct nl802154_state *state, 150 | struct nl_cb *cb, 151 | struct nl_msg *msg, 152 | int argc, char **argv, 153 | enum id_input id) 154 | { 155 | unsigned long backoffs; 156 | char *end; 157 | 158 | if (argc < 1) 159 | return 1; 160 | 161 | /* BACKOFFS */ 162 | backoffs = strtoul(argv[0], &end, 0); 163 | if (*end != '\0') 164 | return 1; 165 | 166 | if (backoffs > UINT8_MAX) 167 | return 1; 168 | 169 | NLA_PUT_U8(msg, NL802154_ATTR_MAX_CSMA_BACKOFFS, backoffs); 170 | 171 | return 0; 172 | 173 | nla_put_failure: 174 | return -ENOBUFS; 175 | } 176 | COMMAND(set, max_csma_backoffs, "", 177 | NL802154_CMD_SET_MAX_CSMA_BACKOFFS, 0, CIB_NETDEV, 178 | handle_max_csma_backoffs, NULL); 179 | 180 | 181 | static int handle_lbt_mode(struct nl802154_state *state, 182 | struct nl_cb *cb, 183 | struct nl_msg *msg, 184 | int argc, char **argv, 185 | enum id_input id) 186 | { 187 | unsigned long mode; 188 | char *end; 189 | 190 | if (argc < 1) 191 | return 1; 192 | 193 | /* LBT_MODE */ 194 | mode = strtoul(argv[0], &end, 0); 195 | if (*end != '\0') 196 | return 1; 197 | 198 | if (mode > UINT8_MAX) 199 | return 1; 200 | 201 | NLA_PUT_U8(msg, NL802154_ATTR_LBT_MODE, mode); 202 | 203 | return 0; 204 | 205 | nla_put_failure: 206 | return -ENOBUFS; 207 | } 208 | COMMAND(set, lbt, "<1|0>", 209 | NL802154_CMD_SET_LBT_MODE, 0, CIB_NETDEV, handle_lbt_mode, NULL); 210 | 211 | static int handle_ackreq_default(struct nl802154_state *state, 212 | struct nl_cb *cb, 213 | struct nl_msg *msg, 214 | int argc, char **argv, 215 | enum id_input id) 216 | { 217 | unsigned long ackreq; 218 | char *end; 219 | 220 | if (argc < 1) 221 | return 1; 222 | 223 | /* ACKREQ_DEFAULT */ 224 | ackreq = strtoul(argv[0], &end, 0); 225 | if (*end != '\0') 226 | return 1; 227 | 228 | if (ackreq > UINT8_MAX) 229 | return 1; 230 | 231 | NLA_PUT_U8(msg, NL802154_ATTR_ACKREQ_DEFAULT, ackreq); 232 | 233 | return 0; 234 | 235 | nla_put_failure: 236 | return -ENOBUFS; 237 | } 238 | COMMAND(set, ackreq_default, "<1|0>", 239 | NL802154_CMD_SET_ACKREQ_DEFAULT, 0, CIB_NETDEV, handle_ackreq_default, 240 | NULL); 241 | 242 | static int handle_set_max_associations(struct nl802154_state *state, 243 | struct nl_cb *cb, 244 | struct nl_msg *msg, 245 | int argc, char **argv, 246 | enum id_input id) 247 | { 248 | unsigned long max_associations; 249 | char *end; 250 | 251 | if (argc < 1) 252 | return 1; 253 | 254 | /* Maximum number of PAN entries */ 255 | max_associations = strtoul(argv[0], &end, 0); 256 | if (*end != '\0') 257 | return 1; 258 | 259 | NLA_PUT_U32(msg, NL802154_ATTR_MAX_ASSOCIATIONS, max_associations); 260 | 261 | return 0; 262 | 263 | nla_put_failure: 264 | return -ENOBUFS; 265 | } 266 | COMMAND(set, max_associations, "", 267 | NL802154_CMD_SET_MAX_ASSOCIATIONS, 0, CIB_NETDEV, 268 | handle_set_max_associations, NULL); 269 | 270 | int parse_associated_devices(struct nlattr *nestedassoc) 271 | { 272 | struct nlattr *assoc[NL802154_DEV_ADDR_ATTR_MAX + 1]; 273 | static struct nla_policy assoc_policy[NL802154_DEV_ADDR_ATTR_MAX + 1] = { 274 | [NL802154_DEV_ADDR_ATTR_PEER_TYPE] = { .type = NLA_U8, }, 275 | [NL802154_DEV_ADDR_ATTR_MODE] = { .type = NLA_U8, }, 276 | [NL802154_DEV_ADDR_ATTR_SHORT] = { .type = NLA_U16, }, 277 | [NL802154_DEV_ADDR_ATTR_EXTENDED] = { .type = NLA_U64, }, 278 | }; 279 | bool short_addressing; 280 | uint8_t peer_type; 281 | int ret; 282 | 283 | ret = nla_parse_nested(assoc, NL802154_DEV_ADDR_ATTR_MAX, nestedassoc, 284 | assoc_policy); 285 | if (ret < 0) { 286 | fprintf(stderr, "failed to parse nested attributes! (ret = %d)\n", 287 | ret); 288 | return NL_SKIP; 289 | } 290 | 291 | if (!assoc[NL802154_DEV_ADDR_ATTR_PEER_TYPE] || 292 | !assoc[NL802154_DEV_ADDR_ATTR_SHORT] || 293 | !assoc[NL802154_DEV_ADDR_ATTR_EXTENDED]) 294 | return NL_SKIP; 295 | 296 | peer_type = nla_get_u8(assoc[NL802154_DEV_ADDR_ATTR_PEER_TYPE]); 297 | printf("%s: 0x%04x / 0x%016llx\n", 298 | peer_type == NL802154_PEER_TYPE_PARENT ? "parent" : "child ", 299 | nla_get_u16(assoc[NL802154_DEV_ADDR_ATTR_SHORT]), 300 | nla_get_u64(assoc[NL802154_DEV_ADDR_ATTR_EXTENDED])); 301 | 302 | return NL_OK; 303 | } 304 | 305 | static int print_association_list_handler(struct nl_msg *msg, void *arg) 306 | { 307 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); 308 | struct nlattr *tb[NL802154_ATTR_MAX + 1]; 309 | struct nlattr *nestedassoc; 310 | 311 | nla_parse(tb, NL802154_ATTR_MAX, genlmsg_attrdata(gnlh, 0), 312 | genlmsg_attrlen(gnlh, 0), NULL); 313 | nestedassoc = tb[NL802154_ATTR_PEER]; 314 | if (!nestedassoc) { 315 | fprintf(stderr, "peer info missing!\n"); 316 | return NL_SKIP; 317 | } 318 | return parse_associated_devices(nestedassoc); 319 | } 320 | 321 | static int list_associations_handler(struct nl802154_state *state, 322 | struct nl_cb *cb, 323 | struct nl_msg *msg, 324 | int argc, char **argv, 325 | enum id_input id) 326 | { 327 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_association_list_handler, NULL); 328 | 329 | return 0; 330 | } 331 | TOPLEVEL(list_associations, NULL, NL802154_CMD_LIST_ASSOCIATIONS, 332 | NLM_F_DUMP, CIB_NETDEV, list_associations_handler, 333 | "List the associated devices on this virtual interface"); 334 | 335 | static int associate_handler(struct nl802154_state *state, 336 | struct nl_cb *cb, 337 | struct nl_msg *msg, 338 | int argc, char **argv, 339 | enum id_input id) 340 | { 341 | unsigned int pan_id; 342 | uint64_t laddr = 0; 343 | char *end; 344 | int tpset; 345 | 346 | if (argc < 4) 347 | return 1; 348 | 349 | /* PAN ID */ 350 | if (strcmp(argv[0], "pan_id")) 351 | return 1; 352 | 353 | pan_id = strtoul(argv[1], &end, 0); 354 | if (*end != '\0') 355 | return 1; 356 | 357 | if (pan_id > UINT16_MAX) 358 | return 1; 359 | 360 | argc -= 2; 361 | argv += 2; 362 | 363 | /* Coordinator */ 364 | if (strcmp(argv[0], "coord")) 365 | return 1; 366 | 367 | laddr = strtoull(argv[1], &end, 0); 368 | if (*end != '\0') 369 | return 1; 370 | 371 | NLA_PUT_U16(msg, NL802154_ATTR_PAN_ID, htole16(pan_id)); 372 | NLA_PUT_U64(msg, NL802154_ATTR_EXTENDED_ADDR, htole64(laddr)); 373 | 374 | return 0; 375 | 376 | nla_put_failure: 377 | return -ENOBUFS; 378 | } 379 | TOPLEVEL(associate, "pan_id coord ", NL802154_CMD_ASSOCIATE, 0, 380 | CIB_NETDEV, associate_handler, 381 | "Join a PAN by sending an association request to the given coordinator"); 382 | 383 | static int disassociate_handler(struct nl802154_state *state, 384 | struct nl_cb *cb, 385 | struct nl_msg *msg, 386 | int argc, char **argv, 387 | enum id_input id) 388 | { 389 | bool use_extended_addressing; 390 | uint64_t laddr; 391 | unsigned int saddr; 392 | char *end; 393 | int tpset; 394 | 395 | if (argc < 2) 396 | return 1; 397 | 398 | if (!strcmp(argv[0], "ext_addr")) { 399 | use_extended_addressing = true; 400 | laddr = strtoull(argv[1], &end, 0); 401 | if (*end != '\0') 402 | return 1; 403 | } else if (!strcmp(argv[0], "short_addr")) { 404 | use_extended_addressing = false; 405 | saddr = strtoul(argv[1], &end, 0); 406 | if (*end != '\0') 407 | return 1; 408 | 409 | if (saddr > UINT16_MAX - 2) 410 | return 1; 411 | } else { 412 | return 1; 413 | } 414 | 415 | if (use_extended_addressing) 416 | NLA_PUT_U64(msg, NL802154_ATTR_EXTENDED_ADDR, htole64(laddr)); 417 | else 418 | NLA_PUT_U16(msg, NL802154_ATTR_SHORT_ADDR, htole16(saddr)); 419 | 420 | return 0; 421 | 422 | nla_put_failure: 423 | return -ENOBUFS; 424 | } 425 | TOPLEVEL(disassociate, "short_addr|ext_addr ", NL802154_CMD_DISASSOCIATE, 426 | 0, CIB_NETDEV, disassociate_handler, 427 | "Send a disassociation notification to a device"); 428 | -------------------------------------------------------------------------------- /src/nl802154.h: -------------------------------------------------------------------------------- 1 | #ifndef __NL802154_H 2 | #define __NL802154_H 3 | 4 | // SPDX-FileCopyrightText: 2014 Alexander Aring 5 | // 6 | // SPDX-License-Identifier: ISC 7 | 8 | /* 802.15.4 netlink interface public header */ 9 | 10 | #define NL802154_GENL_NAME "nl802154" 11 | 12 | enum nl802154_commands { 13 | /* don't change the order or add anything between, this is ABI! */ 14 | /* currently we don't shipping this file via uapi, ignore the above one */ 15 | NL802154_CMD_UNSPEC, 16 | 17 | NL802154_CMD_GET_WPAN_PHY, /* can dump */ 18 | NL802154_CMD_SET_WPAN_PHY, 19 | NL802154_CMD_NEW_WPAN_PHY, 20 | NL802154_CMD_DEL_WPAN_PHY, 21 | 22 | NL802154_CMD_GET_INTERFACE, /* can dump */ 23 | NL802154_CMD_SET_INTERFACE, 24 | NL802154_CMD_NEW_INTERFACE, 25 | NL802154_CMD_DEL_INTERFACE, 26 | 27 | NL802154_CMD_SET_CHANNEL, 28 | 29 | NL802154_CMD_SET_PAN_ID, 30 | NL802154_CMD_SET_SHORT_ADDR, 31 | 32 | NL802154_CMD_SET_TX_POWER, 33 | NL802154_CMD_SET_CCA_MODE, 34 | NL802154_CMD_SET_CCA_ED_LEVEL, 35 | 36 | NL802154_CMD_SET_MAX_FRAME_RETRIES, 37 | 38 | NL802154_CMD_SET_BACKOFF_EXPONENT, 39 | NL802154_CMD_SET_MAX_CSMA_BACKOFFS, 40 | 41 | NL802154_CMD_SET_LBT_MODE, 42 | 43 | NL802154_CMD_SET_ACKREQ_DEFAULT, 44 | 45 | NL802154_CMD_SET_WPAN_PHY_NETNS, 46 | 47 | NL802154_CMD_SET_SEC_PARAMS, 48 | NL802154_CMD_GET_SEC_KEY, /* can dump */ 49 | NL802154_CMD_NEW_SEC_KEY, 50 | NL802154_CMD_DEL_SEC_KEY, 51 | NL802154_CMD_GET_SEC_DEV, /* can dump */ 52 | NL802154_CMD_NEW_SEC_DEV, 53 | NL802154_CMD_DEL_SEC_DEV, 54 | NL802154_CMD_GET_SEC_DEVKEY, /* can dump */ 55 | NL802154_CMD_NEW_SEC_DEVKEY, 56 | NL802154_CMD_DEL_SEC_DEVKEY, 57 | NL802154_CMD_GET_SEC_LEVEL, /* can dump */ 58 | NL802154_CMD_NEW_SEC_LEVEL, 59 | NL802154_CMD_DEL_SEC_LEVEL, 60 | 61 | NL802154_CMD_SCAN_EVENT, 62 | NL802154_CMD_TRIGGER_SCAN, 63 | NL802154_CMD_ABORT_SCAN, 64 | NL802154_CMD_SCAN_DONE, 65 | NL802154_CMD_SEND_BEACONS, 66 | NL802154_CMD_STOP_BEACONS, 67 | NL802154_CMD_ASSOCIATE, 68 | NL802154_CMD_DISASSOCIATE, 69 | NL802154_CMD_SET_MAX_ASSOCIATIONS, 70 | NL802154_CMD_LIST_ASSOCIATIONS, 71 | 72 | /* add new commands above here */ 73 | 74 | /* used to define NL802154_CMD_MAX below */ 75 | __NL802154_CMD_AFTER_LAST, 76 | NL802154_CMD_MAX = __NL802154_CMD_AFTER_LAST - 1 77 | }; 78 | 79 | enum nl802154_attrs { 80 | /* don't change the order or add anything between, this is ABI! */ 81 | /* currently we don't shipping this file via uapi, ignore the above one */ 82 | NL802154_ATTR_UNSPEC, 83 | 84 | NL802154_ATTR_WPAN_PHY, 85 | NL802154_ATTR_WPAN_PHY_NAME, 86 | 87 | NL802154_ATTR_IFINDEX, 88 | NL802154_ATTR_IFNAME, 89 | NL802154_ATTR_IFTYPE, 90 | 91 | NL802154_ATTR_WPAN_DEV, 92 | 93 | NL802154_ATTR_PAGE, 94 | NL802154_ATTR_CHANNEL, 95 | 96 | NL802154_ATTR_PAN_ID, 97 | NL802154_ATTR_SHORT_ADDR, 98 | 99 | NL802154_ATTR_TX_POWER, 100 | 101 | NL802154_ATTR_CCA_MODE, 102 | NL802154_ATTR_CCA_OPT, 103 | NL802154_ATTR_CCA_ED_LEVEL, 104 | 105 | NL802154_ATTR_MAX_FRAME_RETRIES, 106 | 107 | NL802154_ATTR_MAX_BE, 108 | NL802154_ATTR_MIN_BE, 109 | NL802154_ATTR_MAX_CSMA_BACKOFFS, 110 | 111 | NL802154_ATTR_LBT_MODE, 112 | 113 | NL802154_ATTR_GENERATION, 114 | 115 | NL802154_ATTR_CHANNELS_SUPPORTED, 116 | NL802154_ATTR_SUPPORTED_CHANNEL, 117 | 118 | NL802154_ATTR_EXTENDED_ADDR, 119 | 120 | NL802154_ATTR_WPAN_PHY_CAPS, 121 | 122 | NL802154_ATTR_SUPPORTED_COMMANDS, 123 | 124 | NL802154_ATTR_ACKREQ_DEFAULT, 125 | 126 | NL802154_ATTR_PAD, 127 | 128 | NL802154_ATTR_PID, 129 | NL802154_ATTR_NETNS_FD, 130 | 131 | NL802154_ATTR_COORDINATOR, 132 | NL802154_ATTR_SCAN_TYPE, 133 | NL802154_ATTR_SCAN_FLAGS, 134 | NL802154_ATTR_SCAN_CHANNELS, 135 | NL802154_ATTR_SCAN_PREAMBLE_CODES, 136 | NL802154_ATTR_SCAN_MEAN_PRF, 137 | NL802154_ATTR_SCAN_DURATION, 138 | NL802154_ATTR_SCAN_DONE_REASON, 139 | NL802154_ATTR_BEACON_INTERVAL, 140 | NL802154_ATTR_MAX_ASSOCIATIONS, 141 | NL802154_ATTR_PEER, 142 | 143 | /* add attributes here, update the policy in nl802154.c */ 144 | 145 | #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL 146 | NL802154_ATTR_SEC_ENABLED, 147 | NL802154_ATTR_SEC_OUT_LEVEL, 148 | NL802154_ATTR_SEC_OUT_KEY_ID, 149 | NL802154_ATTR_SEC_FRAME_COUNTER, 150 | 151 | NL802154_ATTR_SEC_LEVEL, 152 | NL802154_ATTR_SEC_DEVICE, 153 | NL802154_ATTR_SEC_DEVKEY, 154 | NL802154_ATTR_SEC_KEY, 155 | #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ 156 | 157 | __NL802154_ATTR_AFTER_LAST, 158 | NL802154_ATTR_MAX = __NL802154_ATTR_AFTER_LAST - 1 159 | }; 160 | 161 | enum nl802154_iftype { 162 | /* for backwards compatibility TODO */ 163 | NL802154_IFTYPE_UNSPEC = -1, 164 | 165 | NL802154_IFTYPE_NODE, 166 | NL802154_IFTYPE_MONITOR, 167 | NL802154_IFTYPE_COORD, 168 | 169 | /* keep last */ 170 | NUM_NL802154_IFTYPES, 171 | NL802154_IFTYPE_MAX = NUM_NL802154_IFTYPES - 1 172 | }; 173 | 174 | /** 175 | * enum nl802154_wpan_phy_capability_attr - wpan phy capability attributes 176 | * 177 | * @__NL802154_CAP_ATTR_INVALID: attribute number 0 is reserved 178 | * @NL802154_CAP_ATTR_CHANNELS: a nested attribute for nl802154_channel_attr 179 | * @NL802154_CAP_ATTR_TX_POWERS: a nested attribute for 180 | * nl802154_wpan_phy_tx_power 181 | * @NL802154_CAP_ATTR_MIN_CCA_ED_LEVEL: minimum value for cca_ed_level 182 | * @NL802154_CAP_ATTR_MAX_CCA_ED_LEVEL: maxmimum value for cca_ed_level 183 | * @NL802154_CAP_ATTR_CCA_MODES: nl802154_cca_modes flags 184 | * @NL802154_CAP_ATTR_CCA_OPTS: nl802154_cca_opts flags 185 | * @NL802154_CAP_ATTR_MIN_MINBE: minimum of minbe value 186 | * @NL802154_CAP_ATTR_MAX_MINBE: maximum of minbe value 187 | * @NL802154_CAP_ATTR_MIN_MAXBE: minimum of maxbe value 188 | * @NL802154_CAP_ATTR_MAX_MINBE: maximum of maxbe value 189 | * @NL802154_CAP_ATTR_MIN_CSMA_BACKOFFS: minimum of csma backoff value 190 | * @NL802154_CAP_ATTR_MAX_CSMA_BACKOFFS: maximum of csma backoffs value 191 | * @NL802154_CAP_ATTR_MIN_FRAME_RETRIES: minimum of frame retries value 192 | * @NL802154_CAP_ATTR_MAX_FRAME_RETRIES: maximum of frame retries value 193 | * @NL802154_CAP_ATTR_IFTYPES: nl802154_iftype flags 194 | * @NL802154_CAP_ATTR_LBT: nl802154_supported_bool_states flags 195 | * @NL802154_CAP_ATTR_MAX: highest cap attribute currently defined 196 | * @__NL802154_CAP_ATTR_AFTER_LAST: internal use 197 | */ 198 | enum nl802154_wpan_phy_capability_attr { 199 | __NL802154_CAP_ATTR_INVALID, 200 | 201 | NL802154_CAP_ATTR_IFTYPES, 202 | 203 | NL802154_CAP_ATTR_CHANNELS, 204 | NL802154_CAP_ATTR_TX_POWERS, 205 | 206 | NL802154_CAP_ATTR_CCA_ED_LEVELS, 207 | NL802154_CAP_ATTR_CCA_MODES, 208 | NL802154_CAP_ATTR_CCA_OPTS, 209 | 210 | NL802154_CAP_ATTR_MIN_MINBE, 211 | NL802154_CAP_ATTR_MAX_MINBE, 212 | 213 | NL802154_CAP_ATTR_MIN_MAXBE, 214 | NL802154_CAP_ATTR_MAX_MAXBE, 215 | 216 | NL802154_CAP_ATTR_MIN_CSMA_BACKOFFS, 217 | NL802154_CAP_ATTR_MAX_CSMA_BACKOFFS, 218 | 219 | NL802154_CAP_ATTR_MIN_FRAME_RETRIES, 220 | NL802154_CAP_ATTR_MAX_FRAME_RETRIES, 221 | 222 | NL802154_CAP_ATTR_LBT, 223 | 224 | /* keep last */ 225 | __NL802154_CAP_ATTR_AFTER_LAST, 226 | NL802154_CAP_ATTR_MAX = __NL802154_CAP_ATTR_AFTER_LAST - 1 227 | }; 228 | 229 | /** 230 | * enum nl802154_scan_types - Scan types 231 | * 232 | * @__NL802154_SCAN_INVALID: scan type number 0 is reserved 233 | * @NL802154_SCAN_ED: An ED scan allows a device to obtain a measure of the peak 234 | * energy in each requested channel 235 | * @NL802154_SCAN_ACTIVE: Locate any coordinator transmitting Beacon frames using 236 | * a Beacon Request command 237 | * @NL802154_SCAN_PASSIVE: Locate any coordinator transmitting Beacon frames 238 | * @NL802154_SCAN_ORPHAN: Relocate coordinator following a loss of synchronisation 239 | * @NL802154_SCAN_ENHANCED_ACTIVE: Same as Active using Enhanced Beacon Request 240 | * command instead of Beacon Request command 241 | * @NL802154_SCAN_RIT_PASSIVE: Passive scan for RIT Data Request command frames 242 | * instead of Beacon frames 243 | * @NL802154_SCAN_ATTR_MAX: Maximum SCAN attribute number 244 | */ 245 | enum nl802154_scan_types { 246 | __NL802154_SCAN_INVALID, 247 | NL802154_SCAN_ED, 248 | NL802154_SCAN_ACTIVE, 249 | NL802154_SCAN_PASSIVE, 250 | NL802154_SCAN_ORPHAN, 251 | NL802154_SCAN_ENHANCED_ACTIVE, 252 | NL802154_SCAN_RIT_PASSIVE, 253 | 254 | /* keep last */ 255 | NL802154_SCAN_ATTR_MAX, 256 | }; 257 | 258 | /** 259 | * enum nl802154_scan_done_reasons - End of scan reasons 260 | * 261 | * @__NL802154_SCAN_DONE_REASON_INVALID: scan done reason number 0 is reserved. 262 | * @NL802154_SCAN_DONE_REASON_FINISHED: The scan just finished naturally after 263 | * going through all the requested and possible (complex) channels. 264 | * @NL802154_SCAN_DONE_REASON_ABORTED: The scan was aborted upon user request. 265 | * a Beacon Request command 266 | * @NL802154_SCAN_DONE_REASON_MAX: Maximum scan done reason attribute number. 267 | */ 268 | enum nl802154_scan_done_reasons { 269 | __NL802154_SCAN_DONE_REASON_INVALID, 270 | NL802154_SCAN_DONE_REASON_FINISHED, 271 | NL802154_SCAN_DONE_REASON_ABORTED, 272 | 273 | /* keep last */ 274 | NL802154_SCAN_DONE_REASON_MAX, 275 | }; 276 | 277 | /** 278 | * enum nl802154_coord - Netlink attributes for a coordinator 279 | * 280 | * @__NL802154_COORD_INVALID: invalid 281 | * @NL802154_COORD_PANID: PANID of the coordinator (2 bytes) 282 | * @NL802154_COORD_ADDR: Coordinator address, (8 bytes or 2 bytes) 283 | * @NL802154_COORD_CHANNEL: channel number, related to @NL802154_COORD_PAGE (u8) 284 | * @NL802154_COORD_PAGE: channel page, related to @NL802154_COORD_CHANNEL (u8) 285 | * @NL802154_COORD_PREAMBLE_CODE: Preamble code used when the beacon was received, 286 | * this is PHY dependent and optional (u8) 287 | * @NL802154_COORD_MEAN_PRF: Mean PRF used when the beacon was received, 288 | * this is PHY dependent and optional (u8) 289 | * @NL802154_COORD_SUPERFRAME_SPEC: superframe specification of the PAN (u16) 290 | * @NL802154_COORD_LINK_QUALITY: signal quality of beacon in unspecified units, 291 | * scaled to 0..255 (u8) 292 | * @NL802154_COORD_GTS_PERMIT: set to true if GTS is permitted on this PAN 293 | * @NL802154_COORD_PAYLOAD_DATA: binary data containing the raw data from the 294 | * frame payload, (only if beacon or probe response had data) 295 | * @NL802154_COORD_PAD: attribute used for padding for 64-bit alignment 296 | * @NL802154_COORD_MAX: highest coordinator attribute 297 | */ 298 | enum nl802154_coord { 299 | __NL802154_COORD_INVALID, 300 | NL802154_COORD_PANID, 301 | NL802154_COORD_ADDR, 302 | NL802154_COORD_CHANNEL, 303 | NL802154_COORD_PAGE, 304 | NL802154_COORD_PREAMBLE_CODE, 305 | NL802154_COORD_MEAN_PRF, 306 | NL802154_COORD_SUPERFRAME_SPEC, 307 | NL802154_COORD_LINK_QUALITY, 308 | NL802154_COORD_GTS_PERMIT, 309 | NL802154_COORD_PAYLOAD_DATA, 310 | NL802154_COORD_PAD, 311 | 312 | /* keep last */ 313 | NL802154_COORD_MAX, 314 | }; 315 | 316 | /** 317 | * enum nl802154_cca_modes - cca modes 318 | * 319 | * @__NL802154_CCA_INVALID: cca mode number 0 is reserved 320 | * @NL802154_CCA_ENERGY: Energy above threshold 321 | * @NL802154_CCA_CARRIER: Carrier sense only 322 | * @NL802154_CCA_ENERGY_CARRIER: Carrier sense with energy above threshold 323 | * @NL802154_CCA_ALOHA: CCA shall always report an idle medium 324 | * @NL802154_CCA_UWB_SHR: UWB preamble sense based on the SHR of a frame 325 | * @NL802154_CCA_UWB_MULTIPLEXED: UWB preamble sense based on the packet with 326 | * the multiplexed preamble 327 | * @__NL802154_CCA_ATTR_AFTER_LAST: Internal 328 | * @NL802154_CCA_ATTR_MAX: Maximum CCA attribute number 329 | */ 330 | enum nl802154_cca_modes { 331 | __NL802154_CCA_INVALID, 332 | NL802154_CCA_ENERGY, 333 | NL802154_CCA_CARRIER, 334 | NL802154_CCA_ENERGY_CARRIER, 335 | NL802154_CCA_ALOHA, 336 | NL802154_CCA_UWB_SHR, 337 | NL802154_CCA_UWB_MULTIPLEXED, 338 | 339 | /* keep last */ 340 | __NL802154_CCA_ATTR_AFTER_LAST, 341 | NL802154_CCA_ATTR_MAX = __NL802154_CCA_ATTR_AFTER_LAST - 1 342 | }; 343 | 344 | /** 345 | * enum nl802154_cca_opts - additional options for cca modes 346 | * 347 | * @NL802154_CCA_OPT_ENERGY_CARRIER_OR: NL802154_CCA_ENERGY_CARRIER with OR 348 | * @NL802154_CCA_OPT_ENERGY_CARRIER_AND: NL802154_CCA_ENERGY_CARRIER with AND 349 | */ 350 | enum nl802154_cca_opts { 351 | NL802154_CCA_OPT_ENERGY_CARRIER_AND, 352 | NL802154_CCA_OPT_ENERGY_CARRIER_OR, 353 | 354 | /* keep last */ 355 | __NL802154_CCA_OPT_ATTR_AFTER_LAST, 356 | NL802154_CCA_OPT_ATTR_MAX = __NL802154_CCA_OPT_ATTR_AFTER_LAST - 1 357 | }; 358 | 359 | /** 360 | * enum nl802154_supported_bool_states - bool states for bool capability entry 361 | * 362 | * @NL802154_SUPPORTED_BOOL_FALSE: indicates to set false 363 | * @NL802154_SUPPORTED_BOOL_TRUE: indicates to set true 364 | * @__NL802154_SUPPORTED_BOOL_INVALD: reserved 365 | * @NL802154_SUPPORTED_BOOL_BOTH: indicates to set true and false 366 | * @__NL802154_SUPPORTED_BOOL_AFTER_LAST: Internal 367 | * @NL802154_SUPPORTED_BOOL_MAX: highest value for bool states 368 | */ 369 | enum nl802154_supported_bool_states { 370 | NL802154_SUPPORTED_BOOL_FALSE, 371 | NL802154_SUPPORTED_BOOL_TRUE, 372 | /* to handle them in a mask */ 373 | __NL802154_SUPPORTED_BOOL_INVALD, 374 | NL802154_SUPPORTED_BOOL_BOTH, 375 | 376 | /* keep last */ 377 | __NL802154_SUPPORTED_BOOL_AFTER_LAST, 378 | NL802154_SUPPORTED_BOOL_MAX = __NL802154_SUPPORTED_BOOL_AFTER_LAST - 1 379 | }; 380 | 381 | enum nl802154_dev_addr_modes { 382 | NL802154_DEV_ADDR_NONE, 383 | __NL802154_DEV_ADDR_INVALID, 384 | NL802154_DEV_ADDR_SHORT, 385 | NL802154_DEV_ADDR_EXTENDED, 386 | 387 | /* keep last */ 388 | __NL802154_DEV_ADDR_AFTER_LAST, 389 | NL802154_DEV_ADDR_MAX = __NL802154_DEV_ADDR_AFTER_LAST - 1 390 | }; 391 | 392 | enum nl802154_dev_addr_attrs { 393 | NL802154_DEV_ADDR_ATTR_UNSPEC, 394 | 395 | NL802154_DEV_ADDR_ATTR_PAN_ID, 396 | NL802154_DEV_ADDR_ATTR_MODE, 397 | NL802154_DEV_ADDR_ATTR_SHORT, 398 | NL802154_DEV_ADDR_ATTR_EXTENDED, 399 | NL802154_DEV_ADDR_ATTR_PAD, 400 | NL802154_DEV_ADDR_ATTR_PEER_TYPE, 401 | 402 | /* keep last */ 403 | __NL802154_DEV_ADDR_ATTR_AFTER_LAST, 404 | NL802154_DEV_ADDR_ATTR_MAX = __NL802154_DEV_ADDR_ATTR_AFTER_LAST - 1 405 | }; 406 | 407 | enum nl802154_peer_type { 408 | NL802154_PEER_TYPE_UNSPEC, 409 | 410 | NL802154_PEER_TYPE_PARENT, 411 | NL802154_PEER_TYPE_CHILD, 412 | 413 | /* keep last */ 414 | __NL802154_PEER_TYPE_AFTER_LAST, 415 | NL802154_PEER_TYPE_MAX = __NL802154_PEER_TYPE_AFTER_LAST - 1 416 | }; 417 | 418 | #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL 419 | 420 | enum nl802154_key_id_modes { 421 | NL802154_KEY_ID_MODE_IMPLICIT, 422 | NL802154_KEY_ID_MODE_INDEX, 423 | NL802154_KEY_ID_MODE_INDEX_SHORT, 424 | NL802154_KEY_ID_MODE_INDEX_EXTENDED, 425 | 426 | /* keep last */ 427 | __NL802154_KEY_ID_MODE_AFTER_LAST, 428 | NL802154_KEY_ID_MODE_MAX = __NL802154_KEY_ID_MODE_AFTER_LAST - 1 429 | }; 430 | 431 | enum nl802154_key_id_attrs { 432 | NL802154_KEY_ID_ATTR_UNSPEC, 433 | 434 | NL802154_KEY_ID_ATTR_MODE, 435 | NL802154_KEY_ID_ATTR_INDEX, 436 | NL802154_KEY_ID_ATTR_IMPLICIT, 437 | NL802154_KEY_ID_ATTR_SOURCE_SHORT, 438 | NL802154_KEY_ID_ATTR_SOURCE_EXTENDED, 439 | NL802154_KEY_ID_ATTR_PAD, 440 | 441 | /* keep last */ 442 | __NL802154_KEY_ID_ATTR_AFTER_LAST, 443 | NL802154_KEY_ID_ATTR_MAX = __NL802154_KEY_ID_ATTR_AFTER_LAST - 1 444 | }; 445 | 446 | enum nl802154_seclevels { 447 | NL802154_SECLEVEL_NONE, 448 | NL802154_SECLEVEL_MIC32, 449 | NL802154_SECLEVEL_MIC64, 450 | NL802154_SECLEVEL_MIC128, 451 | NL802154_SECLEVEL_ENC, 452 | NL802154_SECLEVEL_ENC_MIC32, 453 | NL802154_SECLEVEL_ENC_MIC64, 454 | NL802154_SECLEVEL_ENC_MIC128, 455 | 456 | /* keep last */ 457 | __NL802154_SECLEVEL_AFTER_LAST, 458 | NL802154_SECLEVEL_MAX = __NL802154_SECLEVEL_AFTER_LAST - 1 459 | }; 460 | 461 | enum nl802154_frames { 462 | NL802154_FRAME_BEACON, 463 | NL802154_FRAME_DATA, 464 | NL802154_FRAME_ACK, 465 | NL802154_FRAME_CMD, 466 | 467 | /* keep last */ 468 | __NL802154_FRAME_AFTER_LAST, 469 | NL802154_FRAME_MAX = __NL802154_FRAME_AFTER_LAST - 1 470 | }; 471 | 472 | enum nl802154_cmd_frames { 473 | __NL802154_CMD_FRAME_INVALID, 474 | NL802154_CMD_FRAME_ASSOC_REQUEST, 475 | NL802154_CMD_FRAME_ASSOC_RESPONSE, 476 | NL802154_CMD_FRAME_DISASSOC_NOTIFY, 477 | NL802154_CMD_FRAME_DATA_REQUEST, 478 | NL802154_CMD_FRAME_PAN_ID_CONFLICT_NOTIFY, 479 | NL802154_CMD_FRAME_ORPHAN_NOTIFY, 480 | NL802154_CMD_FRAME_BEACON_REQUEST, 481 | NL802154_CMD_FRAME_COORD_REALIGNMENT, 482 | NL802154_CMD_FRAME_GTS_REQUEST, 483 | 484 | /* keep last */ 485 | __NL802154_CMD_FRAME_AFTER_LAST, 486 | NL802154_CMD_FRAME_MAX = __NL802154_CMD_FRAME_AFTER_LAST - 1 487 | }; 488 | 489 | enum nl802154_seclevel_attrs { 490 | NL802154_SECLEVEL_ATTR_UNSPEC, 491 | 492 | NL802154_SECLEVEL_ATTR_LEVELS, 493 | NL802154_SECLEVEL_ATTR_FRAME, 494 | NL802154_SECLEVEL_ATTR_CMD_FRAME, 495 | NL802154_SECLEVEL_ATTR_DEV_OVERRIDE, 496 | 497 | /* keep last */ 498 | __NL802154_SECLEVEL_ATTR_AFTER_LAST, 499 | NL802154_SECLEVEL_ATTR_MAX = __NL802154_SECLEVEL_ATTR_AFTER_LAST - 1 500 | }; 501 | 502 | /* TODO what is this? couldn't find in mib */ 503 | enum { 504 | NL802154_DEVKEY_IGNORE, 505 | NL802154_DEVKEY_RESTRICT, 506 | NL802154_DEVKEY_RECORD, 507 | 508 | /* keep last */ 509 | __NL802154_DEVKEY_AFTER_LAST, 510 | NL802154_DEVKEY_MAX = __NL802154_DEVKEY_AFTER_LAST - 1 511 | }; 512 | 513 | enum nl802154_dev { 514 | NL802154_DEV_ATTR_UNSPEC, 515 | 516 | NL802154_DEV_ATTR_FRAME_COUNTER, 517 | NL802154_DEV_ATTR_PAN_ID, 518 | NL802154_DEV_ATTR_SHORT_ADDR, 519 | NL802154_DEV_ATTR_EXTENDED_ADDR, 520 | NL802154_DEV_ATTR_SECLEVEL_EXEMPT, 521 | NL802154_DEV_ATTR_KEY_MODE, 522 | NL802154_DEV_ATTR_PAD, 523 | 524 | /* keep last */ 525 | __NL802154_DEV_ATTR_AFTER_LAST, 526 | NL802154_DEV_ATTR_MAX = __NL802154_DEV_ATTR_AFTER_LAST - 1 527 | }; 528 | 529 | enum nl802154_devkey { 530 | NL802154_DEVKEY_ATTR_UNSPEC, 531 | 532 | NL802154_DEVKEY_ATTR_FRAME_COUNTER, 533 | NL802154_DEVKEY_ATTR_EXTENDED_ADDR, 534 | NL802154_DEVKEY_ATTR_ID, 535 | NL802154_DEVKEY_ATTR_PAD, 536 | 537 | /* keep last */ 538 | __NL802154_DEVKEY_ATTR_AFTER_LAST, 539 | NL802154_DEVKEY_ATTR_MAX = __NL802154_DEVKEY_ATTR_AFTER_LAST - 1 540 | }; 541 | 542 | enum nl802154_key { 543 | NL802154_KEY_ATTR_UNSPEC, 544 | 545 | NL802154_KEY_ATTR_ID, 546 | NL802154_KEY_ATTR_USAGE_FRAMES, 547 | NL802154_KEY_ATTR_USAGE_CMDS, 548 | NL802154_KEY_ATTR_BYTES, 549 | 550 | /* keep last */ 551 | __NL802154_KEY_ATTR_AFTER_LAST, 552 | NL802154_KEY_ATTR_MAX = __NL802154_KEY_ATTR_AFTER_LAST - 1 553 | }; 554 | 555 | #define NL802154_KEY_SIZE 16 556 | #define NL802154_CMD_FRAME_NR_IDS 256 557 | 558 | #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ 559 | 560 | #endif /* __NL802154_H */ 561 | -------------------------------------------------------------------------------- /src/nl_extras.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2014 Alexander Aring 2 | // 3 | // SPDX-License-Identifier: ISC 4 | 5 | #ifndef __NL_EXTRAS_H 6 | #define __NL_EXTRAS_H 7 | 8 | #if (LIBNL_VER_MIN < 2) || (LIBNL_VER_MIN == 2) && (LIBNL_VER_MIC <= 26) 9 | 10 | #ifndef NLA_S8 11 | 12 | #define NLA_S8 13 13 | #define NLA_PUT_S8(n, attrtype, value) \ 14 | NLA_PUT_TYPE(n, int8_t, attrtype, value) 15 | 16 | static inline int8_t nla_get_s8(struct nlattr *nla) 17 | { 18 | return *(int8_t *) nla_data(nla); 19 | } 20 | 21 | #endif /* NLA_S8 */ 22 | 23 | #ifndef NLA_S16 24 | 25 | #define NLA_S16 14 26 | #define NLA_PUT_S16(n, attrtype, value) \ 27 | NLA_PUT_TYPE(n, int16_t, attrtype, value) 28 | 29 | #endif /* NLA_S16 */ 30 | 31 | #ifndef NLA_S32 32 | 33 | #define NLA_S32 15 34 | #define NLA_PUT_S32(n, attrtype, value) \ 35 | NLA_PUT_TYPE(n, int32_t, attrtype, value) 36 | 37 | static inline int32_t nla_get_s32(struct nlattr *nla) 38 | { 39 | return *(int32_t *) nla_data(nla); 40 | } 41 | 42 | #endif /* NLA_S32 */ 43 | 44 | #ifndef NLA_S64 45 | 46 | #define NLA_S64 16 47 | #define NLA_PUT_S64(n, attrtype, value) \ 48 | NLA_PUT_TYPE(n, int64_t, attrtype, value) 49 | 50 | #endif /* NLA_S64 */ 51 | 52 | #endif /* LIBNL_VER_* */ 53 | 54 | #endif /* __NL_EXTRAS_H */ 55 | -------------------------------------------------------------------------------- /src/phy.c: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2014 Alexander Aring 2 | // 3 | // SPDX-License-Identifier: ISC 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "nl_extras.h" 21 | #include "nl802154.h" 22 | #include "iwpan.h" 23 | 24 | static int handle_channel_set(struct nl802154_state *state, 25 | struct nl_cb *cb, 26 | struct nl_msg *msg, 27 | int argc, char **argv, 28 | enum id_input id) 29 | { 30 | unsigned long channel; 31 | unsigned long page; 32 | char *end; 33 | 34 | if (argc < 2) 35 | return 1; 36 | 37 | /* PAGE */ 38 | page = strtoul(argv[0], &end, 10); 39 | if (*end != '\0') 40 | return 1; 41 | 42 | /* CHANNEL */ 43 | channel = strtoul(argv[1], &end, 10); 44 | if (*end != '\0') 45 | return 1; 46 | 47 | if (page > UINT8_MAX || channel > UINT8_MAX) 48 | return 1; 49 | 50 | NLA_PUT_U8(msg, NL802154_ATTR_PAGE, page); 51 | NLA_PUT_U8(msg, NL802154_ATTR_CHANNEL, channel); 52 | 53 | return 0; 54 | 55 | nla_put_failure: 56 | return -ENOBUFS; 57 | } 58 | COMMAND(set, channel, " ", 59 | NL802154_CMD_SET_CHANNEL, 0, CIB_PHY, handle_channel_set, NULL); 60 | 61 | static int handle_tx_power_set(struct nl802154_state *state, 62 | struct nl_cb *cb, 63 | struct nl_msg *msg, 64 | int argc, char **argv, 65 | enum id_input id) 66 | { 67 | float dbm; 68 | char *end; 69 | 70 | if (argc < 1) 71 | return 1; 72 | 73 | /* TX_POWER */ 74 | dbm = strtof(argv[0], &end); 75 | if (*end != '\0') 76 | return 1; 77 | 78 | NLA_PUT_S32(msg, NL802154_ATTR_TX_POWER, DBM_TO_MBM(dbm)); 79 | 80 | return 0; 81 | 82 | nla_put_failure: 83 | return -ENOBUFS; 84 | } 85 | COMMAND(set, tx_power, "", 86 | NL802154_CMD_SET_TX_POWER, 0, CIB_PHY, handle_tx_power_set, NULL); 87 | 88 | static int handle_cca_mode_set(struct nl802154_state *state, 89 | struct nl_cb *cb, 90 | struct nl_msg *msg, 91 | int argc, char **argv, 92 | enum id_input id) 93 | { 94 | enum nl802154_cca_modes cca_mode; 95 | char *end; 96 | 97 | if (argc < 1) 98 | return 1; 99 | 100 | /* CCA_MODE */ 101 | cca_mode = strtoul(argv[0], &end, 10); 102 | if (*end != '\0') 103 | return 1; 104 | 105 | if (cca_mode == NL802154_CCA_ENERGY_CARRIER) { 106 | enum nl802154_cca_opts cca_opt; 107 | 108 | if (argc < 2) 109 | return 1; 110 | 111 | /* CCA_OPT */ 112 | cca_opt = strtoul(argv[1], &end, 10); 113 | if (*end != '\0') 114 | return 1; 115 | 116 | NLA_PUT_U32(msg, NL802154_ATTR_CCA_OPT, cca_opt); 117 | } 118 | 119 | NLA_PUT_U32(msg, NL802154_ATTR_CCA_MODE, cca_mode); 120 | 121 | return 0; 122 | 123 | nla_put_failure: 124 | return -ENOBUFS; 125 | } 126 | COMMAND(set, cca_mode, ">", 127 | NL802154_CMD_SET_CCA_MODE, 0, CIB_PHY, handle_cca_mode_set, NULL); 128 | 129 | static int handle_cca_ed_level(struct nl802154_state *state, 130 | struct nl_cb *cb, 131 | struct nl_msg *msg, 132 | int argc, char **argv, 133 | enum id_input id) 134 | { 135 | float level; 136 | char *end; 137 | 138 | if (argc < 1) 139 | return 1; 140 | 141 | /* CCA_ED_LEVEL */ 142 | level = strtof(argv[0], &end); 143 | if (*end != '\0') 144 | return 1; 145 | 146 | NLA_PUT_S32(msg, NL802154_ATTR_CCA_ED_LEVEL, DBM_TO_MBM(level)); 147 | 148 | return 0; 149 | 150 | nla_put_failure: 151 | return -ENOBUFS; 152 | } 153 | COMMAND(set, cca_ed_level, "", 154 | NL802154_CMD_SET_CCA_ED_LEVEL, 0, CIB_PHY, handle_cca_ed_level, NULL); 155 | 156 | #ifndef NETNS_RUN_DIR 157 | #define NETNS_RUN_DIR "/var/run/netns" 158 | #endif 159 | static int netns_get_fd(const char *name) 160 | { 161 | char pathbuf[MAXPATHLEN]; 162 | const char *path, *ptr; 163 | 164 | path = name; 165 | ptr = strchr(name, '/'); 166 | if (!ptr) { 167 | snprintf(pathbuf, sizeof(pathbuf), "%s/%s", 168 | NETNS_RUN_DIR, name ); 169 | path = pathbuf; 170 | } 171 | return open(path, O_RDONLY); 172 | } 173 | 174 | static int handle_netns(struct nl802154_state *state, 175 | struct nl_cb *cb, 176 | struct nl_msg *msg, 177 | int argc, char **argv, 178 | enum id_input id) 179 | { 180 | char *end; 181 | int fd; 182 | 183 | if (argc < 1 || !*argv[0]) 184 | return 1; 185 | 186 | if (argc == 1) { 187 | NLA_PUT_U32(msg, NL802154_ATTR_PID, 188 | strtoul(argv[0], &end, 10)); 189 | if (*end != '\0') { 190 | printf("Invalid parameter: pid(%s)\n", argv[0]); 191 | return 1; 192 | } 193 | return 0; 194 | } 195 | 196 | if (argc != 2 || strcmp(argv[0], "name")) 197 | return 1; 198 | 199 | if ((fd = netns_get_fd(argv[1])) >= 0) { 200 | NLA_PUT_U32(msg, NL802154_ATTR_NETNS_FD, fd); 201 | return 0; 202 | } else { 203 | printf("Invalid parameter: nsname(%s)\n", argv[0]); 204 | } 205 | 206 | return 1; 207 | 208 | nla_put_failure: 209 | return -ENOBUFS; 210 | } 211 | COMMAND(set, netns, "{ | name }", 212 | NL802154_CMD_SET_WPAN_PHY_NETNS, 0, CIB_PHY, handle_netns, 213 | "Put this wpan device into a different network namespace:\n" 214 | " - change network namespace by process id\n" 215 | " - change network namespace by name from "NETNS_RUN_DIR"\n" 216 | " or by absolute path (man ip-netns)\n"); 217 | -------------------------------------------------------------------------------- /src/scan.c: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 David Girault 2 | // 3 | // SPDX-License-Identifier: ISC 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "nl802154.h" 20 | #include "nl_extras.h" 21 | #include "iwpan.h" 22 | 23 | static char scantypebuf[100]; 24 | 25 | static const char *scantype_name(enum nl802154_scan_types scantype) 26 | { 27 | switch (scantype) { 28 | case NL802154_SCAN_ED: 29 | return "ed"; 30 | case NL802154_SCAN_ACTIVE: 31 | return "active"; 32 | case NL802154_SCAN_PASSIVE: 33 | return "passive"; 34 | case NL802154_SCAN_ENHANCED_ACTIVE: 35 | return "enhanced"; 36 | case NL802154_SCAN_RIT_PASSIVE: 37 | return "rit"; 38 | default: 39 | sprintf(scantypebuf, "Invalid scantype (%d)", scantype); 40 | return scantypebuf; 41 | } 42 | } 43 | 44 | /* for help */ 45 | #define SCAN_TYPES "Valid scanning types are: ed, active, passive, enhanced, rit." 46 | 47 | /* return 0 if ok, internal error otherwise */ 48 | static int get_scan_type(int *argc, char ***argv, enum nl802154_scan_types *type) 49 | { 50 | char *tpstr; 51 | 52 | if (*argc < 2) 53 | return 1; 54 | 55 | if (strcmp((*argv)[0], "type")) 56 | return 1; 57 | 58 | tpstr = (*argv)[1]; 59 | *argc -= 2; 60 | *argv += 2; 61 | 62 | if (strcmp(tpstr, "ed") == 0) { 63 | *type = NL802154_SCAN_ED; 64 | return 0; 65 | } else if (strcmp(tpstr, "active") == 0) { 66 | *type = NL802154_SCAN_ACTIVE; 67 | return 0; 68 | } else if (strcmp(tpstr, "passive") == 0) { 69 | *type = NL802154_SCAN_PASSIVE; 70 | return 0; 71 | } else if (strcmp(tpstr, "enhanced") == 0) { 72 | *type = NL802154_SCAN_ENHANCED_ACTIVE; 73 | return 0; 74 | } else if (strcmp(tpstr, "rit") == 0) { 75 | *type = NL802154_SCAN_RIT_PASSIVE; 76 | return 0; 77 | } 78 | 79 | fprintf(stderr, "invalid interface type %s\n", tpstr); 80 | return 2; 81 | } 82 | 83 | static int get_option_value(int *argc, char ***argv, const char *marker, unsigned long *result, bool *valid) 84 | { 85 | unsigned long value; 86 | char *tpstr, *end; 87 | 88 | *valid = false; 89 | 90 | if (*argc < 2) 91 | return 0; 92 | 93 | if (strcmp((*argv)[0], marker)) 94 | return 0; 95 | 96 | tpstr = (*argv)[1]; 97 | *argc -= 2; 98 | *argv += 2; 99 | 100 | value = strtoul(tpstr, &end, 0); 101 | if (*end != '\0') 102 | return 1; 103 | 104 | *result = value; 105 | *valid = true; 106 | 107 | return 0; 108 | } 109 | 110 | static int scan_trigger_handler(struct nl802154_state *state, 111 | struct nl_cb *cb, 112 | struct nl_msg *msg, 113 | int argc, char **argv, 114 | enum id_input id) 115 | { 116 | enum nl802154_scan_types type; 117 | unsigned long page, channels, duration; 118 | int tpset; 119 | bool valid_page, valid_channels, valid_duration; 120 | 121 | if (argc < 2) 122 | return 1; 123 | 124 | tpset = get_scan_type(&argc, &argv, &type); 125 | if (tpset) 126 | return tpset; 127 | 128 | tpset = get_option_value(&argc, &argv, "page", &page, &valid_page); 129 | if (tpset) 130 | return tpset; 131 | if (valid_page && page > UINT8_MAX) 132 | return 1; 133 | 134 | tpset = get_option_value(&argc, &argv, "channels", &channels, &valid_channels); 135 | if (tpset) 136 | return tpset; 137 | if (valid_channels && channels > UINT32_MAX) 138 | return 1; 139 | 140 | tpset = get_option_value(&argc, &argv, "duration", &duration, &valid_duration); 141 | if (tpset) 142 | return tpset; 143 | if (valid_duration && duration > UINT8_MAX) 144 | return 1; 145 | 146 | if (argc) 147 | return 1; 148 | 149 | /* Mandatory argument */ 150 | NLA_PUT_U8(msg, NL802154_ATTR_SCAN_TYPE, type); 151 | /* Optional arguments */ 152 | if (valid_duration) 153 | NLA_PUT_U8(msg, NL802154_ATTR_SCAN_DURATION, duration); 154 | if (valid_page) 155 | NLA_PUT_U8(msg, NL802154_ATTR_PAGE, page); 156 | if (valid_channels) 157 | NLA_PUT_U32(msg, NL802154_ATTR_SCAN_CHANNELS, channels); 158 | 159 | /* TODO: support IES parameters for active scans */ 160 | 161 | return 0; 162 | 163 | nla_put_failure: 164 | return -ENOBUFS; 165 | } 166 | 167 | static int scan_abort_handler(struct nl802154_state *state, 168 | struct nl_cb *cb, 169 | struct nl_msg *msg, 170 | int argc, char **argv, 171 | enum id_input id) 172 | { 173 | return 0; 174 | } 175 | 176 | struct ieee802154_addr { 177 | uint16_t pan_id; 178 | uint8_t addr_len; 179 | union { 180 | uint16_t short_addr; 181 | uint64_t extended_addr; 182 | }; 183 | struct ieee802154_addr *next; 184 | }; 185 | 186 | static struct ieee802154_addr *known_coord_list; 187 | 188 | static bool coord_addrs_are_equal(struct ieee802154_addr *a, 189 | struct ieee802154_addr *b) 190 | { 191 | if (a->pan_id != b->pan_id) 192 | return false; 193 | 194 | if (a->addr_len != b->addr_len) 195 | return false; 196 | 197 | if (a->addr_len == 2 && a->short_addr == b->short_addr) 198 | return true; 199 | else if (a->extended_addr == b->extended_addr) 200 | return true; 201 | 202 | return false; 203 | } 204 | 205 | static bool coord_is_known(struct ieee802154_addr *addr) 206 | { 207 | struct ieee802154_addr *item = known_coord_list; 208 | 209 | while (item) { 210 | if (coord_addrs_are_equal(addr, item)) 211 | return true; 212 | 213 | item = item->next; 214 | } 215 | 216 | return false; 217 | } 218 | 219 | static void record_coord(struct ieee802154_addr *addr) 220 | { 221 | struct ieee802154_addr *item; 222 | 223 | if (!known_coord_list) { 224 | known_coord_list = addr; 225 | return; 226 | } 227 | 228 | item = known_coord_list; 229 | if (!item->next) { 230 | item->next = addr; 231 | return; 232 | } 233 | 234 | do { 235 | item = item->next; 236 | } while (item->next); 237 | 238 | item->next = addr; 239 | } 240 | 241 | static void flush_coords(void) 242 | { 243 | struct ieee802154_addr *item, *next; 244 | 245 | if (!known_coord_list) 246 | return; 247 | 248 | item = known_coord_list; 249 | do { 250 | next = item->next; 251 | free(item); 252 | item = next; 253 | } while (item); 254 | } 255 | 256 | static struct ieee802154_addr *parse_new_coordinator(struct nlattr *nestedcoord) 257 | { 258 | struct nlattr *pan[NL802154_COORD_MAX + 1]; 259 | static struct nla_policy pan_policy[NL802154_COORD_MAX + 1] = { 260 | [NL802154_COORD_PANID] = { .type = NLA_U16, }, 261 | [NL802154_COORD_ADDR] = { .minlen = 2, .maxlen = 8, }, /* 2 or 8 */ 262 | [NL802154_COORD_CHANNEL] = { .type = NLA_U8, }, 263 | [NL802154_COORD_PAGE] = { .type = NLA_U8, }, 264 | [NL802154_COORD_PREAMBLE_CODE] = { .type = NLA_U8, }, 265 | [NL802154_COORD_MEAN_PRF] = { .type = NLA_U8, }, 266 | [NL802154_COORD_SUPERFRAME_SPEC] = { .type = NLA_U16, }, 267 | [NL802154_COORD_LINK_QUALITY] = { .type = NLA_U8, }, 268 | [NL802154_COORD_GTS_PERMIT] = { .type = NLA_FLAG, }, 269 | }; 270 | struct ieee802154_addr *addr; 271 | struct nlattr *coord; 272 | char dev[20]; 273 | int ret; 274 | 275 | ret = nla_parse_nested(pan, NL802154_COORD_MAX, nestedcoord, pan_policy); 276 | if (ret < 0) { 277 | fprintf(stderr, "failed to parse nested attributes! (ret = %d)\n", 278 | ret); 279 | return NULL; 280 | } 281 | if (!pan[NL802154_COORD_PANID] || !pan[NL802154_COORD_ADDR]) 282 | return NULL; 283 | 284 | addr = malloc(sizeof(*addr)); 285 | if (!addr) 286 | return NULL; 287 | 288 | addr->pan_id = nla_get_u16(pan[NL802154_COORD_PANID]); 289 | coord = pan[NL802154_COORD_ADDR]; 290 | addr->addr_len = nla_len(coord); 291 | if (addr->addr_len == 2) 292 | addr->short_addr = nla_get_u16(coord); 293 | else 294 | addr->extended_addr = nla_get_u64(coord); 295 | 296 | return addr; 297 | } 298 | 299 | static int print_new_coordinator(struct nlattr *nestedcoord, 300 | struct nlattr *ifattr) 301 | { 302 | struct nlattr *pan[NL802154_COORD_MAX + 1]; 303 | static struct nla_policy pan_policy[NL802154_COORD_MAX + 1] = { 304 | [NL802154_COORD_PANID] = { .type = NLA_U16, }, 305 | [NL802154_COORD_ADDR] = { .minlen = 2, .maxlen = 8, }, /* 2 or 8 */ 306 | [NL802154_COORD_CHANNEL] = { .type = NLA_U8, }, 307 | [NL802154_COORD_PAGE] = { .type = NLA_U8, }, 308 | [NL802154_COORD_PREAMBLE_CODE] = { .type = NLA_U8, }, 309 | [NL802154_COORD_MEAN_PRF] = { .type = NLA_U8, }, 310 | [NL802154_COORD_SUPERFRAME_SPEC] = { .type = NLA_U16, }, 311 | [NL802154_COORD_LINK_QUALITY] = { .type = NLA_U8, }, 312 | [NL802154_COORD_GTS_PERMIT] = { .type = NLA_FLAG, }, 313 | }; 314 | char dev[20]; 315 | int ret; 316 | 317 | ret = nla_parse_nested(pan, NL802154_COORD_MAX, nestedcoord, pan_policy); 318 | if (ret < 0) { 319 | fprintf(stderr, "failed to parse nested attributes! (ret = %d)\n", 320 | ret); 321 | return NL_SKIP; 322 | } 323 | if (!pan[NL802154_COORD_PANID]) 324 | return NL_SKIP; 325 | 326 | printf("PAN 0x%04x", le16toh(nla_get_u16(pan[NL802154_COORD_PANID]))); 327 | if (ifattr) { 328 | if_indextoname(nla_get_u32(ifattr), dev); 329 | printf(" (on %s)", dev); 330 | } 331 | printf("\n"); 332 | if (pan[NL802154_COORD_ADDR]) { 333 | struct nlattr *coord = pan[NL802154_COORD_ADDR]; 334 | if (nla_len(coord) == 2) { 335 | uint16_t addr = nla_get_u16(coord); 336 | printf("\tcoordinator 0x%04x\n", le16toh(addr)); 337 | } else { 338 | uint64_t addr = nla_get_u64(coord); 339 | printf("\tcoordinator 0x%016" PRIx64 "\n", le64toh(addr)); 340 | } 341 | } 342 | if (pan[NL802154_COORD_PAGE]) { 343 | printf("\tpage %u\n", nla_get_u8(pan[NL802154_COORD_PAGE])); 344 | } 345 | if (pan[NL802154_COORD_CHANNEL]) { 346 | printf("\tchannel %u\n", nla_get_u8(pan[NL802154_COORD_CHANNEL])); 347 | } 348 | if (pan[NL802154_COORD_SUPERFRAME_SPEC]) { 349 | printf("\tsuperframe spec. 0x%x\n", nla_get_u16( 350 | pan[NL802154_COORD_SUPERFRAME_SPEC])); 351 | } 352 | if (pan[NL802154_COORD_LINK_QUALITY]) { 353 | printf("\tLQI %x\n", nla_get_u8( 354 | pan[NL802154_COORD_LINK_QUALITY])); 355 | } 356 | if (pan[NL802154_COORD_GTS_PERMIT]) { 357 | printf("\tGTS permitted\n"); 358 | } 359 | 360 | /* TODO: Beacon IES display/decoding */ 361 | 362 | return NL_OK; 363 | } 364 | 365 | static int parse_and_print_new_coordinator(struct nlattr *nestedcoord, 366 | struct nlattr *ifattr) 367 | { 368 | struct ieee802154_addr *addr; 369 | 370 | addr = parse_new_coordinator(nestedcoord); 371 | if (!addr) 372 | return NL_SKIP; 373 | 374 | if (coord_is_known(addr)) { 375 | free(addr); 376 | } else { 377 | record_coord(addr); 378 | print_new_coordinator(nestedcoord, ifattr); 379 | } 380 | 381 | return NL_OK; 382 | } 383 | 384 | static int parse_and_print_scan_event(struct nl_msg *msg, void *arg) 385 | { 386 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); 387 | struct nlattr *tb[NL802154_ATTR_MAX + 1]; 388 | struct nlattr *nestedcoord; 389 | 390 | nla_parse(tb, NL802154_ATTR_MAX, genlmsg_attrdata(gnlh, 0), 391 | genlmsg_attrlen(gnlh, 0), NULL); 392 | 393 | nestedcoord = tb[NL802154_ATTR_COORDINATOR]; 394 | if (!nestedcoord) { 395 | fprintf(stderr, "coordinator info missing!\n"); 396 | return NL_SKIP; 397 | } 398 | 399 | return parse_and_print_new_coordinator(nestedcoord, tb[NL802154_ATTR_IFINDEX]); 400 | } 401 | 402 | struct scan_done { 403 | volatile int done; 404 | int devidx; 405 | }; 406 | 407 | static int scan_message_handler(struct nl_msg *msg, void *arg) 408 | { 409 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); 410 | struct scan_done *sd = (struct scan_done *)arg; 411 | 412 | if (gnlh->cmd == NL802154_CMD_SCAN_EVENT) 413 | return parse_and_print_scan_event(msg, arg); 414 | 415 | if (gnlh->cmd != NL802154_CMD_SCAN_DONE && 416 | gnlh->cmd != NL802154_CMD_ABORT_SCAN) 417 | return 0; 418 | 419 | if (sd->devidx != -1) { 420 | struct nlattr *tb[NL802154_ATTR_MAX + 1]; 421 | nla_parse(tb, NL802154_ATTR_MAX, genlmsg_attrdata(gnlh, 0), 422 | genlmsg_attrlen(gnlh, 0), NULL); 423 | if (!tb[NL802154_ATTR_IFINDEX] || 424 | nla_get_u32(tb[NL802154_ATTR_IFINDEX]) != sd->devidx) 425 | return 0; 426 | } 427 | 428 | sd->done = 1; 429 | return 0; 430 | } 431 | 432 | static int no_seq_check(struct nl_msg *msg, void *arg) 433 | { 434 | return NL_OK; 435 | } 436 | 437 | struct abort_info { 438 | char *cmd[3]; 439 | struct nl802154_state *state; 440 | enum id_input id; 441 | struct scan_done *sd; 442 | }; 443 | 444 | static struct abort_info abort_info = { 445 | .cmd = { NULL, "scan", "abort" }, 446 | }; 447 | 448 | static void scan_sigint_handler(int signo) 449 | { 450 | /* dev scan abort */ 451 | handle_cmd(abort_info.state, abort_info.id, 3, abort_info.cmd); 452 | abort_info.sd->done = 1; 453 | nl_socket_set_nonblocking(abort_info.state->nl_sock); 454 | } 455 | 456 | static int scan_handler(struct nl802154_state *state, struct nl_cb *cb, 457 | struct nl_msg *msg, int argc, char **argv, 458 | enum id_input id) 459 | { 460 | struct scan_done sd; 461 | int ret, group; 462 | 463 | /* Abort scan upon SIGINT */ 464 | abort_info.cmd[0] = argv[0]; 465 | abort_info.state = state; 466 | abort_info.id = id; 467 | abort_info.sd = &sd; 468 | signal(SIGINT, scan_sigint_handler); 469 | 470 | /* Create netlink message to trigger the scan */ 471 | msg = nlmsg_alloc(); 472 | if (!msg) { 473 | fprintf(stderr, "failed to allocate netlink message\n"); 474 | return 2; 475 | } 476 | 477 | cb = nl_cb_alloc(iwpan_debug ? NL_CB_DEBUG : NL_CB_DEFAULT); 478 | if (!cb) { 479 | fprintf(stderr, "failed to allocate netlink callbacks\n"); 480 | ret = 2; 481 | goto free_msg; 482 | } 483 | 484 | genlmsg_put(msg, 0, 0, state->nl802154_id, 0, 0, 485 | NL802154_CMD_TRIGGER_SCAN, 0); 486 | 487 | sd.devidx = if_nametoindex(*argv); 488 | if (sd.devidx == 0) 489 | sd.devidx = -1; 490 | 491 | NLA_PUT_U32(msg, NL802154_ATTR_IFINDEX, sd.devidx); 492 | 493 | /* Skip " scan" */ 494 | argc -= 2; 495 | argv += 2; 496 | 497 | /* Parse the "trigger" command */ 498 | ret = scan_trigger_handler(state, cb, msg, argc, argv, id); 499 | if (ret) 500 | goto nla_put_failure; 501 | 502 | /* Configure socket to receive messages in Scan multicast group */ 503 | group = genl_ctrl_resolve_grp(state->nl_sock, "nl802154", "scan"); 504 | if (group < 0) { 505 | ret = group; 506 | goto nla_put_failure; 507 | } 508 | 509 | ret = nl_socket_add_membership(state->nl_sock, group); 510 | if (ret) 511 | goto nla_put_failure; 512 | 513 | /* Install scan message handler */ 514 | nl_socket_set_cb(state->nl_sock, cb); 515 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, scan_message_handler, &sd); 516 | 517 | /* No sequence checking for multicast messages */ 518 | nl_socket_disable_seq_check(state->nl_sock); 519 | 520 | /* Trigger the scan */ 521 | ret = nl_send_auto_complete(state->nl_sock, msg); 522 | if (ret < 0) 523 | goto nla_put_failure; 524 | 525 | ret = 0; 526 | 527 | /* Receive messages */ 528 | sd.done = 0; 529 | do { 530 | nl_recvmsgs(state->nl_sock, cb); 531 | } while (!sd.done); 532 | 533 | flush_coords(); 534 | 535 | nla_put_failure: 536 | nl_cb_put(cb); 537 | free_msg: 538 | nlmsg_free(msg); 539 | 540 | return ret; 541 | } 542 | TOPLEVEL(scan, "type [page ] [channels ] [duration ]", 543 | 0, 0, CIB_NETDEV, scan_handler, 544 | "Scan on this virtual interface with the given configuration.\n" 545 | SCAN_TYPES); 546 | COMMAND(scan, abort, NULL, NL802154_CMD_ABORT_SCAN, 0, CIB_NETDEV, scan_abort_handler, 547 | "Abort ongoing scanning on this virtual interface"); 548 | COMMAND(scan, abort, NULL, NL802154_CMD_ABORT_SCAN, 0, CIB_PHY, scan_abort_handler, NULL); 549 | COMMAND(scan, trigger, 550 | "type [page ] [channels ] [duration ]", 551 | NL802154_CMD_TRIGGER_SCAN, 0, CIB_NETDEV, scan_trigger_handler, 552 | "Launch scanning on this virtual interface with the given configuration.\n" 553 | SCAN_TYPES); 554 | COMMAND(scan, trigger, 555 | "type [page ] [channels ] [duration ]", 556 | NL802154_CMD_TRIGGER_SCAN, 0, CIB_PHY, scan_trigger_handler, NULL); 557 | 558 | SECTION(beacons); 559 | 560 | static int send_beacons_handler(struct nl802154_state *state, struct nl_cb *cb, 561 | struct nl_msg *msg, int argc, char **argv, 562 | enum id_input id) 563 | { 564 | unsigned long interval; 565 | bool valid_interval; 566 | int tpset; 567 | 568 | tpset = get_option_value(&argc, &argv, "interval", &interval, &valid_interval); 569 | if (tpset) 570 | return tpset; 571 | if (valid_interval && interval > UINT8_MAX) 572 | return 1; 573 | 574 | if (argc) 575 | return 1; 576 | 577 | /* Optional arguments */ 578 | if (valid_interval) 579 | NLA_PUT_U8(msg, NL802154_ATTR_BEACON_INTERVAL, interval); 580 | 581 | return 0; 582 | 583 | nla_put_failure: 584 | return -ENOBUFS; 585 | } 586 | 587 | static int stop_beacons_handler(struct nl802154_state *state, struct nl_cb *cb, 588 | struct nl_msg *msg, int argc, char **argv, 589 | enum id_input id) 590 | { 591 | return 0; 592 | } 593 | 594 | COMMAND(beacons, stop, NULL, 595 | NL802154_CMD_STOP_BEACONS, 0, CIB_NETDEV, stop_beacons_handler, 596 | "Stop sending beacons on this interface."); 597 | COMMAND(beacons, send, "[interval ]", 598 | NL802154_CMD_SEND_BEACONS, 0, CIB_NETDEV, send_beacons_handler, 599 | "Send beacons on this virtual interface at a regular pace."); 600 | -------------------------------------------------------------------------------- /src/sections.c: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2014 Alexander Aring 2 | // 3 | // SPDX-License-Identifier: ISC 4 | 5 | #include "iwpan.h" 6 | 7 | SECTION(get); 8 | SECTION(set); 9 | -------------------------------------------------------------------------------- /wpan-hwsim/Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Alexander Aring 2 | # 3 | # SPDX-License-Identifier: ISC 4 | 5 | bin_PROGRAMS = wpan-hwsim 6 | 7 | wpan_hwsim_SOURCES = wpan-hwsim.c \ 8 | mac802154_hwsim.h 9 | 10 | wpan_hwsim_CFLAGS = $(AM_CFLAGS) $(LIBNL3_CFLAGS) 11 | wpan_hwsim_LDADD = $(LIBNL3_LIBS) 12 | -------------------------------------------------------------------------------- /wpan-hwsim/mac802154_hwsim.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2018 Alexander Aring 2 | // 3 | // SPDX-License-Identifier: ISC 4 | 5 | #ifndef __MAC802154_HWSIM_H 6 | #define __MAC802154_HWSIM_H 7 | 8 | /* mac802154 hwsim netlink commands 9 | * 10 | * @MAC802154_HWSIM_CMD_UNSPEC: unspecified command to catch error 11 | * @MAC802154_HWSIM_CMD_GET_RADIO: fetch information about existing radios 12 | * @MAC802154_HWSIM_CMD_SET_RADIO: change radio parameters during runtime 13 | * @MAC802154_HWSIM_CMD_NEW_RADIO: create a new radio with the given parameters 14 | * returns the radio ID (>= 0) or negative on errors, if successful 15 | * then multicast the result 16 | * @MAC802154_HWSIM_CMD_DEL_RADIO: destroy a radio, reply is multicasted 17 | * @MAC802154_HWSIM_CMD_GET_EDGE: fetch information about existing edges 18 | * @MAC802154_HWSIM_CMD_SET_EDGE: change edge parameters during runtime 19 | * @MAC802154_HWSIM_CMD_DEL_EDGE: delete edges between radios 20 | * @MAC802154_HWSIM_CMD_NEW_EDGE: create a new edge between two radios 21 | * @__MAC802154_HWSIM_CMD_MAX: enum limit 22 | */ 23 | enum { 24 | MAC802154_HWSIM_CMD_UNSPEC, 25 | 26 | MAC802154_HWSIM_CMD_GET_RADIO, 27 | MAC802154_HWSIM_CMD_SET_RADIO, 28 | MAC802154_HWSIM_CMD_NEW_RADIO, 29 | MAC802154_HWSIM_CMD_DEL_RADIO, 30 | 31 | MAC802154_HWSIM_CMD_GET_EDGE, 32 | MAC802154_HWSIM_CMD_SET_EDGE, 33 | MAC802154_HWSIM_CMD_DEL_EDGE, 34 | MAC802154_HWSIM_CMD_NEW_EDGE, 35 | 36 | __MAC802154_HWSIM_CMD_MAX, 37 | }; 38 | 39 | #define MAC802154_HWSIM_CMD_MAX (__MAC802154_HWSIM_MAX - 1) 40 | 41 | /* mac802154 hwsim netlink attributes 42 | * 43 | * @MAC802154_HWSIM_ATTR_UNSPEC: unspecified attribute to catch error 44 | * @MAC802154_HWSIM_ATTR_RADIO_ID: u32 attribute to identify the radio 45 | * @MAC802154_HWSIM_ATTR_EDGE: nested attribute of edges 46 | * @MAC802154_HWSIM_ATTR_EDGES: list if nested attributes which contains the 47 | * edge information according the radio id 48 | * @__MAC802154_HWSIM_ATTR_MAX: enum limit 49 | */ 50 | enum { 51 | MAC802154_HWSIM_ATTR_UNSPEC, 52 | MAC802154_HWSIM_ATTR_RADIO_ID, 53 | MAC802154_HWSIM_ATTR_RADIO_EDGE, 54 | MAC802154_HWSIM_ATTR_RADIO_EDGES, 55 | __MAC802154_HWSIM_ATTR_MAX, 56 | }; 57 | 58 | #define MAC802154_HWSIM_ATTR_MAX (__MAC802154_HWSIM_ATTR_MAX - 1) 59 | 60 | /* mac802154 hwsim edge netlink attributes 61 | * 62 | * @MAC802154_HWSIM_EDGE_ATTR_UNSPEC: unspecified attribute to catch error 63 | * @MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID: radio id where the edge points to 64 | * @MAC802154_HWSIM_EDGE_ATTR_LQI: LQI value which the endpoint radio will 65 | * receive for this edge 66 | * @__MAC802154_HWSIM_ATTR_MAX: enum limit 67 | */ 68 | enum { 69 | MAC802154_HWSIM_EDGE_ATTR_UNSPEC, 70 | MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID, 71 | MAC802154_HWSIM_EDGE_ATTR_LQI, 72 | __MAC802154_HWSIM_EDGE_ATTR_MAX, 73 | }; 74 | 75 | #define MAC802154_HWSIM_EDGE_ATTR_MAX (__MAC802154_HWSIM_EDGE_ATTR_MAX - 1) 76 | 77 | #endif /* __MAC802154_HWSIM_H */ 78 | -------------------------------------------------------------------------------- /wpan-hwsim/wpan-hwsim.c: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2018 Alexander Aring 2 | // 3 | // SPDX-License-Identifier: ISC 4 | 5 | /* Linux IEEE 802.15.4 hwsim tool */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "mac802154_hwsim.h" 22 | #include "config.h" 23 | 24 | static struct nl_sock *nl_sock; 25 | static int nlhwsim_id; 26 | 27 | static int nlhwsim_init(void) 28 | { 29 | int err; 30 | 31 | nl_sock = nl_socket_alloc(); 32 | if (!nl_sock) { 33 | fprintf(stderr, "Failed to allocate netlink socket.\n"); 34 | return -ENOMEM; 35 | } 36 | 37 | nl_socket_set_buffer_size(nl_sock, 8192, 8192); 38 | 39 | if (genl_connect(nl_sock)) { 40 | fprintf(stderr, "Failed to connect to generic netlink.\n"); 41 | err = -ENOLINK; 42 | goto out_handle_destroy; 43 | } 44 | 45 | nlhwsim_id = genl_ctrl_resolve(nl_sock, "MAC802154_HWSIM"); 46 | if (nlhwsim_id < 0) { 47 | fprintf(stderr, "MAC802154_HWSIM not found.\n"); 48 | err = -ENOENT; 49 | nl_close(nl_sock); 50 | goto out_handle_destroy; 51 | } 52 | 53 | return 0; 54 | 55 | out_handle_destroy: 56 | nl_socket_free(nl_sock); 57 | return err; 58 | } 59 | 60 | static void nlhwsim_cleanup(void) 61 | { 62 | nl_close(nl_sock); 63 | nl_socket_free(nl_sock); 64 | } 65 | 66 | static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, 67 | void *arg) 68 | { 69 | int *ret = arg; 70 | *ret = err->error; 71 | return NL_STOP; 72 | } 73 | 74 | static int hwsim_create(void) 75 | { 76 | struct nl_msg *msg; 77 | struct nl_cb *cb; 78 | int idx = 0, ret; 79 | 80 | cb = nl_cb_alloc(NL_CB_DEFAULT); 81 | if (!cb) 82 | return -ENOMEM; 83 | 84 | msg = nlmsg_alloc(); 85 | if (!msg) { 86 | nl_cb_put(cb); 87 | return -ENOMEM; 88 | } 89 | 90 | genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, nlhwsim_id, 0, 0, 91 | MAC802154_HWSIM_CMD_NEW_RADIO, 0); 92 | ret = nl_send_auto(nl_sock, msg); 93 | if (ret < 0) { 94 | nl_cb_put(cb); 95 | return ret; 96 | } 97 | 98 | nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &idx); 99 | nl_recvmsgs(nl_sock, cb); 100 | printf("wpan_hwsim radio%d registered.\n", idx); 101 | 102 | nl_cb_put(cb); 103 | 104 | return 0; 105 | } 106 | 107 | static int nl_msg_dot_cb(struct nl_msg* msg, void* arg) 108 | { 109 | static struct nla_policy edge_policy[MAC802154_HWSIM_EDGE_ATTR_MAX + 1] = { 110 | [MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID] = { .type = NLA_U32 }, 111 | [MAC802154_HWSIM_EDGE_ATTR_LQI] = { .type = NLA_U8 }, 112 | }; 113 | struct nlmsghdr *nlh = nlmsg_hdr(msg); 114 | struct genlmsghdr *gnlh = (struct genlmsghdr*) nlmsg_data(nlh); 115 | struct nlattr *attrs[MAC802154_HWSIM_ATTR_MAX + 1]; 116 | struct nlattr *edge[MAC802154_HWSIM_EDGE_ATTR_MAX + 1]; 117 | unsigned int *ignore_idx = arg; 118 | struct nlattr *nl_edge; 119 | uint32_t idx, idx2; 120 | uint8_t lqi = 0xff; 121 | int rem_edge; 122 | int rc; 123 | 124 | nla_parse(attrs, MAC802154_HWSIM_ATTR_MAX, genlmsg_attrdata(gnlh, 0), 125 | genlmsg_attrlen(gnlh, 0), NULL); 126 | 127 | if (!attrs[MAC802154_HWSIM_ATTR_RADIO_ID]) 128 | return NL_SKIP; 129 | 130 | idx = nla_get_u32(attrs[MAC802154_HWSIM_ATTR_RADIO_ID]); 131 | if (idx == *ignore_idx) 132 | return NL_SKIP; 133 | 134 | if (!attrs[MAC802154_HWSIM_ATTR_RADIO_EDGES]) 135 | return NL_SKIP; 136 | 137 | nla_for_each_nested(nl_edge, attrs[MAC802154_HWSIM_ATTR_RADIO_EDGES], 138 | rem_edge) { 139 | rc = nla_parse_nested(edge, MAC802154_HWSIM_EDGE_ATTR_MAX, 140 | nl_edge, edge_policy); 141 | if (rc) 142 | return NL_SKIP; 143 | 144 | idx2 = nla_get_u32(edge[MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID]); 145 | if (idx2 == *ignore_idx) 146 | continue; 147 | 148 | lqi = nla_get_u32(edge[MAC802154_HWSIM_EDGE_ATTR_LQI]); 149 | printf("\t%" PRIu32 " -> %" PRIu32 "[label=%" PRIu8 "];\n", 150 | idx, idx2, lqi); 151 | } 152 | 153 | return NL_SKIP; 154 | } 155 | 156 | static int nl_msg_cb(struct nl_msg* msg, void* arg) 157 | { 158 | static struct nla_policy edge_policy[MAC802154_HWSIM_EDGE_ATTR_MAX + 1] = { 159 | [MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID] = { .type = NLA_U32 }, 160 | [MAC802154_HWSIM_EDGE_ATTR_LQI] = { .type = NLA_U8 }, 161 | }; 162 | struct nlmsghdr *nlh = nlmsg_hdr(msg); 163 | struct genlmsghdr *gnlh = (struct genlmsghdr*) nlmsg_data(nlh); 164 | struct nlattr *attrs[MAC802154_HWSIM_ATTR_MAX + 1]; 165 | struct nlattr *edge[MAC802154_HWSIM_EDGE_ATTR_MAX + 1]; 166 | unsigned int *ignore_idx = arg; 167 | struct nlattr *nl_edge; 168 | uint32_t idx; 169 | uint8_t lqi; 170 | int rem_edge; 171 | int rc; 172 | 173 | nla_parse(attrs, MAC802154_HWSIM_ATTR_MAX, genlmsg_attrdata(gnlh, 0), 174 | genlmsg_attrlen(gnlh, 0), NULL); 175 | 176 | if (!attrs[MAC802154_HWSIM_ATTR_RADIO_ID]) 177 | return NL_SKIP; 178 | 179 | idx = nla_get_u32(attrs[MAC802154_HWSIM_ATTR_RADIO_ID]); 180 | 181 | if (idx == *ignore_idx) 182 | return NL_SKIP; 183 | 184 | printf("wpan_hwsim radio%" PRIu32 ":\n", idx); 185 | 186 | if (!attrs[MAC802154_HWSIM_ATTR_RADIO_EDGES]) 187 | return NL_SKIP; 188 | 189 | nla_for_each_nested(nl_edge, attrs[MAC802154_HWSIM_ATTR_RADIO_EDGES], 190 | rem_edge) { 191 | rc = nla_parse_nested(edge, MAC802154_HWSIM_EDGE_ATTR_MAX, 192 | nl_edge, edge_policy); 193 | if (rc) 194 | return NL_SKIP; 195 | 196 | idx = nla_get_u32(edge[MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID]); 197 | 198 | if (idx == *ignore_idx) 199 | continue; 200 | 201 | printf("\tedge:\n"); 202 | printf("\t\tradio%" PRIu32 "\n", idx); 203 | lqi = nla_get_u32(edge[MAC802154_HWSIM_EDGE_ATTR_LQI]); 204 | printf("\t\tlqi: 0x%02x\n", lqi); 205 | } 206 | 207 | return NL_SKIP; 208 | } 209 | 210 | static int hwsim_dump(bool dot, int unsigned ignore_idx) 211 | { 212 | struct nl_msg *msg; 213 | int rc; 214 | 215 | nl_socket_modify_cb(nl_sock, NL_CB_VALID, NL_CB_CUSTOM, 216 | dot ? nl_msg_dot_cb : nl_msg_cb, &ignore_idx); 217 | msg = nlmsg_alloc(); 218 | if (!msg) 219 | return -ENOMEM; 220 | 221 | genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, nlhwsim_id, 0, NLM_F_DUMP, 222 | MAC802154_HWSIM_CMD_GET_RADIO, 0); 223 | 224 | if (dot) 225 | printf("digraph {\n"); 226 | 227 | rc = nl_send_sync(nl_sock, msg); 228 | if (rc < 0) 229 | return rc; 230 | 231 | if (dot) 232 | printf("}\n"); 233 | 234 | return rc; 235 | } 236 | 237 | static int hwsim_del(uint32_t idx) 238 | { 239 | struct nl_msg *msg; 240 | struct nl_cb *cb; 241 | int err = 0; 242 | int rc; 243 | 244 | cb = nl_cb_alloc(NL_CB_DEFAULT); 245 | if (!cb) 246 | return -ENOMEM; 247 | 248 | msg = nlmsg_alloc(); 249 | if (!msg) { 250 | nl_cb_put(cb); 251 | return -ENOMEM; 252 | } 253 | 254 | genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, nlhwsim_id, 0, 0, 255 | MAC802154_HWSIM_CMD_DEL_RADIO, 0); 256 | nla_put_u32(msg, MAC802154_HWSIM_ATTR_RADIO_ID, idx); 257 | 258 | nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); 259 | rc = nl_send_auto(nl_sock, msg); 260 | nl_recvmsgs(nl_sock, cb); 261 | if (err < 0) 262 | fprintf(stderr, "Failed to remove radio: %s\n", strerror(abs(err))); 263 | nl_cb_put(cb); 264 | 265 | return rc; 266 | } 267 | 268 | static int hwsim_cmd_edge(int cmd, uint32_t idx, uint32_t idx2, uint8_t lqi) 269 | { 270 | struct nlattr* edge; 271 | struct nl_msg *msg; 272 | struct nl_cb *cb; 273 | int err = 0; 274 | int rc; 275 | 276 | cb = nl_cb_alloc(NL_CB_DEFAULT); 277 | if (!cb) 278 | return -ENOMEM; 279 | 280 | msg = nlmsg_alloc(); 281 | if (!msg) { 282 | nl_cb_put(cb); 283 | return -ENOMEM; 284 | } 285 | 286 | genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, nlhwsim_id, 0, 0, 287 | cmd, 0); 288 | nla_put_u32(msg, MAC802154_HWSIM_ATTR_RADIO_ID, idx); 289 | 290 | edge = nla_nest_start(msg, MAC802154_HWSIM_ATTR_RADIO_EDGE); 291 | if (!edge) { 292 | nl_cb_put(cb); 293 | return -ENOMEM; 294 | } 295 | 296 | nla_put_u32(msg, MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID, idx2); 297 | if (cmd == MAC802154_HWSIM_CMD_SET_EDGE) 298 | nla_put_u8(msg, MAC802154_HWSIM_EDGE_ATTR_LQI, lqi); 299 | 300 | nla_nest_end(msg, edge); 301 | 302 | nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); 303 | rc = nl_send_auto(nl_sock, msg); 304 | nl_recvmsgs(nl_sock, cb); 305 | if (err < 0) 306 | fprintf(stderr, "Failed to add or remove edge: %s\n", strerror(abs(err))); 307 | nl_cb_put(cb); 308 | 309 | return rc; 310 | } 311 | 312 | static int hwsim_do_cmd(uint32_t cmd, unsigned int idx, unsigned int idx2, 313 | uint8_t lqi, bool dot, unsigned int ignore_idx) 314 | { 315 | int rc; 316 | 317 | rc = nlhwsim_init(); 318 | if (rc) 319 | return 1; 320 | 321 | switch (cmd) { 322 | case MAC802154_HWSIM_CMD_GET_RADIO: 323 | rc = hwsim_dump(dot, ignore_idx); 324 | break; 325 | case MAC802154_HWSIM_CMD_DEL_RADIO: 326 | rc = hwsim_del(idx); 327 | break; 328 | case MAC802154_HWSIM_CMD_NEW_RADIO: 329 | rc = hwsim_create(); 330 | break; 331 | case MAC802154_HWSIM_CMD_NEW_EDGE: 332 | case MAC802154_HWSIM_CMD_DEL_EDGE: 333 | case MAC802154_HWSIM_CMD_SET_EDGE: 334 | rc = hwsim_cmd_edge(cmd, idx, idx2, lqi); 335 | break; 336 | default: 337 | rc = -EINVAL; 338 | break; 339 | } 340 | 341 | nlhwsim_cleanup(); 342 | 343 | return rc; 344 | } 345 | 346 | static void print_usage(void) 347 | { 348 | printf("wpan_hwsim [OPTION...] [CMD...]\n"); 349 | printf("\n"); 350 | printf(" possible options:\n"); 351 | printf(" -h, --help show this help\n"); 352 | printf(" -v, --version show version\n"); 353 | printf(" -d, --dot dump topology as dot format\n"); 354 | printf(" -i, --ignore filter one node from dump (useful for virtual monitors)\n"); 355 | printf("\n"); 356 | printf(" possible commands:\n"); 357 | printf(" add add new hwsim radio\n"); 358 | printf(" del IDX del hwsim radio according idx\n"); 359 | printf(" edge add IDX IDX add edge between radios\n"); 360 | printf(" edge del IDX IDX delete edge between radios\n"); 361 | printf(" edge lqi IDX IDX LQI set lqi value for a specific edge\n"); 362 | printf("\n"); 363 | printf(" To dump all node information just call this program without any command\n"); 364 | } 365 | 366 | static void print_version(void) 367 | { 368 | printf("wpan-hwsim " PACKAGE_VERSION "\n"); 369 | } 370 | 371 | int main(int argc, const char *argv[]) 372 | { 373 | unsigned long int idx, idx2, lqi = 0, ignore_idx = ULONG_MAX; 374 | bool dot = false; 375 | int cmd; 376 | int rc; 377 | int c; 378 | 379 | while (1) { 380 | int option_index = 0; 381 | static struct option long_options[] = { 382 | {"help", no_argument, 0, 'h' }, 383 | {"version", no_argument, 0, 'v' }, 384 | {"dot", no_argument, 0, 'd' }, 385 | {"ignore", required_argument, 0, 'i' }, 386 | {0, 0, 0, 0 } 387 | }; 388 | 389 | c = getopt_long(argc, (char **)argv, "vhdi:", 390 | long_options, &option_index); 391 | if (c == -1) 392 | break; 393 | 394 | switch (c) { 395 | case 'v': 396 | print_version(); 397 | return EXIT_SUCCESS; 398 | case 'h': 399 | print_usage(); 400 | return EXIT_SUCCESS; 401 | case 'd': 402 | dot = true; 403 | break; 404 | case 'i': 405 | ignore_idx = strtoul(optarg, NULL, 0); 406 | if (ignore_idx == ULONG_MAX) { 407 | fprintf(stderr, "Invalid radio index for ignore argument\n"); 408 | return EXIT_FAILURE; 409 | } 410 | break; 411 | default: 412 | print_usage(); 413 | return EXIT_FAILURE; 414 | } 415 | } 416 | 417 | if (optind + 1 > argc) { 418 | rc = hwsim_do_cmd(MAC802154_HWSIM_CMD_GET_RADIO, 0, 0, 0, dot, 419 | ignore_idx); 420 | if (rc < 0) 421 | return EXIT_FAILURE; 422 | else 423 | goto out; 424 | } 425 | 426 | if (!strncmp(argv[optind], "add", 3)) { 427 | rc = hwsim_do_cmd(MAC802154_HWSIM_CMD_NEW_RADIO, 0, 0, 0, 0, 0); 428 | if (rc < 0) 429 | return EXIT_FAILURE; 430 | } else if (!strncmp(argv[optind], "del", 3)) { 431 | if (optind + 2 > argc) { 432 | fprintf(stderr, "Missing radio index for delete\n"); 433 | return EXIT_FAILURE; 434 | } else { 435 | idx = strtoul(argv[optind + 1], NULL, 0); 436 | if (idx == ULONG_MAX) { 437 | fprintf(stderr, "Invalid radio index for delete\n"); 438 | return EXIT_FAILURE; 439 | } 440 | 441 | rc = hwsim_do_cmd(MAC802154_HWSIM_CMD_DEL_RADIO, idx, 0, 0, 0, 0); 442 | if (rc < 0) 443 | return EXIT_FAILURE; 444 | } 445 | } else if (!strncmp(argv[optind], "edge", 4)) { 446 | if (optind + 4 > argc) { 447 | fprintf(stderr, "Missing edge radio index information\n"); 448 | return EXIT_FAILURE; 449 | } else { 450 | if (!strncmp(argv[optind + 1], "add", 3)) { 451 | cmd = MAC802154_HWSIM_CMD_NEW_EDGE; 452 | } else if (!strncmp(argv[optind + 1], "del", 3)) { 453 | cmd = MAC802154_HWSIM_CMD_DEL_EDGE; 454 | } else if (!strncmp(argv[optind + 1], "lqi", 3)) { 455 | cmd = MAC802154_HWSIM_CMD_SET_EDGE; 456 | } else { 457 | fprintf(stderr, "Invalid edge command\n"); 458 | return EXIT_FAILURE; 459 | } 460 | 461 | if (cmd == MAC802154_HWSIM_CMD_SET_EDGE) { 462 | if (optind + 5 > argc) { 463 | fprintf(stderr, "LQI information missing\n"); 464 | return EXIT_FAILURE; 465 | } 466 | 467 | lqi = strtoul(argv[optind + 4], NULL, 0); 468 | if (lqi == ULONG_MAX || lqi > 0xff) { 469 | fprintf(stderr, "Invalid lqi value\n"); 470 | return EXIT_FAILURE; 471 | } 472 | } 473 | 474 | idx = strtoul(argv[optind + 2], NULL, 0); 475 | if (idx == ULONG_MAX) { 476 | fprintf(stderr, "Invalid first radio index for edge command\n"); 477 | return EXIT_FAILURE; 478 | } 479 | 480 | idx2 = strtoul(argv[optind + 3], NULL, 0); 481 | if (idx2 == ULONG_MAX) { 482 | fprintf(stderr, "Invalid second radio index for edge command\n"); 483 | return EXIT_FAILURE; 484 | } 485 | 486 | rc = hwsim_do_cmd(cmd, idx, idx2, lqi, 0, 0); 487 | if (rc < 0) 488 | return EXIT_FAILURE; 489 | } 490 | } else { 491 | fprintf(stderr, "Unknown command\n"); 492 | print_usage(); 493 | return EXIT_FAILURE; 494 | } 495 | 496 | out: 497 | return EXIT_SUCCESS; 498 | } 499 | -------------------------------------------------------------------------------- /wpan-ping/.gitignore: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2015 Stefan Schmidt 2 | # 3 | # SPDX-License-Identifier: ISC 4 | 5 | .deps/ 6 | wpan-ping 7 | -------------------------------------------------------------------------------- /wpan-ping/Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2015 Stefan Schmidt 2 | # 3 | # SPDX-License-Identifier: ISC 4 | 5 | bin_PROGRAMS = wpan-ping 6 | 7 | wpan_ping_SOURCES = wpan-ping.c 8 | 9 | wpan_ping_CFLAGS = $(AM_CFLAGS) $(LIBNL3_CFLAGS) 10 | wpan_ping_LDADD = $(LIBNL3_LIBS) 11 | 12 | EXTRA_DIST = README.wpan-ping 13 | -------------------------------------------------------------------------------- /wpan-ping/README.wpan-ping: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2015 Stefan Schmidt 2 | // 3 | // SPDX-License-Identifier: ISC 4 | 5 | wpan-ping aims to offer ping/ping6 like functionality on a IEEE 802.15.4 level. 6 | 7 | No control message protocol is defined so we will simply use DGRAM's over a 8 | plain socket with a server component to emulate the behaviour. 9 | 10 | One can specify packet count (-c) as well as size (-s) and the interface to be 11 | used. 12 | 13 | Example usage server side: 14 | -------------------------- 15 | ./wpan-ping -d 0x0001 #0x0001 is the client short address 16 | Server mode. Waiting for packets... 17 | 18 | Example usage client side: 19 | -------------------------- 20 | ./wpan-ping -a 0x0003 -c 5 -s 114 #0x0003 is the server short address 21 | PING 0x0003 (PAN ID 0xbeef) 114 data bytes 22 | 114 bytes from 0x0003 seq=0 time=22.0 ms 23 | 114 bytes from 0x0003 seq=1 time=27.9 ms 24 | 114 bytes from 0x0003 seq=2 time=21.3 ms 25 | 114 bytes from 0x0003 seq=3 time=21.3 ms 26 | 114 bytes from 0x0003 seq=4 time=20.3 ms 27 | 28 | --- 0x0003 ping statistics --- 29 | 5 packets transmitted, 5 received, 0% packet loss 30 | rtt min/avg/max = 20.261/22.539/27.895 ms 31 | -------------------------------------------------------------------------------- /wpan-ping/wpan-ping.c: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2015 Stefan Schmidt 2 | // 3 | // SPDX-License-Identifier: ISC 4 | 5 | /* Linux IEEE 802.15.4 ping tool */ 6 | 7 | #ifdef HAVE_CONFIG_H 8 | #include 9 | #endif 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "../src/nl802154.h" 34 | 35 | #define MIN_PAYLOAD_LEN 5 36 | #define MAX_PAYLOAD_LEN 105 //116 with short address 37 | #define IEEE802154_ADDR_LEN 8 38 | /* Set the dispatch header to not 6lowpan for compat */ 39 | #define NOT_A_6LOWPAN_FRAME 0x00 40 | #define DEFAULT_INTERVAL 500 41 | 42 | #define DEBUG 0 43 | 44 | enum { 45 | IEEE802154_ADDR_NONE = 0x0, 46 | IEEE802154_ADDR_SHORT = 0x2, 47 | IEEE802154_ADDR_LONG = 0x3, 48 | }; 49 | 50 | struct ieee802154_addr_sa { 51 | int addr_type; 52 | uint16_t pan_id; 53 | union { 54 | uint8_t hwaddr[IEEE802154_ADDR_LEN]; 55 | uint16_t short_addr; 56 | }; 57 | }; 58 | 59 | struct sockaddr_ieee802154 { 60 | sa_family_t family; 61 | struct ieee802154_addr_sa addr; 62 | }; 63 | 64 | #ifdef _GNU_SOURCE 65 | static const struct option perf_long_opts[] = { 66 | { "daemon", no_argument, NULL, 'd' }, 67 | { "address", required_argument, NULL, 'a' }, 68 | { "extended", no_argument, NULL, 'e' }, 69 | { "count", required_argument, NULL, 'c' }, 70 | { "size", required_argument, NULL, 's' }, 71 | { "interface", required_argument, NULL, 'i' }, 72 | { "version", no_argument, NULL, 'v' }, 73 | { "help", no_argument, NULL, 'h' }, 74 | { NULL, 0, NULL, 0 }, 75 | }; 76 | #endif 77 | 78 | struct config { 79 | char packet_len; 80 | unsigned short packets; 81 | bool extended; 82 | bool server; 83 | char *interface; 84 | struct nl_sock *nl_sock; 85 | int nl802154_id; 86 | struct sockaddr_ieee802154 src; 87 | struct sockaddr_ieee802154 dst; 88 | unsigned short interval; 89 | }; 90 | 91 | extern char *optarg; 92 | 93 | static void usage(const char *name) { 94 | printf("Usage: %s OPTIONS\n" 95 | "OPTIONS:\n" 96 | "--daemon |-d\n" 97 | "--address | -a server address (short e.g. 0x1234 or extended e.g. 00:11:22:33:44:55:66:77)\n" 98 | "--extended | -e use extended addressing scheme for -a / --address (default is the short)\n" 99 | "--count | -c number of packets\n" 100 | "--size | -s packet length\n" 101 | "--interface | -i listen on this interface (default wpan0)\n" 102 | "--interval | -I wait interval in milliseconds between sending packets (default 500ms)\n" 103 | "--version | -v print out version\n" 104 | "--help | -h this usage text\n", name); 105 | } 106 | 107 | static int nl802154_init(struct config *conf) 108 | { 109 | int err; 110 | 111 | conf->nl_sock = nl_socket_alloc(); 112 | if (!conf->nl_sock) { 113 | fprintf(stderr, "Failed to allocate netlink socket.\n"); 114 | return -ENOMEM; 115 | } 116 | 117 | nl_socket_set_buffer_size(conf->nl_sock, 8192, 8192); 118 | 119 | if (genl_connect(conf->nl_sock)) { 120 | fprintf(stderr, "Failed to connect to generic netlink.\n"); 121 | err = -ENOLINK; 122 | goto out_handle_destroy; 123 | } 124 | 125 | conf->nl802154_id = genl_ctrl_resolve(conf->nl_sock, "nl802154"); 126 | if (conf->nl802154_id < 0) { 127 | fprintf(stderr, "nl802154 not found.\n"); 128 | err = -ENOENT; 129 | goto out_handle_destroy; 130 | } 131 | 132 | return 0; 133 | 134 | out_handle_destroy: 135 | nl_socket_free(conf->nl_sock); 136 | return err; 137 | } 138 | 139 | static void nl802154_cleanup(struct config *conf) 140 | { 141 | nl_close(conf->nl_sock); 142 | nl_socket_free(conf->nl_sock); 143 | } 144 | 145 | static int nl_msg_cb(struct nl_msg* msg, void* arg) 146 | { 147 | struct config *conf = arg; 148 | struct nlmsghdr *nlh = nlmsg_hdr(msg); 149 | struct nlattr *attrs[NL802154_ATTR_MAX+1]; 150 | uint64_t temp; 151 | 152 | struct genlmsghdr *gnlh = (struct genlmsghdr*) nlmsg_data(nlh); 153 | 154 | nla_parse(attrs, NL802154_ATTR_MAX, genlmsg_attrdata(gnlh, 0), 155 | genlmsg_attrlen(gnlh, 0), NULL); 156 | 157 | if (!attrs[NL802154_ATTR_SHORT_ADDR] || !attrs[NL802154_ATTR_PAN_ID] 158 | || !attrs[NL802154_ATTR_EXTENDED_ADDR]) 159 | return NL_SKIP; 160 | 161 | conf->src.family = AF_IEEE802154; 162 | conf->src.addr.pan_id = conf->dst.addr.pan_id = nla_get_u16(attrs[NL802154_ATTR_PAN_ID]); 163 | 164 | if (!conf->extended) { 165 | conf->src.addr.addr_type = IEEE802154_ADDR_SHORT; 166 | conf->src.addr.short_addr = nla_get_u16(attrs[NL802154_ATTR_SHORT_ADDR]); 167 | } else { 168 | conf->src.addr.addr_type = IEEE802154_ADDR_LONG; 169 | temp = htobe64(nla_get_u64(attrs[NL802154_ATTR_EXTENDED_ADDR])); 170 | memcpy(&conf->src.addr.hwaddr, &temp, IEEE802154_ADDR_LEN); 171 | } 172 | 173 | return NL_SKIP; 174 | } 175 | 176 | static int get_interface_info(struct config *conf) { 177 | struct nl_msg *msg; 178 | 179 | nl802154_init(conf); 180 | 181 | /* Build and send message */ 182 | nl_socket_modify_cb(conf->nl_sock, NL_CB_VALID, NL_CB_CUSTOM, nl_msg_cb, conf); 183 | msg = nlmsg_alloc(); 184 | genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, conf->nl802154_id, 0, NLM_F_DUMP, NL802154_CMD_GET_INTERFACE, 0); 185 | nla_put_string(msg, NL802154_ATTR_IFNAME, conf->interface); 186 | nl_send_sync(conf->nl_sock, msg); 187 | 188 | nl802154_cleanup(conf); 189 | return 0; 190 | } 191 | 192 | #if DEBUG 193 | static void dump_packet(unsigned char *buf, int len) { 194 | int i; 195 | 196 | fprintf(stdout, "Packet payload:"); 197 | for (i = 0; i < len; i++) { 198 | printf(" %x", buf[i]); 199 | } 200 | printf("\n"); 201 | } 202 | #endif 203 | 204 | static int generate_packet(unsigned char *buf, struct config *conf, unsigned int seq_num) { 205 | int i; 206 | 207 | buf[0] = NOT_A_6LOWPAN_FRAME; 208 | buf[1] = conf->packet_len; 209 | buf[2] = seq_num >> 8; /* Upper byte */ 210 | buf[3] = seq_num & 0xFF; /* Lower byte */ 211 | for (i = 4; i < conf->packet_len; i++) { 212 | buf[i] = 0xAB; 213 | } 214 | 215 | return 0; 216 | } 217 | 218 | static int print_address(char *addr, uint8_t dst_extended[IEEE802154_ADDR_LEN]) 219 | { 220 | snprintf(addr, 24, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", dst_extended[0], 221 | dst_extended[1], dst_extended[2], dst_extended[3], dst_extended[4], 222 | dst_extended[5], dst_extended[6], dst_extended[7]); 223 | return 0; 224 | } 225 | 226 | static void sleeping(struct timeval ping_start_time, struct timeval timeout) { 227 | struct timeval curr_time; 228 | long sec, usec, interval_usec, timeout_usec; 229 | long sleep_usec = 0; 230 | gettimeofday(&curr_time, NULL); 231 | sec = curr_time.tv_sec - ping_start_time.tv_sec; 232 | usec = curr_time.tv_usec - ping_start_time.tv_usec; 233 | if (usec < 0) { 234 | usec += 1000000; 235 | sec--; 236 | } 237 | interval_usec = sec * 1000000 + usec; 238 | timeout_usec = timeout.tv_sec * 1000000 + timeout.tv_usec; 239 | if (interval_usec < timeout_usec) { 240 | sleep_usec = timeout_usec - interval_usec; 241 | usleep(sleep_usec); 242 | } 243 | } 244 | 245 | static int measure_roundtrip(struct config *conf, int sd) { 246 | unsigned char *buf; 247 | struct timeval ping_start_time, start_time, end_time, timeout; 248 | long sec = 0, usec = 0; 249 | long sec_max = 0, usec_max = 0; 250 | long sec_min = 2147483647, usec_min = 2147483647; 251 | long sum_sec = 0, sum_usec = 0; 252 | int i, ret, count; 253 | unsigned short seq_num; 254 | float rtt_min = 0.0, rtt_avg = 0.0, rtt_max = 0.0; 255 | float packet_loss = 100.0; 256 | char addr[24]; 257 | 258 | if (conf->extended) 259 | print_address(addr, conf->dst.addr.hwaddr); 260 | 261 | if (conf->extended) 262 | fprintf(stdout, "PING %s (PAN ID 0x%04x) %i data bytes\n", 263 | addr, conf->dst.addr.pan_id, conf->packet_len); 264 | else 265 | fprintf(stdout, "PING 0x%04x (PAN ID 0x%04x) %i data bytes\n", 266 | conf->dst.addr.short_addr, conf->dst.addr.pan_id, conf->packet_len); 267 | buf = (unsigned char *)malloc(MAX_PAYLOAD_LEN); 268 | 269 | /* default 500ms seconds packet receive timeout */ 270 | if (conf->interval >= 1000) { /* when interval is larger than 1s */ 271 | double d_interval = (conf->interval * 1.0) / 1000; 272 | timeout.tv_sec = (int)d_interval; 273 | timeout.tv_usec = (int)((d_interval - timeout.tv_sec) * 1000000); 274 | } else { 275 | timeout.tv_sec = 0; 276 | timeout.tv_usec = conf->interval * 1000; 277 | } 278 | ret = setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&timeout,sizeof(struct timeval)); 279 | if (ret < 0) { 280 | perror("setsockopt receive timeout"); 281 | } 282 | 283 | count = 0; 284 | for (i = 0; i < conf->packets; i++) { 285 | gettimeofday(&ping_start_time, NULL); 286 | generate_packet(buf, conf, i); 287 | seq_num = (buf[2] << 8)| buf[3]; 288 | ret = sendto(sd, buf, conf->packet_len, 0, (struct sockaddr *)&conf->dst, sizeof(conf->dst)); 289 | if (ret < 0) { 290 | perror("sendto"); 291 | } 292 | gettimeofday(&start_time, NULL); 293 | ret = recv(sd, buf, conf->packet_len, 0); 294 | if (buf[0] != NOT_A_6LOWPAN_FRAME) { 295 | printf("Non-wpanping packet was received\n"); 296 | continue; 297 | } 298 | if (seq_num != ((buf[2] << 8)| buf[3])) { 299 | printf("Sequenze number did not match\n"); 300 | continue; 301 | } 302 | if (ret > 0) { 303 | gettimeofday(&end_time, NULL); 304 | count++; 305 | sec = end_time.tv_sec - start_time.tv_sec; 306 | sum_sec += sec; 307 | usec = end_time.tv_usec - start_time.tv_usec; 308 | if (usec < 0) { 309 | usec += 1000000; 310 | sec--; 311 | sum_sec--; 312 | } 313 | 314 | sum_usec += usec; 315 | if (sec > sec_max) 316 | sec_max = sec; 317 | else if (sec < sec_min) 318 | sec_min = sec; 319 | if (usec > usec_max) 320 | usec_max = usec; 321 | else if (usec < usec_min) 322 | usec_min = usec; 323 | if (sec > 0) 324 | fprintf(stdout, "Warning: packet return time over a second!\n"); 325 | 326 | if (conf->extended) 327 | fprintf(stdout, "%i bytes from %s seq=%i time=%.1f ms\n", ret, 328 | addr, (int)seq_num, (float)usec/1000); 329 | else 330 | fprintf(stdout, "%i bytes from 0x%04x seq=%i time=%.1f ms\n", ret, 331 | conf->dst.addr.short_addr, (int)seq_num, (float)usec/1000); 332 | } else 333 | fprintf(stderr, "Hit %i ms packet timeout\n", conf->interval); 334 | /* sleeping */ 335 | sleeping(ping_start_time, timeout); 336 | } 337 | 338 | if (count) 339 | packet_loss = 100 - ((100 * count)/conf->packets); 340 | 341 | if (usec_min) 342 | rtt_min = (float)usec_min/1000; 343 | if (sum_usec && count) 344 | rtt_avg = ((float)sum_usec/(float)count)/1000; 345 | if (usec_max) 346 | rtt_max = (float)usec_max/1000; 347 | 348 | if (conf->extended) 349 | fprintf(stdout, "\n--- %s ping statistics ---\n", addr); 350 | else 351 | fprintf(stdout, "\n--- 0x%04x ping statistics ---\n", conf->dst.addr.short_addr); 352 | fprintf(stdout, "%i packets transmitted, %i received, %.0f%% packet loss\n", 353 | conf->packets, count, packet_loss); 354 | fprintf(stdout, "rtt min/avg/max = %.3f/%.3f/%.3f ms\n", rtt_min, rtt_avg, rtt_max); 355 | 356 | free(buf); 357 | return 0; 358 | } 359 | 360 | static void init_server(int sd) { 361 | ssize_t len; 362 | unsigned char *buf; 363 | struct sockaddr_ieee802154 src; 364 | socklen_t addrlen; 365 | 366 | addrlen = sizeof(src); 367 | 368 | len = 0; 369 | fprintf(stdout, "Server mode. Waiting for packets...\n"); 370 | buf = (unsigned char *)malloc(MAX_PAYLOAD_LEN); 371 | 372 | while (1) { 373 | len = recvfrom(sd, buf, MAX_PAYLOAD_LEN, 0, (struct sockaddr *)&src, &addrlen); 374 | if (len < 0) { 375 | perror("recvfrom"); 376 | continue; 377 | } 378 | #if DEBUG 379 | dump_packet(buf, len); 380 | #endif 381 | if (buf[0] == NOT_A_6LOWPAN_FRAME) { 382 | /* Send same packet back */ 383 | len = sendto(sd, buf, len, 0, (struct sockaddr *)&src, addrlen); 384 | if (len < 0) { 385 | perror("sendto"); 386 | continue; 387 | } 388 | } 389 | } 390 | free(buf); 391 | } 392 | 393 | static int init_network(struct config *conf) { 394 | int sd; 395 | int ret; 396 | 397 | sd = socket(PF_IEEE802154, SOCK_DGRAM, 0); 398 | if (sd < 0) { 399 | perror("socket"); 400 | return 1; 401 | } 402 | 403 | /* Bind socket on this side */ 404 | ret = bind(sd, (struct sockaddr *)&conf->src, sizeof(conf->src)); 405 | if (ret) { 406 | perror("bind"); 407 | close(sd); 408 | return 1; 409 | } 410 | 411 | if (conf->server) 412 | init_server(sd); 413 | else 414 | measure_roundtrip(conf, sd); 415 | 416 | shutdown(sd, SHUT_RDWR); 417 | close(sd); 418 | return 0; 419 | } 420 | 421 | static int parse_dst_addr(struct config *conf, char *arg) 422 | { 423 | int i; 424 | 425 | if (!arg) 426 | return -1; 427 | 428 | /* PAN ID is filled from netlink in get_interface_info */ 429 | conf->dst.family = AF_IEEE802154; 430 | 431 | if (!conf->extended) { 432 | conf->dst.addr.addr_type = IEEE802154_ADDR_SHORT; 433 | conf->dst.addr.short_addr = strtol(arg, NULL, 16); 434 | return 0; 435 | } 436 | 437 | conf->dst.addr.addr_type = IEEE802154_ADDR_LONG; 438 | 439 | for (i = 0; i < IEEE802154_ADDR_LEN; i++) { 440 | int temp; 441 | char *cp = strchr(arg, ':'); 442 | if (cp) { 443 | *cp = 0; 444 | cp++; 445 | } 446 | if (sscanf(arg, "%x", &temp) != 1) 447 | return -1; 448 | if (temp < 0 || temp > 255) 449 | return -1; 450 | 451 | conf->dst.addr.hwaddr[i] = temp; 452 | if (!cp) 453 | break; 454 | arg = cp; 455 | } 456 | if (i < IEEE802154_ADDR_LEN - 1) 457 | return -1; 458 | 459 | return 0; 460 | } 461 | 462 | int main(int argc, char *argv[]) { 463 | int c, ret; 464 | struct config *conf; 465 | char *dst_addr = NULL; 466 | 467 | conf = calloc(1, sizeof(struct config)); 468 | 469 | /* Default to interface wpan0 if nothing else is given */ 470 | conf->interface = "wpan0"; 471 | 472 | /* Default to minimum packet size */ 473 | conf->packet_len = MIN_PAYLOAD_LEN; 474 | 475 | /* Default to short addressing */ 476 | conf->extended = false; 477 | 478 | /* Default to client mode */ 479 | conf->server = false; 480 | 481 | /* Default to 65535 packets being sent */ 482 | conf->packets = USHRT_MAX; 483 | 484 | /* Default to 500ms for interval value */ 485 | conf->interval = DEFAULT_INTERVAL; 486 | 487 | if (argc < 2) { 488 | usage(argv[0]); 489 | exit(1); 490 | } 491 | 492 | while (1) { 493 | #ifdef _GNU_SOURCE 494 | int opt_idx = -1; 495 | c = getopt_long(argc, argv, "a:ec:s:i:dvhI:", perf_long_opts, &opt_idx); 496 | #else 497 | c = getopt(argc, argv, "a:ec:s:i:dvhI:"); 498 | #endif 499 | if (c == -1) 500 | break; 501 | switch(c) { 502 | case 'a': 503 | dst_addr = optarg; 504 | break; 505 | case 'e': 506 | conf->extended = true; 507 | break; 508 | case 'd': 509 | conf->server = true; 510 | break; 511 | case 'c': 512 | conf->packets = atoi(optarg); 513 | break; 514 | case 's': 515 | conf->packet_len = atoi(optarg); 516 | if (conf->packet_len >= MAX_PAYLOAD_LEN || conf->packet_len < MIN_PAYLOAD_LEN) { 517 | printf("Packet size must be between %i and %i.\n", 518 | MIN_PAYLOAD_LEN, MAX_PAYLOAD_LEN - 1); 519 | free(conf); 520 | return 1; 521 | } 522 | break; 523 | case 'i': 524 | conf->interface = optarg; 525 | break; 526 | case 'I': 527 | conf->interval = atoi(optarg); 528 | break; 529 | case 'v': 530 | fprintf(stdout, "wpan-ping " PACKAGE_VERSION "\n"); 531 | free(conf); 532 | return 1; 533 | case 'h': 534 | usage(argv[0]); 535 | free(conf); 536 | return 1; 537 | default: 538 | usage(argv[0]); 539 | free(conf); 540 | return 1; 541 | } 542 | } 543 | 544 | get_interface_info(conf); 545 | 546 | if (!conf->server) { 547 | ret = parse_dst_addr(conf, dst_addr); 548 | if (ret< 0) { 549 | fprintf(stderr, "Address given in wrong format.\n"); 550 | return 1; 551 | } 552 | } 553 | init_network(conf); 554 | free(conf); 555 | return 0; 556 | } 557 | --------------------------------------------------------------------------------