├── .gitignore ├── demos ├── framesstatus │ ├── webserver │ │ ├── sgi1.txt │ │ ├── qbv1.txt │ │ ├── README.txt │ │ ├── stopcapture.sh │ │ ├── startcapture.sh │ │ ├── txrxbytes.sh │ │ ├── tsn.py │ │ └── templates │ │ │ └── fcap.html │ ├── README.txt │ ├── Makefile │ └── enetctscap.c └── cnc │ ├── templates │ ├── configp8021cb.html │ ├── configQav.html │ ├── index.html │ ├── configQbu.html │ ├── configQbv.html │ └── indexdevice.html │ ├── README.txt │ ├── 0001-lnctool-to-make-install-transapi-yang-model-proper.patch │ ├── 0002-automatic-python3-authorizing-with-root-password-non.patch │ └── static │ └── xml2json.js ├── lib ├── libtsn.pc.in ├── multicast.c └── genl_basic.c ├── main ├── readinput.h ├── Makefile ├── main.h ├── ptptool.c ├── fill.h ├── cmd.h ├── main.c ├── readinput.c └── fill.c ├── README ├── source_arm64.sh ├── LICENSE ├── samples └── pktgen │ ├── pktgen_sample01_simple.sh │ ├── pktgen_twoqueue.sh │ ├── functions.sh │ └── parameters.sh ├── tools ├── README.txt ├── event.c └── timestamping.c ├── Makefile └── include └── tsn └── genl_tsn.h /.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demos/framesstatus/webserver/sgi1.txt: -------------------------------------------------------------------------------- 1 | t0 0b -1 50000000 0 2 | t1 1b -1 50000000 0 3 | -------------------------------------------------------------------------------- /demos/framesstatus/webserver/qbv1.txt: -------------------------------------------------------------------------------- 1 | t0 11111111b 50000000 2 | t1 00000000b 50000000 3 | -------------------------------------------------------------------------------- /demos/framesstatus/webserver/README.txt: -------------------------------------------------------------------------------- 1 | This demo shows diagram for frame timestamping. 2 | 3 | How to Run Demo? 4 | ---------------- 5 | Run 'startcapture.sh' to capture the tx/rx frames. 6 | -------------------------------------------------------------------------------- /demos/framesstatus/webserver/stopcapture.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | #Copyright 2019 NXP 4 | # 5 | 6 | killall txrxbytes.sh 7 | killall enetctscap 8 | 9 | rm -f ./static/txrxpkts.json 10 | rm -f ./static/tsnframetstamp.json 11 | rm -f config.py 12 | -------------------------------------------------------------------------------- /lib/libtsn.pc.in: -------------------------------------------------------------------------------- 1 | libdir=@libdir@ 2 | includedir=@includedir@ 3 | 4 | Name: tsn 5 | Description: libtsn for NXP LS1028A 6 | Version: @version@ 7 | Requires: libnl-3.0 libnl-genl-3.0 libcjson readline 8 | Libs: -L${libdir} -ltsn 9 | Cflags: -I${includedir}/tsn 10 | -------------------------------------------------------------------------------- /main/readinput.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 | /* 3 | * Copyright 2018 NXP 4 | */ 5 | 6 | #ifndef READINPUT_H_ 7 | #define READINPUT_H_ 8 | 9 | #include "main.h" 10 | 11 | 12 | #define EDITOR "vi" 13 | 14 | void initialize_readline(void); 15 | char *readinput(const char *instruction, const char *tmpfile, FILE *output); 16 | 17 | #endif /* READINPUT_H_ */ 18 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | tsntool is a tool to configure TSN funtionalities in user space. 2 | It can be used for TSN endpoint and TSN switch using netlink 3 | interface. 4 | 5 | 1. Libraries requirement 6 | Libraries libnl-3.0, libtermcap, libreadline libcjson are needed. 7 | 8 | 2. How to compile the tsntool: 9 | 10 | 2.1 Setup cross compile environment 11 | 2.2 Compile binary: 12 | > source source_arm64.sh 13 | > make 14 | 15 | The tsntool and libtsn.so will be generated. 16 | -------------------------------------------------------------------------------- /demos/framesstatus/webserver/startcapture.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | #Copyright 2019 NXP 4 | # 5 | 6 | if [ -z "$1" ]; then 7 | echo 8 | echo usage: $0 [network-interface] 9 | echo 10 | echo e.g. $0 eth0 11 | echo 12 | echo shows bits-per-second 13 | exit 14 | fi 15 | 16 | mkdir -p /tmp/static/ 17 | 18 | ./txrxbytes.sh $1 & 19 | ./enetctscap -i $1 -c 0 -f & 20 | 21 | sleep 1 22 | 23 | ln -s /tmp/static/txrxpkts.json ./static/ 24 | ln -s /tmp/static/tsnframetstamp.json ./static/ 25 | 26 | echo "CONFIGPORT = \"$1\"" > config.py 27 | python tsn.py 28 | -------------------------------------------------------------------------------- /source_arm64.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ ! -n "$1" ] 3 | then 4 | echo "How to use:" 5 | echo "source source_arm64.sh " 6 | fi 7 | 8 | if [ ! -f $1/.config ] 9 | then 10 | echo "Not right kernel folder" 11 | fi 12 | 13 | if [ ! -f $1/include/uapi/linux/tsn.h ] 14 | then 15 | echo "Not right kernel folder" 16 | fi 17 | 18 | export KERNELDIR=$1 19 | export PKG_CONFIG_SYSROOT_DIR= 20 | export PKG_CONFIG_PATH=$SDKTARGETSYSROOT/lib/pkgconfig 21 | 22 | if [ ! -f ./include/linux/tsn.h ]; then 23 | sudo mkdir include/linux/ 24 | fi 25 | 26 | sudo cp ${KERNELDIR}/include/uapi/linux/tsn.h include/linux/ 27 | sudo cp ${KERNELDIR}/include/uapi/linux/net_tstamp.h include/linux/ 28 | -------------------------------------------------------------------------------- /demos/cnc/templates/configp8021cb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Config Qbv 6 | 8 | 34 | 35 | 36 |

Not support setting p8021cb

37 | 38 | 39 | -------------------------------------------------------------------------------- /demos/cnc/templates/configQav.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Config Qbv 6 | 8 | 34 | 35 | 36 |

Not support setting credit-base shaper

37 | 38 | 39 | -------------------------------------------------------------------------------- /demos/framesstatus/webserver/txrxbytes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | #Copyright 2019 NXP 4 | # 5 | 6 | INTERVAL="1" # update interval in seconds 7 | 8 | if [ -z "$1" ]; then 9 | echo 10 | echo usage: $0 [network-interface] 11 | echo 12 | echo e.g. $0 eth0 13 | echo 14 | echo shows bits-per-second 15 | exit 16 | fi 17 | 18 | IF=$1 19 | JSON_FMT='{\n "TIME":"%s",\n "TX":"%s",\n "RX":"%s"\n}\n' 20 | 21 | while true 22 | do 23 | R1=`cat /sys/class/net/$1/statistics/rx_bytes` 24 | T1=`cat /sys/class/net/$1/statistics/tx_bytes` 25 | sleep $INTERVAL 26 | R2=`cat /sys/class/net/$1/statistics/rx_bytes` 27 | T2=`cat /sys/class/net/$1/statistics/tx_bytes` 28 | #TXPPS=`expr $T2 - $T1` 29 | #RXPPS=`expr $R2 - $R1` 30 | TXPPS=$[$[ $T2 - $T1 ] * 8] 31 | RXPPS=$[$[ $R2 - $R1 ] * 8] 32 | TIME=`date +%s` 33 | #echo "TX $1: $TXPPS pkts/s RX $1: $RXPPS pkts/s" 34 | printf "$JSON_FMT" "$TIME" "$TXPPS" "$RXPPS" > /tmp/static/txrxpkts.json 35 | done 36 | 37 | -------------------------------------------------------------------------------- /main/Makefile: -------------------------------------------------------------------------------- 1 | BINNAME = tsntool 2 | CFLAGS = 3 | SOURCES = fill.c cmd.c readinput.c main.c 4 | readlink = readlink 5 | srcdir = . 6 | 7 | 8 | LIBS = -lxml2 -lreadline 9 | ifeq "$(shell pwd)" "$(shell $(readlink) -f $(srcdir))" 10 | OBJDIR= .obj 11 | else 12 | OBJDIR= . 13 | endif 14 | CC = gcc 15 | CLFAGS += -g -O2 16 | 17 | 18 | LIBNL_CFLAGS = -I/usr/include/libnl3 -I$(shell pwd)/../include/ 19 | LIBNL_LIBS = -lnl-3 20 | 21 | AM_CFLAGS = -Wall -Wextra -Wformat=2 $(LIBCONFIG_CFLAGS) $(LIBNL_CFLAGS) 22 | AM_LDFLAGS = $(LIBCONFIG_LIBS) $(LIBNL_LIBS) -lrt 23 | 24 | CFLAGS += $(AM_CFLAGS) 25 | LIBS += $(AM_LDFLAGS) 26 | 27 | 28 | INCLUDES = readinput.h main.h 29 | 30 | OBJS = $(SOURCES:%.c=$(OBJDIR)/%.o) 31 | 32 | all: $(BINNAME) 33 | $(BINNAME) : $(OBJS) 34 | $(CC) $(CFLAGS) $(OBJS) $(LIBS) -o $(BINNAME) 35 | 36 | $(OBJDIR)/%.o: %.c 37 | @[ -d $$(dirname $@) ] || \ 38 | (mkdir -p $$(dirname $@)) 39 | $(CC) $(CFLAGS) -c $^ -o $@ 40 | 41 | .PHONY:clean 42 | clean: 43 | rm -rf *.o; rm -rf $(BINNAME); rm -rf .obj/ 44 | -------------------------------------------------------------------------------- /demos/framesstatus/README.txt: -------------------------------------------------------------------------------- 1 | This demo shows the timestamping frames by web every second. 2 | 3 | How to Make 4 | ----------- 5 | Just run 'make' at this folder to get enetctscap. 6 | 7 | How to run at OpenIL 8 | -------------------- 9 | Suppose run capture the timestamp at eno2, and web brower tracking at eno0 port. 10 | 11 | Copy the folder ./webserver to OpenIL one folder. 12 | Run the 'startcapture.sh eno2' at boards. 13 | 14 | ifconfig to check the eno0 ip address(10.193.20.71 as example). 15 | Web browser open url: 16 | http://10.193.20.71:8180/ 17 | 18 | 19 | Run Qbv Enable 20 | -------------- 21 | Set a cycletime is 100ms. 22 | 23 | >tsntool 24 | 25 | tsntool> qbvset --device eno2 --entryfile qbv1.txt --basetime 100000000 26 | 27 | You can modify the qbv1.txt. Suggest the cycletime is times of 100ms. 28 | 29 | Stop Demo 30 | --------- 31 | Run script 'stopcapture.sh' 32 | 33 | Capture Receiving Frames Timestamping Only 34 | ------------------------------------------ 35 | Modify the startcapture.sh with 'enetctscap' command to: 36 | 37 | ./enetctscap -i $1 -r & 38 | -------------------------------------------------------------------------------- /demos/framesstatus/Makefile: -------------------------------------------------------------------------------- 1 | LIB_CFLAGS = $(CFLAGS) 2 | LIB_LDFLAGS ?= $(LDFLAGS) 3 | LIB_CFLAGS += -Wall -Wextra -g -fstack-protector-all -Ilib -fPIC 4 | LIB_CFLAGS += -Iinclude $(shell pkg-config --cflags libnl-3.0 libnl-genl-3.0) $(shell pkg-config --cflags libcjson) 5 | #LIB_LDFLAGS += -lnl-3 6 | 7 | BIN_CFLAGS = $(CFLAGS) 8 | BIN_LDFLAGS = $(LDFLAGS) 9 | BIN_CFLAGS += -Wall -Wextra -Wno-error=unused-parameter -Wno-error=sign-compare -g -fstack-protector-all 10 | BIN_CFLAGS += $(shell pkg-config --cflags libnl-3.0 libnl-genl-3.0 libcjson) -I../../include 11 | BIN_LDFLAGS += -L../../ -ltsn $(shell pkg-config --libs libnl-3.0 libnl-genl-3.0 libcjson) -lpthread -lm -lrt 12 | BIN_LDFLAGS += -lreadline -ltermcap -L. 13 | BIN_LDFLAGS += -Wl,-rpath,$(shell pwd) # Compiled lib at local folder 14 | 15 | FRAMECAP = enetctscap 16 | #CYCLECAP = cyclecap 17 | 18 | build: $(FRAMECAP) $(CYCLECAP) 19 | 20 | $(FRAMECAP): $(FRAMECAP).o 21 | $(CC) $(FRAMECAP).o -o webserver/$(FRAMECAP) $(BIN_LDFLAGS) 22 | 23 | #$(CYCLECAP): $(CYCLECAP).o 24 | # $(CC) $(CYCLECAP).o -o $(CYCLECAP) $(BIN_LDFLAGS) 25 | 26 | $(FRAMECAP).o: $(FRAMECAP).c 27 | $(CC) -c $(FRAMECAP).c -o $(FRAMECAP).o $(BIN_CFLAGS) 28 | 29 | #$(CYCLECAP).o: $(CYCLECAP).c 30 | # $(CC) -c $(CYCLECAP).c -o $(CYCLECAP).o $(BIN_CFLAGS) 31 | 32 | clean: 33 | rm -rf *.o webserver/$(FRAMECAP) $(CYCLECAP) 34 | 35 | .PHONY: clean build 36 | -------------------------------------------------------------------------------- /main/main.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 | /* 3 | * Copyright 2018-2019 NXP 4 | */ 5 | 6 | #ifndef _FELIX_MAIN_H_ 7 | #define _FELIX_MAIN_H_ 8 | 9 | extern const char *cli_version; 10 | extern int cli_quit; 11 | void print_version(void); 12 | void cli_cmd_list(void); 13 | 14 | extern int VERBOSE_CONDITION; 15 | 16 | #define _log(file, fmt, ...) do { \ 17 | if (VERBOSE_CONDITION) { \ 18 | fprintf(file, "%s@%d: " fmt "\n", \ 19 | __func__, __LINE__, ##__VA_ARGS__); \ 20 | } else { \ 21 | fprintf(file, fmt "\n", ##__VA_ARGS__); \ 22 | } \ 23 | } while (0) 24 | 25 | #define logc(file, condition, ...) do { \ 26 | if (condition) { \ 27 | _log(file, __VA_ARGS__); \ 28 | } \ 29 | } while (0) 30 | 31 | #define loge(...) _log(stderr, __VA_ARGS__) 32 | #define logi(...) _log(stdout, __VA_ARGS__) 33 | #define logv(...) logc(stdout, VERBOSE_CONDITION, __VA_ARGS__) 34 | 35 | #define SWITCH_NUMBER 1 36 | #define SWITCH_PORTS_NUMBER 5 37 | #define MAX_ARG_LEN 30 38 | 39 | extern char some_msg[4096]; 40 | #define UNUSED __attribute__((__unused__)) 41 | #define INSTRUCTION(output, format, args...) {snprintf(some_msg, 4095, format, ##args); fprintf(output, "\n %s", some_msg); } 42 | #define ERROR(function, format, args...) {snprintf(some_msg, 4095, format, ##args); fprintf(stderr, "%s: %s\n", function, some_msg); } 43 | enum { 44 | OPT_TYPE_SWITCH, 45 | OPT_TYPE_TSN 46 | }; 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 NXP 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | ALTERNATIVELY, this software may be distributed under the terms of the 14 | GNU General Public License ("GPL") as published by the Free Software 15 | Foundation, either version 2 of that License or (at your option) any 16 | later version. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE 22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /main/ptptool.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 | /* 3 | * Copyright 2018-2019 NXP 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #define DEVICE "/dev/ptp0" 21 | 22 | static void usage(char *pid) 23 | { 24 | fprintf(stderr, 25 | "usage: %s [options]\n" 26 | " -d name ptp device name\n" 27 | " -g get ptp time\n" 28 | " -h prints help\n", 29 | pid); 30 | } 31 | 32 | int testptp(int argc, char *argv[]) 33 | { 34 | struct timespec ts; 35 | 36 | char *pname; 37 | int c, cnt, fd; 38 | 39 | char *device = DEVICE; 40 | clockid_t clkid; 41 | 42 | optind = 0; 43 | optarg = EOF; 44 | 45 | pname = strrchr(argv[0], '/'); 46 | 47 | pname = pname ? 1 + pname : argv[0]; 48 | 49 | while (EOF != (c = getopt(argc, argv, "d:gh"))) { 50 | switch (c) { 51 | case 'd': 52 | device = optarg; 53 | break; 54 | case 'g': 55 | break; 56 | case 'h': 57 | usage(pname); 58 | return 0; 59 | case '?': 60 | default: 61 | usage(pname); 62 | return -1; 63 | } 64 | } 65 | optind = 0; 66 | optarg = EOF; 67 | 68 | fd = open(device, O_RDWR); 69 | if (fd < 0) { 70 | fprintf(stderr, "ptpname %s: %s\n", device, strerror(errno)); 71 | return -1; 72 | } 73 | 74 | clkid = (~(clockid_t) (fd) << 3) | 3; 75 | if (clkid == -1) { 76 | fprintf(stderr, "failed to get clock id\n"); 77 | return -1; 78 | } 79 | 80 | if (clock_gettime(clkid, &ts)) 81 | printf("clock_gettime error\n"); 82 | else 83 | printf("ptp time: %ld.%09ld\n", ts.tv_sec, ts.tv_nsec); 84 | 85 | 86 | close(fd); 87 | return 0; 88 | } 89 | -------------------------------------------------------------------------------- /demos/cnc/README.txt: -------------------------------------------------------------------------------- 1 | INSTALL: 2 | -------- 3 | Suppose you are installing the demo on a Centos PC or Ubuntu PC 4 | as the webserver. 5 | 6 | CNC demo require python3 and related libs:pyang libnetconf and libssh. 7 | (refer Openil UG Chapter NETCONF/YANG 5.4). 8 | 9 | libnetconf need to add a patch to fix the demo python support. 10 | 11 | libnetconf commit point: 12 | commit 62a983a3f6259107619128f4a850188b8f420b8b 13 | Author: Michal Vasko 14 | Date: Tue Oct 18 10:14:24 2016 +0200 15 | 16 | BUGFIX copy-paste error 17 | 18 | patching two patches: 19 | git am 0001-lnctool-to-make-install-transapi-yang-model-proper.patch 20 | git am 0002-automatic-python3-authorizing-with-root-password-non.patch 21 | 22 | Then compile the python lib: 23 | cd libnetconf/python; 24 | python3 setup.py build; python3 setup.py install; 25 | (if rebuild, you need to remove the folder rm build -rf before rebuild) 26 | 27 | AVAHI DAEMON INSTALL: 28 | ------------- 29 | Except those libs. Below libs and deamon also be required for both 30 | server and client side(take Centos7.2 as example): 31 | 32 | sudo yum install nss-mdns avahi avahi-tools 33 | 34 | Setup avahi daemon disable the ipv6: 35 | 36 | /etc/avahi/avahi-daemon.conf 37 | use-ipv6=no 38 | publish-a-on-ipv6=no 39 | 40 | sudo systemctl start avahi-daemon.service 41 | 42 | OPENIL BOARDS: 43 | -------------- 44 | On the OpenIL board, avahi-daemon and netopeer server are required: 45 | BR2_PACKAGE_AVAHI=y 46 | BR2_PACKAGE_AVAHI_AUTOIPD=y 47 | BR2_PACKAGE_AVAHI_DAEMON=y 48 | BR2_PACKAGE_AVAHI_LIBDNSSD_COMPATIBILITY=y 49 | BR2_PACKAGE_NSS_MDNS=y 50 | BR2_PACKAGE_NETOPEER=y 51 | 52 | RUN: 53 | ---- 54 | run the webserver at the PC side. 55 | sudo python3 cnc.py 56 | Open a browser in local domain machine. 57 | Input the ip of webserver PC: 58 | http://10.193.20.147:8180 59 | 60 | 61 | Docker Run: 62 | You could not to install the softwares as up steps. Replace by running a docker image: 63 | Just to run: 64 | 65 | docker run --net=host -t -i liupoer/cncdemo:v1 /bin/bash /etc/rc.d/rc.local 66 | 67 | HISTORY: 68 | v1: Support Qbv and Qbu setting. 69 | Recommend to check the boards with tsntool to check the real configure 70 | for comparation. 71 | 72 | -------------------------------------------------------------------------------- /demos/framesstatus/webserver/tsn.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # 3 | #Copyright 2019 NXP 4 | # 5 | 6 | from flask import Flask, render_template, request 7 | from flask import jsonify 8 | import json 9 | import subprocess 10 | 11 | app = Flask(__name__) 12 | app.config['SECRET_KEY'] = "dfdfdffdad" 13 | 14 | app.config.from_object('config') 15 | 16 | interface= app.config["CONFIGPORT"] 17 | 18 | @app.route('/') 19 | def index(): 20 | return render_template('fcap.html') 21 | 22 | @app.route('/qbvenable') 23 | def qbvenable(): 24 | output = subprocess.Popen('tsntool qbvset --device %s --entryfile qbv1.txt --basetime 100000000' %(interface), \ 25 | shell = True, stdout =subprocess.PIPE, stderr=subprocess.STDOUT) 26 | return output.stdout.read() 27 | 28 | @app.route('/qbvdisable') 29 | def qbvdisable(): 30 | output = subprocess.Popen('tsntool qbvset --device %s --disable' %(interface), \ 31 | shell = True, stdout =subprocess.PIPE, stderr=subprocess.STDOUT) 32 | return output.stdout.read() 33 | 34 | @app.route('/qcienable') 35 | def qcienable(): 36 | output = subprocess.Popen('tsntool qcisfiset --device %s --index 2 --gateid 2' %(interface), \ 37 | shell = True, stdout =subprocess.PIPE, stderr=subprocess.STDOUT) 38 | output = subprocess.Popen('tsntool qcisgiset --device %s --index 2 --initgate 1 --gatelistfile sgi1.txt' %(interface), \ 39 | shell = True, stdout =subprocess.PIPE, stderr=subprocess.STDOUT) 40 | 41 | return output.stdout.read() 42 | 43 | @app.route('/qcidisable') 44 | def qcidisable(): 45 | output = subprocess.Popen('tsntool qcisfiset --device %s --index 2 --disable' %(interface), \ 46 | shell = True, stdout =subprocess.PIPE, stderr=subprocess.STDOUT) 47 | output = subprocess.Popen('tsntool qcisgiset --device %s --index 2 --disable' %(interface), \ 48 | shell = True, stdout =subprocess.PIPE, stderr=subprocess.STDOUT) 49 | 50 | return output.stdout.read() 51 | 52 | @app.route('/clientpostdata', methods=['POST']) 53 | def clientpostdata(): 54 | if request.method == 'POST': 55 | a = request.form['mydata'] 56 | print(a) 57 | d = {'name': 'xmr', 'age': 18} 58 | return jsonify(d) 59 | 60 | if __name__ == '__main__': 61 | app.run(host = "0.0.0.0" , port = 8180, debug = True) 62 | -------------------------------------------------------------------------------- /demos/cnc/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | TSN Devices List 7 | 9 | 38 | 47 | 48 | 49 | 50 | 51 | 69 | 70 | 71 |

CNC Test

72 | 73 |

devices list

