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