From 06c1a277fddb8c0c7c1c0e333303318a696026bd Mon Sep 17 00:00:00 2001 From: yangyanjun Date: Tue, 6 Sep 2022 10:05:05 +0800 Subject: [PATCH 1/2] =?UTF-8?q?newip=E4=BB=93=E6=96=B0=E5=A2=9Emd=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: yangyanjun --- README.en.md | 36 ---- README.md | 39 ---- README_zh.md | 270 ++++++++++++++++++++++++++++ figures/image-20220901152326770.png | Bin 0 -> 14905 bytes figures/image-20220901152539801.png | Bin 0 -> 19225 bytes figures/image-20220901155226078.png | Bin 0 -> 5330 bytes 6 files changed, 270 insertions(+), 75 deletions(-) delete mode 100644 README.en.md delete mode 100644 README.md create mode 100644 README_zh.md create mode 100644 figures/image-20220901152326770.png create mode 100644 figures/image-20220901152539801.png create mode 100644 figures/image-20220901155226078.png diff --git a/README.en.md b/README.en.md deleted file mode 100644 index 34ee340..0000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# communication_sfc_newip - -#### Description -{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**} - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md deleted file mode 100644 index edec071..0000000 --- a/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# communication_sfc_newip - -#### 介绍 -{**以下是 Gitee 平台说明,您可以替换此简介** -Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台 -无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)} - -#### 软件架构 -软件架构说明 - - -#### 安装教程 - -1. xxxx -2. xxxx -3. xxxx - -#### 使用说明 - -1. xxxx -2. xxxx -3. xxxx - -#### 参与贡献 - -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request - - -#### 特技 - -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README_zh.md b/README_zh.md new file mode 100644 index 0000000..36e5e7b --- /dev/null +++ b/README_zh.md @@ -0,0 +1,270 @@ +# NewIP内核协议栈 + +## 简介 + +NewIP支持可变长多语义地址,可变长定制化报头封装,通过精简报文头开销,提升数据传输效率。 + +``` +备注: +IPv4地址长度固定4字节,IPv6地址长度固定16字节。 +IPv4网络层报头长度20~60字节,IPv6网络层报头长度40字节。 +``` + +NewIP灵活极简报文头如下图所示,通过LLC Header中的EtherType = 0xEADD标识。Bitmap是一组由0和1组成的二进制序列,每个二进制位的数值用于表示特定目标特性的存在性。 + +![image-20220901152326770](figures/image-20220901152326770.png) + +NewIP内核协议栈架构图如下,用户态调用Socket API创建NewIP socket,采用NewIP极简帧头封装进行收发包。 + +![image-20220901152539801](figures/image-20220901152539801.png) + + + +## 目录 + +NewIP内核协议栈主要代码目录结构如下: + +``` +/foundation/communication/sfc/newip +├── patches +│ └── linux-5.10 # NewIP内核侵入式修改对应的补丁文件 +└── code + ├── common # NewIP 通用代码 + └── linux # NewIP Linux内核代码 + ├── include # NewIP 头文件 + │ ├── linux + │ ├── net + │ └── uapi + └── net + └── newip # NewIP 功能代码 +``` + +## 编译构建 + +NewIP内核协议栈使能,需要在对应款型的内核defconfig文件中设置CONFIG_NEWIP=y,删除out/kernel目录后重新编译。 + +kernel\linux\config\linux-5.10\arch\arm64\configs\rk3568_standard_defconfig + +```c +CONFIG_NEWIP=y +``` + +代码编译完成后,通过下面命令可以确认newip协议栈代码是否使能成功。 + +```c +root@nip-server:~/harmony_master/harmony# find out/ -name *nip*.o +out/rk3568/obj/third_party/glib/glib/glib_source/guniprop.o +out/kernel/OBJ/linux-5.10/net/newip/nip_addrconf_core.o +out/kernel/OBJ/linux-5.10/net/newip/nip_hdr_decap.o +out/kernel/OBJ/linux-5.10/net/newip/nip_addr.o +out/kernel/OBJ/linux-5.10/net/newip/nip_checksum.o +out/kernel/OBJ/linux-5.10/net/newip/tcp_nip_output.o +... +``` + +禁用NewIP内核协议栈,删除CONFIG_NEWIP使能开关,删除out/kernel目录后重新编译。 + +```c +# CONFIG_NEWIP is not set +``` + +## 说明 + +### 报头字段说明 + +NewIP报文格式由NewIP报头与Payload两部分组成,如下图所示: + +![image-20220901155226078](figures/image-20220901155226078.png) + +1) Dispatch:指示封装子类,数值0b0表示其为极简封装子类,长度为1比特;(0b表示后面数值为二进制)。 + +2) Bitmap:变长,Bitmap默认为紧跟在Dispatch有效位后面的7比特,Bitmap字段长度可持续扩展。Bitmap最后一位置0表示Bitmap结束,最后一位置1表示Bitmap向后扩展1 Byte,直至最后一位置0。 +2) Value: 标识字段的值,长度为1 Byte的整数倍,类型及长度由报头字段语义表确定。 + + + +### 可变长地址格式说明 + +NewIP采用自解释编码,编码格式如下所示: + +| First Byte | Semantics | 地址段有效范围 | +| ---------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| 0x00 | Address is 0 | 【1字节】0 ~ 220 (0x00 ~ 0xDC) | +| 0x01 | Address is 1 | | +| 0x02 | Address is 2 | | +| ... | ... | | +| 0xDC | Address is 220 | | +| 0xDD | An 16-bit address, which is 0 + 256 * (0xDD - 0xDD) + the last byte value | 【2字节】221 ~ 255 (0x**DD**DD ~ 0x**DD**FF) | +| 0xDE | An 16-bit address, which is 0 + 256 * (0xDE - 0xDD) + the last byte value | 【2字节】256 ~ 511 (0x**DE**00 ~ 0x**DE**FF) | +| 0xDF | An 16-bit address, which is 0 + 256 * (0xDF - 0xDD) + the last byte value | 【2字节】512 ~ 767 (0x**DF**00 ~ 0x**DF**FF) | +| | | | +| ... | ... | | +| 0xF0 | An 16-bit address, which is 0 + 256 * (0xF0 - 0xDD) + the last byte value | 【2字节】4864 ~ 5119 (0x**F0**00 ~ 0x**F0**FF) | +| 0xF1 | An 16-bit address is followed | 【3字节】5120 ~ 65535 (0x**F1** 1400 ~ 0x**F1** FFFF) | +| 0xF2 | An 32-bit address is followed | 【5字节】65536 ~ 4,294,967,295 (0x**F2** 0001 0000 ~ 0x**F2** FFFF FFFF) | +| 0xF3 | An 48-bit address is followed | 【7字节】4,294,967,296 ~ 281,474,976,710,655 (0x**F3** 0001 0000 0000 ~ 0x**F3** FFFF FFFF FFFF) | +| 0xFE | An 56-bit address is followed | 【8字节】0 ~ 72,057,594,037,927,935 (0x**FE**00 0000 0000 0000 ~ 0x**FE**FF FFFF FFFF FFFF) | + +### 接口说明 + +NewIP短地址结构如下: + +```c +enum nip_8bit_addr_index { + NIP_8BIT_ADDR_INDEX_0 = 0, + NIP_8BIT_ADDR_INDEX_1 = 1, + NIP_8BIT_ADDR_INDEX_2 = 2, + NIP_8BIT_ADDR_INDEX_3 = 3, + NIP_8BIT_ADDR_INDEX_4 = 4, + NIP_8BIT_ADDR_INDEX_5 = 5, + NIP_8BIT_ADDR_INDEX_6 = 6, + NIP_8BIT_ADDR_INDEX_7 = 7, + NIP_8BIT_ADDR_INDEX_MAX, +}; + +enum nip_16bit_addr_index { + NIP_16BIT_ADDR_INDEX_0 = 0, + NIP_16BIT_ADDR_INDEX_1 = 1, + NIP_16BIT_ADDR_INDEX_2 = 2, + NIP_16BIT_ADDR_INDEX_3 = 3, + NIP_16BIT_ADDR_INDEX_MAX, +}; + +enum nip_32bit_addr_index { + NIP_32BIT_ADDR_INDEX_0 = 0, + NIP_32BIT_ADDR_INDEX_1 = 1, + NIP_32BIT_ADDR_INDEX_MAX, +}; + +#define nip_addr_field8 v.u.field8 +#define nip_addr_field16 v.u.field16 +#define nip_addr_field32 v.u.field32 + +#pragma pack(1) +struct nip_addr_field { + union { + unsigned char field8[NIP_8BIT_ADDR_INDEX_MAX]; + unsigned short field16[NIP_16BIT_ADDR_INDEX_MAX]; /* big-endian */ + unsigned int field32[NIP_32BIT_ADDR_INDEX_MAX]; /* big-endian */ + } u; +}; + +struct nip_addr { + unsigned char bitlen; /* The address length is in bit (not byte) */ + struct nip_addr_field v; +}; +#pragma pack() + +/* The following structure must be larger than V4. System calls use V4. + * If the definition is smaller than V4, the read process will have memory overruns + * v4: include\linux\socket.h --> sockaddr (16Byte) + */ +#define POD_SOCKADDR_SIZE 3 +struct sockaddr_nin { + unsigned short sin_family; /* [2Byte] AF_NINET */ + unsigned short sin_port; /* [2Byte] Transport layer port, big-endian */ + struct nip_addr sin_addr; /* [9Byte] NIP address */ + + unsigned char sin_zero[POD_SOCKADDR_SIZE]; /* [3Byte] Byte alignment */ +}; +``` + + + +NewIP协议socket接口列表如下: + +| 函数 | 输入 | 输出 | 返回值 | 接口具体描述 | +| -------- | ------------------------------------------------------------ | ---------------------------------------------- | ---------------- | ------------------------------------------------------------ | +| socket | int **domain**, int type, int **protocol** | NA | Socket句柄sockfd | 创建NewIP 协议类型socket,并返回socket实例所对应的句柄。**domain参数填写 AF_NINET,表示创建NewIP协议类型socket。protocol参数填写IPPROTO_TCP或IPPROTO_UDP**。 | +| bind | int sockfd, const **struct sockaddr_nin** *myaddr, socklen_t addrlen | NA | int,返回错误码 | 将创建的socket绑定到指定的IP地址和端口上。**myaddr->sin_family填写AF_NINET**。 | +| listen | int socket, int backlog | NA | int,返回错误码 | 服务端监听NewIP地址和端口 | +| connect | int sockfd, const **struct sockaddr_nin** *addr, aocklen_t addrlen | NA | int,返回错误码 | 客户端创建至服务端的连接 | +| accept | int sockfd, **struct sockaddr_nin** *address, socklen_t *address_len | NA | 返回socket的fd | 服务端返回已建链成功的socket | +| send | int sockfd, const void *msg, int len, unsigned int flags, const **struct sockaddr_nin** *dst_addr, int addrlen | NA | int,返回错误码 | 用于socket已连接的NewIP类型数据发送 | +| recv | int sockfd, size_t len, int flags, **struct sockaddr_nin** *src_addr, | void **buf, int* *fromlen | int,返回错误码 | 用于socket已连接的NewIP类型数据接收 | +| close | int sockfd | NA | int,返回错误码 | 关闭socket,释放资源 | +| ioctl | int sockfd, unsigned long cmd, ... | NA | int,返回错误码 | 对NewIP协议栈相关信息进行查询或更改。 | +| sendto | int sockfd, const void *msg, int len, unsigned int flags, const **struct sockaddr** *dst_addr, int addrlen | NA | int,返回错误码 | 用于socket无连接的NewIP类型数据发送 | +| recvfrom | int sockfd, size_t len, int flags, | void *buf, struct sockaddr *from, int *fromlen | int,返回错误码 | 用于socket无连接的NewIP类型数据接收 | + +### 使用说明 + +NewIP可变长地址配置,路由配置,UDP/TCP收发包demo代码链接如下,NewIP协议栈用户态接口使用方法可以参考demo代码。 + +https://gitee.com/openharmony-sig/communication_sfc_newip/tree/master/examples + +| 文件名 | 功能 | +| --------------------- | ----------------------------- | +| nip_addr_cfg_demo.c | NewIP可变长地址配置demo代码 | +| nip_route_cfg_demo.c | NewIP路由配置demo代码 | +| nip_udp_server_demo.c | NewIP UDP收发包服务端demo代码 | +| nip_udp_client_demo.c | NewIP UDP收发包客户端demo代码 | +| nip_tcp_server_demo.c | NewIP TCP收发包服务端demo代码 | +| nip_tcp_client_demo.c | NewIP TCP收发包客户端demo代码 | +| nip_lib.c | 接口索引获取等API接口demo代码 | + +**基础操作步骤:** + +1、将demo代码拷贝到Linux编译机上,make clean,make all编译demo代码。 + +2、将编译生成二级制文件上传到xxx开发板。 + +3、执行“ifconfig xxx up”开启网卡设备,xxx表示网卡名,比如eth0,wlan0。 + +4、在1号开发板sh下执行“./nip_addr_cfg_demo server”给服务端配置0xDE00(2字节)变长地址,在2号开发板sh下执行“./nip_addr_cfg_demo client”给客户端配置0x50(1字节)变长地址,通过“cat /proc/net/nip_addr”查看内核地址配置结果。 + +5、在1号开发板sh下执行“./nip_route_cfg_demo server”给服务端配置路由,在2号开发板sh下执行“./nip_route_cfg_demo client”给客户端配置路由,通过“cat /proc/net/nip_route”查看内核路由配置结果。 + +以上步骤操作完成后,可以进行UDP/TCP收发包,收发包demo默认使用上面步骤中配置的地址和路由。 + + + +**UDP收发包操作步骤:** + +先在服务端执行“./nip_udp_server_demo”,然后再在客户端执行“./nip_udp_client_demo”,客户端会发送10个NewIP报文,客户端收到报文后再发送给服务端。 + +``` +服务端sh窗口打印内容: +Received -- 1661826989 498038 NIP_UDP # 0 -- from 0x50:57605 +Sending -- 1661826989 498038 NIP_UDP # 0 -- to 0x50:57605 +Received -- 1661826990 14641 NIP_UDP # 1 -- from 0x50:57605 +Sending -- 1661826990 14641 NIP_UDP # 1 -- to 0x50:57605 +Received -- 1661826990 518388 NIP_UDP # 2 -- from 0x50:57605 +Sending -- 1661826990 518388 NIP_UDP # 2 -- to 0x50:57605 +... +Received -- 1661827011 590576 NIP_UDP # 9 -- from 0x50:37758 +Sending -- 1661827011 590576 NIP_UDP # 9 -- to 0x50:37758 + +客户端sh窗口打印内容: +Received --1661827007 55221 NIP_UDP # 0 sock 3 success: 1/ 1/no= 0 +Received --1661827007 557926 NIP_UDP # 1 sock 3 success: 2/ 2/no= 1 +Received --1661827008 62653 NIP_UDP # 2 sock 3 success: 3/ 3/no= 2 +... +Received --1661827011 590576 NIP_UDP # 9 sock 3 success: 10/ 10/no= 9 +``` + + + +**TCP收发包操作步骤:** + +先在服务端执行“./nip_tcp_server_demo”,然后再在客户端执行“./nip_tcp_client_demo”,客户端会发送10个NewIP报文,客户端收到报文后再发送给服务端。 + +``` +服务端sh窗口打印内容: +Received -- 1661760202 560605 NIP_TCP # 0 --:1024 +Sending -- 1661760202 560605 NIP_TCP # 0 --:1024 +Received -- 1661760203 69254 NIP_TCP # 1 --:1024 +Sending -- 1661760203 69254 NIP_TCP # 1 --:1024 +Received -- 1661760203 571604 NIP_TCP # 2 --:1024 +Sending -- 1661760203 571604 NIP_TCP # 2 --:1024 +... +Received -- 1661760207 86544 NIP_TCP # 9 --:1024 +Sending -- 1661760207 86544 NIP_TCP # 9 --:1024 + +客户端sh窗口打印内容: +Received --1661760202 560605 NIP_TCP # 0 sock 3 success: 1/ 1/no= 0 +Received --1661760203 69254 NIP_TCP # 1 sock 3 success: 2/ 2/no= 1 +... +Received --1661760207 86544 NIP_TCP # 9 sock 3 success: 10/ 10/no= 9 +``` + diff --git a/figures/image-20220901152326770.png b/figures/image-20220901152326770.png new file mode 100644 index 0000000000000000000000000000000000000000..37823919b253df5831f619fdca0201836e5955a5 GIT binary patch literal 14905 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 Date: Wed, 7 Sep 2022 11:15:47 +0800 Subject: [PATCH 2/2] =?UTF-8?q?newip=E5=BF=AB=E9=80=9Fkeepalive=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E4=BD=BF=E8=83=BD=E5=BC=80=E5=85=B3=EF=BC=8C=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=8C=89=E4=BA=A7=E5=93=81=E5=AE=9A=E5=88=B6=E3=80=82?= =?UTF-8?q?demo=E4=BB=A3=E7=A0=81=E5=BC=80=E6=BA=90license=E9=97=AE?= =?UTF-8?q?=E9=A2=98=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: yangyanjun --- code/linux/net/newip/Kconfig | 9 +++++++++ code/linux/net/newip/tcp_nip.c | 6 ++++++ examples/nip_lib.h | 15 ++++++++++----- examples/nip_uapi.h | 34 ++-------------------------------- 4 files changed, 27 insertions(+), 37 deletions(-) diff --git a/code/linux/net/newip/Kconfig b/code/linux/net/newip/Kconfig index 85b1982..06cbebb 100644 --- a/code/linux/net/newip/Kconfig +++ b/code/linux/net/newip/Kconfig @@ -14,3 +14,12 @@ menuconfig NEWIP To compile this protocol support as a module, choose M here: the module will be called NewIP. + +if NEWIP +config NEWIP_FAST_KEEPALIVE + tristate "NewIP fast keepalive" + default n + help + Support for NewIP fast keepalive. +endif # NEWIP + diff --git a/code/linux/net/newip/tcp_nip.c b/code/linux/net/newip/tcp_nip.c index 5d1aba3..3d27c37 100644 --- a/code/linux/net/newip/tcp_nip.c +++ b/code/linux/net/newip/tcp_nip.c @@ -623,6 +623,7 @@ static const struct inet_connection_sock_af_ops newip_specific = { .mtu_reduced = NULL, }; +#if IS_ENABLED(CONFIG_NEWIP_FAST_KEEPALIVE) #define MAX_NIP_TCP_KEEPIDLE 32767 #define MAX_NIP_TCP_KEEPINTVL 32767 #define MAX_NIP_TCP_KEEPCNT 255 @@ -679,11 +680,13 @@ static int tcp_nip_keepalive_para_update(struct sock *sk, return 0; } +#endif #define NIP_PKT_TOTAL_LEN_BOUNDARY 100000 // 100K #define NIP_KEEPALIVE_PROBES 255 void tcp_nip_keepalive_enable(struct sock *sk) { +#if IS_ENABLED(CONFIG_NEWIP_FAST_KEEPALIVE) int ret; struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb = tcp_nip_send_head(sk); @@ -734,10 +737,12 @@ void tcp_nip_keepalive_enable(struct sock *sk) __func__, HZ, tp->keepalive_time, tp->keepalive_probes, tp->keepalive_intvl); tp->nip_keepalive_enable = true; +#endif } void tcp_nip_keepalive_disable(struct sock *sk) { +#if IS_ENABLED(CONFIG_NEWIP_FAST_KEEPALIVE) struct tcp_sock *tp = tcp_sk(sk); if (!tp->nip_keepalive_enable) @@ -776,6 +781,7 @@ void tcp_nip_keepalive_disable(struct sock *sk) DEBUG("%s ok, HZ=%u, idle_ka_probes_out=%u", __func__, HZ, g_nip_idle_ka_probes_out); tp->nip_keepalive_enable = false; +#endif } static void tcp_nip_rtt_init(struct sock *sk) diff --git a/examples/nip_lib.h b/examples/nip_lib.h index d52e5f8..3a38b93 100644 --- a/examples/nip_lib.h +++ b/examples/nip_lib.h @@ -27,17 +27,22 @@ #ifndef _NIP_LIB_H #define _NIP_LIB_H +/* AF_NINET by reading/sys/module/newip/parameters/af_ninet file to get the type value */ +#define AF_NINET 45 + +#define DEMO_INPUT_1 2 /* The DEMO program contains one parameter */ + /* Eth0 and wlan0 are optional. Change the value based on the actual interface */ #define NIC_NAME "eth0" #define BUFLEN 2048 #define LISTEN_MAX 3 -#define PKTCNT 10 // Number of sent packets -#define PKTLEN 1024 // Length of sent packet -#define SLEEP_US 500000 // Packet sending interval (ms) +#define PKTCNT 10 /* Number of sent packets */ +#define PKTLEN 1024 /* Length of sent packet */ +#define SLEEP_US 500000 /* Packet sending interval (ms) */ #define SELECT_TIME 600 -#define TCP_SERVER_PORT 5556 // TCP Server Port -#define UDP_SERVER_PORT 9090 // UDP Server Port +#define TCP_SERVER_PORT 5556 /* TCP Server Port */ +#define UDP_SERVER_PORT 9090 /* UDP Server Port */ int nip_get_ifindex(const char *ifname, int *ifindex); diff --git a/examples/nip_uapi.h b/examples/nip_uapi.h index 6475ff3..150c107 100644 --- a/examples/nip_uapi.h +++ b/examples/nip_uapi.h @@ -1,40 +1,10 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (c) 2022 Huawei Device Co., Ltd. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* Copyright (c) 2022 Huawei Device Co., Ltd. */ #ifndef _NIP_UAPI_H #define _NIP_UAPI_H #include "nip.h" -/* AF_NINET by reading/sys/module/newip/parameters/AF_NINET file to get the type value */ -#define PF_NINET 45 -#define AF_NINET PF_NINET - -#define DEMO_INPUT_1 2 // The DEMO program contains one parameter - /* The following structure must be larger than V4. System calls use V4. * If the definition is smaller than V4, the read process will have memory overruns * v4: include\linux\socket.h --> sockaddr (16Byte) -- Gitee