74 |
75 |
76 | 77 |
78 | 79 |
80 | 81 | 82 | -------------------------------------------------------------------------------- /main/fill.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 | /* 3 | * Copyright 2018-2019 NXP 4 | */ 5 | 6 | #ifndef __END_DEF_FILL_H__ 7 | #define __END_DEF_FILL_H__ 8 | int fill_tsn_cap_get(char *portname); 9 | int fill_qbv_set(char *portname, char *config, bool enable, uint8_t configchange, 10 | uint64_t basetime, uint32_t cycletime, 11 | uint32_t cycletimeext, uint32_t maxsdu, uint8_t initgate); 12 | int fill_qbv_get(char *portname); 13 | int fill_qci_cap_get(char *portname); 14 | 15 | int fill_qci_sfi_set(char *portname, uint32_t streamfilterid, uint8_t enable, 16 | int32_t streamhandle, int8_t priority, uint32_t gateid, 17 | uint16_t maxsdu, int32_t flowmeterid, uint8_t osenable, uint8_t oversize); 18 | 19 | int fill_qci_sfi_get(char *portname, int32_t streamfilter); 20 | 21 | int fill_cb_streamid_set(char *portname, uint32_t index, int8_t enable, 22 | struct tsn_cb_streamid *streamid); 23 | 24 | int fill_cbstreamid_get(char *portname, int32_t index); 25 | 26 | int fill_qci_sgi_set(char *portname, uint32_t index, bool enable, char *listtable, 27 | bool configchange, bool blkinvrx_en, bool blkinvrx, 28 | bool blkoctex_en, bool blkoctex, 29 | bool initgate, int8_t initipv, uint64_t basetime, 30 | uint32_t cycletime, uint32_t cycletimeext); 31 | 32 | int fill_qci_sgi_status_get(char *portname, int32_t sgi_handle); 33 | 34 | int fill_qci_fmi_set(char *portname, uint32_t index, bool enable, uint32_t cir, 35 | uint32_t cbs, uint32_t eir, uint32_t ebs, bool cf, bool cm, 36 | bool dropyellow, bool markred_en, bool markred); 37 | int fill_qci_fmi_get(char *portname, uint32_t index); 38 | int fill_cbs_set(char *portname, uint8_t tc, uint8_t percentage); 39 | int fill_cbs_get(char *portname, uint8_t tc); 40 | int fill_tsd_set(char *portname, bool enable, uint32_t period, uint32_t frame_num, bool imme); 41 | int fill_tsd_get(char *portname); 42 | int fill_qbu_set(char *portname, uint8_t preemptable); 43 | int fill_qbu_get_status(char *portname); 44 | int fill_ct_set(char *portname, uint8_t queue_stat); 45 | int fill_cbgen_set(char *portname, uint32_t index, uint8_t iport_mask, uint8_t split_mask, uint8_t seq_len, uint32_t seq_num); 46 | int fill_cbrec_set(char *portname, uint32_t index, uint8_t seq_len, uint8_t his_len, bool rtag_pop_en); 47 | int fill_cbstatus_get(char *portname, int32_t index); 48 | int fill_dscp_set(char *portname, bool disable, int index, int cos, int dpl); 49 | #endif 50 | -------------------------------------------------------------------------------- /samples/pktgen/pktgen_sample01_simple.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # Simple example: 5 | # * pktgen sending with single thread and single interface 6 | # * flow variation via random UDP source port 7 | # 8 | basedir=`dirname $0` 9 | source ${basedir}/functions.sh 10 | root_check_run_with_sudo "$@" 11 | 12 | # Parameter parsing via include 13 | # - go look in parameters.sh to see which setting are avail 14 | # - required param is the interface "-i" stored in $DEV 15 | source ${basedir}/parameters.sh 16 | # 17 | # Set some default params, if they didn't get set 18 | if [ -z "$DEST_IP" ]; then 19 | [ -z "$IP6" ] && DEST_IP="198.18.0.42" || DEST_IP="FD00::1" 20 | fi 21 | [ -z "$CLONE_SKB" ] && CLONE_SKB="0" 22 | # Example enforce param "-m" for dst_mac 23 | #[ -z "$DST_MAC" ] && usage && err 2 "Must specify -m dst_mac" 24 | [ -z "$DST_MAC" ] && DST_MAC="02:5D:AE:BA:E0:00" 25 | [ -z "$COUNT" ] && COUNT="2" # Zero means indefinitely 26 | [ -z "$QUEUES" ] && QUEUES="0" # 27 | 28 | # Base Config 29 | DELAY="0" # Zero means max speed 30 | 31 | # Flow variation random source port between min and max 32 | UDP_MIN=9 33 | UDP_MAX=109 34 | 35 | # General cleanup everything since last run 36 | # (especially important if other threads were configured by other scripts) 37 | pg_ctrl "reset" 38 | 39 | # Add remove all other devices and add_device $DEV to thread 0 40 | thread=0 41 | pg_thread $thread "rem_device_all" 42 | pg_thread $thread "add_device" $DEV 43 | 44 | # How many packets to send (zero means indefinitely) 45 | pg_set $DEV "count $COUNT" 46 | 47 | # Reduce alloc cost by sending same SKB many times 48 | # - this obviously affects the randomness within the packet 49 | pg_set $DEV "clone_skb $CLONE_SKB" 50 | 51 | # Set packet size 52 | pg_set $DEV "pkt_size $PKT_SIZE" 53 | 54 | # Delay between packets (zero means max speed) 55 | pg_set $DEV "delay $DELAY" 56 | 57 | # Flag example disabling timestamping 58 | pg_set $DEV "flag NO_TIMESTAMP" 59 | pg_set $DEV "flag QUEUE_MAP_RND" 60 | # Destination 61 | pg_set $DEV "dst_mac $DST_MAC" 62 | pg_set $DEV "dst$IP6 $DEST_IP" 63 | 64 | # Setup random UDP port src range 65 | pg_set $DEV "flag UDPSRC_RND" 66 | pg_set $DEV "udp_src_min $UDP_MIN" 67 | pg_set $DEV "udp_src_max $UDP_MAX" 68 | pg_set $DEV "queue_map_min $QUEUES" 69 | pg_set $DEV "queue_map_max $QUEUES" 70 | 71 | # start_run 72 | echo "Running... ctrl^C to stop" >&2 73 | pg_ctrl "start" 74 | echo "Done" >&2 75 | 76 | # Print results 77 | echo "Result device: $DEV" 78 | cat /proc/net/pktgen/$DEV 79 | -------------------------------------------------------------------------------- /tools/README.txt: -------------------------------------------------------------------------------- 1 | ----- 2 | event: 3 | ----- 4 | event is an example to receiving tsn multicast event and create a alarm for user. 5 | It is require to run the phc2sys command as background to sync ptp wit system clock. 6 | 7 | like: 8 | 9 | ./phc2sys -s /dev/ptp0 -O 2 10 | 11 | or: 12 | 13 | ./phc2sys -s /dev/ptp0 -w 14 | 15 | ------ 16 | Alarm: 17 | ------ 18 | There are three APIs to create alarms. All APIs are in the libtsn.so. 19 | 20 | Create a blocking alarm. Never return: 21 | 22 | int set_period_alarm(uint64_t ts, uint64_t offset, uint64_t cycle, 23 | void (*callback_func)(void *data), void *data) 24 | 25 | Create a non-blocking alarm: 26 | 27 | pthread_t *create_alarm_common(uint64_t ts, uint32_t offset, uint32_t cycle, 28 | void (*callback_func)(void *data), void *data) 29 | 30 | Create a internal tsn multicast alarm: 31 | 32 | int create_alarm_thread(uint64_t ts, uint32_t offset, uint32_t cycle, 33 | uint32_t iface, uint32_t infotype, 34 | void (*callback_func)(void *data), void *data) 35 | 36 | ts: The require to be current time or future time. 37 | offset: base on the ts, offset of it. 38 | cycle: the period time. 39 | callback_func: callback function user provide. 40 | data: parameter of callback function. 41 | 42 | In the event.c you can test create blocking alarm by run: 43 | 44 | event -t 1 45 | 46 | In the event.c you can test create non-blocking alarm by run: 47 | 48 | event -t 2 49 | 50 | At same time you can run the tsntool to set Qbv enable and create another 51 | internal alarm for the tsn Qbv alarm by gate list cycle. 52 | 53 | ---------- 54 | multicast: 55 | ---------- 56 | Ready to receiving tsn multicast. You need to create a alarm_info structure. 57 | event.c provide the example: 58 | 59 | alarminfo.qbvmc.callback_func = handle_alarm_qbv; 60 | alarminfo.qbvmc.data = &a; 61 | alarminfo.qcimc.callback_func = handle_alarm_qci; 62 | alarminfo.qcimc.data = &b; 63 | 64 | wait_tsn_multicast(&alarminfo); 65 | 66 | at last call wait_tsn_multicast(&alarminfo). will never return and keep waiting 67 | the multicast message. 68 | 69 | ------------ 70 | timestamping 71 | ------------ 72 | This app is use for sending frames and get timestamping for the frames. 73 | Here are some command examples: 74 | 75 | repeat send/receive frames with timestamping: 76 | ./timestamping -i eno0 -c 0 77 | 78 | send 2 frames 79 | ./timestamping -i eno0 -c 2 80 | 81 | fully send 2 frames without waiting the timestamping event. 82 | ./timestamping -i eno0 -c 2 -f 83 | 84 | repeat fully sending frames 85 | ./timestamping -i eno0 -c 0 -f 86 | 87 | only receive frame with timestamping repeat 88 | ./timestamping -i eno0 -r 89 | 90 | -------------------------------------------------------------------------------- /main/cmd.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 | /* 3 | * Copyright 2018-2019 NXP 4 | */ 5 | 6 | #ifndef _CLI_CMD_H_ 7 | #define _CLI_CMD_H_ 8 | #include 9 | #include "main.h" 10 | 11 | struct cli_cmd { 12 | const char *cmd; 13 | int (*handler)(int argc, char *argv[], int number); 14 | const char *help; 15 | struct option long_options[MAX_ARG_LEN]; 16 | }; 17 | 18 | extern struct cli_cmd cli_commands[]; 19 | 20 | int cli_cmd_help(UNUSED int argc, UNUSED char *argv[], UNUSED int number); 21 | int cli_cmd_version(UNUSED int argc, UNUSED char *argv[], UNUSED int number); 22 | int cli_cmd_quit(UNUSED int argc, UNUSED char *argv[], UNUSED int number); 23 | int cli_cmd_verbose(UNUSED int argc, UNUSED char *argv[], UNUSED int cmdnumber); 24 | int cli_cmd_tsn_cap_get(UNUSED int argc, UNUSED char *argv[], 25 | UNUSED int cmdnumber); 26 | 27 | int cli_cmd_qci_cap_get(UNUSED int argc, UNUSED char *argv[], 28 | UNUSED int cmdnumber); 29 | 30 | int cli_cmd_qbv_set(UNUSED int argc, UNUSED char *argv[], UNUSED int number); 31 | int cli_cmd_qbv_get(UNUSED int argc, UNUSED char *argv[], UNUSED int number); 32 | int cli_cmd_qci_sfi_set(UNUSED int argc, UNUSED char *argv[], UNUSED int number); 33 | int cli_cmd_qci_sfi_get(UNUSED int argc, UNUSED char *argv[], UNUSED int number); 34 | int cli_cmd_qci_sfi_counters_get(UNUSED int argc, UNUSED char *argv[], UNUSED int number); 35 | int cli_cmd_streamid_set(UNUSED int argc, UNUSED char *argv[], UNUSED int number); 36 | int cli_cmd_streamid_get(UNUSED int argc, UNUSED char *argv[], UNUSED int number); 37 | int cli_cmd_qci_sgi_set(UNUSED int argc, UNUSED char *argv[], UNUSED int number); 38 | int cli_cmd_qci_sgi_get(UNUSED int argc, UNUSED char *argv[], UNUSED int number); 39 | int cli_cmd_qci_fmi_set(UNUSED int argc, UNUSED char *argv[], UNUSED int number); 40 | int cli_cmd_qci_fmi_get(UNUSED int argc, UNUSED char *argv[], UNUSED int number); 41 | int cli_cmd_cbs_set(UNUSED int argc, UNUSED char *argv[], UNUSED int number); 42 | int cli_cmd_cbs_get(UNUSED int argc, UNUSED char *argv[], UNUSED int number); 43 | int cli_cmd_tsd_set(UNUSED int argc, UNUSED char *argv[], UNUSED int number); 44 | int cli_cmd_tsd_get(UNUSED int argc, UNUSED char *argv[], UNUSED int number); 45 | int cli_cmd_qbu_set(UNUSED int argc, UNUSED char *argv[], UNUSED int cmdnumber); 46 | int cli_cmd_qbu_get_status(UNUSED int argc, UNUSED char *argv[], UNUSED int cmdnumber); 47 | int cli_sendip(UNUSED int argc, UNUSED char *argv[], UNUSED int cmdnumber); 48 | int cli_regtool(UNUSED int argc, UNUSED char *argv[], UNUSED int cmdnumber); 49 | int cli_ptptool(UNUSED int argc, UNUSED char *argv[], UNUSED int cmdnumber); 50 | int cli_cmd_ct_set(UNUSED int argc, UNUSED char *argv[], UNUSED int cmdnumber); 51 | int cli_cmd_cbgen_set(UNUSED int argc, UNUSED char *argv[], UNUSED int cmdnumber); 52 | int cli_cmd_cbrec_set(UNUSED int argc, UNUSED char *argv[], UNUSED int cmdnumber); 53 | int cli_cmd_cbstatus_get(UNUSED int argc, UNUSED char *argv[], UNUSED int cmdnumber); 54 | int cli_cmd_dscp_set(UNUSED int argc, UNUSED char *argv[], UNUSED int cmdnumber); 55 | #endif 56 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PREFIX ?= /usr 2 | BINDIR ?= $(PREFIX)/bin 3 | INCLUDEDIR ?= $(PREFIX)/include 4 | LIBDIR ?= $(PREFIX)/lib 5 | 6 | PKG_CONFIG ?= pkg-config 7 | LIB_CFLAGS = $(CFLAGS) 8 | LIB_LDFLAGS ?= $(LDFLAGS) 9 | LIB_CFLAGS += -Wall -Wextra -g -fstack-protector-all -Ilib -fPIC 10 | LIB_CFLAGS += -Iinclude $(shell $(PKG_CONFIG) --cflags libnl-3.0 libnl-genl-3.0) $(shell $(PKG_CONFIG) --cflags libcjson) -Imain 11 | #LIB_LDFLAGS += -lnl-3 12 | 13 | BIN_CFLAGS = $(CFLAGS) 14 | BIN_LDFLAGS = $(LDFLAGS) 15 | BIN_CFLAGS += -Wall -Wextra -Wno-error=unused-parameter -Wno-error=sign-compare -Wno-format-security -g -fstack-protector-all -Imain 16 | BIN_CFLAGS += $(shell $(PKG_CONFIG) --cflags libnl-3.0 libnl-genl-3.0 libcjson) -Iinclude 17 | BIN_LDFLAGS += -ltsn $(shell $(PKG_CONFIG) --libs libnl-3.0 libnl-genl-3.0 libcjson) -lpthread -lm -lrt 18 | BIN_LDFLAGS += -lreadline -ltermcap -L. 19 | BIN_LDFLAGS += -Wl,-rpath,$(shell pwd) # Compiled lib at local folder 20 | 21 | BIN_SRC = 22 | LIB_SRC = 23 | BIN_SRC += $(shell find main -name "*.[c|h]") # All .c and .h file 24 | BIN_DEPS = $(patsubst %.c, %.o, $(BIN_SRC)) # All .o and .h files 25 | BIN_OBJ = $(filter %.o, $(BIN_DEPS)) # Only the .o files 26 | 27 | LIB_SRC += $(shell find lib -name "*.[c|h]") # All .c and .h files 28 | LIB_DEPS = $(patsubst %.c, %.o, $(LIB_SRC)) # All .o and .h files 29 | LIB_OBJ = $(filter %.o, $(LIB_DEPS)) # Only the .o files 30 | 31 | TSN_BIN = tsntool 32 | TSN_LIB = libtsn.so 33 | TSN_LIB_PC = libtsn.pc 34 | TSN_EVENT = event 35 | TSTAMP_BIN = timestamping 36 | 37 | LIB_VERSION = 0 38 | 39 | build: $(TSN_LIB) $(TSN_BIN) 40 | 41 | tools: $(TSN_EVENT) $(TSTAMP_BIN) 42 | 43 | $(TSN_LIB): $(LIB_DEPS) 44 | $(CC) -shared $(LIB_OBJ) -o $@ $(LIB_LDFLAGS) 45 | 46 | $(TSN_BIN): $(BIN_DEPS) $(TSN_LIB) 47 | $(CC) $(BIN_OBJ) -o $@ $(BIN_LDFLAGS) 48 | 49 | $(TSN_EVENT): tools/$(TSN_EVENT).o $(TSN_LIB) 50 | $(CC) tools/$(TSN_EVENT).o -o tools/$(TSN_EVENT) $(BIN_LDFLAGS) 51 | 52 | $(TSTAMP_BIN): tools/$(TSTAMP_BIN).o 53 | $(CC) tools/$(TSTAMP_BIN).o -o tools/$(TSTAMP_BIN) -lpthread -lm 54 | 55 | lib/%.o: lib/%.c 56 | $(CC) -c $^ -o $@ $(LIB_CFLAGS) 57 | 58 | main/%.o: main/%.c 59 | $(CC) -c $^ -o $@ $(BIN_CFLAGS) 60 | 61 | tools/$(TSN_EVENT).o: tools/$(TSN_EVENT).c 62 | $(CC) -c tools/$(TSN_EVENT).c -o tools/$(TSN_EVENT).o $(BIN_CFLAGS) 63 | 64 | tools/$(TSTAMP_BIN).o: tools/$(TSTAMP_BIN).c 65 | $(CC) -c tools/$(TSTAMP_BIN).c -o tools/$(TSTAMP_BIN).o $(BIN_CFLAGS) 66 | 67 | $(TSN_LIB_PC): lib/libtsn.pc.in 68 | sed -e "s#@includedir@#$(INCLUDEDIR)#g" \ 69 | -e "s#@libdir@#$(LIBDIR)#g" \ 70 | -e "s#@version@#$(LIB_VERSION)#g" \ 71 | $< > $@ 72 | 73 | install: include/tsn/genl_tsn.h $(TSN_LIB) $(TSN_BIN) $(TSN_LIB_PC) 74 | install -d -m 0755 $(DESTDIR)$(BINDIR) 75 | install -d -m 0755 $(DESTDIR)$(LIBDIR) 76 | install -d -m 0755 $(DESTDIR)$(INCLUDEDIR)/tsn 77 | install -m 0755 $(TSN_BIN) $(DESTDIR)$(BINDIR)/ 78 | install -m 0644 $(TSN_LIB) $(DESTDIR)$(LIBDIR)/ 79 | install -m 0644 include/tsn/genl_tsn.h $(DESTDIR)$(INCLUDEDIR)/tsn 80 | install -D -m 644 $(TSN_LIB_PC) $(DESTDIR)$(LIBDIR)/pkgconfig/libtsn.pc 81 | 82 | clean: 83 | rm -rf $(TSN_BIN) $(TSN_LIB) $(TSN_LIB_PC) $(LIB_OBJ) $(BIN_OBJ) tools/*.o tools/$(TSN_EVENT) tools/$(TSTAMP_BIN) 84 | 85 | .PHONY: clean build 86 | -------------------------------------------------------------------------------- /samples/pktgen/pktgen_twoqueue.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # Multiqueue: Using pktgen threads for sending on multiple CPUs 5 | # * adding devices to kernel threads 6 | # * notice the naming scheme for keeping device names unique 7 | # * nameing scheme: dev@thread_number 8 | # * flow variation via random UDP source port 9 | # 10 | basedir=`dirname $0` 11 | source ${basedir}/functions.sh 12 | root_check_run_with_sudo "$@" 13 | # 14 | # Required param: -i dev in $DEV 15 | source ${basedir}/parameters.sh 16 | 17 | [ -z "$COUNT" ] && COUNT="10" # Zero means indefinitely 18 | 19 | # Base Config 20 | DELAY="0" # Zero means max speed 21 | [ -z "$CLONE_SKB" ] && CLONE_SKB="0" 22 | 23 | # Flow variation random source port between min and max 24 | UDP_MIN=9 25 | UDP_MAX=109 26 | 27 | # (example of setting default params in your script) 28 | if [ -z "$DEST_IP" ]; then 29 | [ -z "$IP6" ] && DEST_IP="198.18.0.42" || DEST_IP="FD00::1" 30 | fi 31 | [ -z "$DST_MAC" ] && DST_MAC="90:e2:ba:ff:ff:ff" 32 | 33 | # General cleanup everything since last run 34 | pg_ctrl "reset" 35 | 36 | queuen=$QUEUES; 37 | queuen=$[$queuen + 1]; 38 | if [ "$queuen" -gt 7 ] 39 | then 40 | queuen=0; 41 | fi 42 | 43 | L_THREAD=$[$F_THREAD + 1]; 44 | echo "inject to queue ${QUEUES} ${queuen} thread ${F_THREAD} ${L_THREAD}" 45 | 46 | # Threads are specified with parameter -t value in $THREADS 47 | for ((thread = $F_THREAD; thread <= $L_THREAD; thread++)); do 48 | # The device name is extended with @name, using thread number to 49 | # make then unique, but any name will do. 50 | dev=${DEV}@${thread} 51 | echo "add device $dev" 52 | # Add remove all other devices and add_device $dev to thread 53 | pg_thread $thread "rem_device_all" 54 | pg_thread $thread "add_device" $dev 55 | 56 | # Notice config queue to map to cpu (mirrors smp_processor_id()) 57 | # It is beneficial to map IRQ /proc/irq/*/smp_affinity 1:1 to CPU number 58 | #pg_set $dev "flag QUEUE_MAP_CPU" 59 | if [ $thread == 0 ] 60 | then 61 | pg_set $dev "queue_map_min $QUEUES" 62 | pg_set $dev "queue_map_max $QUEUES" 63 | pg_set $dev "pkt_size PKT_SIZE" 64 | pg_set $dev "dst_mac $DST_MAC" 65 | else 66 | pg_set $dev "queue_map_min $queuen" 67 | pg_set $dev "queue_map_max $queuen" 68 | pg_set $dev "pkt_size 1500" 69 | pg_set $dev "dst_mac 90:e2:ba:ff:ff:ff" 70 | fi 71 | # Base config of dev 72 | pg_set $dev "count $COUNT" 73 | pg_set $dev "clone_skb $CLONE_SKB" 74 | #pg_set $dev "pkt_size $PKT_SIZE" 75 | pg_set $dev "delay $DELAY" 76 | 77 | # Flag example disabling timestamping 78 | pg_set $dev "flag NO_TIMESTAMP" 79 | 80 | # Destination 81 | pg_set $dev "dst$IP6 $DEST_IP" 82 | 83 | # Setup random UDP port src range 84 | pg_set $dev "flag UDPSRC_RND" 85 | pg_set $dev "flag QUEUE_MAP_RND" 86 | pg_set $dev "udp_src_min $UDP_MIN" 87 | pg_set $dev "udp_src_max $UDP_MAX" 88 | done 89 | 90 | # start_run 91 | echo "Running... ctrl^C to stop" >&2 92 | pg_ctrl "start" 93 | echo "Done" >&2 94 | 95 | # Print results 96 | for ((thread = $F_THREAD; thread <= $L_THREAD; thread++)); do 97 | dev=${DEV}@${thread} 98 | echo "Device: $dev" 99 | cat /proc/net/pktgen/$dev | grep -A2 "Result:" 100 | done 101 | -------------------------------------------------------------------------------- /samples/pktgen/functions.sh: -------------------------------------------------------------------------------- 1 | # 2 | # Common functions used by pktgen scripts 3 | # - Depending on bash 3 (or higher) syntax 4 | # 5 | # Author: Jesper Dangaaard Brouer 6 | # License: GPL 7 | 8 | ## -- General shell logging cmds -- 9 | function err() { 10 | local exitcode=$1 11 | shift 12 | echo "ERROR: $@" >&2 13 | exit $exitcode 14 | } 15 | 16 | function warn() { 17 | echo "WARN : $@" >&2 18 | } 19 | 20 | function info() { 21 | if [[ -n "$VERBOSE" ]]; then 22 | echo "INFO : $@" >&2 23 | fi 24 | } 25 | 26 | ## -- Pktgen proc config commands -- ## 27 | export PROC_DIR=/proc/net/pktgen 28 | # 29 | # Three different shell functions for configuring the different 30 | # components of pktgen: 31 | # pg_ctrl(), pg_thread() and pg_set(). 32 | # 33 | # These functions correspond to pktgens different components. 34 | # * pg_ctrl() control "pgctrl" (/proc/net/pktgen/pgctrl) 35 | # * pg_thread() control the kernel threads and binding to devices 36 | # * pg_set() control setup of individual devices 37 | function pg_ctrl() { 38 | local proc_file="pgctrl" 39 | proc_cmd ${proc_file} "$@" 40 | } 41 | 42 | function pg_thread() { 43 | local thread=$1 44 | local proc_file="kpktgend_${thread}" 45 | shift 46 | proc_cmd ${proc_file} "$@" 47 | } 48 | 49 | function pg_set() { 50 | local dev=$1 51 | local proc_file="$dev" 52 | shift 53 | proc_cmd ${proc_file} "$@" 54 | } 55 | 56 | # More generic replacement for pgset(), that does not depend on global 57 | # variable for proc file. 58 | function proc_cmd() { 59 | local result 60 | local proc_file=$1 61 | # after shift, the remaining args are contained in $@ 62 | shift 63 | local proc_ctrl=${PROC_DIR}/$proc_file 64 | if [[ ! -e "$proc_ctrl" ]]; then 65 | err 3 "proc file:$proc_ctrl does not exists (dev added to thread?)" 66 | else 67 | if [[ ! -w "$proc_ctrl" ]]; then 68 | err 4 "proc file:$proc_ctrl not writable, not root?!" 69 | fi 70 | fi 71 | 72 | if [[ "$DEBUG" == "yes" ]]; then 73 | echo "cmd: $@ > $proc_ctrl" 74 | fi 75 | # Quoting of "$@" is important for space expansion 76 | echo "$@" > "$proc_ctrl" 77 | local status=$? 78 | 79 | result=$(grep "Result: OK:" $proc_ctrl) 80 | # Due to pgctrl, cannot use exit code $? from grep 81 | if [[ "$result" == "" ]]; then 82 | grep "Result:" $proc_ctrl >&2 83 | fi 84 | if (( $status != 0 )); then 85 | err 5 "Write error($status) occurred cmd: \"$@ > $proc_ctrl\"" 86 | fi 87 | } 88 | 89 | # Old obsolete "pgset" function, with slightly improved err handling 90 | function pgset() { 91 | local result 92 | 93 | if [[ "$DEBUG" == "yes" ]]; then 94 | echo "cmd: $1 > $PGDEV" 95 | fi 96 | echo $1 > $PGDEV 97 | local status=$? 98 | 99 | result=`cat $PGDEV | fgrep "Result: OK:"` 100 | if [[ "$result" == "" ]]; then 101 | cat $PGDEV | fgrep Result: 102 | fi 103 | if (( $status != 0 )); then 104 | err 5 "Write error($status) occurred cmd: \"$1 > $PGDEV\"" 105 | fi 106 | } 107 | 108 | ## -- General shell tricks -- 109 | 110 | function root_check_run_with_sudo() { 111 | # Trick so, program can be run as normal user, will just use "sudo" 112 | # call as root_check_run_as_sudo "$@" 113 | if [ "$EUID" -ne 0 ]; then 114 | if [ -x $0 ]; then # Directly executable use sudo 115 | info "Not root, running with sudo" 116 | sudo "$0" "$@" 117 | exit $? 118 | fi 119 | err 4 "cannot perform sudo run of $0" 120 | fi 121 | } 122 | -------------------------------------------------------------------------------- /samples/pktgen/parameters.sh: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: GPL-2.0 3 | # Common parameter parsing for pktgen scripts 4 | # 5 | 6 | function usage() { 7 | echo "" 8 | echo "Usage: $0 [-vx] -i ethX" 9 | echo " -i : (\$DEV) output interface/device (required)" 10 | echo " -q : (\$QUEUES) which queue" 11 | echo " -s : (\$PKT_SIZE) packet size" 12 | echo " -d : (\$DEST_IP) destination IP" 13 | echo " -m : (\$DST_MAC) destination MAC-addr" 14 | echo " -t : (\$THREADS) threads to start" 15 | echo " -f : (\$F_THREAD) index of first thread (zero indexed CPU number)" 16 | echo " -c : (\$SKB_CLONE) SKB clones send before alloc new SKB" 17 | echo " -n : (\$COUNT) num messages to send per thread, 0 means indefinitely" 18 | echo " -b : (\$BURST) HW level bursting of SKBs" 19 | echo " -v : (\$VERBOSE) verbose" 20 | echo " -x : (\$DEBUG) debug" 21 | echo " -6 : (\$IP6) IPv6" 22 | echo "" 23 | } 24 | 25 | ## --- Parse command line arguments / parameters --- 26 | ## echo "Commandline options:" 27 | while getopts "s:i:q:d:m:f:t:c:n:b:vxh6" option; do 28 | case $option in 29 | i) # interface 30 | export DEV=$OPTARG 31 | info "Output device set to: DEV=$DEV" 32 | ;; 33 | q) #queue 34 | export QUEUES=$OPTARG 35 | info "Queue select QUEUES=$QUEUES" 36 | ;; 37 | s) 38 | export PKT_SIZE=$OPTARG 39 | info "Packet size set to: PKT_SIZE=$PKT_SIZE bytes" 40 | ;; 41 | d) # destination IP 42 | export DEST_IP=$OPTARG 43 | info "Destination IP set to: DEST_IP=$DEST_IP" 44 | ;; 45 | m) # MAC 46 | export DST_MAC=$OPTARG 47 | info "Destination MAC set to: DST_MAC=$DST_MAC" 48 | ;; 49 | f) 50 | export F_THREAD=$OPTARG 51 | info "Index of first thread (zero indexed CPU number): $F_THREAD" 52 | ;; 53 | t) 54 | export THREADS=$OPTARG 55 | info "Number of threads to start: $THREADS" 56 | ;; 57 | c) 58 | export CLONE_SKB=$OPTARG 59 | info "CLONE_SKB=$CLONE_SKB" 60 | ;; 61 | n) 62 | export COUNT=$OPTARG 63 | info "COUNT=$COUNT" 64 | ;; 65 | b) 66 | export BURST=$OPTARG 67 | info "SKB bursting: BURST=$BURST" 68 | ;; 69 | v) 70 | export VERBOSE=yes 71 | info "Verbose mode: VERBOSE=$VERBOSE" 72 | ;; 73 | x) 74 | export DEBUG=yes 75 | info "Debug mode: DEBUG=$DEBUG" 76 | ;; 77 | 6) 78 | export IP6=6 79 | info "IP6: IP6=$IP6" 80 | ;; 81 | h|?|*) 82 | usage; 83 | err 2 "[ERROR] Unknown parameters!!!" 84 | esac 85 | done 86 | shift $(( $OPTIND - 1 )) 87 | 88 | if [ -z "$PKT_SIZE" ]; then 89 | # NIC adds 4 bytes CRC 90 | export PKT_SIZE=60 91 | info "Default packet size set to: set to: $PKT_SIZE bytes" 92 | fi 93 | 94 | if [ -z "$F_THREAD" ]; then 95 | # First thread (F_THREAD) reference the zero indexed CPU number 96 | export F_THREAD=0 97 | fi 98 | 99 | if [ -z "$THREADS" ]; then 100 | export THREADS=1 101 | fi 102 | 103 | export L_THREAD=$(( THREADS + F_THREAD - 1 )) 104 | 105 | if [ -z "$DEV" ]; then 106 | usage 107 | err 2 "Please specify output device" 108 | fi 109 | 110 | if [ -z "$DST_MAC" ]; then 111 | warn "Missing destination MAC address" 112 | fi 113 | 114 | if [ -z "$DEST_IP" ]; then 115 | warn "Missing destination IP address" 116 | fi 117 | 118 | if [ ! -d /proc/net/pktgen ]; then 119 | info "Loading kernel module: pktgen" 120 | # modprobe pktgen 121 | fi 122 | -------------------------------------------------------------------------------- /tools/event.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 | /* 3 | * Copyright 2019 NXP 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | #define DEVICE "/dev/ptp0" 27 | #define SOURCED "eno0" 28 | 29 | #ifndef CLOCK_INVALID 30 | #define CLOCK_INVALID -1 31 | #endif 32 | 33 | #include "tsn/genl_tsn.h" 34 | 35 | struct info_type namelist[] = { 36 | [TSN_MCGRP_QBV] = {.name = "qbv"}, 37 | [TSN_MCGRP_QCI] = {.name = "qci"}, 38 | }; 39 | 40 | struct alarm_info alarminfo; 41 | static int testmode = 0; 42 | 43 | static void handle_alarm_qbv(int *s) 44 | { 45 | struct timespec ts; 46 | 47 | if (clock_gettime(alarminfo.clkid, &ts)) { 48 | perror("clock_gettime"); 49 | } else { 50 | printf("qbv clock time: %ld.%09ld or %s", 51 | ts.tv_sec, ts.tv_nsec, ctime(&ts.tv_sec)); 52 | } 53 | 54 | printf("received qbv signal %d\n", *s); 55 | } 56 | 57 | static void handle_alarm_qci(int *s) 58 | { 59 | struct timespec ts; 60 | 61 | if (clock_gettime(alarminfo.clkid, &ts)) { 62 | perror("clock_gettime"); 63 | } else { 64 | printf("qci clock time: %ld.%09ld or %s", 65 | ts.tv_sec, ts.tv_nsec, ctime(&ts.tv_sec)); 66 | } 67 | 68 | printf("received qci signal %d\n", *s); 69 | } 70 | 71 | static void usage(char *progname) 72 | { 73 | fprintf(stderr, 74 | "usage: %s [options]\n" 75 | " -d name device to open\n" 76 | " -t testmode\n" 77 | " -h prints this message\n", 78 | progname); 79 | } 80 | 81 | int parse_args(int argc, char *argv[]) 82 | { 83 | char *progname; 84 | char *device = DEVICE; 85 | int c; 86 | 87 | progname = strrchr(argv[0], '/'); 88 | progname = progname ? 1+progname : argv[0]; 89 | while (EOF != (c = getopt(argc, argv, "d:t:h"))) { 90 | switch (c) { 91 | case 'd': 92 | device = optarg; 93 | break; 94 | case 't': 95 | testmode = atoi(optarg); 96 | printf("testmode is %d\n", testmode); 97 | if (testmode != 1 || testmode != 2) 98 | return -1; 99 | printf("ok\n"); 100 | break; 101 | case 'h': 102 | usage(progname); 103 | return 0; 104 | case '?': 105 | default: 106 | usage(progname); 107 | return -1; 108 | } 109 | } 110 | 111 | memset(&alarminfo, 0, sizeof(alarminfo)); 112 | 113 | strcpy(alarminfo.ptpdev, device); 114 | 115 | alarminfo.clkid = CLOCK_REALTIME; 116 | 117 | return 0; 118 | } 119 | 120 | void die(char * s) 121 | { 122 | perror(s); 123 | exit(1); 124 | } 125 | 126 | void main(int argc, char *argv[]) 127 | { 128 | struct timespec ts; 129 | uint64_t nowns; 130 | int a = 1, b = 2; 131 | pthread_t *th = NULL; 132 | 133 | parse_args(argc, argv); 134 | 135 | if (clock_gettime(alarminfo.clkid, &ts)) { 136 | perror("clock_gettime"); 137 | } else { 138 | printf("clock time: %ld.%09ld or %s", 139 | ts.tv_sec, ts.tv_nsec, ctime(&ts.tv_sec)); 140 | } 141 | 142 | /* create a blocking timer */ 143 | if (testmode == 1) { 144 | nowns = pctns(&ts); 145 | set_period_alarm(nowns, 0, 2000000000ULL, handle_alarm_qbv, &a); 146 | } 147 | 148 | /* create a non-block timer */ 149 | if (testmode == 2) { 150 | nowns = pctns(&ts); 151 | th = create_alarm_common(nowns, 0, 2000000000ULL, handle_alarm_qci, &b); 152 | } 153 | 154 | alarminfo.qbvmc.callback_func = handle_alarm_qbv; 155 | alarminfo.qbvmc.data = &a; 156 | alarminfo.qcimc.callback_func = handle_alarm_qci; 157 | alarminfo.qcimc.data = &b; 158 | 159 | wait_tsn_multicast(&alarminfo); 160 | 161 | if (th) { 162 | delete_alarm_common(th); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /demos/cnc/0001-lnctool-to-make-install-transapi-yang-model-proper.patch: -------------------------------------------------------------------------------- 1 | From f0277020c146840f8fefc62249f8b37bca5259b1 Mon Sep 17 00:00:00 2001 2 | From: Po Liu 3 | Date: Tue, 6 Dec 2016 22:19:49 +0800 4 | Subject: [PATCH 1/2] lnctool: to make install transapi yang model proper 5 | 6 | Signed-off-by: Po Liu 7 | --- 8 | dev-tools/lnctool/generator/Makefile.in | 34 +++++++++++++++++++++++++++++++- 9 | dev-tools/lnctool/generator/configure.in | 18 +++++++++++++++++ 10 | 2 files changed, 51 insertions(+), 1 deletion(-) 11 | 12 | diff --git a/dev-tools/lnctool/generator/Makefile.in b/dev-tools/lnctool/generator/Makefile.in 13 | index 357583c..bfc0f07 100644 14 | --- a/dev-tools/lnctool/generator/Makefile.in 15 | +++ b/dev-tools/lnctool/generator/Makefile.in 16 | @@ -17,11 +17,20 @@ INSTALL = @INSTALL@ 17 | INSTALL_PROGRAM = @INSTALL_PROGRAM@ 18 | INSTALL_DATA = @INSTALL_DATA@ 19 | LIBS = @LIBS@ 20 | -CFLAGS = @CFLAGS@ 21 | +CFLAGS = -Wall -pthread @CFLAGS@ 22 | CPPFLAGS = @CPPFLAGS@ 23 | LDFLAGS = @LDFLAGS@ 24 | LIBTOOL = $(libtool) --tag=CC --quiet 25 | 26 | +NETOPEER_MANAGER = @NETOPEER_MANAGER@ 27 | +NETOPEER_DIR = @NETOPEER_DIR@ 28 | + 29 | +MODEL = @PROJECTNAME@.yin \ 30 | + @PROJECTNAME@-config.rng \ 31 | + @PROJECTNAME@-gdefs-config.rng \ 32 | + @PROJECTNAME@-schematron.xsl 33 | + 34 | + 35 | SRCS = @PROJECTNAME@.c 36 | OBJDIR = .obj 37 | LOBJS = $(SRCS:%.c=$(OBJDIR)/%.lo) 38 | @@ -40,6 +49,29 @@ $(OBJDIR)/%.lo: %.c 39 | install: $(TARGET) 40 | $(INSTALL) -m 775 -d $(DESTDIR)/$(libdir) 41 | $(LIBTOOL) --mode=install cp $(TARGET) $(DESTDIR)/$(libdir)/; 42 | + $(INSTALL) -d $(NETOPEER_DIR)/$(PKGNAME)/ 43 | + @for i in $(MODEL); do \ 44 | + $(INSTALL_DATA) -m 600 $$i $(NETOPEER_DIR)/$(PKGNAME)/; \ 45 | + done 46 | + if test -n "$(NETOPEER_MANAGER)"; then \ 47 | + if test -n "`$(NETOPEER_MANAGER) list | grep "^$(PKGNAME) ("`"; then \ 48 | + $(NETOPEER_MANAGER) rm --name $(PKGNAME); \ 49 | + fi; \ 50 | + $(NETOPEER_MANAGER) add --name $(PKGNAME) \ 51 | + --model $(NETOPEER_DIR)/$(PKGNAME)/$(PKGNAME).yin \ 52 | + --transapi $(DESTDIR)/$(libdir)/$(PKGNAME).so \ 53 | + --datastore $(NETOPEER_DIR)/$(PKGNAME)/datastore.xml; \ 54 | + fi 55 | + 56 | +.PHONY: uninstall 57 | +uninstall: 58 | + $(LIBTOOL) --mode=uninstall rm -rf $(DESTDIR)/$(libdir)/$(TARGET); 59 | + rm -rf $(NETOPEER_DIR)/$(PKGNAME)/ 60 | + if test -n "$(NETOPEER_MANAGER)"; then \ 61 | + if test -n "`$(NETOPEER_MANAGER) list | grep "^$(PKGNAME) ("`"; then \ 62 | + $(NETOPEER_MANAGER) rm --name $(PKGNAME); \ 63 | + fi; \ 64 | + fi 65 | 66 | .PHONY: clean 67 | clean: 68 | diff --git a/dev-tools/lnctool/generator/configure.in b/dev-tools/lnctool/generator/configure.in 69 | index 5caf5f2..68cbeee 100644 70 | --- a/dev-tools/lnctool/generator/configure.in 71 | +++ b/dev-tools/lnctool/generator/configure.in 72 | @@ -12,6 +12,13 @@ AC_ARG_ENABLE([debug], 73 | CFLAGS="$CFLAGS -O3" 74 | ) 75 | 76 | +AC_ARG_WITH([netopeer-confdir], 77 | + [AC_HELP_STRING([--with-netopeer-confdir=DIR], [Netopeer server configuration directory (by default ${sysconfdir}/netopeer/)])], 78 | + [NETOPEER_DIR="$withval"], 79 | + [NETOPEER_DIR="${sysconfdir}/netopeer/"] 80 | +) 81 | +AC_SUBST(NETOPEER_DIR) 82 | + 83 | # --with-libxml2=path-to-libxml2-git-repository 84 | AC_ARG_WITH([libxml2], 85 | [AC_HELP_STRING([--with-libxml2], [specific libxml2 location])], 86 | @@ -38,7 +45,18 @@ if test -z "$libtool"; then 87 | fi 88 | AC_SUBST(libtool) 89 | 90 | +AC_PATH_PROG(NETOPEER_MANAGER, [netopeer-manager], []) 91 | +AC_SUBST(NETOPEER_MANAGER) 92 | + 93 | # Checks for libraries. 94 | +export PKG_CONFIG_PATH=/usr/local/${libdir##*/}/pkgconfig:$PKG_CONFIG_PATH 95 | + 96 | +PKG_CHECK_MODULES(LIBNETCONF, libnetconf) 97 | +CPPFLAGS="$CPPFLAGS $LIBNETCONF_CFLAGS" 98 | +LIBS="$LIBS $LIBNETCONF_LIBS" 99 | +LIBNETCONF_DATADIR="`$PKG_CONFIG --variable=datadir libnetconf`" 100 | +AC_SUBST(LIBNETCONF_DATADIR) 101 | + 102 | AC_SEARCH_LIBS([ncds_apply_rpc2all], [netconf], ,AC_MSG_ERROR([libnetconf not found!])) 103 | 104 | # Check for libxml2. 105 | -- 106 | 1.8.3.1 107 | 108 | -------------------------------------------------------------------------------- /demos/framesstatus/webserver/templates/fcap.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | // 7 | // 8 | 9 | 12 | 36 | 37 | 38 | 39 |
40 |
41 |
42 | 43 | 44 | 45 | 46 |
47 |

48 | 49 | 50 | 136 | 137 | 138 | 139 | 208 | 209 | 210 | 211 | -------------------------------------------------------------------------------- /main/main.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 | /* 3 | * Copyright 2018-2019 NXP 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | #include "readinput.h" 17 | #include "cmd.h" 18 | #include "main.h" 19 | #include "tsn/genl_tsn.h" 20 | 21 | //#define UNUSED __attribute__((__unused__)) 22 | 23 | #define SHOW_NO_OUTPUT 0x00 24 | #define SHOW_OUTPUT 0x01 25 | #define SHOW_RAW 0x02 26 | #define SHOW_RAW_ONLY 0x04 27 | #define PROMPT "tsntool> " 28 | 29 | int VERBOSE_CONDITION = 0; 30 | 31 | extern struct cli_cmd cli_commands[]; 32 | 33 | #define TSNTOOL_VERSION "V0.4" 34 | char some_msg[4096]; 35 | const char *cli_version = 36 | "tsntool " TSNTOOL_VERSION "\n" 37 | "Copyright 2018-2019 NXP\n"; 38 | 39 | int cli_quit; 40 | static int show_raw; 41 | 42 | static const char *commands_help = 43 | "tsn Commands:\n" 44 | " command line:\n"; 45 | 46 | int multiline; 47 | 48 | void signal_handler(int sig) 49 | { 50 | switch (sig) { 51 | case SIGINT: 52 | case SIGTERM: 53 | case SIGQUIT: 54 | case SIGABRT: 55 | multiline = 0; 56 | rl_line_buffer[0] = '\0'; 57 | rl_end = rl_point = 0; 58 | rl_reset_line_state(); 59 | fprintf(stdout, "\n"); 60 | rl_redisplay(); 61 | break; 62 | default: 63 | exit(EXIT_FAILURE); 64 | break; 65 | } 66 | } 67 | 68 | void cli_cmd_list(void); 69 | 70 | static void usage(void) 71 | { 72 | fprintf(stderr, "%s\n", cli_version); 73 | fprintf(stderr, 74 | "\n" 75 | "Usage:\n" 76 | " tsntool -h\n" 77 | " tsntool -v\n" 78 | " tsntool interactive mode\n" 79 | "\n" 80 | "Options:\n" 81 | " -h help (show this usage text)\n" 82 | " -v shown version information\n" 83 | "\n%s", 84 | commands_help); 85 | 86 | cli_cmd_list(); 87 | } 88 | 89 | void print_version(void) 90 | { 91 | fprintf(stdout, "tsntool, version %s\n", TSNTOOL_VERSION); 92 | fprintf(stdout, "compile time: %s, %s\n", __DATE__, __TIME__); 93 | } 94 | 95 | 96 | void cli_cmd_list(void) 97 | { 98 | struct cli_cmd *cmd; 99 | 100 | cmd = cli_commands; 101 | while (cmd->cmd) { 102 | printf(" %-12s\t%s\n", cmd->cmd, cmd->help); 103 | cmd++; 104 | } 105 | } 106 | 107 | static int request(int argc, char *argv[]) 108 | { 109 | struct cli_cmd *cmd, *match = NULL; 110 | int count; 111 | int ret = 0; 112 | int number = 0, cmdnumber; 113 | 114 | count = 0; 115 | cmd = cli_commands; 116 | while (cmd->cmd) { 117 | if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) { 118 | match = cmd; 119 | count++; 120 | cmdnumber = number; 121 | } 122 | cmd++; 123 | number++; 124 | } 125 | 126 | if (count > 1) { 127 | /* found two parameter in the cli_commands */ 128 | printf("Ambiguous command '%s'; possible commands:", argv[0]); 129 | cmd = cli_commands; 130 | while (cmd->cmd) { 131 | if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 132 | 0) { 133 | printf(" %s", cmd->cmd); 134 | } 135 | cmd++; 136 | } 137 | printf("\n"); 138 | ret = -1; 139 | } else if (count == 0) { 140 | /* Maybe it is one line command */ 141 | usage(); 142 | } else { 143 | /* = 1 in the commands cli_commands */ 144 | ret = match->handler(argc, &argv[0], cmdnumber); 145 | } 146 | 147 | return ret; 148 | } 149 | 150 | static void cli_interactive(void) 151 | { 152 | const int max_args = MAX_ARG_LEN; 153 | char *cmd, *argv[max_args], *pos; 154 | int argc; 155 | 156 | setlinebuf(stdout); 157 | printf("\nInteractive mode\n\n"); 158 | using_history(); 159 | //stifle_history(1000); 160 | 161 | initialize_readline(); 162 | 163 | do { 164 | cmd = readline(PROMPT); 165 | if (!cmd) 166 | break; 167 | if (*cmd) 168 | add_history(cmd); 169 | argc = 0; 170 | pos = cmd; 171 | for (;;) { 172 | while (*pos == ' ') 173 | pos++; 174 | if (*pos == '\0') 175 | break; 176 | argv[argc] = pos; 177 | argc++; 178 | if (argc == max_args) 179 | break; 180 | while (*pos != '\0' && *pos != ' ') 181 | pos++; 182 | if (*pos == ' ') 183 | *pos++ = '\0'; 184 | } 185 | printf("\n"); 186 | if (argc) 187 | request(argc, argv); 188 | free(cmd); 189 | } while (!cli_quit); 190 | } 191 | 192 | int main(int argc, char *argv[]) 193 | { 194 | struct sigaction action; 195 | int interactive; 196 | int c; 197 | int raw = 0; 198 | int ret = 0; 199 | int run = 0; 200 | sigset_t block_mask; 201 | 202 | /* signal handling */ 203 | sigfillset(&block_mask); 204 | action.sa_handler = signal_handler; 205 | action.sa_mask = block_mask; 206 | action.sa_flags = 0; 207 | sigaction(SIGINT, &action, NULL); 208 | sigaction(SIGQUIT, &action, NULL); 209 | sigaction(SIGABRT, &action, NULL); 210 | sigaction(SIGTERM, &action, NULL); 211 | 212 | for (;;) { 213 | opterr = 0; 214 | c = getopt(argc, argv, "hrv"); 215 | if (c < 0 || run) 216 | break; 217 | switch (c) { 218 | case 'h': 219 | usage(); 220 | return 0; 221 | case 'v': 222 | printf("%s\n", cli_version); 223 | return 0; 224 | case 'r': 225 | if (raw) { 226 | usage(); 227 | return -1; 228 | } 229 | raw = SHOW_RAW; 230 | break; 231 | default: 232 | run = 1; 233 | optind = 0; 234 | optarg = EOF; 235 | 236 | break; 237 | } 238 | } 239 | 240 | show_raw = raw; 241 | 242 | interactive = argc == optind; 243 | 244 | ret = genl_tsn_init(); 245 | if (ret < 0) { 246 | loge("generic netlink init failure.\n"); 247 | return -1; 248 | } 249 | 250 | if (interactive) { 251 | tsn_echo_test("test netlink", 1); 252 | 253 | printf("%s\n\n", cli_version); 254 | 255 | cli_interactive(); 256 | } else 257 | ret = request(argc - 1, &argv[1]); 258 | 259 | genl_tsn_close(); 260 | return ret; 261 | } 262 | -------------------------------------------------------------------------------- /demos/cnc/0002-automatic-python3-authorizing-with-root-password-non.patch: -------------------------------------------------------------------------------- 1 | From 01a091076743d7701e551409d8eb3a36c3fd0c5f Mon Sep 17 00:00:00 2001 2 | From: Po Liu 3 | Date: Wed, 21 Aug 2019 14:46:08 +0800 4 | Subject: [PATCH 2/2] automatic python3 authorizing with root password none 5 | 6 | Signed-off-by: Po Liu 7 | --- 8 | python/session.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 9 | 1 file changed, 167 insertions(+) 10 | 11 | diff --git a/python/session.c b/python/session.c 12 | index 32df523..baa0b18 100644 13 | --- a/python/session.c 14 | +++ b/python/session.c 15 | @@ -2,6 +2,9 @@ 16 | #include 17 | 18 | #include 19 | +#include 20 | +#include 21 | +#include 22 | 23 | #include "netconf.h" 24 | 25 | @@ -21,6 +24,165 @@ static PyMemberDef ncSessionMembers[] = { 26 | 27 | #define SESSION_CHECK(self) if(!(self->session)){PyErr_SetString(libnetconfError,"Session closed.");return NULL;} 28 | 29 | +static int callback_ssh_hostkey_check_python(const char* hostname, ssh_session session) 30 | +{ 31 | + char* hexa; 32 | + int c, state, ret; 33 | + ssh_key srv_pubkey; 34 | + unsigned char *hash_sha1 = NULL; 35 | + size_t hlen; 36 | + enum ssh_keytypes_e srv_pubkey_type; 37 | + char answer[5]; 38 | + 39 | + state = ssh_is_server_known(session); 40 | + 41 | + ret = ssh_get_publickey(session, &srv_pubkey); 42 | + if (ret < 0) { 43 | + PySys_WriteStdout("Unable to get server public key."); 44 | + return EXIT_FAILURE; 45 | + } 46 | + 47 | + srv_pubkey_type = ssh_key_type(srv_pubkey); 48 | + ret = ssh_get_publickey_hash(srv_pubkey, SSH_PUBLICKEY_HASH_SHA1, &hash_sha1, &hlen); 49 | + ssh_key_free(srv_pubkey); 50 | + if (ret < 0) { 51 | + PySys_WriteStdout("Failed to calculate SHA1 hash of the server public key."); 52 | + return EXIT_FAILURE; 53 | + } 54 | + 55 | + hexa = ssh_get_hexa(hash_sha1, hlen); 56 | + 57 | + switch (state) { 58 | + case SSH_SERVER_KNOWN_OK: 59 | + break; /* ok */ 60 | + 61 | + case SSH_SERVER_KNOWN_CHANGED: 62 | + PySys_WriteStdout("Remote host key changed, the connection will be terminated!"); 63 | + goto fail; 64 | + 65 | + case SSH_SERVER_FOUND_OTHER: 66 | + PySys_WriteStdout("The iemote host key was not found but another type of key was, the connection will be terminated."); 67 | + goto fail; 68 | + 69 | + case SSH_SERVER_FILE_NOT_FOUND: 70 | + PySys_WriteStdout("Could not find the known hosts file."); 71 | + /* fallback to SSH_SERVER_NOT_KNOWN behavior */ 72 | + 73 | + case SSH_SERVER_NOT_KNOWN: 74 | +#ifdef ENABLE_DNSSEC 75 | + if (srv_pubkey_type != SSH_KEYTYPE_UNKNOWN || srv_pubkey_type != SSH_KEYTYPE_RSA1) { 76 | + if (srv_pubkey_type == SSH_KEYTYPE_DSS) { 77 | + ret = callback_ssh_hostkey_hash_dnssec_check(hostname, hash_sha1, 2, 1); 78 | + } else if (srv_pubkey_type == SSH_KEYTYPE_RSA) { 79 | + ret = callback_ssh_hostkey_hash_dnssec_check(hostname, hash_sha1, 1, 1); 80 | + } else if (srv_pubkey_type == SSH_KEYTYPE_ECDSA) { 81 | + ret = callback_ssh_hostkey_hash_dnssec_check(hostname, hash_sha1, 3, 1); 82 | + } 83 | + 84 | + /* DNSSEC SSHFP check successful, that's enough */ 85 | + if (ret == 0) { 86 | + DBG("DNSSEC SSHFP check successful"); 87 | + ssh_write_knownhost(session); 88 | + ssh_clean_pubkey_hash(&hash_sha1); 89 | + ssh_string_free_char(hexa); 90 | + return EXIT_SUCCESS; 91 | + } 92 | + } 93 | +#endif 94 | + 95 | + /* try to get result from user */ 96 | + fprintf(stdout, "The authenticity of the host \'%s\' cannot be established.\n", hostname); 97 | + fprintf(stdout, "%s key fingerprint is %s.\n", ssh_key_type_to_char(srv_pubkey_type), hexa); 98 | + 99 | +#ifdef ENABLE_DNSSEC 100 | + if (ret == 2) { 101 | + fprintf(stdout, "No matching host key fingerprint found in DNS.\n"); 102 | + } else if (ret == 1) { 103 | + fprintf(stdout, "Matching host key fingerprint found in DNS.\n"); 104 | + } 105 | +#endif 106 | + 107 | + fprintf(stdout, "Are you sure you want to continue connecting (yes/no)? yes\n "); 108 | + 109 | + ret = ssh_write_knownhost(session); 110 | + if (ret < 0) { 111 | + PySys_WriteStdout("Adding the known host %s failed (%s).", hostname, strerror(errno)); 112 | + } 113 | + break; 114 | + 115 | + case SSH_SERVER_ERROR: 116 | + ssh_clean_pubkey_hash(&hash_sha1); 117 | + fprintf(stderr,"%s",ssh_get_error(session)); 118 | + return -1; 119 | + } 120 | + 121 | + ssh_clean_pubkey_hash(&hash_sha1); 122 | + ssh_string_free_char(hexa); 123 | + return EXIT_SUCCESS; 124 | + 125 | +fail: 126 | + ssh_clean_pubkey_hash(&hash_sha1); 127 | + ssh_string_free_char(hexa); 128 | + return EXIT_FAILURE; 129 | +} 130 | + 131 | +static char* callback_sshauth_password_python(const char* username, 132 | + const char* hostname) 133 | +{ 134 | + char* buf, *newbuf; 135 | + int buflen = 1024, len = 0; 136 | + char c = 0; 137 | + struct termios newterm, oldterm; 138 | + FILE* tty; 139 | + 140 | + buf = malloc (buflen * sizeof(char)); 141 | + if (buf == NULL) { 142 | + printf("Memory allocation failed (%s:%d - %s).", __FILE__, __LINE__, strerror(errno)); 143 | + return (NULL); 144 | + } 145 | + 146 | + if ((tty = fopen("/dev/tty", "r+")) == NULL) { 147 | + printf("Unable to open the current terminal (%s:%d - %s).", __FILE__, __LINE__, strerror(errno)); 148 | + return (NULL); 149 | + } 150 | + 151 | + if (tcgetattr(fileno(tty), &oldterm) != 0) { 152 | + printf("Unable to get terminal settings (%d: %s).", __LINE__, strerror(errno)); 153 | + return (NULL); 154 | + } 155 | + 156 | + fprintf(tty, "%s@%s password: ", username, hostname); 157 | + fflush(tty); 158 | + 159 | + /* system("stty -echo"); */ 160 | + newterm = oldterm; 161 | + newterm.c_lflag &= ~ECHO; 162 | + newterm.c_lflag &= ~ICANON; 163 | + tcflush(fileno(tty), TCIFLUSH); 164 | + if (tcsetattr(fileno(tty), TCSANOW, &newterm) != 0) { 165 | + printf("Unable to change terminal settings for hiding password (%d: %s).", __LINE__, strerror(errno)); 166 | + return (NULL); 167 | + } 168 | + 169 | + //buf[0] = '\r'; 170 | + buf[0] = 0; /* terminating null byte */ 171 | + 172 | + /* system ("stty echo"); */ 173 | + if (tcsetattr(fileno(tty), TCSANOW, &oldterm) != 0) { 174 | + printf("Unable to restore terminal settings (%d: %s).", __LINE__, strerror(errno)); 175 | + /* 176 | + * terminal probably still hides input characters, but we have password 177 | + * and anyway we are unable to set terminal to the previous state, so 178 | + * just continue 179 | + */ 180 | + } 181 | + fprintf(tty, "\n"); 182 | + 183 | + PySys_WriteStdout("alreayd input password\r\n"); 184 | + fclose(tty); 185 | + return (buf); 186 | +} 187 | + 188 | static void ncSessionFree(ncSessionObject *self) 189 | { 190 | PyObject *err_type, *err_value, *err_traceback; 191 | @@ -694,6 +856,11 @@ static int ncSessionInit(ncSessionObject *self, PyObject *args, PyObject *keywor 192 | cpblts = global_cpblts; 193 | } 194 | 195 | + //nc_set_keypair_path("~/.ssh/id_rsa","~/.ssh/id_rsa.pub"); 196 | + nc_ssh_pref(NC_SSH_AUTH_INTERACTIVE, -1); 197 | + nc_callback_sshauth_password(callback_sshauth_password_python); 198 | + nc_callback_ssh_host_authenticity_check(callback_ssh_hostkey_check_python); 199 | + 200 | if (host != NULL) { 201 | /* Client side */ 202 | if (fd_in != -1 && fd_out != -1) { 203 | -- 204 | 1.8.3.1 205 | 206 | -------------------------------------------------------------------------------- /demos/cnc/static/xml2json.js: -------------------------------------------------------------------------------- 1 | /* This work is licensed under Creative Commons GNU LGPL License. 2 | 3 | License: http://creativecommons.org/licenses/LGPL/2.1/ 4 | Version: 0.9 5 | Author: Stefan Goessner/2006 6 | Web: http://goessner.net/ 7 | */ 8 | function xml2json(xml, tab) { 9 | var X = { 10 | toObj: function(xml) { 11 | var o = {}; 12 | if (xml.nodeType==1) { // element node .. 13 | if (xml.attributes.length) // element with attributes .. 14 | for (var i=0; i 1) 56 | o = X.escape(X.innerXml(xml)); 57 | else 58 | for (var n=xml.firstChild; n; n=n.nextSibling) 59 | o["#cdata"] = X.escape(n.nodeValue); 60 | } 61 | } 62 | if (!xml.attributes.length && !xml.firstChild) o = null; 63 | } 64 | else if (xml.nodeType==9) { // document.node 65 | o = X.toObj(xml.documentElement); 66 | } 67 | else 68 | alert("unhandled node type: " + xml.nodeType); 69 | return o; 70 | }, 71 | toJson: function(o, name, ind) { 72 | var json = name ? ("\""+name+"\"") : ""; 73 | if (o instanceof Array) { 74 | for (var i=0,n=o.length; i 1 ? ("\n"+ind+"\t"+o.join(",\n"+ind+"\t")+"\n"+ind) : o.join("")) + "]"; 77 | } 78 | else if (o == null) 79 | json += (name&&":") + "null"; 80 | else if (typeof(o) == "object") { 81 | var arr = []; 82 | for (var m in o) 83 | arr[arr.length] = X.toJson(o[m], m, ind+"\t"); 84 | json += (name?":{":"{") + (arr.length > 1 ? ("\n"+ind+"\t"+arr.join(",\n"+ind+"\t")+"\n"+ind) : arr.join("")) + "}"; 85 | } 86 | else if (typeof(o) == "string") 87 | json += (name&&":") + "\"" + o.toString() + "\""; 88 | else 89 | json += (name&&":") + o.toString(); 90 | return json; 91 | }, 92 | innerXml: function(node) { 93 | var s = "" 94 | if ("innerHTML" in node) 95 | s = node.innerHTML; 96 | else { 97 | var asXml = function(n) { 98 | var s = ""; 99 | if (n.nodeType == 1) { 100 | s += "<" + n.nodeName; 101 | for (var i=0; i"; 108 | } 109 | else 110 | s += "/>"; 111 | } 112 | else if (n.nodeType == 3) 113 | s += n.nodeValue; 114 | else if (n.nodeType == 4) 115 | s += ""; 116 | return s; 117 | }; 118 | for (var c=node.firstChild; c; c=c.nextSibling) 119 | s += asXml(c); 120 | } 121 | return s; 122 | }, 123 | escape: function(txt) { 124 | return txt.replace(/[\\]/g, "\\\\") 125 | .replace(/[\"]/g, '\\"') 126 | .replace(/[\n]/g, '\\n') 127 | .replace(/[\r]/g, '\\r'); 128 | }, 129 | removeWhite: function(e) { 130 | e.normalize(); 131 | for (var n = e.firstChild; n; ) { 132 | if (n.nodeType == 3) { // text node 133 | if (!n.nodeValue.match(/[^ \f\n\r\t\v]/)) { // pure whitespace text node 134 | var nxt = n.nextSibling; 135 | e.removeChild(n); 136 | n = nxt; 137 | } 138 | else 139 | n = n.nextSibling; 140 | } 141 | else if (n.nodeType == 1) { // element node 142 | X.removeWhite(n); 143 | n = n.nextSibling; 144 | } 145 | else // any other node 146 | n = n.nextSibling; 147 | } 148 | return e; 149 | } 150 | }; 151 | if (xml.nodeType == 9) // document node 152 | xml = xml.documentElement; 153 | var json = X.toJson(X.toObj(X.removeWhite(xml)), xml.nodeName, "\t"); 154 | return "{\n" + tab + (tab ? json.replace(/\t/g, tab) : json.replace(/\t|\n/g, "")) + "\n}"; 155 | } 156 | -------------------------------------------------------------------------------- /demos/cnc/templates/configQbu.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Config Qbv 6 | 8 | 61 | 62 | 63 | 149 | 150 |
151 |
152 | *device: 153 | 163 |

164 | enable 165 | disable 166 |
   167 |
TC0
168 | preemptable 169 | express 170 | 171 |
TC1
172 | preemptable 173 | express 174 | 175 |
TC2
176 | preemptable 177 | express 178 | 179 |
TC3
180 | preemptable 181 | express 182 | 183 |
TC4
184 | preemptable 185 | express 186 | 187 |
TC5
188 | preemptable 189 | express 190 | 191 |
TC6
192 | preemptable 193 | express 194 | 195 |
TC7
196 | preemptable 197 | express 198 |


199 | 200 | 201 |
202 | 203 | 204 | -------------------------------------------------------------------------------- /demos/cnc/templates/configQbv.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Config Qbv 6 | 8 | 73 | 89 | 90 | 98 | 99 | 100 | 101 | 215 |
216 |
217 | *device: 218 | 228 |

229 | enable 230 | disable 231 |

  basetime: 232 | 233 |
example: s.ns

234 | *gate control list: 235 |

236 |
237 | GATE        PERIOD 238 |
239 | 240 | 241 | 242 | 243 |
244 |


245 | 246 | 247 | 248 |
249 | 250 | 251 | -------------------------------------------------------------------------------- /demos/cnc/templates/indexdevice.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Device Config 6 | 8 | 9 | 86 | 94 | 95 | 170 | 171 | 241 | 242 | 243 | 244 |
245 | 246 | 249 | 250 | 252 | 253 | 271 | 276 | 278 | 279 | 280 | 281 | 282 | -------------------------------------------------------------------------------- /main/readinput.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 | /* 3 | * Copyright 2018 NXP 4 | */ 5 | 6 | #define _GNU_SOURCE 7 | #define _BSD_SOURCE 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "readinput.h" 22 | #include "cmd.h" 23 | 24 | #define EDITOR_DEFAULT "vi" 25 | #define EDITOR_ENV "TEXT_EDITOR" 26 | 27 | extern int multiline; 28 | volatile char *last_tmpfile; 29 | 30 | extern struct cli_cmd cli_commands[]; 31 | 32 | extern struct cli_options *opts; 33 | 34 | /* 35 | * Generator function for command completion. STATE lets us know whether 36 | * to start from scratch; without any state (i.e. STATE == 0), then we 37 | * start at the top of the list. 38 | */ 39 | char *cmd_generator(const char *text, int state) 40 | { 41 | static int list_index, len; 42 | char *name; 43 | 44 | /* 45 | * If this is a new word to complete, initialize now. This includes 46 | * saving the length of TEXT for efficiency, and initializing the index 47 | * variable to 0. 48 | */ 49 | if (!state) { 50 | list_index = 0; 51 | len = strlen(text); 52 | } 53 | 54 | /* Return the next name which partially matches from the command list. */ 55 | while ((name = cli_commands[list_index].cmd) != NULL) { 56 | list_index++; 57 | 58 | if (strncmp(name, text, len) == 0) 59 | return strdup(name); 60 | } 61 | 62 | /* If no names matched, then return NULL. */ 63 | return ((char *) NULL); 64 | } 65 | 66 | char *para_generator(const char *text, int state) 67 | { 68 | static int list_index, len; 69 | char *name; 70 | static int cmdnumber; 71 | 72 | /* If this is a new word to complete, initialize now. This includes 73 | * saving the length of TEXT for efficiency, and initializing the index 74 | * variable to 0. 75 | */ 76 | if (!state) { 77 | list_index = 0; 78 | len = strlen(text); 79 | cmdnumber = 0; 80 | while ((cli_commands[cmdnumber].cmd != NULL) && 81 | (strstr(rl_line_buffer, cli_commands[cmdnumber].cmd) == NULL)) { 82 | cmdnumber++; 83 | } 84 | 85 | if (cli_commands[cmdnumber].cmd == NULL) 86 | return ((char *)NULL); 87 | } 88 | 89 | /* Return the next name which partially matches from the command list. */ 90 | while ((name = cli_commands[cmdnumber].long_options[list_index].name) != NULL) { 91 | list_index++; 92 | 93 | if (strncmp(name, text, len) == 0) { 94 | char *para; 95 | 96 | para = malloc(strlen(name) + 2); 97 | sprintf(para, "--%s", name); 98 | return para; 99 | } 100 | } 101 | 102 | /* If no names matched, then return NULL. */ 103 | return ((char *) NULL); 104 | } 105 | 106 | /** 107 | * \brief Attempt to complete available program commands. 108 | * 109 | * Attempt to complete on the contents of #text. #start and #end bound the 110 | * region of rl_line_buffer that contains the word to complete. #text is the 111 | * word to complete. We can use the entire contents of rl_line_buffer in case 112 | * we want to do some simple parsing. 113 | * 114 | * \return The array of matches, or NULL if there aren't any. 115 | */ 116 | char **cmd_completion(const char *text, int start, UNUSED int end) 117 | { 118 | char **matches; 119 | 120 | matches = (char **) NULL; 121 | 122 | /* 123 | * If this word is at the start of the line, then it is a command 124 | * to complete. Otherwise it is the name of a file in the current 125 | * directory. 126 | */ 127 | if (start == 0) { 128 | matches = rl_completion_matches(text, cmd_generator); 129 | } else if (text[0] == '-' && text[1] == '-') { 130 | matches = rl_completion_matches(text + 2, para_generator); 131 | } 132 | 133 | return (matches); 134 | } 135 | 136 | int bind_del_hent(UNUSED int count, UNUSED int key) 137 | { 138 | HIST_ENTRY *hent; 139 | 140 | hent = remove_history(where_history()); 141 | if (hent != NULL) { 142 | free(hent->line); 143 | free(hent->timestamp); 144 | free(hent->data); 145 | free(hent); 146 | 147 | hent = previous_history(); 148 | if (hent != NULL) { 149 | rl_extend_line_buffer(strlen(hent->line)+1); 150 | strcpy(rl_line_buffer, hent->line); 151 | rl_end = rl_point = strlen(hent->line); 152 | } else { 153 | history_set_pos(history_length); 154 | rl_line_buffer[0] = '\0'; 155 | rl_point = 0; 156 | } 157 | } 158 | 159 | return 0; 160 | } 161 | 162 | int bind_cr(UNUSED int count, UNUSED int key) 163 | { 164 | if (multiline == 0) { 165 | rl_point = rl_end; 166 | rl_redisplay(); 167 | rl_done = 1; 168 | } 169 | printf("\n"); 170 | 171 | return 0; 172 | } 173 | 174 | int bind_esc(UNUSED int count, UNUSED int key) 175 | { 176 | if (multiline == 1) { 177 | rl_point = rl_end; 178 | rl_redisplay(); 179 | rl_done = 1; 180 | printf("\n"); 181 | } 182 | return 0; 183 | } 184 | 185 | char *ins_old_content; 186 | 187 | int bind_ins_content(UNUSED int count, UNUSED int key) 188 | { 189 | if (ins_old_content != NULL) { 190 | rl_extend_line_buffer(strlen(rl_line_buffer)+strlen(ins_old_content)); 191 | memmove(rl_line_buffer+rl_point+strlen(ins_old_content), rl_line_buffer+rl_point, rl_end-rl_point+1); 192 | memcpy(rl_line_buffer+rl_point, ins_old_content, strlen(ins_old_content)); 193 | rl_end += strlen(ins_old_content); 194 | rl_point += strlen(ins_old_content); 195 | } 196 | return 0; 197 | } 198 | 199 | /** 200 | * \brief Tell the GNU Readline library how to complete commands. 201 | * 202 | * We want to try to complete on command names if this is the first word in the 203 | * line, or on filenames if not. 204 | */ 205 | void initialize_readline(void) 206 | { 207 | /* Allow conditional parsing of the ~/.inputrc file. */ 208 | rl_readline_name = "netconf"; 209 | 210 | /* Tell the completer that we want a crack first. */ 211 | rl_attempted_completion_function = cmd_completion; 212 | 213 | rl_bind_key('\n', bind_cr); 214 | rl_bind_key('\r', bind_cr); 215 | rl_bind_key(CTRL('d'), bind_esc); 216 | rl_bind_key(CTRL('x'), bind_del_hent); 217 | rl_bind_key(CTRL('a'), bind_ins_content); 218 | } 219 | 220 | char *readinput(const char *instruction, const char *tmpfile, FILE *output) 221 | { 222 | int tmpfd = -1, oldfd, ret, size, old_history_pos; 223 | pid_t pid, wait_pid; 224 | char *tmpname = NULL, *input = NULL, *old_content = NULL, *ptr, *ptr2; 225 | const char *editor = NULL; 226 | 227 | editor = getenv(EDITOR_ENV); 228 | if (editor == NULL) { 229 | editor = EDITOR; 230 | } 231 | if (editor == NULL) { 232 | editor = getenv("EDITOR"); 233 | } 234 | if (editor == NULL) { 235 | editor = EDITOR_DEFAULT; 236 | } 237 | 238 | /* Create a unique temporary file */ 239 | asprintf(&tmpname, "/tmp/tmpXXXXXX.txt"); 240 | tmpfd = mkstemps(tmpname, 4); 241 | if (tmpfd == -1) { 242 | ERROR("readinput", "Failed to create a temporary file (%s).", strerror(errno)); 243 | goto fail; 244 | } 245 | 246 | /* Read the old content, if any */ 247 | if (tmpfile != NULL) { 248 | oldfd = open(tmpfile, O_RDONLY); 249 | if (oldfd != -1) { 250 | size = lseek(oldfd, 0, SEEK_END); 251 | lseek(oldfd, 0, SEEK_SET); 252 | if (size > 0) { 253 | old_content = malloc(size+1); 254 | old_content[size] = '\0'; 255 | ret = read(oldfd, old_content, size); 256 | if (ret != size) { 257 | free(old_content); 258 | old_content = NULL; 259 | } 260 | } 261 | close(oldfd); 262 | } 263 | } 264 | 265 | if (strcmp(editor, "NONE") == 0) { 266 | INSTRUCTION(output, "(finish input by Ctrl-D, add previous content from history by Ctrl-A)"); 267 | INSTRUCTION(output, instruction); 268 | INSTRUCTION(output, "\n"); 269 | 270 | multiline = 1; 271 | if (old_content != NULL) 272 | ins_old_content = old_content; 273 | 274 | /* calling readline resets history position */ 275 | old_history_pos = where_history(); 276 | input = readline(NULL); 277 | history_set_pos(old_history_pos); 278 | 279 | ins_old_content = NULL; 280 | multiline = 0; 281 | 282 | if (input == NULL) { 283 | /* not really a fail, just no input */ 284 | goto fail; 285 | } 286 | 287 | ret = write(tmpfd, input, strlen(input)); 288 | if (ret < strlen(input)) { 289 | ERROR("readinput", "Failed to write the content into a temp file (%s).", strerror(errno)); 290 | goto fail; 291 | } 292 | 293 | } else { 294 | if (old_content != NULL) { 295 | ret = write(tmpfd, old_content, strlen(old_content)); 296 | if (ret < strlen(old_content)) { 297 | ERROR("readinput", "Failed to write the previous content (%s).", strerror(errno)); 298 | goto fail; 299 | } 300 | 301 | } else if (instruction != NULL) { 302 | ret = write(tmpfd, "\n#Example entry:\n", 7); 303 | ret += write(tmpfd, instruction, strlen(instruction)); 304 | ret += write(tmpfd, "\n#End Example\n", 5); 305 | if (ret < 6+strlen(instruction)+5) { 306 | ERROR("readinput", "Failed to write the instruction (%s).", strerror(errno)); 307 | goto fail; 308 | } 309 | 310 | ret = lseek(tmpfd, 0, SEEK_SET); 311 | if (ret == -1) { 312 | ERROR("readinput", "Rewinding the temporary file failed (%s).", strerror(errno)); 313 | goto fail; 314 | } 315 | } 316 | 317 | pid = vfork(); 318 | if (pid == -1) { 319 | ERROR("readinput", "Fork failed (%s).", strerror(errno)); 320 | goto fail; 321 | } else if (pid == 0) { 322 | /* child */ 323 | execlp(editor, editor, tmpname, (char *)NULL); 324 | 325 | ERROR("readinput", "Exec failed (%s).", strerror(errno)); 326 | exit(1); 327 | } else { 328 | /* parent */ 329 | wait_pid = wait(&ret); 330 | if (wait_pid != pid) { 331 | ERROR("readinput", "Child process other than the editor exited, weird."); 332 | goto fail; 333 | } 334 | if (!WIFEXITED(ret)) { 335 | ERROR("readinput", "Editor exited in a non-standard way."); 336 | goto fail; 337 | } 338 | } 339 | 340 | /* Get the size of the input */ 341 | size = lseek(tmpfd, 0, SEEK_END); 342 | if (size == -1) { 343 | ERROR("readinput", "Failed to get the size of the temporary file (%s).", strerror(errno)); 344 | goto fail; 345 | } else if (size == 0) { 346 | /* not a fail, just no input */ 347 | goto fail; 348 | } 349 | lseek(tmpfd, 0, SEEK_SET); 350 | 351 | input = malloc(size+1); 352 | input[size] = '\0'; 353 | 354 | /* Read the input */ 355 | ret = read(tmpfd, input, size); 356 | if (ret < size) { 357 | ERROR("readinput", "Failed to read from the temporary file (%s).", strerror(errno)); 358 | goto fail; 359 | } 360 | 361 | /* Remove the instruction comment */ 362 | if (old_content == NULL && instruction != NULL) { 363 | ptr = strstr(input, "\n\n"); 367 | /* The user could have deleted or modified the comment, ignore it then */ 368 | if (ptr2 != NULL) { 369 | ptr2 += 5; 370 | memmove(ptr, ptr2, strlen(ptr2)+1); 371 | 372 | /* Save the modified content */ 373 | if (ftruncate(tmpfd, 0) == -1) { 374 | ERROR("readinput", "Failed to truncate the temporary file (%s).", strerror(errno)); 375 | goto fail; 376 | } 377 | lseek(tmpfd, 0, SEEK_SET); 378 | ret = write(tmpfd, input, strlen(input)); 379 | if (ret < strlen(input)) { 380 | ERROR("readinput", "Failed to write to the temporary file (%s).", strerror(errno)); 381 | goto fail; 382 | } 383 | } 384 | } 385 | } 386 | 387 | cleanup: 388 | 389 | close(tmpfd); 390 | free(old_content); 391 | free((char *)last_tmpfile); 392 | last_tmpfile = tmpname; 393 | 394 | return input; 395 | 396 | fail: 397 | if (tmpfd > -1) 398 | close(tmpfd); 399 | if (tmpname != NULL) 400 | unlink(tmpname); 401 | free(tmpname); 402 | free(old_content); 403 | free((char *)last_tmpfile); 404 | last_tmpfile = NULL; 405 | free(input); 406 | 407 | return NULL; 408 | } 409 | -------------------------------------------------------------------------------- /lib/multicast.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 | /* 3 | * Copyright 2019 NXP 4 | */ 5 | #define _GNU_SOURCE 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "tsn/genl_tsn.h" 18 | 19 | struct alarm_node { 20 | pthread_t thread; 21 | uint32_t iface; 22 | uint32_t infotype; 23 | uint64_t ts; 24 | uint32_t offset; 25 | uint32_t cycle; 26 | void (*callback_func)(void *data); 27 | void *data; 28 | struct alarm_node *next; 29 | }; 30 | 31 | static int running = 1; 32 | static struct alarm_node *head_msg = NULL; 33 | 34 | static int set_realtime(pthread_t thread, int priority, int cpu) 35 | { 36 | cpu_set_t cpuset; 37 | struct sched_param sp; 38 | int err, policy; 39 | 40 | int min = sched_get_priority_min(SCHED_FIFO); 41 | int max = sched_get_priority_max(SCHED_FIFO); 42 | 43 | fprintf(stderr, "min %d max %d\n", min, max); 44 | 45 | if (priority < 0) { 46 | return 0; 47 | } 48 | 49 | err = pthread_getschedparam(thread, &policy, &sp); 50 | if (err) { 51 | fprintf(stderr, "pthread_getschedparam: %s\n", strerror(err)); 52 | return -1; 53 | } 54 | 55 | sp.sched_priority = priority; 56 | 57 | err = pthread_setschedparam(thread, SCHED_FIFO, &sp); 58 | if (err) { 59 | fprintf(stderr, "pthread_setschedparam: %s\n", strerror(err)); 60 | return -1; 61 | } 62 | 63 | if (cpu < 0) { 64 | return 0; 65 | } 66 | CPU_ZERO(&cpuset); 67 | CPU_SET(cpu, &cpuset); 68 | err = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset); 69 | if (err) { 70 | fprintf(stderr, "pthread_setaffinity_np: %s\n", strerror(err)); 71 | return -1; 72 | } 73 | 74 | return 0; 75 | } 76 | 77 | int64_t pctns(struct timespec *t) 78 | { 79 | return (t->tv_sec * 1000000000ULL + t->tv_nsec); 80 | } 81 | 82 | int set_period_alarm(uint64_t ts, uint64_t offset, uint64_t cycle, 83 | void (*callback_func)(void *data), void *data) 84 | { 85 | struct timespec now; 86 | 87 | if (!callback_func) 88 | return -1; 89 | 90 | ts += offset; 91 | 92 | now.tv_sec = ts/1000000000ULL; 93 | now.tv_nsec = ts - ts/1000000000ULL*1000000000ULL; 94 | 95 | while (running) { 96 | clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &now, NULL); 97 | callback_func(data); 98 | ts = pctns(&now) + cycle; 99 | now.tv_sec = ts/1000000000ULL; 100 | now.tv_nsec = ts - ts/1000000000ULL*1000000000ULL; 101 | } 102 | } 103 | 104 | void alarm_thread(void *data) 105 | { 106 | struct alarm_node *msg = (struct alarm_node *)data; 107 | 108 | set_realtime(pthread_self(), 1, 0); 109 | 110 | set_period_alarm(msg->ts, msg->offset, msg->cycle, msg->callback_func, msg->data); 111 | } 112 | 113 | int create_alarm_thread(uint64_t ts, uint32_t offset, uint32_t cycle, 114 | uint32_t iface, uint32_t infotype, 115 | void (*callback_func)(void *data), void *data) 116 | { 117 | struct alarm_node *msg, *node, *node1; 118 | 119 | node = head_msg; 120 | node1 = head_msg; 121 | while (node) { 122 | if (node->iface == iface && node->infotype == infotype) { 123 | pthread_cancel(node->thread); 124 | if (node == head_msg) { 125 | head_msg = node1->next; 126 | } else { 127 | node1->next = node->next; 128 | } 129 | free(node); 130 | break; 131 | } 132 | node1 = node; 133 | node = node->next; 134 | } 135 | 136 | msg = (struct alarm_node *)malloc(sizeof(struct alarm_node)); 137 | memset(msg, 0, sizeof(*msg)); 138 | 139 | node = head_msg; 140 | node1 = head_msg; 141 | while (node) { 142 | node1 = node; 143 | node = node->next; 144 | } 145 | 146 | if (!head_msg) { 147 | head_msg = msg; 148 | } else { 149 | node1->next = msg; 150 | } 151 | 152 | msg->ts = ts; 153 | msg->offset = offset; 154 | msg->cycle = cycle; 155 | msg->iface = iface; 156 | msg->infotype = infotype; 157 | msg->next = NULL; 158 | msg->callback_func = callback_func; 159 | msg->data = data; 160 | 161 | pthread_create(&msg->thread, NULL, alarm_thread, msg); 162 | 163 | return 0; 164 | } 165 | 166 | pthread_t *create_alarm_common(uint64_t ts, uint32_t offset, uint32_t cycle, 167 | void (*callback_func)(void *data), void *data) 168 | { 169 | struct alarm_node *msg, *node, *node1; 170 | int res; 171 | 172 | msg = (struct alarm_node *)malloc(sizeof(struct alarm_node)); 173 | memset(msg, 0, sizeof(*msg)); 174 | 175 | node = head_msg; 176 | node1 = head_msg; 177 | while (node) { 178 | node1 = node; 179 | node = node->next; 180 | } 181 | 182 | if (!head_msg) { 183 | head_msg = msg; 184 | } else { 185 | node1->next = msg; 186 | } 187 | 188 | msg->ts = ts; 189 | msg->offset = offset; 190 | msg->cycle = cycle; 191 | msg->iface = 0; 192 | msg->infotype = TSN_MCGRP_MAX + 1; 193 | msg->next = NULL; 194 | msg->callback_func = callback_func; 195 | msg->data = data; 196 | 197 | res = pthread_create(&msg->thread, NULL, alarm_thread, msg); 198 | if (res) { 199 | printf("Create alarm failed\n"); 200 | free(msg); 201 | return NULL; 202 | } 203 | 204 | return &msg->thread; 205 | } 206 | 207 | int delete_alarm_common(pthread_t *thread) 208 | { 209 | struct alarm_node *node, *node1; 210 | void *result; 211 | int res; 212 | 213 | node = head_msg; 214 | node1 = head_msg; 215 | while (node) { 216 | if (node->thread == *thread) { 217 | pthread_cancel(node->thread); 218 | res = pthread_join(node->thread, &result); 219 | if (res) { 220 | printf("Create alarm failed\n"); 221 | return -1; 222 | } 223 | if (node == head_msg) { 224 | head_msg = node1->next; 225 | } else { 226 | node1->next = node->next; 227 | } 228 | free(node); 229 | return 0; 230 | } 231 | node1 = node; 232 | node = node->next; 233 | } 234 | 235 | return -1; 236 | } 237 | 238 | int delete_one_alarm(uint32_t iface, uint32_t infotype) 239 | { 240 | struct alarm_node *node, *node1; 241 | 242 | node = head_msg; 243 | node1 = head_msg; 244 | while (node) { 245 | if (node->iface == iface && node->infotype == infotype) { 246 | pthread_cancel(node->thread); 247 | if (node == head_msg) { 248 | head_msg = node1->next; 249 | } else { 250 | node1->next = node->next; 251 | } 252 | free(node); 253 | return 0; 254 | } 255 | node1 = node; 256 | node = node->next; 257 | } 258 | 259 | return -1; 260 | } 261 | 262 | void clear_alarms() 263 | { 264 | struct alarm_node *node, *node1; 265 | 266 | node = head_msg; 267 | node1 = head_msg; 268 | while (node) { 269 | pthread_cancel(node->thread); 270 | node1 = node; 271 | node = node->next; 272 | free(node1); 273 | } 274 | 275 | head_msg = NULL; 276 | } 277 | 278 | int genl_sendto_msg(struct nl_sock *sd, uint16_t nlmsg_type, uint32_t nlmsg_pid, 279 | uint8_t genl_cmd, uint8_t genl_version, uint16_t nla_type, 280 | void *nla_data, int nla_len) 281 | { 282 | struct nlattr *na; 283 | int r, buflen; 284 | char *buf; 285 | msgtemplate_t msg; 286 | 287 | if (nlmsg_type == 0) 288 | return 0; 289 | 290 | msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); 291 | msg.n.nlmsg_type = nlmsg_type; 292 | msg.n.nlmsg_flags = NLM_F_REQUEST; 293 | msg.n.nlmsg_seq = 0; 294 | /* 295 | * nlmsg_pid 296 | * Linux 297 | */ 298 | msg.n.nlmsg_pid = nlmsg_pid; 299 | msg.g.cmd = genl_cmd; 300 | msg.g.version = genl_version; 301 | na = (struct nlattr *) GENLMSG_USER_DATA(&msg); 302 | na->nla_type = nla_type; 303 | na->nla_len = nla_len + 1 + NLA_HDRLEN; 304 | memcpy(NLA_DATA(na), nla_data, nla_len); 305 | msg.n.nlmsg_len += NLMSG_ALIGN(na->nla_len); 306 | 307 | buf = (char *) &msg; 308 | buflen = msg.n.nlmsg_len; 309 | while ((r = nl_sendto(sd, buf, buflen)) < buflen) { 310 | if (r > 0) { 311 | buf += r; 312 | buflen -= r; 313 | } else if (errno != EAGAIN) { 314 | return -1; 315 | } 316 | } 317 | return 0; 318 | } 319 | 320 | static int tsn_multicast_cb(struct nl_msg *msg, void *data) 321 | 322 | { 323 | struct nlmsghdr *nlh = nlmsg_hdr(msg); 324 | struct genlmsghdr *genlh = genlmsg_hdr(nlh); 325 | struct tsn_family_groups *p_fmgroups = data; 326 | struct alarm_info *ai; 327 | struct nlattr *payload = genlmsg_attrdata(genlh, NLMSG_ALIGN(MAX_USER_SIZE)); 328 | 329 | if (!payload) { 330 | printf("multicast message with none payload!\n"); 331 | return 0; 332 | } 333 | 334 | if (!p_fmgroups) { 335 | printf("No family groups information!\n"); 336 | return 0; 337 | } 338 | 339 | ai = p_fmgroups->ai; 340 | 341 | if (!ai) { 342 | printf("No alarm_info input!\n"); 343 | return 0; 344 | } 345 | 346 | if (nlh->nlmsg_type == p_fmgroups->family_id) { 347 | switch(genlh->cmd) { 348 | case TSN_CMD_QBV_SET: 349 | { 350 | int nlalen, remain; 351 | int interface; 352 | uint64_t cctime; 353 | uint32_t cytime; 354 | bool enable = 0; 355 | struct nlattr *nla; 356 | 357 | nlalen = genlmsg_attrlen(genlh, NLMSG_ALIGN(MAX_USER_SIZE)); 358 | printf("got tsn qbv multicast command!\n"); 359 | nla_for_each_attr(nla, payload, nlalen, remain) { 360 | if (nla->nla_type == TSN_QBV_ATTR_CTRL_BASETIME) { 361 | cctime = nla_get_u64(nla); 362 | printf("got configchangetime %lld\n", cctime); 363 | } 364 | if (nla->nla_type == TSN_QBV_ATTR_CTRL_CYCLETIME) { 365 | cytime = nla_get_u32(nla); 366 | printf("got cycle time %ld\n", cytime); 367 | } 368 | if (nla->nla_type == TSN_QBV_ATTR_ENABLE + TSN_QBV_ATTR_CTRL_MAX) { 369 | enable = 1; 370 | printf("got qbv enable flag\n"); 371 | } 372 | if (nla->nla_type == TSN_QBV_ATTR_DISABLE + TSN_QBV_ATTR_CTRL_MAX) { 373 | enable = 0; 374 | printf("got qbv disable flag\n"); 375 | } 376 | if (nla->nla_type == TSN_QBV_ATTR_CTRL_UNSPEC) { 377 | interface = nla_get_u32(nla); 378 | printf("got interface is %d\n", interface); 379 | } 380 | } 381 | if (enable) { 382 | ai->qbvmc.cct = cctime; 383 | ai->qbvmc.ifidx = interface; 384 | create_alarm_thread(cctime, ai->qbvmc.offset, cytime, 385 | interface, TSN_MCGRP_QBV, 386 | ai->qbvmc.callback_func, 387 | ai->qbvmc.data); 388 | } else { 389 | delete_one_alarm(interface, TSN_MCGRP_QBV); 390 | } 391 | } 392 | break; 393 | case TSN_CMD_QCI_SGI_SET: 394 | printf("got tsn qci multicast command!\n"); 395 | break; 396 | default: 397 | printf("not support command type!\n"); 398 | break; 399 | } 400 | } else if (nlh->nlmsg_type == GENL_ID_CTRL) { 401 | printf("got control message!"); 402 | return 0; 403 | } 404 | 405 | return 0; 406 | } 407 | 408 | int wait_tsn_multicast(struct alarm_info *ainfo) 409 | { 410 | char family_name[] = "TSN_GEN_CTRL"; 411 | int rc; 412 | struct tsn_family_groups fmgroups; 413 | struct nl_sock * s = nl_socket_alloc(); 414 | 415 | fmgroups.ai = ainfo; 416 | 417 | if (!s) { 418 | printf("nl_socket_alloc"); 419 | return -1; 420 | } 421 | 422 | nl_socket_disable_seq_check(s); 423 | nl_socket_modify_cb(s, NL_CB_VALID, NL_CB_CUSTOM, tsn_multicast_cb, &fmgroups); 424 | 425 | if (genl_connect(s)) { 426 | nl_socket_free(s); 427 | printf("nl_connect"); 428 | return -1; 429 | } 430 | 431 | rc = genl_sendto_msg(s, GENL_ID_CTRL, 0, CTRL_CMD_GETFAMILY, 1, 432 | CTRL_ATTR_FAMILY_NAME, (void *)family_name, 433 | strlen(family_name)+1); 434 | if (rc < 0) { 435 | nl_socket_free(s); 436 | printf("failure: send simple\n"); 437 | printf("nl_send_simple"); 438 | return -1; 439 | } 440 | 441 | //Retrieve the kernel's answer. 442 | nl_recvmsgs_default(s); 443 | 444 | rc = genl_ctrl_resolve(s, family_name); 445 | if (rc < 0) { 446 | printf("got error when genl_ctrl_resolve\n"); 447 | nl_socket_free(s); 448 | printf("genl_ctrl_resolve"); 449 | return -1; 450 | } 451 | 452 | printf("family id is %d\n", rc); 453 | fmgroups.family_id = rc; 454 | 455 | rc = genl_ctrl_resolve_grp(s, family_name, TSN_MULTICAST_GROUP_QBV); 456 | if (rc < 0) { 457 | printf("got error when genl_ctrl_resolve_grp\n"); 458 | } 459 | 460 | printf("group id qbv is %d\n", rc); 461 | fmgroups.mc[TSN_MCGRP_QBV] = rc; 462 | nl_socket_add_memberships(s, rc, 0); 463 | 464 | rc = genl_ctrl_resolve_grp(s, family_name, TSN_MULTICAST_GROUP_QCI); 465 | if (rc < 0) { 466 | printf("got error when genl_ctrl_resolve_grp\n"); 467 | } 468 | 469 | printf("group id qci is %d\n", rc); 470 | fmgroups.mc[TSN_MCGRP_QCI] = rc; 471 | nl_socket_add_memberships(s, rc, 0); 472 | 473 | while(1) 474 | nl_recvmsgs_default(s); 475 | 476 | clear_alarms(); 477 | nl_socket_free(s); 478 | 479 | return 0; 480 | } 481 | 482 | -------------------------------------------------------------------------------- /include/tsn/genl_tsn.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 | /* 3 | * Copyright 2018-2019 NXP 4 | */ 5 | 6 | #ifndef __TSN_GENETLINK_H 7 | #define __TSN_GENETLINK_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #define MAX_MSGSIZE 256 19 | #ifndef SOL_NETLINK 20 | #define SOL_NETLINK 270 21 | #endif 22 | 23 | #ifndef NETLINK_LISTEN_ALL_NSID 24 | #define NETLINK_LISTEN_ALL_NSID 8 25 | #endif 26 | 27 | typedef unsigned char u8; 28 | typedef unsigned int u32; 29 | typedef unsigned short u16; 30 | 31 | #define ptptime_t uint64_t 32 | #define NUM_THREADS 100 33 | 34 | #include 35 | 36 | extern int VERBOSE; 37 | 38 | #define _llog(file, fmt, ...) do { \ 39 | if (VERBOSE) { \ 40 | fprintf(file, "%s@%d: " fmt "\n", \ 41 | __func__, __LINE__, ##__VA_ARGS__); \ 42 | } else { \ 43 | fprintf(file, fmt "\n", ##__VA_ARGS__); \ 44 | } \ 45 | } while (0) 46 | 47 | #define llogc(file, condition, ...) do { \ 48 | if (condition) { \ 49 | _llog(file, __VA_ARGS__); \ 50 | } \ 51 | } while (0) 52 | 53 | #define lloge(...) _llog(stderr, __VA_ARGS__) 54 | #define llogi(...) _llog(stdout, __VA_ARGS__) 55 | #define llogv(...) llogc(stdout, VERBOSE, __VA_ARGS__) 56 | 57 | #if 0 58 | #define TSN_GENL_NAME "TSN_GEN_CTRL" 59 | #define TSN_GENL_VERSION 0x1 60 | 61 | #define MAX_USER_SIZE 0 62 | #define MAX_ATTR_SIZE 3072 63 | #define MAX_TOTAL_MSG_SIZE (MAX_USER_SIZE + MAX_ATTR_SIZE) 64 | #define MAX_ENTRY_SIZE 2048 65 | #define MAX_ENTRY_NUMBER 128 66 | #define MAX_IFNAME_COUNT 64 67 | /* 68 | * Commands sent from userspace 69 | * Not versioned. New commands should only be inserted at the enum's end 70 | * prior to __TSN_CMD_MAX 71 | */ 72 | 73 | enum { 74 | TSN_CMD_UNSPEC = 0, /* Reserved */ 75 | TSN_CMD_QBV_SET, 76 | TSN_CMD_QBV_GET, 77 | TSN_CMD_QBV_GET_STATUS, 78 | TSN_CMD_CB_STREAMID_SET, 79 | TSN_CMD_CB_STREAMID_GET, 80 | TSN_CMD_CB_STREAMID_GET_COUNTS, 81 | TSN_CMD_QCI_CAP_GET, /* Qci capability get(length capability get) */ 82 | TSN_CMD_QCI_SFI_SET, 83 | TSN_CMD_QCI_SFI_GET, 84 | TSN_CMD_QCI_SFI_GET_COUNTS, 85 | TSN_CMD_QCI_SGI_SET, 86 | TSN_CMD_QCI_SGI_GET, 87 | TSN_CMD_QCI_SGI_GET_STATUS, 88 | TSN_CMD_QCI_FMI_SET, 89 | TSN_CMD_QCI_FMI_GET, 90 | TSN_CMD_CBS_SET, 91 | TSN_CMD_CBS_GET, 92 | TSN_CMD_ECHO, /* user->kernel request/get-response */ 93 | TSN_CMD_REPLY, /* kernel->user event */ 94 | __TSN_CMD_MAX, 95 | }; 96 | #define TSN_CMD_MAX (__TSN_CMD_MAX - 1) 97 | 98 | enum { 99 | TSN_CMD_ATTR_UNSPEC = 0, 100 | TSN_CMD_ATTR_MESG, /* demo message */ 101 | TSN_CMD_ATTR_DATA, /* demo data */ 102 | TSN_ATTR_IFNAME, 103 | TSN_ATTR_PORT_NUMBER, 104 | TSN_ATTR_QBV, 105 | TSN_ATTR_STREAM_IDENTIFY, /* stream identify */ 106 | TSN_ATTR_QCI_SP, /* psfp port capbility parameters */ 107 | TSN_ATTR_QCI_SFI, /* psfp stream filter instance */ 108 | TSN_ATTR_QCI_SGI, /* psfp stream gate instance */ 109 | TSN_ATTR_QCI_FMI, /* psfp flow meter instance */ 110 | TSN_ATTR_CBS, /* credit-based shaper */ 111 | __TSN_CMD_ATTR_MAX, 112 | }; 113 | #define TSN_CMD_ATTR_MAX (__TSN_CMD_ATTR_MAX - 1) 114 | 115 | enum { 116 | TSN_CBS_ATTR_UNSPEC, 117 | TSN_CBS_ATTR_TC_INDEX, 118 | TSN_CBS_ATTR_BW, 119 | __TSN_CBS_ATTR_MAX, 120 | TSN_CBS_ATTR_MAX = __TSN_CBS_ATTR_MAX - 1, 121 | }; 122 | 123 | enum { 124 | TSN_STREAMID_ATTR_UNSPEC, 125 | TSN_STREAMID_ATTR_INDEX, 126 | TSN_STREAMID_ATTR_ENABLE, 127 | TSN_STREAMID_ATTR_DISABLE, 128 | TSN_STREAMID_ATTR_STREAM_HANDLE, 129 | TSN_STREAMID_ATTR_SSID, 130 | TSN_STREAMID_ATTR_IFOP, 131 | TSN_STREAMID_ATTR_OFOP, 132 | TSN_STREAMID_ATTR_IFIP, 133 | TSN_STREAMID_ATTR_OFIP, 134 | TSN_STREAMID_ATTR_TYPE, 135 | TSN_STREAMID_ATTR_DMAC, 136 | TSN_STREAMID_ATTR_TAGGED, 137 | TSN_STREAMID_ATTR_VID, 138 | TSN_STREAMID_ATTR_COUNTERS_PSI, 139 | TSN_STREAMID_ATTR_COUNTERS_PSO, 140 | TSN_STREAMID_ATTR_COUNTERS_PSPPI, 141 | TSN_STREAMID_ATTR_COUNTERS_PSPPO, 142 | __TSN_STREAMID_ATTR_MAX, 143 | TSN_STREAMID_ATTR_MAX = __TSN_STREAMID_ATTR_MAX - 1, 144 | }; 145 | 146 | enum { 147 | TSN_QCI_SFI_ATTR_UNSPEC, 148 | TSN_QCI_SFI_ATTR_INDEX, 149 | TSN_QCI_SFI_ATTR_ENABLE, 150 | TSN_QCI_SFI_ATTR_DISABLE, 151 | TSN_QCI_SFI_ATTR_STREAM_HANDLE, 152 | TSN_QCI_SFI_ATTR_PRIO_SPEC, 153 | TSN_QCI_SFI_ATTR_GATE_ID, 154 | TSN_QCI_SFI_ATTR_FILTER_TYPE, 155 | TSN_QCI_SFI_ATTR_FLOW_ID, 156 | TSN_QCI_SFI_ATTR_MAXSDU, 157 | TSN_QCI_SFI_ATTR_COUNTERS, 158 | TSN_QCI_SFI_ATTR_OVERSIZE_ENABLE, 159 | TSN_QCI_SFI_ATTR_OVERSIZE, 160 | __TSN_QCI_SFI_ATTR_MAX, 161 | TSN_QCI_SFI_ATTR_MAX = __TSN_QCI_SFI_ATTR_MAX - 1, 162 | }; 163 | 164 | enum { 165 | TSN_QCI_SFI_ATTR_COUNTERS_UNSPEC, 166 | TSN_QCI_SFI_ATTR_MATCH, 167 | TSN_QCI_SFI_ATTR_PASS, 168 | TSN_QCI_SFI_ATTR_DROP, 169 | TSN_QCI_SFI_ATTR_SDU_DROP, 170 | TSN_QCI_SFI_ATTR_SDU_PASS, 171 | TSN_QCI_SFI_ATTR_RED, 172 | __TSN_QCI_SFI_ATTR_COUNT_MAX, 173 | TSN_QCI_SFI_ATTR_COUNT_MAX = __TSN_QCI_SFI_ATTR_COUNT_MAX - 1, 174 | }; 175 | 176 | enum { 177 | TSN_QCI_SGI_ATTR_UNSPEC = 0, 178 | TSN_QCI_SGI_ATTR_INDEX, 179 | TSN_QCI_SGI_ATTR_ENABLE, 180 | TSN_QCI_SGI_ATTR_DISABLE, 181 | TSN_QCI_SGI_ATTR_CONFCHANGE, 182 | TSN_QCI_SGI_ATTR_IRXEN, /* Invalid rx enable*/ 183 | TSN_QCI_SGI_ATTR_IRX, 184 | TSN_QCI_SGI_ATTR_OEXEN, /* Octet exceed enable */ 185 | TSN_QCI_SGI_ATTR_OEX, 186 | TSN_QCI_SGI_ATTR_ADMINENTRY, 187 | TSN_QCI_SGI_ATTR_OPERENTRY, 188 | TSN_QCI_SGI_ATTR_CCTIME, /* config change time */ 189 | TSN_QCI_SGI_ATTR_TICKG, 190 | TSN_QCI_SGI_ATTR_CUTIME, 191 | TSN_QCI_SGI_ATTR_CPENDING, 192 | TSN_QCI_SGI_ATTR_CCERROR, 193 | __TSN_QCI_SGI_ATTR_MAX, 194 | TSN_QCI_SGI_ATTR_MAX = __TSN_QCI_SGI_ATTR_MAX - 1, 195 | }; 196 | 197 | enum { 198 | TSN_SGI_ATTR_CTRL_UNSPEC = 0, 199 | TSN_SGI_ATTR_CTRL_INITSTATE, 200 | TSN_SGI_ATTR_CTRL_LEN, 201 | TSN_SGI_ATTR_CTRL_CYTIME, 202 | TSN_SGI_ATTR_CTRL_CYTIMEEX, 203 | TSN_SGI_ATTR_CTRL_BTIME, 204 | TSN_SGI_ATTR_CTRL_INITIPV, 205 | TSN_SGI_ATTR_CTRL_GCLENTRY, 206 | __TSN_SGI_ATTR_CTRL_MAX, 207 | TSN_SGI_ATTR_CTRL_MAX = __TSN_SGI_ATTR_CTRL_MAX - 1, 208 | }; 209 | 210 | enum { 211 | TSN_SGI_ATTR_GCL_UNSPEC = 0, 212 | TSN_SGI_ATTR_GCL_GATESTATE, 213 | TSN_SGI_ATTR_GCL_IPV, 214 | TSN_SGI_ATTR_GCL_INTERVAL, 215 | TSN_SGI_ATTR_GCL_OCTMAX, 216 | __TSN_SGI_ATTR_GCL_MAX, 217 | TSN_SGI_ATTR_GCL_MAX = __TSN_SGI_ATTR_GCL_MAX - 1, 218 | }; 219 | 220 | enum { 221 | TSN_QCI_FMI_ATTR_UNSPEC = 0, 222 | TSN_QCI_FMI_ATTR_INDEX, 223 | TSN_QCI_FMI_ATTR_CIR, 224 | TSN_QCI_FMI_ATTR_CBS, 225 | TSN_QCI_FMI_ATTR_EIR, 226 | TSN_QCI_FMI_ATTR_EBS, 227 | TSN_QCI_FMI_ATTR_CF, 228 | TSN_QCI_FMI_ATTR_CM, 229 | TSN_QCI_FMI_ATTR_DROPYL, 230 | TSN_QCI_FMI_ATTR_MAREDEN, 231 | TSN_QCI_FMI_ATTR_MARED, 232 | __TSN_QCI_FMI_ATTR_MAX, 233 | TSN_QCI_FMI_ATTR_MAX = __TSN_QCI_FMI_ATTR_MAX - 1, 234 | }; 235 | 236 | enum { 237 | TSN_QBV_ATTR_UNSPEC, 238 | TSN_QBV_ATTR_ENABLE, 239 | TSN_QBV_ATTR_DISABLE, 240 | TSN_QBV_ATTR_CONFIGCHANGE, 241 | TSN_QBV_ATTR_CONFIGCHANGETIME, 242 | TSN_QBV_ATTR_MAXSDU, 243 | TSN_QBV_ATTR_GRANULARITY, 244 | TSN_QBV_ATTR_CURRENTTIME, 245 | TSN_QBV_ATTR_CONFIGPENDING, 246 | TSN_QBV_ATTR_CONFIGCHANGEERROR, 247 | TSN_QBV_ATTR_ADMINENTRY, 248 | TSN_QBV_ATTR_OPERENTRY, 249 | TSN_QBV_ATTR_LISTMAX, 250 | __TSN_QBV_ATTR_MAX, 251 | TSN_QBV_ATTR_MAX = __TSN_QBV_ATTR_MAX - 1, 252 | }; 253 | 254 | enum { 255 | TSN_QBV_ATTR_CTRL_UNSPEC, 256 | TSN_QBV_ATTR_CTRL_LISTCOUNT, 257 | TSN_QBV_ATTR_CTRL_GATESTATE, 258 | TSN_QBV_ATTR_CTRL_CYCLETIME, 259 | TSN_QBV_ATTR_CTRL_CYCLETIMEEXT, 260 | TSN_QBV_ATTR_CTRL_BASETIME, 261 | TSN_QBV_ATTR_CTRL_LISTENTRY, 262 | __TSN_QBV_ATTR_CTRL_MAX, 263 | TSN_QBV_ATTR_CTRL_MAX = __TSN_QBV_ATTR_CTRL_MAX - 1, 264 | }; 265 | 266 | enum { 267 | TSN_QBV_ATTR_ENTRY_UNSPEC, 268 | TSN_QBV_ATTR_ENTRY_ID, 269 | TSN_QBV_ATTR_ENTRY_GC, 270 | TSN_QBV_ATTR_ENTRY_TM, 271 | __TSN_QBV_ATTR_ENTRY_MAX, 272 | TSN_QBV_ATTR_ENTRY_MAX = __TSN_QBV_ATTR_ENTRY_MAX - 1, 273 | }; 274 | #endif 275 | 276 | #define MAX_NAME_LEN 100 277 | 278 | struct info_type { 279 | char name[MAX_NAME_LEN]; 280 | 281 | }; 282 | 283 | struct qbv_multicast { 284 | uint64_t cct; 285 | uint32_t offset; 286 | int ifidx; 287 | bool en; 288 | void *callback_func; 289 | void *data; 290 | }; 291 | 292 | struct qci_multicast { 293 | uint64_t cct; 294 | uint32_t offset; 295 | int ifidx; 296 | bool en; 297 | void *callback_func; 298 | void *data; 299 | }; 300 | 301 | struct alarm_info { 302 | char *ptpdev[MAX_NAME_LEN]; 303 | clockid_t clkid; 304 | struct { 305 | struct qbv_multicast qbvmc; 306 | struct qci_multicast qcimc; 307 | }; 308 | }; 309 | 310 | struct tsn_family_groups { 311 | uint16_t family_id; 312 | uint32_t mc[TSN_MCGRP_MAX]; 313 | struct alarm_info *ai; 314 | }; 315 | 316 | struct linkpara { 317 | int type; 318 | char len; 319 | char name[MAX_NAME_LEN]; 320 | }; 321 | 322 | struct showtable { 323 | int type; 324 | int len1; 325 | struct linkpara *link1; 326 | int len2; 327 | struct linkpara *link2; 328 | int len3; 329 | struct linkpara *link3; 330 | }; 331 | 332 | struct tsn_cap { 333 | bool qbv; 334 | bool qci; 335 | bool qbu; 336 | bool cbs; 337 | bool cb; 338 | bool tbs; 339 | bool cut_through; 340 | }; 341 | /* 342 | * Generic macros for dealing with netlink sockets. Might be duplicated 343 | * elsewhere. It is recommended that commercial grade applications use 344 | * libnl or libnetlink and use the interfaces provided by the library 345 | */ 346 | 347 | #define GENLMSG_ATTR_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN + NLMSG_ALIGN(MAX_USER_SIZE))) 348 | #define GENLMSG_ATTR_PAYLOAD(glh) (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN - NLMSG_ALIGN(MAX_USER_SIZE)) 349 | #define GENLMSG_USER_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN)) 350 | #define NLA_DATA(na) ((void *)((char *)(na) + NLA_HDRLEN)) 351 | #define NLA_PAYLOAD(len) (len - NLA_HDRLEN) 352 | 353 | #define DEBUG 0 354 | 355 | #define PRINTF(fmt, arg...) { \ 356 | if (DEBUG) { \ 357 | printf(fmt, ##arg); \ 358 | } \ 359 | } 360 | 361 | struct msgtemplate { 362 | struct nlmsghdr n; 363 | struct genlmsghdr g; 364 | char user[NLMSG_ALIGN(MAX_USER_SIZE)]; 365 | char attr[NLMSG_ALIGN(MAX_ATTR_SIZE)]; 366 | }; 367 | 368 | typedef struct msgtemplate msgtemplate_t; 369 | 370 | struct global_conf { 371 | __u32 genl_fd; 372 | __u16 genl_familyid; 373 | __u32 pid; 374 | }; 375 | 376 | struct tsn_conf_record { 377 | __u32 pid; 378 | char portname[MAX_NAME_LEN]; 379 | __u32 cmd; 380 | __u32 para; 381 | }; 382 | 383 | extern struct global_conf glb_conf; 384 | 385 | static inline struct nlattr *tsn_nla_nest_start(struct msgtemplate *msg, __u16 nla_type) 386 | { 387 | struct nlattr *na; 388 | 389 | na = (struct nlattr *)((char *)msg + (msg->n.nlmsg_len)); 390 | 391 | na->nla_type = nla_type; 392 | na->nla_len = NLA_HDRLEN; 393 | 394 | msg->n.nlmsg_len += NLMSG_ALIGN(na->nla_len); 395 | 396 | return na; 397 | } 398 | 399 | static inline int tsn_nla_nest_end(struct msgtemplate *msg, struct nlattr *start) 400 | { 401 | start->nla_len = (char *)msg + (msg->n.nlmsg_len) - (char *)start; 402 | 403 | return msg->n.nlmsg_len; 404 | } 405 | 406 | struct msgtemplate *tsn_send_cmd_prepare(__u8 genl_cmd); 407 | void tsn_send_cmd_append_attr(struct msgtemplate *msg, __u16 nla_type, void *nla_data, int nla_len); 408 | int tsn_send_to_kernel(struct msgtemplate *msg); 409 | int tsn_msg_recv_analysis(struct showtable *linkdata, void *para); 410 | 411 | int genl_tsn_init(void); 412 | void genl_tsn_close(void); 413 | int tsn_echo_test(char *string, int data); 414 | 415 | int tsn_capability_get(char *portname, struct tsn_cap *cap); 416 | int tsn_qos_port_qbv_set(char *portname, struct tsn_qbv_conf *adminconf, bool enable); 417 | int tsn_qos_port_qbv_get(char *portname, struct tsn_qbv_conf *qbvconf); 418 | int tsn_qos_port_qbv_status_get(char *portname, struct tsn_qbv_status *qbvstaus); 419 | int tsn_qci_streampara_get(char *portname, 420 | struct tsn_qci_psfp_stream_param *sp); 421 | int tsn_cb_streamid_set(char *portname, uint32_t sid_index, bool enable, 422 | struct tsn_cb_streamid *sid); 423 | int tsn_cb_streamid_get(char *portname, uint32_t sid_index, struct tsn_cb_streamid *sid); 424 | int tsn_qci_psfp_sfi_get(char *portname, uint32_t sfi_handle, 425 | struct tsn_qci_psfp_sfi_conf *sfi); 426 | int tsn_qci_psfp_sfi_set(char *portname, uint32_t sfi_handle, bool enable, 427 | struct tsn_qci_psfp_sfi_conf *sfi); 428 | 429 | int tsn_qci_psfp_sgi_set(char *portname, uint32_t sgi_handle, bool enable, 430 | struct tsn_qci_psfp_sgi_conf *sgi); 431 | int tsn_qci_psfp_sgi_get(char *portname, uint32_t sgi_handle, struct tsn_qci_psfp_sgi_conf *sgi); 432 | int tsn_qci_psfp_sgi_status_get(char *portname, uint32_t sgi_handle, struct tsn_psfp_sgi_status *sgi); 433 | int tsn_qci_psfp_fmi_set(char *portname, uint32_t fmi_id, bool enable, struct tsn_qci_psfp_fmi *fmiconf); 434 | int tsn_qci_psfp_fmi_get(char *portname, uint32_t fmi_id, struct tsn_qci_psfp_fmi *fmiconf); 435 | int tsn_cbs_set(char *portname, uint8_t tc, uint8_t percent); 436 | int tsn_cbs_get(char *portname, uint8_t tc); 437 | int tsn_tsd_set(char *portname, bool enable, uint32_t period, uint32_t frame_num, bool imme); 438 | int tsn_tsd_get(char *portname); 439 | int tsn_qbu_set(char *portname, uint8_t pt_vector); 440 | int tsn_qbu_get_status(char *portname, struct tsn_preempt_status *pts); 441 | int tsn_ct_set(char *portname, uint8_t pt_vector); 442 | int tsn_cbgen_set(char *portname, uint32_t index, struct tsn_seq_gen_conf *sg); 443 | int tsn_cbrec_set(char *portname, uint32_t index, struct tsn_seq_rec_conf *sr); 444 | int tsn_cbstatus_get(char *portname, uint32_t index, 445 | struct tsn_cb_status *cbstat); 446 | int tsn_dscp_set(char *portname, bool disable, int index, 447 | struct tsn_qos_switch_dscp_conf *dscp_conf); 448 | int64_t pctns(struct timespec *t); 449 | int get_net_ifindex_by_name(const char *eth_name, uint32_t *ifindex); 450 | int set_period_alarm(uint64_t ts, uint64_t offset, 451 | uint64_t cycle, void (*callback_func)(void *data), void *data); 452 | pthread_t *create_alarm_common(uint64_t ts, uint32_t offset, uint32_t cycle, 453 | void (*callback_func)(void *data), void *data); 454 | int delete_alarm_common(pthread_t *thread); 455 | int wait_tsn_multicast(); 456 | void get_para_from_json(int type, cJSON *json, void *para); 457 | 458 | bool get_conf_monitor_status(void); 459 | int get_tsn_record(struct tsn_conf_record *record); 460 | 461 | #endif /* _TSN_GENETLINK_KERN_H */ 462 | -------------------------------------------------------------------------------- /demos/framesstatus/enetctscap.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 | /* 3 | * Copyright 2019 NXP 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include "tsn/genl_tsn.h" 27 | 28 | #ifndef JSONFRAME 29 | #define JSONFRAME "/tmp/static/tsnframetstamp.json" 30 | #endif 31 | 32 | #define DEFAULTCYCLE 100000000ULL 33 | 34 | #ifndef SO_TIMESTAMPING 35 | # define SO_TIMESTAMPING 37 36 | # define SCM_TIMESTAMPING SO_TIMESTAMPING 37 | #endif 38 | 39 | #ifndef SO_TIMESTAMPNS 40 | # define SO_TIMESTAMPNS 35 41 | #endif 42 | 43 | #ifndef SIOCGSTAMPNS 44 | # define SIOCGSTAMPNS 0x8907 45 | #endif 46 | 47 | #ifndef SIOCSHWTSTAMP 48 | # define SIOCSHWTSTAMP 0x89b0 49 | #endif 50 | 51 | static int txcount = 0; 52 | static int txcount_flag = 0; 53 | static int nonstop_flag = 0; 54 | static int fully_send = 0; 55 | static int receive_only = 0; 56 | static int debugen = 0; 57 | static uint64_t lastns = 0; 58 | static cJSON *jsonslot = NULL; 59 | static cJSON *pJsonArry; 60 | 61 | #define _DEBUG(file, fmt, ...) do { \ 62 | if (debugen) { \ 63 | fprintf(file, " " fmt, \ 64 | ##__VA_ARGS__); \ 65 | } else { \ 66 | ; \ 67 | } \ 68 | } while (0) 69 | 70 | #define DEBUG(...) _DEBUG(stderr, __VA_ARGS__) 71 | 72 | static void bail(const char *error) 73 | { 74 | printf("%s: %s\n", error, strerror(errno)); 75 | exit(1); 76 | } 77 | 78 | void help() 79 | { 80 | printf("send one ARP package \n \ 81 | -i \n \ 82 | -r RECEIVE MODE \n \ 83 | -l \n \ 84 | -m \n \ 85 | -c \n \ 86 | -p \n \ 87 | -h help \ 88 | \n"); 89 | } 90 | static unsigned char sync_packet[] = { 91 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* dmac */ 92 | 0x11, 0x00, 0x80, 0x00, 0x00, 0x00, 93 | 0x08, 0x00, /* eth header */ 94 | 0x45, 0x00, /* hardware type */ 95 | 0x08, 0x00, /* IP type */ 96 | 0x06, 0x04, /* hw len, protocol len */ 97 | 0x00, 0x01, /* request type: 1: ARP, 2: ARP REPLY */ 98 | 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, /* source mac */ 99 | 0x09, 0x09, 0x09, 0x09, 100 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 101 | 0x0a, 0x0a, 0x0a, 0x0a, 102 | 0x00, 0x80, 103 | 0x00, 0xb0, 104 | 0x00, 0x00, 0x00, 0x00, 105 | 0x00, 0x00, 0x00, 0x00, /* correctionField */ 106 | 0x00, 0x00, 0x00, 0x00, /* reserved */ 107 | 0x00, 0x04, 0x9f, 0xff, 108 | 0xfe, 0x03, 0xd9, 0xe0, 109 | 0x00, 0x01, /* sourcePortIdentity */ 110 | 0x00, 0x1d, /* sequenceId */ 111 | 0x00, /* controlField */ 112 | 0x00, /* logMessageInterval */ 113 | 0x00, 0x00, 0x00, 0x00, 114 | 0x00, 0x00, 0x00, 0x00, 115 | 0x00, 0x00, 116 | 0x00, 0x00, 0x00, 0x00, 117 | 0x00, 0x00, 0x00, 0x00, 118 | 0x00, 0x00, 119 | 0x00, 0x00, 0x00, 0x00, 120 | 0x00, 0x00, 0x00, 0x00, 121 | 0x00, 0x00, /* originTimestamp */ 122 | 0x00, 0x00, 0x00, 0x00, 123 | 0x00, 0x00 /* originTimestamp */ 124 | }; 125 | 126 | #define MAC_LEN 6 127 | int str2mac(const char *s, unsigned char mac[MAC_LEN]) 128 | { 129 | unsigned char buf[MAC_LEN]; 130 | int c; 131 | c = sscanf(s, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", 132 | &buf[0], &buf[1], &buf[2], &buf[3], &buf[4], &buf[5]); 133 | if (c != MAC_LEN) { 134 | return -1; 135 | } 136 | memcpy(mac, buf, MAC_LEN); 137 | return 0; 138 | } 139 | 140 | static void sendpacket(int sock, unsigned int length, char *mac) 141 | { 142 | struct timeval now; 143 | int res; 144 | int i; 145 | 146 | for (i = 0; i < MAC_LEN; i++) 147 | sync_packet[6 + i] = mac[i]; 148 | sync_packet[17] = length >> 8; 149 | sync_packet[18] = (char)(length & 0x00ff); 150 | if (length < sizeof(sync_packet)) 151 | res = send(sock, sync_packet, sizeof(sync_packet), 0); 152 | else { 153 | char *buf = (char *)malloc(length); 154 | 155 | memcpy(buf, sync_packet, sizeof(sync_packet)); 156 | res = send(sock, buf, length, 0); 157 | free(buf); 158 | } 159 | 160 | gettimeofday(&now, 0); 161 | if (res < 0) 162 | DEBUG("%s: %s\n", "send", strerror(errno)); 163 | else 164 | DEBUG("%ld.%06ld: sent %d bytes\n", 165 | (long)now.tv_sec, (long)now.tv_usec, 166 | res); 167 | } 168 | 169 | void insert_json(struct timespec *stamp, int type) 170 | { 171 | FILE *f; 172 | long len; 173 | char *buf = NULL; 174 | cJSON *pjsonsub; 175 | uint64_t tsns; 176 | uint64_t slottime; 177 | 178 | tsns = pctns(stamp); 179 | slottime = tsns / DEFAULTCYCLE * DEFAULTCYCLE; 180 | if (slottime != lastns) { 181 | buf = cJSON_Print(jsonslot); 182 | DEBUG("NOW %s\n", buf); 183 | f = fopen(JSONFRAME,"w"); 184 | if (!f) 185 | return; 186 | fwrite(buf, strlen(buf), 1, f); 187 | free(buf); 188 | fclose(f); 189 | 190 | cJSON_DeleteItemFromObject(jsonslot, "FRAME"); 191 | pJsonArry = cJSON_CreateArray(); 192 | cJSON_AddItemToObject(jsonslot, "FRAME", pJsonArry); 193 | 194 | cJSON_GetObjectItem(jsonslot, "STARTTIME")->valueint = slottime; 195 | cJSON_GetObjectItem(jsonslot, "STARTTIME")->valuedouble = slottime; 196 | } 197 | cJSON_AddItemToArray(pJsonArry, pjsonsub = cJSON_CreateObject()); 198 | cJSON_AddItemToObject(pjsonsub, "TC", cJSON_CreateNumber(type)); 199 | cJSON_AddItemToObject(pjsonsub, "TIME", cJSON_CreateNumber(tsns)); 200 | if (slottime != lastns) { 201 | lastns = slottime; 202 | } 203 | return; 204 | } 205 | 206 | static void printpacket(struct msghdr *msg, int res, 207 | char *data, 208 | int sock, int recvmsg_flags) 209 | { 210 | struct sockaddr_in *from_addr = (struct sockaddr_in *)msg->msg_name; 211 | struct cmsghdr *cmsg; 212 | struct timeval now; 213 | 214 | if (debugen) 215 | gettimeofday(&now, 0); 216 | 217 | DEBUG("%ld.%06ld: received %s data, %d bytes from %s, %zu bytes control messages\n", 218 | (long)now.tv_sec, (long)now.tv_usec, 219 | (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular", 220 | res, 221 | inet_ntoa(from_addr->sin_addr), 222 | msg->msg_controllen); 223 | for (cmsg = CMSG_FIRSTHDR(msg); 224 | cmsg; 225 | cmsg = CMSG_NXTHDR(msg, cmsg)) { 226 | DEBUG(" cmsg len %zu: ", cmsg->cmsg_len); 227 | switch (cmsg->cmsg_level) { 228 | case SOL_SOCKET: 229 | DEBUG("SOL_SOCKET "); 230 | switch (cmsg->cmsg_type) { 231 | case SO_TIMESTAMP: { 232 | struct timeval *stamp = 233 | (struct timeval *)CMSG_DATA(cmsg); 234 | DEBUG("SO_TIMESTAMP %ld.%06ld", 235 | (long)stamp->tv_sec, 236 | (long)stamp->tv_usec); 237 | break; 238 | } 239 | case SO_TIMESTAMPNS: { 240 | struct timespec *stamp = 241 | (struct timespec *)CMSG_DATA(cmsg); 242 | DEBUG("SO_TIMESTAMPNS %ld.%09ld", 243 | (long)stamp->tv_sec, 244 | (long)stamp->tv_nsec); 245 | break; 246 | } 247 | case SO_TIMESTAMPING: { 248 | struct timespec *stamp = 249 | (struct timespec *)CMSG_DATA(cmsg); 250 | DEBUG("SO_TIMESTAMPING "); 251 | stamp++; 252 | /* skip deprecated HW transformed */ 253 | stamp++; 254 | DEBUG("HW raw %ld.%09ld", 255 | (long)stamp->tv_sec, 256 | (long)stamp->tv_nsec); 257 | if (recvmsg_flags & MSG_ERRQUEUE) { 258 | if (!fully_send) { 259 | txcount_flag = 1; 260 | if (nonstop_flag) { 261 | txcount++; 262 | } else { 263 | txcount--; 264 | } 265 | } else { 266 | if (nonstop_flag) { 267 | txcount++; 268 | } else { 269 | txcount--; 270 | if (!txcount) 271 | txcount_flag = 1; 272 | } 273 | } 274 | DEBUG("tx counter %d\n", txcount); 275 | } 276 | insert_json(stamp, (recvmsg_flags & MSG_ERRQUEUE) ? 1 : 2); 277 | break; 278 | } 279 | default: 280 | DEBUG("type %d", cmsg->cmsg_type); 281 | break; 282 | } 283 | break; 284 | case IPPROTO_IP: 285 | DEBUG("IPPROTO_IP "); 286 | switch (cmsg->cmsg_type) { 287 | case IP_RECVERR: { 288 | struct sock_extended_err *err = 289 | (struct sock_extended_err *)CMSG_DATA(cmsg); 290 | DEBUG("IP_RECVERR ee_errno '%s' ee_origin %d => %s", 291 | strerror(err->ee_errno), 292 | err->ee_origin, 293 | #ifdef SO_EE_ORIGIN_TIMESTAMPING 294 | err->ee_origin == SO_EE_ORIGIN_TIMESTAMPING ? 295 | "bounced packet" : "unexpected origin" 296 | #else 297 | "probably SO_EE_ORIGIN_TIMESTAMPING" 298 | #endif 299 | ); 300 | if (res < sizeof(sync)) 301 | DEBUG(" => truncated data?!"); 302 | else if (!memcmp(sync, data + res - sizeof(sync), 303 | sizeof(sync))) 304 | DEBUG(" => GOT OUR DATA BACK (HURRAY!)"); 305 | break; 306 | } 307 | case IP_PKTINFO: { 308 | struct in_pktinfo *pktinfo = 309 | (struct in_pktinfo *)CMSG_DATA(cmsg); 310 | DEBUG("IP_PKTINFO interface index %u", 311 | pktinfo->ipi_ifindex); 312 | break; 313 | } 314 | default: 315 | DEBUG("type %d", cmsg->cmsg_type); 316 | break; 317 | } 318 | break; 319 | default: 320 | DEBUG("level %d type %d", 321 | cmsg->cmsg_level, 322 | cmsg->cmsg_type); 323 | break; 324 | } 325 | DEBUG("\n"); 326 | } 327 | } 328 | 329 | static void recvpacket(int sock, int recvmsg_flags) 330 | { 331 | char data[256]; 332 | struct msghdr msg; 333 | struct iovec entry; 334 | struct sockaddr_in from_addr; 335 | struct { 336 | struct cmsghdr cm; 337 | char control[512]; 338 | } control; 339 | int res; 340 | 341 | memset(&msg, 0, sizeof(msg)); 342 | msg.msg_iov = &entry; 343 | msg.msg_iovlen = 1; 344 | entry.iov_base = data; 345 | entry.iov_len = sizeof(data); 346 | msg.msg_name = (caddr_t)&from_addr; 347 | msg.msg_namelen = sizeof(from_addr); 348 | msg.msg_control = &control; 349 | msg.msg_controllen = sizeof(control); 350 | 351 | res = recvmsg(sock, &msg, recvmsg_flags | MSG_DONTWAIT); 352 | if (res < 0) { 353 | DEBUG("%s %s: %s\n", 354 | "recvmsg", 355 | "regular", 356 | strerror(errno)); 357 | } else { 358 | printpacket(&msg, res, data, 359 | sock, recvmsg_flags); 360 | } 361 | } 362 | 363 | void *rcv_pkt(void *data) 364 | { 365 | int res; 366 | fd_set readfs, errorfs; 367 | int sock; 368 | 369 | sock = *(int *)data; 370 | 371 | while (!txcount_flag) { 372 | FD_ZERO(&readfs); 373 | FD_ZERO(&errorfs); 374 | FD_SET(sock, &readfs); 375 | FD_SET(sock, &errorfs); 376 | 377 | res = select(sock + 1, &readfs, 0, &errorfs, NULL); 378 | /* gettimeofday(&now, 0); 379 | printf("%ld.%06ld: select returned: %d, %s\n", 380 | (long)now.tv_sec, (long)now.tv_usec, 381 | res, 382 | res < 0 ? strerror(errno) : "success"); 383 | */ 384 | if (res > 0) { 385 | recvpacket(sock, 0); 386 | if (!receive_only) 387 | recvpacket(sock, MSG_ERRQUEUE); 388 | } 389 | } 390 | } 391 | 392 | int main(int argc, char **argv) 393 | { 394 | int so_timestamping_flags = 0; 395 | char *interface = NULL; 396 | int sock; 397 | struct ifreq device; 398 | struct ifreq hwtstamp; 399 | struct hwtstamp_config hwconfig, hwconfig_requested; 400 | struct sockaddr_ll addr; 401 | unsigned int length = 0; 402 | int c; 403 | char mac[MAC_LEN] = {0x11, 0x00, 0x80, 0x00, 0x00, 0x00}; 404 | int count = 1; 405 | int prio = 0; 406 | pthread_t receive_pkt; 407 | struct timespec ts; 408 | uint64_t nowns; 409 | 410 | while ((c = getopt (argc, argv, "dp:i:frl:m:c:h")) != -1) { 411 | switch (c) 412 | { 413 | case 'i': 414 | interface = optarg; 415 | break; 416 | case 'f': 417 | fully_send = 1; 418 | break; 419 | case 'r': 420 | receive_only = 1; 421 | break; 422 | case 'l': 423 | length = strtoul(optarg, NULL, 0); 424 | break; 425 | case 'm': 426 | str2mac(optarg, mac); 427 | break; 428 | case 'c': 429 | count = strtoul(optarg, NULL, 0); 430 | break; 431 | case 'p': 432 | prio = strtoul(optarg, NULL, 0); 433 | break; 434 | case 'd': 435 | debugen = 1; 436 | break; 437 | case 'h': 438 | help(); 439 | return; 440 | case '?': 441 | if (optopt == 'c') 442 | fprintf (stderr, "Option -%c requires an argument.\n", optopt); 443 | else if (isprint (optopt)) 444 | fprintf (stderr, "Unknown option `-%c'.\n", optopt); 445 | else 446 | fprintf (stderr, 447 | "Unknown option character `\\x%x'.\n", 448 | optopt); 449 | return 1; 450 | default: 451 | help(); 452 | return; 453 | } 454 | } 455 | 456 | #ifdef DEBUGJSON 457 | while(1) 458 | { 459 | struct timespec st; 460 | st.tv_sec = 2; 461 | st.tv_nsec = 1; 462 | insert_json(&st, 1); 463 | insert_json(&st, 2); 464 | insert_json(&st, 4); 465 | 466 | sleep(1); 467 | } 468 | #endif 469 | if (!interface) 470 | bail("input interface"); 471 | 472 | jsonslot = cJSON_CreateObject(); 473 | pJsonArry = cJSON_CreateArray(); 474 | 475 | if (clock_gettime(CLOCK_REALTIME, &ts)) { 476 | perror("clock_gettime"); 477 | } else { 478 | printf("clock time: %ld.%09ld or %s", 479 | ts.tv_sec, ts.tv_nsec, ctime(&ts.tv_sec)); 480 | } 481 | /* create a non-block timer */ 482 | nowns = pctns(&ts); 483 | cJSON_AddItemToObject(jsonslot, "CYCLE", cJSON_CreateNumber(DEFAULTCYCLE)); 484 | cJSON_AddItemToObject(jsonslot, "STARTTIME", cJSON_CreateNumber(nowns)); 485 | cJSON_AddItemToObject(jsonslot, "FRAME", pJsonArry); 486 | 487 | so_timestamping_flags |= (SOF_TIMESTAMPING_TX_HARDWARE | SOF_TIMESTAMPING_OPT_TSONLY); 488 | so_timestamping_flags |= (SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_OPT_CMSG); 489 | so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE; 490 | 491 | sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); 492 | if (sock < 0) 493 | bail("socket"); 494 | 495 | memset(&device, 0, sizeof(device)); 496 | strncpy(device.ifr_name, interface, sizeof(device.ifr_name)); 497 | if (ioctl(sock, SIOCGIFINDEX, &device) < 0) 498 | bail("getting interface index"); 499 | 500 | /* Set the SIOCSHWTSTAMP ioctl */ 501 | memset(&hwtstamp, 0, sizeof(hwtstamp)); 502 | strncpy(hwtstamp.ifr_name, interface, sizeof(hwtstamp.ifr_name)); 503 | hwtstamp.ifr_data = (void *)&hwconfig; 504 | memset(&hwconfig, 0, sizeof(hwconfig)); 505 | hwconfig.tx_type = 506 | (so_timestamping_flags & SOF_TIMESTAMPING_TX_HARDWARE) ? 507 | HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; 508 | hwconfig.rx_filter = 509 | (so_timestamping_flags & SOF_TIMESTAMPING_RX_HARDWARE) ? 510 | HWTSTAMP_FILTER_ALL : HWTSTAMP_FILTER_NONE; 511 | hwconfig_requested = hwconfig; 512 | if (ioctl(sock, SIOCSHWTSTAMP, &hwtstamp) < 0) { 513 | if ((errno == EINVAL || errno == ENOTSUP) && 514 | hwconfig_requested.tx_type == HWTSTAMP_TX_OFF && 515 | hwconfig_requested.rx_filter == HWTSTAMP_FILTER_NONE) 516 | printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n"); 517 | else 518 | printf("SIOCSHWTSTAMP: operation not supported!\n"); 519 | } 520 | 521 | /* bind to PTP port */ 522 | addr.sll_ifindex = device.ifr_ifindex; 523 | addr.sll_family = AF_PACKET; 524 | addr.sll_protocol = htons(ETH_P_ALL); 525 | if (bind(sock, 526 | (struct sockaddr *)&addr, 527 | sizeof(struct sockaddr_ll)) < 0) 528 | bail("bind"); 529 | if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, interface, strlen(interface))) 530 | bail("setsockopt SO_BINDTODEVICE"); 531 | if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(int))) 532 | bail("setsockopt SO_PRIORITY"); 533 | 534 | if (so_timestamping_flags && 535 | setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, 536 | &so_timestamping_flags, 537 | sizeof(so_timestamping_flags)) < 0) 538 | printf("setsockopt SO_TIMESTAMPING not supported\n"); 539 | 540 | /* send receiv frames */ 541 | txcount = count; 542 | if (!count) 543 | nonstop_flag = 1; 544 | 545 | if (receive_only) { 546 | while(1) 547 | rcv_pkt(&sock); 548 | } 549 | 550 | if (fully_send) 551 | pthread_create(&receive_pkt, NULL, rcv_pkt, &sock); 552 | 553 | while (count || nonstop_flag) { 554 | /* write one packet */ 555 | sendpacket(sock, length, mac); 556 | if (!nonstop_flag) 557 | count--; 558 | if (!fully_send) { 559 | txcount_flag = 0; 560 | rcv_pkt(&sock); 561 | } 562 | } 563 | 564 | if (fully_send) { 565 | pthread_join(receive_pkt, NULL); 566 | pthread_cancel(receive_pkt); 567 | } 568 | 569 | cJSON_Delete(jsonslot); 570 | 571 | return 0; 572 | } 573 | -------------------------------------------------------------------------------- /tools/timestamping.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 | /* 3 | * Copyright 2019 NXP 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #ifndef SO_TIMESTAMPING 27 | # define SO_TIMESTAMPING 37 28 | # define SCM_TIMESTAMPING SO_TIMESTAMPING 29 | #endif 30 | 31 | #ifndef SO_TIMESTAMPNS 32 | # define SO_TIMESTAMPNS 35 33 | #endif 34 | 35 | #ifndef SIOCGSTAMPNS 36 | # define SIOCGSTAMPNS 0x8907 37 | #endif 38 | 39 | #ifndef SIOCSHWTSTAMP 40 | # define SIOCSHWTSTAMP 0x89b0 41 | #endif 42 | 43 | static int txcount = 0; 44 | static int txcount_flag = 0; 45 | static int nonstop_flag = 0; 46 | static int fully_send = 0; 47 | static int receive_only = 0; 48 | static int debugen = 0; 49 | static int delay_us = 0; 50 | static int send_now = 0; 51 | #ifndef CLOCK_TAI 52 | #define CLOCK_TAI 11 53 | #endif 54 | 55 | #ifndef SCM_TXTIME 56 | #define SO_TXTIME 61 57 | #define SCM_TXTIME SO_TXTIME 58 | #endif 59 | #define _DEBUG(file, fmt, ...) do { \ 60 | if (debugen) { \ 61 | fprintf(file, " " fmt, \ 62 | ##__VA_ARGS__); \ 63 | } else { \ 64 | ; \ 65 | } \ 66 | } while (0) 67 | 68 | #define DEBUG(...) _DEBUG(stderr, __VA_ARGS__) 69 | 70 | static void bail(const char *error) 71 | { 72 | printf("%s: %s\n", error, strerror(errno)); 73 | exit(1); 74 | } 75 | 76 | void help() 77 | { 78 | printf("send one ARP package \n \ 79 | -i \n \ 80 | -r RECEIVE MODE \n \ 81 | -l \n \ 82 | -m \n \ 83 | -c \n \ 84 | -p \n \ 85 | -b \n \ 86 | -d \n \ 87 | -h help \ 88 | \n"); 89 | } 90 | static unsigned char sync_packet[] = { 91 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* dmac */ 92 | 0x11, 0x00, 0x80, 0x00, 0x00, 0x00, 93 | 0x08, 0x00, /* eth header */ 94 | 0x45, 0x00, /* hardware type */ 95 | 0x08, 0x00, /* IP type */ 96 | 0x06, 0x04, /* hw len, protocol len */ 97 | 0x00, 0x01, /* request type: 1: ARP, 2: ARP REPLY */ 98 | 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, /* source mac */ 99 | 0x09, 0x09, 0x09, 0x09, 100 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 101 | 0x0a, 0x0a, 0x0a, 0x0a, 102 | 0x00, 0x80, 103 | 0x00, 0xb0, 104 | 0x00, 0x00, 0x00, 0x00, 105 | 0x00, 0x00, 0x00, 0x00, /* correctionField */ 106 | 0x00, 0x00, 0x00, 0x00, /* reserved */ 107 | 0x00, 0x04, 0x9f, 0xff, 108 | 0xfe, 0x03, 0xd9, 0xe0, 109 | 0x00, 0x01, /* sourcePortIdentity */ 110 | 0x00, 0x1d, /* sequenceId */ 111 | 0x00, /* controlField */ 112 | 0x00, /* logMessageInterval */ 113 | 0x00, 0x00, 0x00, 0x00, 114 | 0x00, 0x00, 0x00, 0x00, 115 | 0x00, 0x00, 116 | 0x00, 0x00, 0x00, 0x00, 117 | 0x00, 0x00, 0x00, 0x00, 118 | 0x00, 0x00, 119 | 0x00, 0x00, 0x00, 0x00, 120 | 0x00, 0x00, 0x00, 0x00, 121 | 0x00, 0x00, /* originTimestamp */ 122 | 0x00, 0x00, 0x00, 0x00, 123 | 0x00, 0x00 /* originTimestamp */ 124 | }; 125 | 126 | #define MAC_LEN 6 127 | int str2mac(const char *s, unsigned char mac[MAC_LEN]) 128 | { 129 | unsigned char buf[MAC_LEN]; 130 | int c; 131 | c = sscanf(s, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", 132 | &buf[0], &buf[1], &buf[2], &buf[3], &buf[4], &buf[5]); 133 | if (c != MAC_LEN) { 134 | return -1; 135 | } 136 | memcpy(mac, buf, MAC_LEN); 137 | return 0; 138 | } 139 | 140 | static uint64_t gettime_ns(void) 141 | { 142 | struct timespec ts; 143 | 144 | if (clock_gettime(CLOCK_TAI, &ts)) 145 | printf("error gettime"); 146 | 147 | return ts.tv_sec * (1000ULL * 1000 * 1000) + ts.tv_nsec; 148 | } 149 | 150 | static int do_send_one(int fdt, int length) 151 | { 152 | char control[CMSG_SPACE(sizeof(uint64_t))]; 153 | struct msghdr msg = {0}; 154 | struct iovec iov = {0}; 155 | struct cmsghdr *cm; 156 | uint64_t tdeliver; 157 | int ret; 158 | char *buf; 159 | 160 | buf = (char *)malloc(length); 161 | memcpy(buf, sync_packet, sizeof(sync_packet)); 162 | 163 | iov.iov_base = buf; 164 | iov.iov_len = length; 165 | 166 | msg.msg_iov = &iov; 167 | msg.msg_iovlen = 1; 168 | 169 | if (delay_us >= 0) { 170 | memset(control, 0, sizeof(control)); 171 | msg.msg_control = &control; 172 | msg.msg_controllen = sizeof(control); 173 | 174 | tdeliver = gettime_ns() + delay_us * 1000; 175 | printf("set TXTIME is %ld\n", tdeliver); 176 | cm = CMSG_FIRSTHDR(&msg); 177 | cm->cmsg_level = SOL_SOCKET; 178 | cm->cmsg_type = SCM_TXTIME; 179 | cm->cmsg_len = CMSG_LEN(sizeof(tdeliver)); 180 | memcpy(CMSG_DATA(cm), &tdeliver, sizeof(tdeliver)); 181 | } 182 | 183 | ret = sendmsg(fdt, &msg, 0); 184 | if (ret == -1) 185 | printf("error write, return error sendmsg!\n"); 186 | if (ret == 0) 187 | printf("error write: 0B"); 188 | 189 | free(buf); 190 | return ret; 191 | } 192 | 193 | static void sendpacket(int sock, unsigned int length, unsigned char *mac) 194 | { 195 | struct timeval now, nowb; 196 | int res; 197 | int i; 198 | 199 | for (i = 0; i < MAC_LEN; i++) 200 | sync_packet[6 + i] = mac[i]; 201 | sync_packet[17] = length >> 8; 202 | sync_packet[18] = (char)(length & 0x00ff); 203 | 204 | gettimeofday(&nowb, 0); 205 | 206 | if (length < sizeof(sync_packet)) 207 | res = send(sock, sync_packet, sizeof(sync_packet), 0); 208 | else { 209 | #if 0 210 | char *buf = (char *)malloc(length); 211 | 212 | memcpy(buf, sync_packet, sizeof(sync_packet)); 213 | res = send(sock, buf, length, 0); 214 | free(buf); 215 | #endif 216 | res = do_send_one(sock, length); 217 | } 218 | 219 | gettimeofday(&now, 0); 220 | if (res < 0) 221 | DEBUG("%s: %s\n", "send", strerror(errno)); 222 | else 223 | DEBUG("%ld.%06ld - %ld.%06ld: sent %d bytes\n", 224 | (long)nowb.tv_sec, (long)nowb.tv_usec, 225 | (long)now.tv_sec, (long)now.tv_usec, 226 | res); 227 | } 228 | 229 | static void printpacket(struct msghdr *msg, int res, 230 | int recvmsg_flags) 231 | { 232 | struct sockaddr_in *from_addr = (struct sockaddr_in *)msg->msg_name; 233 | struct cmsghdr *cmsg; 234 | struct timeval now; 235 | 236 | if (debugen) 237 | gettimeofday(&now, 0); 238 | 239 | DEBUG("%ld.%06ld: received %s data, %d bytes from %s, %zu bytes control messages\n", 240 | (long)now.tv_sec, (long)now.tv_usec, 241 | (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular", 242 | res, 243 | inet_ntoa(from_addr->sin_addr), 244 | msg->msg_controllen); 245 | for (cmsg = CMSG_FIRSTHDR(msg); 246 | cmsg; 247 | cmsg = CMSG_NXTHDR(msg, cmsg)) { 248 | DEBUG(" cmsg len %zu: ", cmsg->cmsg_len); 249 | switch (cmsg->cmsg_level) { 250 | case SOL_SOCKET: 251 | DEBUG("SOL_SOCKET "); 252 | switch (cmsg->cmsg_type) { 253 | case SO_TIMESTAMP: { 254 | struct timeval *stamp = 255 | (struct timeval *)CMSG_DATA(cmsg); 256 | DEBUG("SO_TIMESTAMP %ld.%06ld", 257 | (long)stamp->tv_sec, 258 | (long)stamp->tv_usec); 259 | break; 260 | } 261 | case SO_TIMESTAMPNS: { 262 | struct timespec *stamp = 263 | (struct timespec *)CMSG_DATA(cmsg); 264 | DEBUG("SO_TIMESTAMPNS %ld.%09ld", 265 | (long)stamp->tv_sec, 266 | (long)stamp->tv_nsec); 267 | break; 268 | } 269 | case SO_TIMESTAMPING: { 270 | struct timespec *stamp = 271 | (struct timespec *)CMSG_DATA(cmsg); 272 | DEBUG("SO_TIMESTAMPING "); 273 | stamp++; 274 | /* skip deprecated HW transformed */ 275 | stamp++; 276 | DEBUG("HW raw %ld.%09ld", 277 | (long)stamp->tv_sec, 278 | (long)stamp->tv_nsec); 279 | if (recvmsg_flags & MSG_ERRQUEUE) { 280 | if (!fully_send) { 281 | txcount_flag = 1; 282 | if (nonstop_flag) { 283 | txcount++; 284 | } else { 285 | txcount--; 286 | } 287 | } else { 288 | if (nonstop_flag) { 289 | txcount++; 290 | } else { 291 | txcount--; 292 | if (!txcount) 293 | txcount_flag = 1; 294 | } 295 | } 296 | DEBUG("tx counter %d\n", txcount); 297 | } 298 | break; 299 | } 300 | default: 301 | DEBUG("type %d", cmsg->cmsg_type); 302 | break; 303 | } 304 | break; 305 | case IPPROTO_IP: 306 | DEBUG("IPPROTO_IP "); 307 | switch (cmsg->cmsg_type) { 308 | case IP_RECVERR: { 309 | struct sock_extended_err *err = 310 | (struct sock_extended_err *)CMSG_DATA(cmsg); 311 | DEBUG("IP_RECVERR ee_errno '%s' ee_origin %d => %s", 312 | strerror(err->ee_errno), 313 | err->ee_origin, 314 | #ifdef SO_EE_ORIGIN_TIMESTAMPING 315 | err->ee_origin == SO_EE_ORIGIN_TIMESTAMPING ? 316 | "bounced packet" : "unexpected origin" 317 | #else 318 | "probably SO_EE_ORIGIN_TIMESTAMPING" 319 | #endif 320 | ); 321 | break; 322 | } 323 | case IP_PKTINFO: { 324 | struct in_pktinfo *pktinfo = 325 | (struct in_pktinfo *)CMSG_DATA(cmsg); 326 | DEBUG("IP_PKTINFO interface index %u", 327 | pktinfo->ipi_ifindex); 328 | break; 329 | } 330 | default: 331 | DEBUG("type %d", cmsg->cmsg_type); 332 | break; 333 | } 334 | break; 335 | default: 336 | DEBUG("level %d type %d", 337 | cmsg->cmsg_level, 338 | cmsg->cmsg_type); 339 | break; 340 | } 341 | DEBUG("\n"); 342 | } 343 | } 344 | 345 | static void recvpacket(int sock, int recvmsg_flags) 346 | { 347 | char data[256]; 348 | struct msghdr msg; 349 | struct iovec entry; 350 | struct sockaddr_in from_addr; 351 | struct { 352 | struct cmsghdr cm; 353 | char control[512]; 354 | } control; 355 | int res; 356 | 357 | memset(&msg, 0, sizeof(msg)); 358 | msg.msg_iov = &entry; 359 | msg.msg_iovlen = 1; 360 | entry.iov_base = data; 361 | entry.iov_len = sizeof(data); 362 | msg.msg_name = (caddr_t)&from_addr; 363 | msg.msg_namelen = sizeof(from_addr); 364 | msg.msg_control = &control; 365 | msg.msg_controllen = sizeof(control); 366 | 367 | res = recvmsg(sock, &msg, recvmsg_flags | MSG_DONTWAIT); 368 | if (res < 0) 369 | DEBUG("%s %s: %s\n", 370 | "recvmsg", 371 | "regular", 372 | strerror(errno)); 373 | else 374 | printpacket(&msg, res, recvmsg_flags); 375 | } 376 | 377 | void *rcv_pkt(void *data) 378 | { 379 | int res; 380 | fd_set readfs, errorfs; 381 | int sock; 382 | 383 | sock = *(int *)data; 384 | 385 | while (!txcount_flag) { 386 | FD_ZERO(&readfs); 387 | FD_ZERO(&errorfs); 388 | FD_SET(sock, &readfs); 389 | FD_SET(sock, &errorfs); 390 | 391 | res = select(sock + 1, &readfs, 0, &errorfs, NULL); 392 | if (res > 0) { 393 | recvpacket(sock, 0); 394 | if (!receive_only) 395 | recvpacket(sock, MSG_ERRQUEUE); 396 | } 397 | } 398 | 399 | return 0; 400 | } 401 | 402 | static void setsockopt_txtime(int fd) 403 | { 404 | struct sock_txtime so_txtime_val = { 405 | .clockid = CLOCK_TAI, 406 | /*.flags = SOF_TXTIME_DEADLINE_MODE | SOF_TXTIME_REPORT_ERRORS */ 407 | .flags = SOF_TXTIME_REPORT_ERRORS 408 | }; 409 | struct sock_txtime so_txtime_val_read = { 0 }; 410 | socklen_t vallen = sizeof(so_txtime_val); 411 | 412 | if (send_now) 413 | so_txtime_val.flags |= SOF_TXTIME_DEADLINE_MODE; 414 | 415 | if (setsockopt(fd, SOL_SOCKET, SO_TXTIME, 416 | &so_txtime_val, sizeof(so_txtime_val))) 417 | printf("setsockopt txtime error!\n"); 418 | 419 | if (getsockopt(fd, SOL_SOCKET, SO_TXTIME, 420 | &so_txtime_val_read, &vallen)) 421 | printf("getsockopt txtime error!\n"); 422 | 423 | if (vallen != sizeof(so_txtime_val) || 424 | memcmp(&so_txtime_val, &so_txtime_val_read, vallen)) 425 | printf("getsockopt txtime: mismatch\n"); 426 | } 427 | 428 | int main(int argc, char **argv) 429 | { 430 | int so_timestamping_flags = 0; 431 | char *interface = NULL; 432 | int sock; 433 | struct ifreq device; 434 | struct ifreq hwtstamp; 435 | struct hwtstamp_config hwconfig, hwconfig_requested; 436 | struct sockaddr_ll addr; 437 | int val; 438 | unsigned char mac[MAC_LEN]; 439 | socklen_t len; 440 | unsigned int length = 0; 441 | int c; 442 | int count = 1; 443 | int prio = 0; 444 | pthread_t receive_pkt; 445 | 446 | while ((c = getopt (argc, argv, "nbd:p:i:frl:m:c:h")) != -1) { 447 | switch (c) 448 | { 449 | case 'i': 450 | interface = optarg; 451 | break; 452 | case 'f': 453 | fully_send = 1; 454 | break; 455 | case 'r': 456 | receive_only = 1; 457 | break; 458 | case 'l': 459 | length = strtoul(optarg, NULL, 0); 460 | break; 461 | case 'm': 462 | if (str2mac(optarg, mac)) 463 | printf("error mac input\n"); 464 | break; 465 | case 'c': 466 | count = strtoul(optarg, NULL, 0); 467 | break; 468 | case 'p': 469 | prio = strtoul(optarg, NULL, 0); 470 | break; 471 | case 'b': 472 | debugen = 1; 473 | break; 474 | case 'd': 475 | delay_us = strtoul(optarg, NULL, 0); 476 | break; 477 | case 'n': 478 | send_now = 1; 479 | break; 480 | case 'h': 481 | help(); 482 | return -1; 483 | case '?': 484 | if (optopt == 'c') 485 | fprintf (stderr, "Option -%c requires an argument.\n", optopt); 486 | else 487 | fprintf (stderr, 488 | "Unknown option character `\\x%x'.\n", 489 | optopt); 490 | return 1; 491 | default: 492 | help(); 493 | return -1; 494 | } 495 | } 496 | 497 | if (!interface) 498 | bail("input interface"); 499 | 500 | so_timestamping_flags |= (SOF_TIMESTAMPING_TX_HARDWARE | SOF_TIMESTAMPING_OPT_TSONLY); 501 | so_timestamping_flags |= (SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_OPT_CMSG); 502 | so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE; 503 | 504 | sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); 505 | if (sock < 0) 506 | bail("socket"); 507 | 508 | memset(&device, 0, sizeof(device)); 509 | strncpy(device.ifr_name, interface, sizeof(device.ifr_name)); 510 | if (ioctl(sock, SIOCGIFINDEX, &device) < 0) 511 | bail("getting interface index"); 512 | 513 | /* Set the SIOCSHWTSTAMP ioctl */ 514 | memset(&hwtstamp, 0, sizeof(hwtstamp)); 515 | strncpy(hwtstamp.ifr_name, interface, sizeof(hwtstamp.ifr_name)); 516 | hwtstamp.ifr_data = (void *)&hwconfig; 517 | memset(&hwconfig, 0, sizeof(hwconfig)); 518 | hwconfig.tx_type = 519 | (so_timestamping_flags & SOF_TIMESTAMPING_TX_HARDWARE) ? 520 | HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; 521 | hwconfig.rx_filter = 522 | (so_timestamping_flags & SOF_TIMESTAMPING_RX_HARDWARE) ? 523 | HWTSTAMP_FILTER_ALL : HWTSTAMP_FILTER_NONE; 524 | hwconfig_requested = hwconfig; 525 | if (ioctl(sock, SIOCSHWTSTAMP, &hwtstamp) < 0) { 526 | if ((errno == EINVAL || errno == ENOTSUP) && 527 | hwconfig_requested.tx_type == HWTSTAMP_TX_OFF && 528 | hwconfig_requested.rx_filter == HWTSTAMP_FILTER_NONE) 529 | printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n"); 530 | else 531 | printf("SIOCSHWTSTAMP: operation not supported!\n"); 532 | } 533 | printf("SIOCSHWTSTAMP: tx_type %d requested, got %d; rx_filter %d requested, got %d\n", 534 | hwconfig_requested.tx_type, hwconfig.tx_type, 535 | hwconfig_requested.rx_filter, hwconfig.rx_filter); 536 | 537 | /* bind to PTP port */ 538 | addr.sll_ifindex = device.ifr_ifindex; 539 | addr.sll_family = AF_PACKET; 540 | addr.sll_protocol = htons(ETH_P_ALL); 541 | if (bind(sock, 542 | (struct sockaddr *)&addr, 543 | sizeof(struct sockaddr_ll)) < 0) 544 | bail("bind"); 545 | if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, interface, strlen(interface))) 546 | bail("setsockopt SO_BINDTODEVICE"); 547 | if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(int))) 548 | bail("setsockopt SO_PRIORITY"); 549 | 550 | if (so_timestamping_flags && 551 | setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, 552 | &so_timestamping_flags, 553 | sizeof(so_timestamping_flags)) < 0) 554 | printf("setsockopt SO_TIMESTAMPING not supported\n"); 555 | 556 | /* verify socket options */ 557 | len = sizeof(val); 558 | 559 | if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &val, &len) < 0) { 560 | printf("%s: %s\n", "getsockopt SO_TIMESTAMPING", 561 | strerror(errno)); 562 | } else { 563 | printf("SO_TIMESTAMPING %d\n", val); 564 | if (val != so_timestamping_flags) 565 | printf(" not the expected value %d\n", 566 | so_timestamping_flags); 567 | } 568 | 569 | setsockopt_txtime(sock); 570 | 571 | txcount = count; 572 | if (!count) 573 | nonstop_flag = 1; 574 | 575 | if (receive_only) { 576 | while(1) 577 | rcv_pkt(&sock); 578 | } 579 | 580 | if (fully_send) 581 | pthread_create(&receive_pkt, NULL, rcv_pkt, &sock); 582 | 583 | while (count || nonstop_flag) { 584 | /* write one packet */ 585 | sendpacket(sock, length, mac); 586 | if (!nonstop_flag) 587 | count--; 588 | if (!fully_send) { 589 | txcount_flag = 0; 590 | rcv_pkt(&sock); 591 | } 592 | } 593 | 594 | if (fully_send) { 595 | pthread_join(receive_pkt, NULL); 596 | pthread_cancel(receive_pkt); 597 | } 598 | 599 | return 0; 600 | } 601 | -------------------------------------------------------------------------------- /main/fill.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 | /* 3 | * Copyright 2018-2019 NXP 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "tsn/genl_tsn.h" 16 | #include "main.h" 17 | #include "fill.h" 18 | int validate_ifname(char *ifname) 19 | { 20 | int sockfd; 21 | int i; 22 | struct ifconf ifconf; 23 | struct ifreq *ifreq; 24 | char buf[512]; 25 | 26 | /* init ifconf */ 27 | ifconf.ifc_len = 512; 28 | ifconf.ifc_buf = buf; 29 | 30 | sockfd = socket(AF_INET, SOCK_DGRAM, 0); 31 | if (sockfd < 0) { 32 | loge("socket"); 33 | return -1; 34 | } 35 | 36 | /* get all interfaces info */ 37 | ioctl(sockfd, SIOCGIFCONF, &ifconf); 38 | 39 | ifreq = (struct ifreq *)ifconf.ifc_buf; 40 | 41 | for (i = ifconf.ifc_len/sizeof(struct ifreq); i > 0; i--) { 42 | /* for ipv4 */ 43 | if (ifreq->ifr_flags == AF_INET) { 44 | if (strcmp(ifname, ifreq->ifr_name) == 0) { 45 | close(sockfd); 46 | return 0; 47 | } 48 | } 49 | ifreq++; 50 | } 51 | 52 | close(sockfd); 53 | return -1; 54 | } 55 | 56 | int get_all_ifname(char *ifnames) 57 | { 58 | int sockfd; 59 | int i, count = 0; 60 | struct ifconf ifconf; 61 | struct ifreq *ifreq; 62 | char buf[512]; 63 | 64 | /* init ifconf */ 65 | ifconf.ifc_len = 512; 66 | ifconf.ifc_buf = buf; 67 | 68 | sockfd = socket(AF_INET, SOCK_DGRAM, 0); 69 | if (sockfd < 0) { 70 | loge("socket"); 71 | return -1; 72 | } 73 | 74 | /* get all interfaces info */ 75 | ioctl(sockfd, SIOCGIFCONF, &ifconf); 76 | 77 | ifreq = (struct ifreq *)ifconf.ifc_buf; 78 | count = ifconf.ifc_len/sizeof(struct ifreq); 79 | for (i = 0; i < count; i++) { 80 | /* for ipv4 */ 81 | if (ifreq->ifr_flags == AF_INET) 82 | strcpy(ifnames + i * IF_NAMESIZE, ifreq->ifr_name); 83 | ifreq++; 84 | } 85 | 86 | close(sockfd); 87 | 88 | return count; 89 | } 90 | 91 | int qbv_str2gate(char *state) 92 | { 93 | int i; 94 | int gate = 0; 95 | 96 | for (i = 0; i < 8; i++) { 97 | if ((*(state + i) != '0') && (*(state + i) != '1')) { 98 | printf("no gate[%i] value, less than 8 gates\n", i); 99 | return -1; 100 | } 101 | 102 | if (state[i] == '1') 103 | gate += (1 << (7 - i)); 104 | } 105 | 106 | if ((*(state + 8) == '0') || (*(state + 8) == '1')) { 107 | printf("gate width larger then 8\n"); 108 | return -1; 109 | } 110 | 111 | return gate; 112 | } 113 | 114 | void qbv_fill_one_entry(struct tsn_qbv_entry *conf, uint32_t num, uint8_t gate, uint32_t period) 115 | { 116 | (conf + num)->gate_state = gate; 117 | (conf + num)->time_interval = period; 118 | 119 | logv("---(conf + %d) = %p, ->gce.gate_state = %02x, ->gce.time_interval = %d\n", num, 120 | (conf + num), (conf + num)->gate_state, (conf + num)->time_interval); 121 | } 122 | 123 | int qbv_entry_parse(char *config, struct tsn_qbv_entry *conf, uint32_t *count, uint32_t *cycletime) 124 | { 125 | char *delim = "\n"; 126 | char *pch; 127 | uint32_t number; 128 | char state[10], st[10]; 129 | uint32_t time; 130 | int gate; 131 | 132 | *count = 0; 133 | 134 | pch = strtok(config, delim); 135 | 136 | while (pch != NULL) { 137 | 138 | while ((*pch == ' ') || (*pch == '\t')) 139 | pch++; 140 | 141 | if (*pch == '#') { 142 | pch = strtok(NULL, delim); 143 | continue; 144 | } 145 | 146 | if ((*pch == 'T') || (*pch == 't')) { 147 | sscanf((pch + 1), "%d %s %d", &number, st, &time); 148 | 149 | logv("qbv entry: number: %d state: %s time: %d", number, st, time); 150 | 151 | if (number < *count) { 152 | loge("ERROR: Duplicate number.\n"); 153 | return -1; 154 | } 155 | 156 | if (number >= MAX_ENTRY_NUMBER) { 157 | loge("ERROR: larger than max entry number"); 158 | return -1; 159 | } 160 | 161 | if (!time) { 162 | logv("WARNING: time period should not be zero.\n"); 163 | pch = strtok(NULL, delim); 164 | continue; 165 | } 166 | 167 | sscanf(st, "%[0-1]", state); 168 | 169 | gate = qbv_str2gate(state); 170 | if (gate < 0) { 171 | loge("gate value is not valid for entry number T%d.\n", number); 172 | return -1; 173 | } 174 | 175 | qbv_fill_one_entry(conf, number, (uint8_t)gate, time); 176 | 177 | (*cycletime) += time; 178 | (*count)++; 179 | } 180 | 181 | pch = strtok(NULL, delim); 182 | } 183 | 184 | return 0; 185 | } 186 | 187 | int fill_tsn_cap_get(char *portname) 188 | { 189 | struct tsn_cap tsn_capa; 190 | 191 | memset(&tsn_capa, 0, sizeof(struct tsn_cap)); 192 | 193 | if (portname == NULL) { 194 | loge("--device could not be NULL.\n"); 195 | return -EINVAL; 196 | } 197 | 198 | return tsn_capability_get(portname, &tsn_capa); 199 | } 200 | 201 | int fill_qci_cap_get(char *portname) 202 | { 203 | struct tsn_qci_psfp_stream_param sp; 204 | 205 | memset(&sp, 0, sizeof(struct tsn_qci_psfp_stream_param)); 206 | 207 | if (portname == NULL) { 208 | loge("--device could not be NULL.\n"); 209 | return -EINVAL; 210 | } 211 | 212 | return tsn_qci_streampara_get(portname, &sp); 213 | } 214 | 215 | int fill_qbv_set(char *portname, char *config, bool enable, uint8_t configchange, 216 | uint64_t basetime, uint32_t cycletime, 217 | uint32_t cycletimeext, uint32_t maxsdu, uint8_t initgate) 218 | { 219 | uint32_t count = 0; 220 | struct tsn_qbv_entry *conf = NULL; 221 | struct tsn_qbv_conf adminconf; 222 | int ret; 223 | 224 | if (portname == NULL) { 225 | loge("--device could not be NULL.\n"); 226 | return -1; 227 | } 228 | 229 | if (enable && (config == NULL)) { 230 | loge("--entryfile could not be NULL if not set --disable.\n"); 231 | return -1; 232 | } 233 | 234 | memset(&adminconf, 0, sizeof(struct tsn_qbv_conf)); 235 | if (enable) 236 | adminconf.gate_enabled = 1; 237 | else 238 | goto qbvset; 239 | 240 | adminconf.admin.gate_states = initgate; 241 | adminconf.admin.cycle_time = cycletime; 242 | adminconf.admin.cycle_time_extension = cycletimeext; 243 | adminconf.admin.base_time = basetime; 244 | adminconf.config_change = configchange; 245 | adminconf.maxsdu = maxsdu; 246 | 247 | 248 | /* malloc space for filling the entry variable */ 249 | conf = (struct tsn_qbv_entry *)malloc(MAX_ENTRY_SIZE); 250 | if (conf == NULL) { 251 | loge("malloc space error.\n"); 252 | return -1; 253 | } 254 | 255 | memset(conf, 0, MAX_ENTRY_SIZE); 256 | 257 | ret = qbv_entry_parse(config, conf, &count, &cycletime); 258 | if (ret < 0) { 259 | free(conf); 260 | return -1; 261 | } 262 | 263 | adminconf.admin.cycle_time = cycletime; 264 | adminconf.admin.control_list = conf; 265 | adminconf.admin.control_list_length = count; 266 | 267 | qbvset: 268 | /* If the port is tsn */ 269 | ret = tsn_qos_port_qbv_set(portname, &adminconf, enable); 270 | if (conf != NULL) 271 | free(conf); 272 | 273 | if (ret < 0) 274 | return ret; 275 | 276 | return 0; 277 | } 278 | 279 | int fill_qbv_get(char *portname) 280 | { 281 | //uint32_t count = 0; 282 | struct tsn_qbv_entry *conf, *status; 283 | struct tsn_qbv_conf qbvconf; 284 | struct tsn_qbv_status qbvstatus; 285 | int ret = 0; 286 | 287 | if (portname == NULL) { 288 | loge("no portname\n"); 289 | return -1; 290 | } 291 | 292 | /* malloc space for filling the entry variable */ 293 | conf = (struct tsn_qbv_entry *)malloc(MAX_ENTRY_SIZE); 294 | if (conf == NULL) { 295 | loge("malloc space error.\n"); 296 | return -1; 297 | } 298 | 299 | status = (struct tsn_qbv_entry *)malloc(MAX_ENTRY_SIZE); 300 | if (status == NULL) { 301 | loge("malloc space error.\n"); 302 | return -1; 303 | } 304 | 305 | memset(conf, 0, MAX_ENTRY_SIZE); 306 | memset(status, 0, MAX_ENTRY_SIZE); 307 | memset(&qbvconf, 0, sizeof(struct tsn_qbv_conf)); 308 | memset(&qbvstatus, 0, sizeof(struct tsn_qbv_status)); 309 | 310 | //count = MAX_ENTRY_SIZE / sizeof(struct tsn_qbv_entry); 311 | 312 | #if 0 313 | ret = tsn_qos_port_qbv_get(portname, &qbvconf); 314 | if (ret < 0) { 315 | loge("got error in tsn_qos_port_qbv_get"); 316 | goto err; 317 | } 318 | #endif 319 | ret = tsn_qos_port_qbv_status_get(portname, &qbvstatus); 320 | 321 | /* TODO: show qbvconf qbvstatus data */ 322 | err: 323 | free(conf); 324 | free(status); 325 | return ret; 326 | } 327 | 328 | int fill_qci_sfi_set(char *portname, uint32_t streamfilterid, uint8_t enable, 329 | int32_t streamhandle, int8_t priority, uint32_t gateid, 330 | uint16_t maxsdu, int32_t flowmeterid, uint8_t osenable, uint8_t oversize) 331 | { 332 | struct tsn_qci_psfp_sfi_conf sficonf; 333 | 334 | if (portname == NULL) { 335 | loge("--device could not be NULL.\n"); 336 | return -1; 337 | } 338 | 339 | memset(&sficonf, 0, sizeof(struct tsn_qci_psfp_sfi_conf)); 340 | 341 | sficonf.stream_handle_spec = streamhandle; 342 | sficonf.priority_spec = priority; 343 | sficonf.stream_gate_instance_id = gateid; 344 | sficonf.stream_filter.maximum_sdu_size = maxsdu; 345 | sficonf.stream_filter.flow_meter_instance_id = flowmeterid; 346 | sficonf.block_oversize_enable = osenable; 347 | sficonf.block_oversize = oversize; 348 | 349 | return tsn_qci_psfp_sfi_set(portname, streamfilterid, enable, &sficonf); 350 | } 351 | 352 | int fill_qci_sfi_get(char *portname, int32_t streamfilter) 353 | { 354 | struct tsn_qci_psfp_sfi_conf sficonf; 355 | int ret = 0; 356 | 357 | memset(&sficonf, 0, sizeof(struct tsn_qci_psfp_sfi_conf)); 358 | 359 | if (portname == NULL) 360 | /*loop to get all ports*/ 361 | printf("try to get all ports stream filter instance\n"); 362 | else if (streamfilter < 0) { 363 | printf("try to get all stream filter instance on port %s\n", portname); 364 | } else 365 | ret = tsn_qci_psfp_sfi_get(portname, (uint16_t)streamfilter, &sficonf); 366 | 367 | return ret; 368 | } 369 | 370 | int fill_cb_streamid_set(char *portname, uint32_t index, int8_t enable, 371 | struct tsn_cb_streamid *streamid) 372 | { 373 | return tsn_cb_streamid_set(portname, index, enable, streamid); 374 | } 375 | 376 | int fill_cbstreamid_get(char *portname, int32_t index) 377 | { 378 | struct tsn_cb_streamid streamid; 379 | int ret = 0; 380 | 381 | memset(&streamid, 0, sizeof(struct tsn_cb_streamid)); 382 | 383 | if (portname == NULL) 384 | /*loop to get all ports*/ 385 | printf("try to get all ports stream identify tables\n"); 386 | else if (index < 0) { 387 | printf("try to get all stream identify tables on port %s\n", portname); 388 | } else 389 | ret = tsn_cb_streamid_get(portname, index, &streamid); 390 | 391 | return ret; 392 | } 393 | 394 | int qcisgi_entry_parse(char *lists, struct tsn_qci_psfp_gcl *gcl, int *count, uint32_t *timetotal) 395 | { 396 | char *delim = "\n"; 397 | char *pch; 398 | int number; 399 | char state[5], st[5]; 400 | int time; 401 | bool gate; 402 | int ipv; 403 | int octet; 404 | 405 | *count = 0; 406 | *timetotal = 0; 407 | 408 | pch = strtok(lists, delim); 409 | 410 | while (pch != NULL) { 411 | 412 | while ((*pch == ' ') || (*pch == '\t')) 413 | pch++; 414 | 415 | if (*pch == '#') { 416 | pch = strtok(NULL, delim); 417 | continue; 418 | } 419 | 420 | if ((*pch == 'T') || (*pch == 't')) { 421 | sscanf((pch + 1), "%d %s %d %d %d", &number, st, &ipv, &time, &octet); 422 | 423 | logv("qcisgi entry: number: %d state: %s ipv: %d time: %d octetmax: %d", 424 | number, st, ipv, time, octet); 425 | 426 | if (number < 0) { 427 | loge("ERROR: tx NUMBER should not less than 0"); 428 | return -1; 429 | } 430 | 431 | if (number < *count) { 432 | loge("ERROR: Duplicate number.\n"); 433 | return -1; 434 | } 435 | 436 | if (number >= MAX_ENTRY_NUMBER) { 437 | loge("ERROR: larger than max entry number"); 438 | return -1; 439 | } 440 | 441 | if (!time) { 442 | logv("WARNING: time period should not be zero.\n"); 443 | pch = strtok(NULL, delim); 444 | continue; 445 | } 446 | 447 | sscanf(st, "%[0-1]", state); 448 | 449 | if (strlen(state) != 1) { 450 | loge("gate state should '1b' or '0b' "); 451 | return -1; 452 | } 453 | 454 | gate = (state[0] == '1') ? 1:0; 455 | (gcl + number)->gate_state = gate; 456 | (gcl + number)->ipv = (int8_t)ipv; 457 | (gcl + number)->time_interval = (uint32_t)time; 458 | (gcl + number)->octet_max = (uint32_t)octet; 459 | 460 | (*timetotal) += time; 461 | (*count)++; 462 | } 463 | 464 | pch = strtok(NULL, delim); 465 | } 466 | 467 | return 0; 468 | } 469 | 470 | int fill_qci_sgi_set(char *portname, uint32_t index, bool enable, char *listtable, 471 | bool configchange, bool blkinvrx_en, bool blkinvrx, 472 | bool blkoctex_en, bool blkoctex, 473 | bool initgate, int8_t initipv, uint64_t basetime, 474 | uint32_t cycletime, uint32_t cycletimeext) 475 | { 476 | struct tsn_qci_psfp_sgi_conf sgiconf; 477 | struct tsn_qci_psfp_gcl *gcl = NULL; 478 | struct tsn_qci_psfp_stream_param sp; 479 | int count = 0; 480 | uint32_t timetotal = 0; 481 | uint32_t memsize = 0; 482 | int ret; 483 | 484 | memset(&sgiconf, 0, sizeof(struct tsn_qci_psfp_sgi_conf)); 485 | memset(&sp, 0, sizeof(struct tsn_qci_psfp_stream_param)); 486 | 487 | sgiconf.gate_enabled = enable; 488 | if (!enable) 489 | goto loadlib; 490 | 491 | sgiconf.config_change = configchange; 492 | sgiconf.block_invalid_rx_enable = blkinvrx_en; 493 | sgiconf.block_invalid_rx = blkinvrx; 494 | sgiconf.block_octets_exceeded_enable = blkoctex_en; 495 | sgiconf.block_octets_exceeded = blkoctex; 496 | sgiconf.admin.gate_states = initgate; 497 | sgiconf.admin.cycle_time_extension = cycletimeext; 498 | sgiconf.admin.base_time = basetime; 499 | sgiconf.admin.init_ipv = initipv; 500 | 501 | if (listtable == NULL) 502 | goto loadlib; 503 | 504 | if (tsn_qci_streampara_get(portname, &sp)) { 505 | memsize = MAX_ENTRY_SIZE; 506 | } else { 507 | count = sp.max_sg_instance; 508 | memsize = sizeof(struct tsn_qci_psfp_gcl) * count; 509 | } 510 | 511 | printf("sgi: memsize is %d\n", memsize); 512 | gcl = (struct tsn_qci_psfp_gcl *)malloc(memsize); 513 | if (gcl == NULL) { 514 | loge("qcisgi malloc gate list error"); 515 | return -1; 516 | } 517 | 518 | memset(gcl, 0, memsize); 519 | 520 | ret = qcisgi_entry_parse(listtable, gcl, &count, &timetotal); 521 | if (ret < 0) { 522 | loge("input file entries error"); 523 | goto err; 524 | } 525 | 526 | sgiconf.admin.gcl = gcl; 527 | sgiconf.admin.control_list_length = count; 528 | if (cycletime > timetotal) 529 | sgiconf.admin.cycle_time = cycletime; 530 | else 531 | sgiconf.admin.cycle_time = timetotal; 532 | 533 | loadlib: 534 | ret = tsn_qci_psfp_sgi_set(portname, index, enable, &sgiconf); 535 | if (ret < 0) 536 | loge("load tsn_qci_psfp_sgi_set error"); 537 | 538 | err: 539 | free(gcl); 540 | return ret; 541 | } 542 | 543 | int fill_qci_sgi_status_get(char *portname, int32_t sgi_handle) 544 | { 545 | struct tsn_qci_psfp_sgi_conf sgiconf; 546 | struct tsn_psfp_sgi_status sgistatus; 547 | struct tsn_qci_psfp_stream_param maxcapb; 548 | struct tsn_qci_psfp_gcl *gcladmin = NULL, *gcloper = NULL; 549 | int memsize; 550 | uint16_t count = 0; 551 | int ret; 552 | 553 | if (portname == NULL) 554 | loge("user must input a --device name\n"); 555 | 556 | memset(&maxcapb, 0, sizeof(struct tsn_qci_psfp_stream_param)); 557 | 558 | if (tsn_qci_streampara_get(portname, &maxcapb)) { 559 | memsize = MAX_ENTRY_SIZE; 560 | } else { 561 | count = maxcapb.max_sg_instance; 562 | memsize = sizeof(struct tsn_qci_psfp_gcl) * count; 563 | } 564 | 565 | printf("sgi: memsize is %d\n", memsize); 566 | 567 | gcladmin = (struct tsn_qci_psfp_gcl *)malloc(memsize); 568 | if (gcladmin == NULL) { 569 | loge("qcisgi admin malloc gate list error"); 570 | return -1; 571 | } 572 | 573 | gcloper = (struct tsn_qci_psfp_gcl *)malloc(memsize); 574 | if (gcloper == NULL) { 575 | free(gcladmin); 576 | loge("qcisgi oper malloc gate list error"); 577 | return -1; 578 | } 579 | 580 | memset(&sgistatus, 0, sizeof(struct tsn_psfp_sgi_status)); 581 | memset(&sgiconf, 0, sizeof(struct tsn_qci_psfp_sgi_conf)); 582 | memset(gcladmin, 0, memsize); 583 | memset(gcloper, 0, memsize); 584 | 585 | sgistatus.oper.gcl = gcloper; 586 | sgiconf.admin.gcl = gcladmin; 587 | 588 | ret = tsn_qci_psfp_sgi_get(portname, sgi_handle, &sgiconf); 589 | if (ret < 0) 590 | goto err; 591 | 592 | // ret = tsn_qci_psfp_sgi_status_get(portname, sgi_handle, &sgistatus); 593 | err: 594 | free(gcladmin); 595 | free(gcloper); 596 | if (ret < 0) 597 | return -1; 598 | 599 | return 0; 600 | } 601 | 602 | int fill_qci_fmi_set(char *portname, uint32_t index, bool enable, uint32_t cir, 603 | uint32_t cbs, uint32_t eir, uint32_t ebs, bool cf, bool cm, 604 | bool dropyellow, bool markred_en, bool markred) 605 | { 606 | struct tsn_qci_psfp_fmi fmi; 607 | 608 | memset(&fmi, 0, sizeof(struct tsn_qci_psfp_fmi)); 609 | 610 | if (portname == NULL) 611 | loge("port name should not be NULL"); 612 | 613 | fmi.cir = cir; 614 | fmi.cbs = cbs; 615 | fmi.eir = eir; 616 | fmi.ebs = ebs; 617 | fmi.cf = cf; 618 | fmi.cm = cm; 619 | fmi.drop_on_yellow = dropyellow; 620 | fmi.mark_red_enable = markred_en; 621 | fmi.mark_red = markred; 622 | 623 | return tsn_qci_psfp_fmi_set(portname, index, enable, &fmi); 624 | } 625 | 626 | int fill_qci_fmi_get(char *portname, uint32_t index) 627 | { 628 | struct tsn_qci_psfp_fmi fmi; 629 | 630 | memset(&fmi, 0, sizeof(struct tsn_qci_psfp_fmi)); 631 | 632 | return tsn_qci_psfp_fmi_get(portname, index, &fmi); 633 | } 634 | 635 | int fill_cbs_set(char *portname, uint8_t tc, uint8_t percentage) 636 | { 637 | return tsn_cbs_set(portname, tc, percentage); 638 | } 639 | 640 | int fill_cbs_get(char *portname, uint8_t tc) 641 | { 642 | 643 | return tsn_cbs_get(portname, tc); 644 | } 645 | 646 | int fill_tsd_set(char *portname, bool enable, uint32_t period, uint32_t frame_num, bool imme) 647 | { 648 | return tsn_tsd_set(portname, enable, period, frame_num, imme); 649 | } 650 | 651 | int fill_tsd_get(char *portname) 652 | { 653 | return tsn_tsd_get(portname); 654 | } 655 | 656 | int fill_qbu_set(char *portname, uint8_t preemptable) 657 | { 658 | return tsn_qbu_set(portname, preemptable); 659 | } 660 | 661 | int fill_qbu_get_status(char *portname) 662 | { 663 | struct tsn_preempt_status pts; 664 | 665 | memset(&pts, 0, sizeof(struct tsn_preempt_status)); 666 | 667 | return tsn_qbu_get_status(portname, &pts); 668 | } 669 | 670 | int fill_ct_set(char *portname, uint8_t queue_stat) 671 | { 672 | return tsn_ct_set(portname, queue_stat); 673 | } 674 | 675 | int fill_cbgen_set(char *portname, uint32_t index, uint8_t iport_mask, 676 | uint8_t split_mask, uint8_t seq_len, uint32_t seq_num) 677 | { 678 | struct tsn_seq_gen_conf sg; 679 | 680 | memset(&sg, 0, sizeof(struct tsn_seq_gen_conf)); 681 | sg.iport_mask = iport_mask; 682 | sg.split_mask = split_mask; 683 | sg.seq_len = seq_len; 684 | sg.seq_num = seq_num; 685 | 686 | return tsn_cbgen_set(portname, index, &sg); 687 | } 688 | 689 | int fill_cbrec_set(char *portname, uint32_t index, uint8_t seq_len, 690 | uint8_t his_len, bool rtag_pop_en) 691 | { 692 | struct tsn_seq_rec_conf sr; 693 | 694 | memset(&sr, 0, sizeof(struct tsn_seq_rec_conf)); 695 | sr.seq_len = seq_len; 696 | sr.his_len = his_len; 697 | sr.rtag_pop_en = rtag_pop_en; 698 | 699 | return tsn_cbrec_set(portname, index, &sr); 700 | } 701 | 702 | int fill_cbstatus_get(char *portname, int32_t index) 703 | { 704 | struct tsn_cb_status cbstat; 705 | int ret = 0; 706 | 707 | memset(&cbstat, 0, sizeof(struct tsn_cb_status)); 708 | 709 | if (portname == NULL) 710 | printf("No device\n"); 711 | else 712 | ret = tsn_cbstatus_get(portname, index, &cbstat); 713 | 714 | return ret; 715 | } 716 | 717 | int fill_dscp_set(char *portname, bool disable, int index, int cos, int dpl) 718 | { 719 | struct tsn_qos_switch_dscp_conf dscp_conf; 720 | 721 | memset(&dscp_conf, 0, sizeof(struct tsn_qos_switch_dscp_conf)); 722 | dscp_conf.cos = cos; 723 | dscp_conf.dpl = dpl; 724 | return tsn_dscp_set(portname, disable, index, &dscp_conf); 725 | } 726 | -------------------------------------------------------------------------------- /lib/genl_basic.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 | /* 3 | * Copyright 2018-2019 NXP 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "tsn/genl_tsn.h" 24 | 25 | #define CONFIG_LIBNL3 26 | 27 | int VERBOSE = 0; 28 | 29 | struct global_conf glb_conf; 30 | /* 31 | * Create a raw netlink socket and bind 32 | */ 33 | static int tsn_create_nl_socket(int protocol) 34 | { 35 | int fd; 36 | struct sockaddr_nl local; 37 | int val; 38 | 39 | /* create socket */ 40 | fd = socket(AF_NETLINK, SOCK_RAW, protocol); 41 | if (fd < 0) 42 | return -1; 43 | 44 | memset(&local, 0, sizeof(local)); 45 | local.nl_family = AF_NETLINK; 46 | local.nl_pid = getpid(); 47 | 48 | /* pid */ 49 | if (bind(fd, (struct sockaddr *) &local, sizeof(local)) < 0) 50 | goto error; 51 | 52 | val = 1; 53 | if (setsockopt(fd, SOL_NETLINK, NETLINK_LISTEN_ALL_NSID, &val, 54 | sizeof val) < 0) { 55 | printf("netlink: could not %s listening to all nsid (%s)", 56 | val ? "enable" : "disable", TSN_GENL_NAME); 57 | goto error; 58 | } 59 | 60 | return fd; 61 | 62 | error: 63 | close(fd); 64 | return -1; 65 | } 66 | 67 | 68 | static int tsn_send_cmd(int sd, __u16 nlmsg_type, __u32 nlmsg_pid, 69 | __u8 genl_cmd, __u16 nla_type, 70 | void *nla_data, int nla_len) 71 | { 72 | struct nlattr *na; 73 | struct sockaddr_nl nladdr; 74 | int r, buflen; 75 | char *buf; 76 | 77 | struct msgtemplate msg; 78 | 79 | /* insert msg, only insert a attr */ 80 | msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN) + NLMSG_ALIGN(MAX_USER_SIZE); 81 | msg.n.nlmsg_type = nlmsg_type; 82 | msg.n.nlmsg_flags = NLM_F_REQUEST; 83 | msg.n.nlmsg_seq = 0; 84 | msg.n.nlmsg_pid = nlmsg_pid; 85 | msg.g.cmd = genl_cmd; 86 | msg.g.version = TSN_GENL_VERSION; 87 | na = (struct nlattr *) GENLMSG_ATTR_DATA(&msg); 88 | na->nla_type = nla_type; 89 | na->nla_len = nla_len + 1 + NLA_HDRLEN; 90 | memcpy(NLA_DATA(na), nla_data, nla_len); 91 | msg.n.nlmsg_len += NLMSG_ALIGN(na->nla_len); 92 | 93 | buf = (char *) &msg; 94 | buflen = msg.n.nlmsg_len; 95 | memset(&nladdr, 0, sizeof(nladdr)); 96 | nladdr.nl_family = AF_NETLINK; 97 | 98 | /* loop */ 99 | while ((r = sendto(sd, buf, buflen, 0, (struct sockaddr *) &nladdr, 100 | sizeof(nladdr))) < buflen) { 101 | if (r > 0) { 102 | buf += r; 103 | buflen -= r; 104 | } else if (errno != EAGAIN) 105 | return -1; 106 | } 107 | 108 | return 0; 109 | } 110 | 111 | struct msgtemplate *tsn_send_cmd_prepare(__u8 genl_cmd) 112 | { 113 | struct msgtemplate *msg; 114 | 115 | msg = (struct msgtemplate *)malloc(sizeof(struct msgtemplate)); 116 | if (msg == NULL) 117 | return NULL; 118 | 119 | /* insert msg, only insert a attr */ 120 | msg->n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); 121 | msg->n.nlmsg_type = glb_conf.genl_familyid; 122 | msg->n.nlmsg_flags = NLM_F_REQUEST; 123 | msg->n.nlmsg_seq = 0; 124 | msg->n.nlmsg_pid = glb_conf.pid; 125 | msg->g.cmd = genl_cmd; 126 | msg->g.version = TSN_GENL_VERSION; 127 | 128 | return msg; 129 | } 130 | 131 | static inline int tsn_nla_attr_size(int payload) 132 | { 133 | return NLA_HDRLEN + payload; 134 | } 135 | 136 | static inline int tsn_nla_total_size(int payload) 137 | { 138 | return NLA_ALIGN(tsn_nla_attr_size(payload)); 139 | } 140 | 141 | static inline int tsn_nla_padlen(int payload) 142 | { 143 | return tsn_nla_total_size(payload) - tsn_nla_attr_size(payload); 144 | } 145 | 146 | void tsn_send_cmd_append_attr(struct msgtemplate *msg, __u16 nla_type, void *nla_data, int nla_len) 147 | { 148 | struct nlattr *na; 149 | 150 | //na = (struct nlattr *) GENLMSG_ATTR_DATA(msg); 151 | na = (struct nlattr *)((char *)msg + (msg->n.nlmsg_len)); 152 | 153 | na->nla_type = nla_type; 154 | na->nla_len = NLMSG_ALIGN(NLA_HDRLEN) + nla_len; 155 | memcpy(NLA_DATA(na), (char *)nla_data, nla_len); 156 | 157 | PRINTF("add attr at %p , value is %x, len is %d na->nla_len is %d\n", na, *((int *)NLA_DATA(na)), nla_len, na->nla_len); 158 | 159 | msg->n.nlmsg_len += NLA_ALIGN(NLA_HDRLEN + nla_len); 160 | memset((unsigned char *) na + na->nla_len, 0, tsn_nla_padlen(nla_len)); 161 | 162 | PRINTF("msg len is %d\n", msg->n.nlmsg_len); 163 | } 164 | 165 | int tsn_send_to_kernel(struct msgtemplate *msg) 166 | { 167 | int buflen, r; 168 | struct sockaddr_nl nladdr; 169 | char *buf; 170 | 171 | buflen = msg->n.nlmsg_len; 172 | memset(&nladdr, 0, sizeof(nladdr)); 173 | nladdr.nl_family = AF_NETLINK; 174 | buf = (char *)msg; 175 | 176 | /* loop */ 177 | while ((r = sendto(glb_conf.genl_fd, buf, buflen, 0, (struct sockaddr *) &nladdr, 178 | sizeof(nladdr))) < buflen) { 179 | if (r > 0) { 180 | buf += r; 181 | buflen -= r; 182 | } else if (errno != EAGAIN) { 183 | free(msg); 184 | return -errno; 185 | } 186 | } 187 | 188 | free(msg); 189 | return 0; 190 | } 191 | #if 0 192 | /* 193 | * Probe the controller in genetlink to find the family id 194 | * for the TSN_GEN_CTRL family 195 | */ 196 | static int tsn_get_family_id(int sd) 197 | { 198 | struct msgtemplate ans; 199 | 200 | char name[100]; 201 | int id = 0, ret; 202 | struct nlattr *na; 203 | int rep_len; 204 | 205 | /* search family id by gen family name */ 206 | strcpy(name, TSN_GENL_NAME); 207 | ret = tsn_send_cmd(sd, GENL_ID_CTRL, getpid(), CTRL_CMD_GETFAMILY, 208 | CTRL_ATTR_FAMILY_NAME, (void *)name, strlen(TSN_GENL_NAME)+1); 209 | if (ret < 0) 210 | return 0; 211 | 212 | /* receiv the kernel message */ 213 | rep_len = recv(sd, &ans, sizeof(ans), 0); 214 | if (ans.n.nlmsg_type == NLMSG_ERROR || (rep_len < 0) || !NLMSG_OK((&ans.n), rep_len)) 215 | return 0; 216 | 217 | /* parse family id */ 218 | na = (struct nlattr *) GENLMSG_ATTR_DATA(&ans); 219 | na = (struct nlattr *) ((char *) na + NLA_ALIGN(na->nla_len)); 220 | if (na->nla_type == CTRL_ATTR_FAMILY_ID) { 221 | id = *(__u16 *) NLA_DATA(na); 222 | } 223 | 224 | return id; 225 | } 226 | #endif 227 | 228 | int genl_send_msg(int sd, u_int16_t nlmsg_type, u_int32_t nlmsg_pid, 229 | u_int8_t genl_cmd, u_int8_t genl_version, u_int16_t nla_type, 230 | void *nla_data, int nla_len) 231 | { 232 | struct nlattr *na; 233 | struct sockaddr_nl nladdr; 234 | int r, buflen; 235 | char *buf; 236 | msgtemplate_t msg; 237 | 238 | if (nlmsg_type == 0) 239 | return 0; 240 | 241 | msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); 242 | msg.n.nlmsg_type = nlmsg_type; 243 | msg.n.nlmsg_flags = NLM_F_REQUEST; 244 | msg.n.nlmsg_seq = 0; 245 | /* 246 | * nlmsg_pid 247 | * Linux 248 | */ 249 | msg.n.nlmsg_pid = nlmsg_pid; 250 | msg.g.cmd = genl_cmd; 251 | msg.g.version = genl_version; 252 | na = (struct nlattr *) GENLMSG_USER_DATA(&msg); 253 | na->nla_type = nla_type; 254 | na->nla_len = nla_len + 1 + NLA_HDRLEN; 255 | memcpy(NLA_DATA(na), nla_data, nla_len); 256 | msg.n.nlmsg_len += NLMSG_ALIGN(na->nla_len); 257 | 258 | buf = (char *) &msg; 259 | buflen = msg.n.nlmsg_len; 260 | memset(&nladdr, 0, sizeof(nladdr)); 261 | nladdr.nl_family = AF_NETLINK; 262 | while ((r = sendto(sd, buf, buflen, 0, (struct sockaddr *) &nladdr, 263 | sizeof(nladdr))) < buflen) { 264 | if (r > 0) { 265 | buf += r; 266 | buflen -= r; 267 | } else if (errno != EAGAIN) { 268 | return -1; 269 | } 270 | } 271 | return 0; 272 | } 273 | 274 | static int genl_get_family_id(int sd, char *family_name) 275 | { 276 | msgtemplate_t ans; 277 | int id, rc; 278 | struct nlattr *na; 279 | int rep_len; 280 | 281 | rc = genl_send_msg(sd, GENL_ID_CTRL, 0, CTRL_CMD_GETFAMILY, 1, 282 | CTRL_ATTR_FAMILY_NAME, (void *)family_name, 283 | strlen(family_name)+1); 284 | if (rc < 0) 285 | return -1; 286 | 287 | rep_len = recv(sd, &ans, sizeof(ans), 0); 288 | if (rep_len < 0) 289 | return 0; 290 | 291 | if (ans.n.nlmsg_type == NLMSG_ERROR || !NLMSG_OK((&ans.n), rep_len)) { 292 | printf("nlmsg type is %d length %d\n", ans.n.nlmsg_type, rep_len); 293 | if (ans.n.nlmsg_type == NLMSG_ERROR) { 294 | struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(&ans.n); 295 | if (ans.n.nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) 296 | printf("ERROR truncated\n"); 297 | else 298 | printf("got error reply. error NO. is %d\n", -err->error); 299 | } 300 | return -1; 301 | } 302 | 303 | na = (struct nlattr *) GENLMSG_USER_DATA(&ans); 304 | na = (struct nlattr *) ((char *) na + NLA_ALIGN(na->nla_len)); 305 | if (na->nla_type == CTRL_ATTR_FAMILY_ID) { 306 | id = *(__u16 *) NLA_DATA(na); 307 | } else { 308 | id = 0; 309 | } 310 | 311 | return id; 312 | } 313 | 314 | int tsn_msg_check(struct msgtemplate msg, int rep_len) 315 | { 316 | if (msg.n.nlmsg_type == NLMSG_ERROR || !NLMSG_OK((&msg.n), rep_len)) { 317 | struct nlmsgerr *err = NLMSG_DATA(&msg); 318 | fprintf(stderr, "fatal reply error, errno %d\n", err->error); 319 | return err->error; 320 | } 321 | 322 | return 0; 323 | } 324 | 325 | int tsn_msg_recv_analysis(struct showtable *linkdata, void *para) 326 | { 327 | int rep_len; 328 | int ret = 0; 329 | int len, len1; 330 | struct nlattr *na, *na1; 331 | struct msgtemplate msg; 332 | int msg_chk = 0; 333 | int data; 334 | char *string; 335 | int remain; 336 | int tsn; 337 | int type; 338 | cJSON *json = NULL; 339 | cJSON *link1 = NULL; 340 | cJSON *link2 = NULL; 341 | cJSON *link3 = NULL; 342 | 343 | if (!linkdata) 344 | tsn = 0; 345 | else 346 | tsn = linkdata->type; 347 | 348 | /* get kernel message echo */ 349 | rep_len = recv(glb_conf.genl_fd, &msg, sizeof(msg), 0); 350 | if (rep_len < 0) { 351 | fprintf(stderr, "nonfatal reply error: errno %d\n", rep_len); 352 | return rep_len; 353 | } 354 | msg_chk = tsn_msg_check(msg, rep_len); 355 | if (msg_chk < 0) { 356 | fprintf(stderr, "nonfatal reply error: errno %d\n", errno); 357 | return -errno; 358 | } 359 | PRINTF("received %d bytes\n", rep_len); 360 | PRINTF("nlmsghdr size=%zu, nlmsg_len=%d, rep_len=%d\n", 361 | sizeof(struct nlmsghdr), msg.n.nlmsg_len, rep_len); 362 | 363 | rep_len = GENLMSG_ATTR_PAYLOAD(&msg.n); 364 | na = (struct nlattr *) GENLMSG_ATTR_DATA(&msg); 365 | len = 0; 366 | 367 | /* there may more than one attr in one message, loop to read */ 368 | while (len < rep_len) { 369 | switch (na->nla_type) { 370 | case TSN_CMD_ATTR_MESG: 371 | /* get kernel string message echo */ 372 | string = (char *) NLA_DATA(na); 373 | printf("echo reply:%s\n", string); 374 | break; 375 | case TSN_CMD_ATTR_DATA: 376 | /* get kernel data echo */ 377 | data = *(int *) NLA_DATA(na); 378 | printf("echo reply:%d\n", data); 379 | ret = data; 380 | break; 381 | case TSN_ATTR_IFNAME: 382 | string = (char *) NLA_DATA(na); 383 | printf("echo reply:%s\n", string); 384 | break; 385 | case TSN_ATTR_QBV: 386 | case TSN_ATTR_CAP: 387 | case TSN_ATTR_STREAM_IDENTIFY: 388 | case TSN_ATTR_QCI_SP: 389 | case TSN_ATTR_QCI_SFI: 390 | case TSN_ATTR_QCI_SGI: 391 | case TSN_ATTR_QCI_FMI: 392 | case TSN_ATTR_CBS: 393 | case TSN_ATTR_QBU: 394 | case TSN_ATTR_CBSTAT: 395 | 396 | if (!tsn) 397 | break; 398 | 399 | if (!linkdata->len1) 400 | break; 401 | 402 | na1 = nla_data(na); 403 | len1 = 0; 404 | int temp = 0; 405 | type = na->nla_type; 406 | 407 | printf("tsn: len: %04x type: %04x data:\n", na->nla_len, na->nla_type); 408 | len1 += NLA_HDRLEN; 409 | json = cJSON_CreateObject(); 410 | 411 | while (len1 < na->nla_len) { 412 | switch (linkdata->link1[na1->nla_type].type) { 413 | case NLA_NESTED: 414 | { 415 | struct nlattr *na2; 416 | int len2 = 0; 417 | int temp2 = 0; 418 | 419 | cJSON_AddItemToObject(json, linkdata->link1[na1->nla_type].name, 420 | link1 = cJSON_CreateObject()); 421 | 422 | printf(" level2: nla->_len: %d type: %d\n\n", na1->nla_len, na1->nla_type); 423 | len2 += NLA_HDRLEN; 424 | na2 = nla_data(na1); 425 | 426 | while(len2 < na1->nla_len) { 427 | switch(linkdata->link2[na2->nla_type].type) { 428 | case NLA_NESTED + __NLA_TYPE_MAX: 429 | 430 | case NLA_NESTED: 431 | { 432 | struct nlattr *na3; 433 | int len3 = 0; 434 | int temp3 = 0; 435 | 436 | cJSON_AddItemToObject(link1, linkdata->link2[na2->nla_type].name, 437 | link2 = cJSON_CreateObject()); 438 | 439 | printf(" level3: nla->_len: %d type: %d\n\n", na2->nla_len, na2->nla_type); 440 | len3 += NLA_HDRLEN; 441 | na3 = nla_data(na2); 442 | while (len3 < na2->nla_len) { 443 | switch(linkdata->link3[na3->nla_type].type) { 444 | case NLA_FLAG + __NLA_TYPE_MAX: 445 | case NLA_FLAG: 446 | printf(" %s \n", linkdata->link3[na3->nla_type].name); 447 | cJSON_AddItemToObject(link2, 448 | linkdata->link3[na3->nla_type].name, 449 | cJSON_CreateString("ON")); 450 | 451 | break; 452 | case NLA_U8 + __NLA_TYPE_MAX: 453 | case NLA_U8: 454 | printf(" %s = %02x\n", linkdata->link3[na3->nla_type].name, *(uint8_t *)NLA_DATA(na3)); 455 | cJSON_AddItemToObject(link2, 456 | linkdata->link3[na3->nla_type].name, 457 | cJSON_CreateNumber(*(uint8_t *)NLA_DATA(na3))); 458 | 459 | break; 460 | case NLA_U16 + __NLA_TYPE_MAX: 461 | case NLA_U16: 462 | printf(" %s = %x\n", linkdata->link3[na3->nla_type].name, *(uint16_t *)NLA_DATA(na3)); 463 | cJSON_AddItemToObject(link2, 464 | linkdata->link3[na3->nla_type].name, 465 | cJSON_CreateNumber(*(uint16_t *)NLA_DATA(na3))); 466 | 467 | break; 468 | case NLA_U32 + __NLA_TYPE_MAX: 469 | case NLA_U32: 470 | printf(" %s = %x\n", linkdata->link3[na3->nla_type].name, *(uint32_t *)NLA_DATA(na3)); 471 | cJSON_AddItemToObject(link2, 472 | linkdata->link3[na3->nla_type].name, 473 | cJSON_CreateNumber(*(uint32_t *)NLA_DATA(na3))); 474 | 475 | break; 476 | case NLA_U64 + __NLA_TYPE_MAX: 477 | case NLA_U64: 478 | printf(" %s = %llx\n", linkdata->link3[na3->nla_type].name, *(uint64_t *)NLA_DATA(na3)); 479 | cJSON_AddItemToObject(link2, 480 | linkdata->link3[na3->nla_type].name, 481 | cJSON_CreateNumber(*(uint64_t *)NLA_DATA(na3))); 482 | 483 | break; 484 | default: 485 | break; 486 | } 487 | len3 += NLA_ALIGN(na3->nla_len); 488 | na3 = nla_next(na3, &remain); 489 | } 490 | } 491 | break; 492 | 493 | case NLA_FLAG: 494 | printf(" %s \n", linkdata->link2[na2->nla_type].name); 495 | cJSON_AddItemToObject(link1, linkdata->link2[na2->nla_type].name, 496 | cJSON_CreateString("ON")); 497 | 498 | break; 499 | case NLA_U8: 500 | printf(" %s = %02x\n", linkdata->link2[na2->nla_type].name, *(uint8_t *)NLA_DATA(na2)); 501 | cJSON_AddItemToObject(link1, linkdata->link2[na2->nla_type].name, 502 | cJSON_CreateNumber(*(uint8_t *)NLA_DATA(na2))); 503 | 504 | break; 505 | case NLA_U16: 506 | printf(" %s = %x\n", linkdata->link2[na2->nla_type].name, *(uint16_t *)NLA_DATA(na2)); 507 | cJSON_AddItemToObject(link1, linkdata->link2[na2->nla_type].name, 508 | cJSON_CreateNumber(*(uint16_t *)NLA_DATA(na2))); 509 | 510 | break; 511 | case NLA_U32: 512 | printf(" %s = %x\n", linkdata->link2[na2->nla_type].name, *(uint32_t *)NLA_DATA(na2)); 513 | cJSON_AddItemToObject(link1, linkdata->link2[na2->nla_type].name, 514 | cJSON_CreateNumber(*(uint32_t *)NLA_DATA(na2))); 515 | 516 | break; 517 | case NLA_U64: 518 | printf(" %s = %llx\n", linkdata->link2[na2->nla_type].name, *(uint64_t *)NLA_DATA(na2)); 519 | cJSON_AddItemToObject(link1, linkdata->link2[na2->nla_type].name, 520 | cJSON_CreateNumber(*(uint64_t *)NLA_DATA(na2))); 521 | 522 | break; 523 | default: 524 | break; 525 | } 526 | 527 | len2 += NLA_ALIGN(na2->nla_len); 528 | na2 = nla_next(na2, &remain); 529 | } 530 | } 531 | break; 532 | case NLA_FLAG: 533 | printf(" %s \n", linkdata->link1[na1->nla_type].name); 534 | if (type == TSN_ATTR_CAP) 535 | cJSON_AddItemToObject(json, linkdata->link1[na1->nla_type].name, 536 | cJSON_CreateString("YES")); 537 | else 538 | cJSON_AddItemToObject(json, linkdata->link1[na1->nla_type].name, 539 | cJSON_CreateString("ON")); 540 | 541 | break; 542 | case NLA_U8: 543 | cJSON_AddItemToObject(json, linkdata->link1[na1->nla_type].name, 544 | cJSON_CreateNumber(*(char *)NLA_DATA(na1))); 545 | 546 | if (linkdata->link1[na1->nla_type].len == 3) 547 | printf(" %s = %02x\n", linkdata->link1[na1->nla_type].name, *(char *)NLA_DATA(na1)); 548 | else 549 | printf(" %s = %02x\n", linkdata->link1[na1->nla_type].name, *(uint8_t *)NLA_DATA(na1)); 550 | break; 551 | case NLA_U16: 552 | cJSON_AddItemToObject(json, linkdata->link1[na1->nla_type].name, 553 | cJSON_CreateNumber(*(uint16_t *)NLA_DATA(na1))); 554 | 555 | printf(" %s = %x\n", linkdata->link1[na1->nla_type].name, *(uint16_t *)NLA_DATA(na1)); 556 | break; 557 | case NLA_U32: 558 | cJSON_AddItemToObject(json, linkdata->link1[na1->nla_type].name, 559 | cJSON_CreateNumber(*(int32_t *)NLA_DATA(na1))); 560 | 561 | if (linkdata->link1[na1->nla_type].len == 3) 562 | printf(" %s = %x\n", linkdata->link1[na1->nla_type].name, *(int32_t *)NLA_DATA(na1)); 563 | else 564 | printf(" %s = %x\n", linkdata->link1[na1->nla_type].name, *(uint32_t *)NLA_DATA(na1)); 565 | break; 566 | case NLA_U64: 567 | cJSON_AddItemToObject(json, linkdata->link1[na1->nla_type].name, 568 | cJSON_CreateNumber(*(uint64_t *)NLA_DATA(na1))); 569 | 570 | if (linkdata->link1[na1->nla_type].len == 4) 571 | printf(" %s = %012llx\n", linkdata->link1[na1->nla_type].name, *(uint64_t *)NLA_DATA(na1)); 572 | else 573 | printf(" %s = %llx\n", linkdata->link1[na1->nla_type].name, *(uint64_t *)NLA_DATA(na1)); 574 | break; 575 | case __NLA_TYPE_MAX + 10: 576 | case __NLA_TYPE_MAX + 11: 577 | { 578 | int j; 579 | uint64_t *p; 580 | char *cnt_name[8][MAX_NAME_LEN]={}; 581 | 582 | if (type == TSN_ATTR_QCI_SFI) 583 | sscanf(linkdata->link1[na1->nla_type].name, "%s %s %s %s %s %s", 584 | cnt_name[0], cnt_name[1], cnt_name[2], 585 | cnt_name[3], cnt_name[4], cnt_name[5]); 586 | else if (type == TSN_ATTR_QCI_FMI) 587 | sscanf(linkdata->link1[na1->nla_type].name, "%s %s %s %s %s %s %s %s", 588 | cnt_name[0], cnt_name[1], cnt_name[2], 589 | cnt_name[3], cnt_name[4], cnt_name[5], 590 | cnt_name[6], cnt_name[7]); 591 | printf("\n ==counters==\n"); 592 | p = nla_data(na1); 593 | for (j = 0; j < (linkdata->link1[na1->nla_type].len / sizeof(uint64_t)); j++) { 594 | printf(" %s : %llx\n", cnt_name[j], *(uint64_t *)(p + j)); 595 | cJSON_AddItemToObject(json, cnt_name[j], 596 | cJSON_CreateNumber(*(uint64_t *)(p + j))); 597 | } 598 | printf("\n === end ===\n"); 599 | } 600 | break; 601 | case NLA_STRING: 602 | string = (char *) NLA_DATA(na); 603 | 604 | printf("netlink string:%s\n", string); 605 | 606 | cJSON_AddItemToObject(json, linkdata->link1[na1->nla_type].name, 607 | cJSON_CreateString(string)); 608 | 609 | break; 610 | default: 611 | break; 612 | } 613 | 614 | len1 += NLA_ALIGN(na1->nla_len); 615 | na1 = nla_next(na1, &remain); 616 | if (temp++ > 100) 617 | break; 618 | } 619 | break; 620 | default: 621 | break; 622 | } 623 | 624 | len += NLA_ALIGN(na->nla_len); 625 | #ifdef CONFIG_LIBNL3 626 | na = nla_next(na, &remain); 627 | #else 628 | na += NLA_ALIGN(na->nla_len); 629 | #endif 630 | if (json) { 631 | char *buf = NULL; 632 | FILE *fp = fopen("/tmp/tsntool.json","w"); 633 | 634 | if (para) 635 | get_para_from_json(type, json, para); 636 | 637 | printf("json structure:\n %s\n",buf = cJSON_Print(json)); 638 | fwrite(buf,strlen(buf),1,fp); 639 | free(buf); 640 | fclose(fp); 641 | cJSON_Delete(json); 642 | json = NULL; 643 | } 644 | } 645 | return ret; 646 | } 647 | 648 | int genl_tsn_init(void) 649 | { 650 | int nl_fd; 651 | int nl_family_id; 652 | int my_pid; 653 | 654 | memset(&glb_conf, 0, sizeof(struct global_conf)); 655 | 656 | /* init socket */ 657 | nl_fd = tsn_create_nl_socket(NETLINK_GENERIC); 658 | if (nl_fd < 0) { 659 | fprintf(stderr, "failed to create netlink socket\n"); 660 | return -1; 661 | } 662 | 663 | glb_conf.genl_fd = nl_fd; 664 | 665 | /* to get family id */ 666 | nl_family_id = genl_get_family_id(nl_fd, TSN_GENL_NAME); 667 | if (!nl_family_id) { 668 | fprintf(stderr, "Error getting family id, errno %d\n", errno); 669 | close(nl_fd); 670 | return -1; 671 | } 672 | PRINTF("family id %d\n", nl_family_id); 673 | glb_conf.genl_familyid = nl_family_id; 674 | 675 | /* send string message */ 676 | my_pid = getpid(); 677 | glb_conf.pid = my_pid; 678 | 679 | return 0; 680 | } 681 | 682 | void genl_tsn_close(void) 683 | { 684 | close(glb_conf.genl_fd); 685 | } 686 | 687 | int tsn_echo_test(char *string, int data) 688 | { 689 | int ret; 690 | 691 | PRINTF("before tsn_send_cmd\n"); 692 | ret = tsn_send_cmd(glb_conf.genl_fd, glb_conf.genl_familyid, glb_conf.pid, TSN_CMD_ECHO, 693 | TSN_CMD_ATTR_MESG, string, strlen(string) + 1); 694 | if (ret < 0) { 695 | fprintf(stderr, "failed to send echo cmd\n"); 696 | return -1; 697 | } 698 | 699 | /* send data message to kernel */ 700 | ret = tsn_send_cmd(glb_conf.genl_fd, glb_conf.genl_familyid, glb_conf.pid, TSN_CMD_ECHO, 701 | TSN_CMD_ATTR_DATA, &data, sizeof(data)); 702 | if (ret < 0) { 703 | fprintf(stderr, "failed to send echo cmd\n"); 704 | return -1; 705 | } 706 | 707 | /* receive message and (only 2 message types) */ 708 | tsn_msg_recv_analysis(NULL, NULL); 709 | tsn_msg_recv_analysis(NULL, NULL); 710 | 711 | return 0; 712 | } 713 | 714 | int get_net_ifindex_by_name(const char *eth_name, uint32_t *ifindex) 715 | { 716 | int skfd = -1; 717 | struct ifreq ifr; 718 | 719 | if(eth_name == NULL) 720 | { 721 | return -1; 722 | } else { 723 | strcpy(ifr.ifr_name, eth_name); 724 | } 725 | 726 | skfd = socket(PF_INET, SOCK_DGRAM, 0); 727 | if (skfd < 0) { 728 | return -1; 729 | } 730 | 731 | if (ioctl(skfd, SIOCGIFINDEX, &ifr) == 0) { 732 | *ifindex = ifr.ifr_ifindex; 733 | printf("Got if index %d by name %s\n", *ifindex, eth_name); 734 | } else { 735 | printf("Do not get any device with name %s\n", eth_name); 736 | close(skfd); 737 | return -1; 738 | } 739 | 740 | close(skfd); 741 | 742 | return 0; 743 | } 744 | --------------------------------------------------------------------------------