Merge #2214 into sdk650pathc0423 from refs/changes/2214/head 2226/target/1
authorsnickersun@163.com <accounts_62b90f7fcf51ade33b6f628e@mail.teambition.com>
Thu, 11 Sep 2025 06:48:16 +0000 (06:48 +0000)
committer袁清梦 <3449750381@qq.com>
Thu, 11 Sep 2025 06:48:16 +0000 (06:48 +0000)
Code review from snickersun@163.com

* refs/changes/2214/head: (5 commits squashed)

  - 给雷鸟添加ppp工程 ,修复4g 不能开电问题

  - Merge branch &#39;sdk650pathc0423&#39; of codeup.aliyun.com:64191456b9581d62e8f81ac0/FD230 into sdk650pathc0423

  - 雷鸟项目 给lwip 添加 apps, ppp, 完善测试代码

  - Merge branch &#39;sdk650pathc0423&#39; of codeup.aliyun.com:64191456b9581d62e8f81ac0/FD230 into sdk650pathc0423

  - Merge branch &#39;sdk650pathc0423&#39; of codeup.aliyun.com:64191456b9581d62e8f81ac0/FD230 into sdk650pathc0423

Signed-off-by: snickersun@163.com <accounts_62b90f7fcf51ade33b6f628e@mail.teambition.com>
Merged-by: Caisking <3449750381@qq.com>
link: https://codeup.aliyun.com/64191456b9581d62e8f81ac0/FD230/change/2214
141 files changed:
code/sdk/application/brandy/brandy_standard/service_init.c
code/sdk/application/wearable/fd230/nativeapp/nativeui/networkandcall/include/NetworkAndCallC.h
code/sdk/application/wearable/ln/service/alipay_msg/alipay_app.cpp
code/sdk/build/config/target_config/brandy/config.py
code/sdk/open_source/openharmony/third_party/lwip/CMakeLists.txt
code/sdk/open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/arch/sio.c
code/sdk/open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/arch/sys_arch.c
code/sdk/open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/include/lwip/lwipopts_default.h
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/FILES [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/Filelists.cmake [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/Filelists.mk [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/altcp_tls/altcp_tls_mbedtls.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/altcp_tls/altcp_tls_mbedtls_mem.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/altcp_tls/altcp_tls_mbedtls_mem.h [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/altcp_tls/altcp_tls_mbedtls_structs.h [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/altcp_proxyconnect.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/fs.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/fs/404.html [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/fs/img/sics.gif [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/fs/index.html [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/fsdata.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/fsdata.h [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/http_client.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/httpd.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/httpd_structs.h [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/makefsdata/makefsdata [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/makefsdata/makefsdata.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/makefsdata/readme.txt [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/makefsdata/tinydir.h [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/lwiperf/lwiperf.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/mdns/mdns.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/mqtt/mqtt.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/netbiosns/netbiosns.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/smtp/smtp.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_asn1.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_asn1.h [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_core.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_core_priv.h [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_mib2.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_mib2_icmp.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_mib2_interfaces.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_mib2_ip.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_mib2_snmp.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_mib2_system.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_mib2_tcp.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_mib2_udp.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_msg.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_msg.h [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_netconn.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_pbuf_stream.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_pbuf_stream.h [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_raw.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_scalar.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_snmpv2_framework.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_snmpv2_usm.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_table.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_threadsync.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_traps.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmpv3.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmpv3_mbedtls.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmpv3_priv.h [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/sntp/sntp.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/tftp/tftp_server.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/core/init.c
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/FILES [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/bridgeif.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/bridgeif_fdb.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/lowpan6_ble.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/PPPD_FOLLOWUP [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/auth.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/ccp.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/chap-md5.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/chap-new.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/chap_ms.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/demand.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/eap.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/ecp.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/eui64.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/fsm.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/ipcp.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/ipv6cp.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/lcp.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/magic.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/mppe.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/multilink.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/polarssl/README [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/polarssl/arc4.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/polarssl/des.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/polarssl/md4.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/polarssl/md5.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/polarssl/sha1.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/ppp.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/pppapi.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/pppcrypt.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/pppoe.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/pppol2tp.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/pppos.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/upap.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/utils.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/vj.c [new file with mode: 0644]
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/zepif.c [new file with mode: 0644]
code/sdk/zhihui/4g/README_SLIP.md [new file with mode: 0644]
code/sdk/zhihui/4g/cat1Service/CMakeLists.txt
code/sdk/zhihui/4g/cat1Service/Service/cat1_ctl.c
code/sdk/zhihui/4g/cat1Service/Service/cat1_utils.c
code/sdk/zhihui/4g/cat1Service/include/cat1_ctl.h
code/sdk/zhihui/4g/verify_slip_config.sh [new file with mode: 0644]
code/sdk/zhihui/CMakeLists.txt
code/sdk/zhihui/SERIAL_PROTOCOL_CONFIG.md [new file with mode: 0644]
code/sdk/zhihui/ppp/CMakeLists.txt [new file with mode: 0644]
code/sdk/zhihui/ppp/README.md [new file with mode: 0644]
code/sdk/zhihui/ppp/README_CAT1_COMPATIBILITY.md [new file with mode: 0644]
code/sdk/zhihui/ppp/README_POWER_CONTROL.md [new file with mode: 0644]
code/sdk/zhihui/ppp/build_test.sh [new file with mode: 0644]
code/sdk/zhihui/ppp/doc/PPP拨号说明.txt [new file with mode: 0644]
code/sdk/zhihui/ppp/doc/chat [new file with mode: 0644]
code/sdk/zhihui/ppp/doc/gprs-dial [new file with mode: 0644]
code/sdk/zhihui/ppp/example/api_shell.c [new file with mode: 0644]
code/sdk/zhihui/ppp/example/ppp_at_cmd.c [new file with mode: 0644]
code/sdk/zhihui/ppp/example/ppp_example.c [new file with mode: 0644]
code/sdk/zhihui/ppp/example/ppp_power_example.c [new file with mode: 0644]
code/sdk/zhihui/ppp/include/api_shell.h [new file with mode: 0644]
code/sdk/zhihui/ppp/include/ppp_api.h [new file with mode: 0644]
code/sdk/zhihui/ppp/include/ppp_at_cmd.h [new file with mode: 0644]
code/sdk/zhihui/ppp/include/ppp_at_handler.h [new file with mode: 0644]
code/sdk/zhihui/ppp/include/ppp_cat1_api.h [new file with mode: 0644]
code/sdk/zhihui/ppp/include/ppp_config.h [new file with mode: 0644]
code/sdk/zhihui/ppp/include/ppp_debug.h [new file with mode: 0644]
code/sdk/zhihui/ppp/include/ppp_log.h [new file with mode: 0644]
code/sdk/zhihui/ppp/include/ppp_lwip.h [new file with mode: 0644]
code/sdk/zhihui/ppp/include/ppp_power_ctl.h [new file with mode: 0644]
code/sdk/zhihui/ppp/include/ppp_serial.h [new file with mode: 0644]
code/sdk/zhihui/ppp/include/ppp_types.h [new file with mode: 0644]
code/sdk/zhihui/ppp/src/ppp_api.c [new file with mode: 0644]
code/sdk/zhihui/ppp/src/ppp_at_handler.c [new file with mode: 0644]
code/sdk/zhihui/ppp/src/ppp_cat1_api.c [new file with mode: 0644]
code/sdk/zhihui/ppp/src/ppp_debug.c [new file with mode: 0644]
code/sdk/zhihui/ppp/src/ppp_lwip.c [new file with mode: 0644]
code/sdk/zhihui/ppp/src/ppp_power_ctl.c [new file with mode: 0644]
code/sdk/zhihui/ppp/src/ppp_serial.c [new file with mode: 0644]
code/sdk/zhihui/utils/custom_utils_at_cmd.c

index 5f6ea91..c507d39 100644 (file)
@@ -71,6 +71,9 @@
 #ifdef SUPPORT_AT_CAT1\r
 #include "cat1_at_cmd.h"\r
 #endif\r
+#ifdef SUPPORT_AT_PPP\r
+#include "ppp_at_cmd.h"\r
+#endif\r
 #include "log_file.h"\r
 #ifdef CONFIG_OTA_UPDATE_SUPPORT\r
 #include "upg.h"\r
@@ -174,6 +177,12 @@ static void register_at_service(void)
     at_cmd_add_moudle(cat1_table, cat1_cmd_count);\r
 #endif\r
 \r
+#ifdef SUPPORT_AT_PPP\r
+    uint32_t ppp_cmd_count;\r
+    at_cmd_table_t *ppp_table = get_ppp_cmd_table(&ppp_cmd_count);\r
+    at_cmd_add_moudle(ppp_table, ppp_cmd_count);\r
+#endif\r
+\r
 #ifdef SUPPORT_ZHIHUI_AI_OLD\r
     uint32_t ai_old_cmd_count;\r
     at_cmd_table_t *ai_old_table = get_ai_old_cmd_table(&ai_old_cmd_count);\r
index b9778fe..339db1e 100644 (file)
@@ -17,8 +17,8 @@ extern "C"
 #include <stdint.h>
 #include <stdbool.h>
 
-    void power_on_cat1(void);
-    void power_off_cat1(void);
+    void cat1_api_power_on_cat1(void);
+    void cat1_api_power_off_cat1(void);
 
 #ifdef __cplusplus
 #if __cplusplus
index a51e1c4..00ee5ed 100644 (file)
-/*\r
- * Copyright (c) CompanyNameMagicTag 2021-2021. All rights reserved.\r
- * Description: broadcast feature implement\r
- * Author: CompanyName\r
- * Create: 2021-11-11\r
- */\r
-\r
-#include "common_def.h"\r
-#include "msg_center.h"\r
-#include <stdio.h>\r
-#include "adapter.h"\r
-#include "msg_center_protocol.h"\r
-#include "msg_center_cmd.h"\r
-#include "wearable_log.h"\r
-#include "alipay_app.h"\r
-#include "../../alipay/driver_adapt/vendor_net.h"\r
-#include "cmsis_os2.h"\r
-#include "sys/time.h"\r
-#include "time.h"\r
-\r
-#ifdef __cplusplus\r
-extern "C" {\r
-#endif\r
-\r
-#define HOSTENT_TIMEOUT 2000\r
-#define TCP_CONNECT_TIMEOUT 2000\r
-#define IPV4_LEN 4\r
-\r
-osSemaphoreId_t g_alipay_hostent_sem = NULL;\r
-osSemaphoreId_t g_alipay_tcp_sem = NULL;\r
-unsigned char g_alipay_ip[IPV4_LEN] = { 0 };\r
-\r
-uint8_t *g_tcp_data = NULL;\r
-uint16_t g_tcp_data_len = 0;\r
-uint16_t g_tcp_data_readed = 0;\r
-uint32_t g_tcp_fd = 0;\r
-uint8_t g_netstatus = 0;\r
-\r
-void msg_center_alipay_reply_ack(uint8_t cmd_id, uint8_t type)\r
-{\r
-    uint8_t ack = 1; /* 与手机APK侧约定 */\r
-    int ret = msg_center_send_data(cmd_id, type, &ack, sizeof(ack));\r
-    WEARABLE_LOGD(WEARABLE_LOG_MODULE_MSG_CENTER, "send replay ret = 0x%x\r\n", ret);\r
-}\r
-\r
-errcode_t msg_center_alipay_network_status(uint8_t cmd_id, uint8_t type, void *usr_data, uint16_t usr_len)\r
-{\r
-    uint8_t tl_len = msg_center_get_tlv_tl_len(usr_data);\r
-    uint16_t payload_len = usr_len - tl_len;\r
-    uint8_t *tlv_payload = (uint8_t *)msg_center_get_tlv_payload(usr_data);\r
-\r
-    g_netstatus = *((uint8_t *)tlv_payload);\r
-    WEARABLE_LOGD(WEARABLE_LOG_MODULE_MSG_CENTER, "msg_center_alipay netstatus = %d\r\n", g_netstatus);\r
-    msg_center_alipay_reply_ack(cmd_id, type);\r
-    return 0;\r
-}\r
-\r
-uint8_t msg_center_alipay_get_network_status(void)\r
-{\r
-    return g_netstatus;\r
-}\r
-\r
-int msg_center_alipay_hostent_request(void *usr_data, uint16_t usr_len)\r
-{\r
-    int ret;\r
-    if (g_alipay_hostent_sem == NULL) {\r
-        g_alipay_hostent_sem = osSemaphoreNew(1, 0, NULL);\r
-    }\r
-\r
-    ret = msg_center_send_data(MSGCENTER_CMD_ALIPAY, MSGCENTER_TYPE_ID_ALIPAY_HOST_INFO, usr_data, usr_len);\r
-    if (ret != ERRCODE_SUCC) {\r
-        return -1;\r
-    } else {\r
-        return 0;\r
-    }\r
-}\r
-\r
-errcode_t msg_center_alipay_hostent_recv(uint8_t cmd_id, uint8_t type, void *usr_data, uint16_t usr_len)\r
-{\r
-    uint8_t tl_len = msg_center_get_tlv_tl_len(usr_data);\r
-    uint16_t payload_len = usr_len - tl_len;\r
-    uint8_t *tlv_payload = (uint8_t *)msg_center_get_tlv_payload(usr_data);\r
-\r
-    (void)memcpy_s(g_alipay_ip, IPV4_LEN, tlv_payload, payload_len);\r
-    WEARABLE_LOGI(WEARABLE_LOG_MODULE_MSG_CENTER, "recv ip info--> %d.%d.%d.%d\r\n",\r
-                  g_alipay_ip[0], g_alipay_ip[1], g_alipay_ip[2], g_alipay_ip[3]);\r
-    osSemaphoreRelease(g_alipay_hostent_sem);\r
-    return ERRCODE_SUCC;\r
-}\r
-\r
-int msg_center_alipay_hostent_reponse(unsigned char *ip)\r
-{\r
-    int ret;\r
-    osStatus_t status = osSemaphoreAcquire(g_alipay_hostent_sem, HOSTENT_TIMEOUT);\r
-    if (status == osOK) {\r
-        (void)memcpy_s(ip, IPV4_LEN, g_alipay_ip, IPV4_LEN);\r
-        ret = 0;\r
-    } else {\r
-        ret = -1;\r
-    }\r
-    WEARABLE_LOGD(WEARABLE_LOG_MODULE_MSG_CENTER, "hostent wait status = 0x%x\r\n", status);\r
-    osSemaphoreDelete(g_alipay_hostent_sem);\r
-    g_alipay_hostent_sem = NULL;\r
-    return ret;\r
-}\r
-\r
-int msg_center_alipay_connect_request(void *usr_data, uint16_t usr_len)\r
-{\r
-    int ret;\r
-    if (g_alipay_tcp_sem == NULL) {\r
-        g_alipay_tcp_sem = osSemaphoreNew(1, 0, NULL);\r
-    }\r
-    ret = msg_center_send_data(MSGCENTER_CMD_ALIPAY, MSGCENTER_TYPE_ID_ALIPAY_CONNECT, usr_data, usr_len);\r
-    if (ret != ERRCODE_SUCC) {\r
-        return -1;\r
-    } else {\r
-        return 0;\r
-    }\r
-}\r
-\r
-errcode_t msg_center_alipay_connect_recv(uint8_t cmd_id, uint8_t type, void *usr_data, uint16_t usr_len)\r
-{\r
-    uint8_t tl_len = msg_center_get_tlv_tl_len(usr_data);\r
-    uint16_t payload_len = usr_len - tl_len;\r
-    uint8_t *tlv_payload = (uint8_t *)msg_center_get_tlv_payload(usr_data);\r
-\r
-    alipay_connect_reponse rep;\r
-    (void)memcpy_s(&rep, sizeof(alipay_connect_reponse), tlv_payload, payload_len);\r
-    WEARABLE_LOGI(WEARABLE_LOG_MODULE_MSG_CENTER, "recv alipay_connect_reponse info--> 0x%x 0x%x\r\n",\r
-                  rep.ret, rep.fd);\r
-    g_tcp_fd = rep.fd;\r
-    osSemaphoreRelease(g_alipay_tcp_sem);\r
-    return ERRCODE_SUCC;\r
-}\r
-\r
-int msg_center_alipay_connect_reponse(void)\r
-{\r
-    int ret;\r
-    osStatus_t status = osSemaphoreAcquire(g_alipay_tcp_sem, TCP_CONNECT_TIMEOUT);\r
-    if (status == osOK) {\r
-        ret = (g_tcp_fd > 0) ? 0 : -1;\r
-    } else {\r
-        ret = -1;\r
-    }\r
-    WEARABLE_LOGD(WEARABLE_LOG_MODULE_MSG_CENTER, "connect wait status = 0x%x\r\n", status);\r
-    return ret;\r
-}\r
-\r
-int msg_center_alipay_tcpdata_request(void *usr_data, uint16_t usr_len)\r
-{\r
-    WEARABLE_LOGD(WEARABLE_LOG_MODULE_MSG_CENTER, "tcpdata send len = 0x%x\r\n", usr_len);\r
-    int ret;\r
-    ret = msg_center_send_data(MSGCENTER_CMD_ALIPAY, MSGCENTER_TYPE_ID_ALIPAY_TCPDATA_RECV, usr_data, usr_len);\r
-    if (ret != ERRCODE_SUCC) {\r
-        return -1;\r
-    } else {\r
-        return (int)usr_len;\r
-    }\r
-}\r
-\r
-int msg_center_alipay_tcpdata_wait(uint32_t timeout)\r
-{\r
-    int ret;\r
-    osStatus_t status = osSemaphoreAcquire(g_alipay_tcp_sem, timeout);\r
-    if (status == osOK) {\r
-        ret = 1;\r
-    } else {\r
-        ret = 0;\r
-    }\r
-    WEARABLE_LOGD(WEARABLE_LOG_MODULE_MSG_CENTER, "tcpdata wait status = 0x%x\r\n", status);\r
-    return ret;\r
-}\r
-\r
-errcode_t msg_center_alipay_tcpdata_recv(uint8_t cmd_id, uint8_t type, void *usr_data, uint16_t usr_len)\r
-{\r
-    uint8_t tl_len = msg_center_get_tlv_tl_len(usr_data);\r
-    uint16_t payload_len = usr_len - tl_len;\r
-    uint8_t *tlv_payload = (uint8_t *)msg_center_get_tlv_payload(usr_data);\r
-    if (g_tcp_data != NULL) {\r
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_MSG_CENTER,\r
-            "The last buff hasn't been read yet, current payload len is %u.\r\n", payload_len);\r
-        return ERRCODE_FAIL;\r
-    }\r
-    g_tcp_data = (uint8_t *)malloc(payload_len);\r
-    if (g_tcp_data != NULL) {\r
-        g_tcp_data_len = payload_len;\r
-        (void)memcpy_s(g_tcp_data, g_tcp_data_len, tlv_payload, payload_len);\r
-    }\r
-\r
-    WEARABLE_LOGI(WEARABLE_LOG_MODULE_MSG_CENTER, "recv tcpdata len = %d\r\n",\r
-                  g_tcp_data_len);\r
-    osSemaphoreRelease(g_alipay_tcp_sem);\r
-    return ERRCODE_SUCC;\r
-}\r
-\r
-int msg_center_alipay_tcpdata_read(void *mem, int len)\r
-{\r
-    int ret = 0;\r
-    if (g_tcp_data != NULL) {\r
-        int read_len = min(len, g_tcp_data_len);\r
-        (void)memcpy_s(mem, len, g_tcp_data + g_tcp_data_readed, read_len);\r
-        g_tcp_data_readed += read_len;\r
-        g_tcp_data_len -= read_len;\r
-        ret = read_len;\r
-        if (g_tcp_data_len == 0) {\r
-            free(g_tcp_data);\r
-            g_tcp_data = NULL;\r
-            g_tcp_data_readed = 0;\r
-        }\r
-    } else {\r
-        ret = -2; // no data for read\r
-    }\r
-    WEARABLE_LOGI(WEARABLE_LOG_MODULE_MSG_CENTER, "read tcpdata len = (%d, %d)\r\n", len, ret);\r
-    return ret;\r
-}\r
-\r
-int msg_center_alipay_close_request(void)\r
-{\r
-    if (g_alipay_tcp_sem != NULL) {\r
-        osSemaphoreDelete(g_alipay_tcp_sem);\r
-        g_alipay_tcp_sem = NULL;\r
-    }\r
-    if (g_tcp_data != NULL) {\r
-        WEARABLE_LOGI(WEARABLE_LOG_MODULE_MSG_CENTER, "need free data buff.\r\n");\r
-        free(g_tcp_data);\r
-        g_tcp_data = NULL;\r
-        g_tcp_data_readed = 0;\r
-    }\r
-    int ret;\r
-    int send_ret =\r
-        msg_center_send_data(MSGCENTER_CMD_ALIPAY, MSGCENTER_TYPE_ID_ALIPAY_CLOSE, &g_tcp_fd, sizeof(g_tcp_fd));\r
-    if (send_ret != ERRCODE_SUCC) {\r
-        ret = -1;\r
-    } else {\r
-        ret = 0;\r
-    }\r
-    g_tcp_fd = 0;\r
-    return ret;\r
-}\r
-\r
-errcode_t msg_center_alipay_close(uint8_t cmd_id, uint8_t type, void *usr_data, uint16_t usr_len)\r
-{\r
-    uint8_t tl_len = msg_center_get_tlv_tl_len(usr_data);\r
-    uint16_t payload_len = usr_len - tl_len;\r
-    uint8_t *tlv_payload = (uint8_t *)msg_center_get_tlv_payload(usr_data);\r
-\r
-    return ERRCODE_SUCC;\r
-}\r
-\r
-errcode_t msg_center_alipay_time_sync(uint8_t cmd_id, uint8_t type, void *usr_data, uint16_t usr_len)\r
-{\r
-    uint8_t tl_len = msg_center_get_tlv_tl_len(usr_data);\r
-    uint16_t payload_len = usr_len - tl_len;\r
-    uint8_t *tlv_payload = (uint8_t *)msg_center_get_tlv_payload(usr_data);\r
-\r
-    uint32_t sec = *((uint32_t *)tlv_payload);\r
-    struct timeval tv = { 0 };\r
-    struct timezone tz = { 0 };\r
-\r
-    tv.tv_sec = sec;\r
-    tv.tv_usec = 0;\r
-    tz.tz_dsttime = 0;\r
-    tz.tz_minuteswest = (8 * 60);\r
-\r
-    settimeofday(&tv, &tz);\r
-    msg_center_alipay_reply_ack(cmd_id, type);\r
-    return ERRCODE_SUCC;\r
-}\r
-\r
-\r
-#ifdef __cplusplus\r
-}\r
+/*
+ * Copyright (c) CompanyNameMagicTag 2021-2021. All rights reserved.
+ * Description: broadcast feature implement
+ * Author: CompanyName
+ * Create: 2021-11-11
+ */
+
+#include "common_def.h"
+#include "msg_center.h"
+#include <stdio.h>
+#include "adapter.h"
+#include "msg_center_protocol.h"
+#include "msg_center_cmd.h"
+#include "wearable_log.h"
+#include "alipay_app.h"
+#include "vendor_net.h"
+#include "cmsis_os2.h"
+#include "sys/time.h"
+#include "time.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define HOSTENT_TIMEOUT 2000
+#define TCP_CONNECT_TIMEOUT 2000
+#define IPV4_LEN 4
+
+osSemaphoreId_t g_alipay_hostent_sem = NULL;
+osSemaphoreId_t g_alipay_tcp_sem = NULL;
+unsigned char g_alipay_ip[IPV4_LEN] = { 0 };
+
+uint8_t *g_tcp_data = NULL;
+uint16_t g_tcp_data_len = 0;
+uint16_t g_tcp_data_readed = 0;
+uint32_t g_tcp_fd = 0;
+uint8_t g_netstatus = 0;
+
+void msg_center_alipay_reply_ack(uint8_t cmd_id, uint8_t type)
+{
+    uint8_t ack = 1; /* 与手机APK侧约定 */
+    int ret = msg_center_send_data(cmd_id, type, &ack, sizeof(ack));
+    WEARABLE_LOGD(WEARABLE_LOG_MODULE_MSG_CENTER, "send replay ret = 0x%x\r\n", ret);
+}
+
+errcode_t msg_center_alipay_network_status(uint8_t cmd_id, uint8_t type, void *usr_data, uint16_t usr_len)
+{
+    uint8_t tl_len = msg_center_get_tlv_tl_len(usr_data);
+    uint16_t payload_len = usr_len - tl_len;
+    uint8_t *tlv_payload = (uint8_t *)msg_center_get_tlv_payload(usr_data);
+
+    g_netstatus = *((uint8_t *)tlv_payload);
+    WEARABLE_LOGD(WEARABLE_LOG_MODULE_MSG_CENTER, "msg_center_alipay netstatus = %d\r\n", g_netstatus);
+    msg_center_alipay_reply_ack(cmd_id, type);
+    return 0;
+}
+
+uint8_t msg_center_alipay_get_network_status(void)
+{
+    return g_netstatus;
+}
+
+int msg_center_alipay_hostent_request(void *usr_data, uint16_t usr_len)
+{
+    int ret;
+    if (g_alipay_hostent_sem == NULL) {
+        g_alipay_hostent_sem = osSemaphoreNew(1, 0, NULL);
+    }
+
+    ret = msg_center_send_data(MSGCENTER_CMD_ALIPAY, MSGCENTER_TYPE_ID_ALIPAY_HOST_INFO, usr_data, usr_len);
+    if (ret != ERRCODE_SUCC) {
+        return -1;
+    } else {
+        return 0;
+    }
+}
+
+errcode_t msg_center_alipay_hostent_recv(uint8_t cmd_id, uint8_t type, void *usr_data, uint16_t usr_len)
+{
+    uint8_t tl_len = msg_center_get_tlv_tl_len(usr_data);
+    uint16_t payload_len = usr_len - tl_len;
+    uint8_t *tlv_payload = (uint8_t *)msg_center_get_tlv_payload(usr_data);
+
+    (void)memcpy_s(g_alipay_ip, IPV4_LEN, tlv_payload, payload_len);
+    WEARABLE_LOGI(WEARABLE_LOG_MODULE_MSG_CENTER, "recv ip info--> %d.%d.%d.%d\r\n",
+                  g_alipay_ip[0], g_alipay_ip[1], g_alipay_ip[2], g_alipay_ip[3]);
+    osSemaphoreRelease(g_alipay_hostent_sem);
+    return ERRCODE_SUCC;
+}
+
+int msg_center_alipay_hostent_reponse(unsigned char *ip)
+{
+    int ret;
+    osStatus_t status = osSemaphoreAcquire(g_alipay_hostent_sem, HOSTENT_TIMEOUT);
+    if (status == osOK) {
+        (void)memcpy_s(ip, IPV4_LEN, g_alipay_ip, IPV4_LEN);
+        ret = 0;
+    } else {
+        ret = -1;
+    }
+    WEARABLE_LOGD(WEARABLE_LOG_MODULE_MSG_CENTER, "hostent wait status = 0x%x\r\n", status);
+    osSemaphoreDelete(g_alipay_hostent_sem);
+    g_alipay_hostent_sem = NULL;
+    return ret;
+}
+
+int msg_center_alipay_connect_request(void *usr_data, uint16_t usr_len)
+{
+    int ret;
+    if (g_alipay_tcp_sem == NULL) {
+        g_alipay_tcp_sem = osSemaphoreNew(1, 0, NULL);
+    }
+    ret = msg_center_send_data(MSGCENTER_CMD_ALIPAY, MSGCENTER_TYPE_ID_ALIPAY_CONNECT, usr_data, usr_len);
+    if (ret != ERRCODE_SUCC) {
+        return -1;
+    } else {
+        return 0;
+    }
+}
+
+errcode_t msg_center_alipay_connect_recv(uint8_t cmd_id, uint8_t type, void *usr_data, uint16_t usr_len)
+{
+    uint8_t tl_len = msg_center_get_tlv_tl_len(usr_data);
+    uint16_t payload_len = usr_len - tl_len;
+    uint8_t *tlv_payload = (uint8_t *)msg_center_get_tlv_payload(usr_data);
+
+    alipay_connect_reponse rep;
+    (void)memcpy_s(&rep, sizeof(alipay_connect_reponse), tlv_payload, payload_len);
+    WEARABLE_LOGI(WEARABLE_LOG_MODULE_MSG_CENTER, "recv alipay_connect_reponse info--> 0x%x 0x%x\r\n",
+                  rep.ret, rep.fd);
+    g_tcp_fd = rep.fd;
+    osSemaphoreRelease(g_alipay_tcp_sem);
+    return ERRCODE_SUCC;
+}
+
+int msg_center_alipay_connect_reponse(void)
+{
+    int ret;
+    osStatus_t status = osSemaphoreAcquire(g_alipay_tcp_sem, TCP_CONNECT_TIMEOUT);
+    if (status == osOK) {
+        ret = (g_tcp_fd > 0) ? 0 : -1;
+    } else {
+        ret = -1;
+    }
+    WEARABLE_LOGD(WEARABLE_LOG_MODULE_MSG_CENTER, "connect wait status = 0x%x\r\n", status);
+    return ret;
+}
+
+int msg_center_alipay_tcpdata_request(void *usr_data, uint16_t usr_len)
+{
+    WEARABLE_LOGD(WEARABLE_LOG_MODULE_MSG_CENTER, "tcpdata send len = 0x%x\r\n", usr_len);
+    int ret;
+    ret = msg_center_send_data(MSGCENTER_CMD_ALIPAY, MSGCENTER_TYPE_ID_ALIPAY_TCPDATA_RECV, usr_data, usr_len);
+    if (ret != ERRCODE_SUCC) {
+        return -1;
+    } else {
+        return (int)usr_len;
+    }
+}
+
+int msg_center_alipay_tcpdata_wait(uint32_t timeout)
+{
+    int ret;
+    osStatus_t status = osSemaphoreAcquire(g_alipay_tcp_sem, timeout);
+    if (status == osOK) {
+        ret = 1;
+    } else {
+        ret = 0;
+    }
+    WEARABLE_LOGD(WEARABLE_LOG_MODULE_MSG_CENTER, "tcpdata wait status = 0x%x\r\n", status);
+    return ret;
+}
+
+errcode_t msg_center_alipay_tcpdata_recv(uint8_t cmd_id, uint8_t type, void *usr_data, uint16_t usr_len)
+{
+    uint8_t tl_len = msg_center_get_tlv_tl_len(usr_data);
+    uint16_t payload_len = usr_len - tl_len;
+    uint8_t *tlv_payload = (uint8_t *)msg_center_get_tlv_payload(usr_data);
+    if (g_tcp_data != NULL) {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_MSG_CENTER,
+            "The last buff hasn't been read yet, current payload len is %u.\r\n", payload_len);
+        return ERRCODE_FAIL;
+    }
+    g_tcp_data = (uint8_t *)malloc(payload_len);
+    if (g_tcp_data != NULL) {
+        g_tcp_data_len = payload_len;
+        (void)memcpy_s(g_tcp_data, g_tcp_data_len, tlv_payload, payload_len);
+    }
+
+    WEARABLE_LOGI(WEARABLE_LOG_MODULE_MSG_CENTER, "recv tcpdata len = %d\r\n",
+                  g_tcp_data_len);
+    osSemaphoreRelease(g_alipay_tcp_sem);
+    return ERRCODE_SUCC;
+}
+
+int msg_center_alipay_tcpdata_read(void *mem, int len)
+{
+    int ret = 0;
+    if (g_tcp_data != NULL) {
+        int read_len = min(len, g_tcp_data_len);
+        (void)memcpy_s(mem, len, g_tcp_data + g_tcp_data_readed, read_len);
+        g_tcp_data_readed += read_len;
+        g_tcp_data_len -= read_len;
+        ret = read_len;
+        if (g_tcp_data_len == 0) {
+            free(g_tcp_data);
+            g_tcp_data = NULL;
+            g_tcp_data_readed = 0;
+        }
+    } else {
+        ret = -2; // no data for read
+    }
+    WEARABLE_LOGI(WEARABLE_LOG_MODULE_MSG_CENTER, "read tcpdata len = (%d, %d)\r\n", len, ret);
+    return ret;
+}
+
+int msg_center_alipay_close_request(void)
+{
+    if (g_alipay_tcp_sem != NULL) {
+        osSemaphoreDelete(g_alipay_tcp_sem);
+        g_alipay_tcp_sem = NULL;
+    }
+    if (g_tcp_data != NULL) {
+        WEARABLE_LOGI(WEARABLE_LOG_MODULE_MSG_CENTER, "need free data buff.\r\n");
+        free(g_tcp_data);
+        g_tcp_data = NULL;
+        g_tcp_data_readed = 0;
+    }
+    int ret;
+    int send_ret =
+        msg_center_send_data(MSGCENTER_CMD_ALIPAY, MSGCENTER_TYPE_ID_ALIPAY_CLOSE, &g_tcp_fd, sizeof(g_tcp_fd));
+    if (send_ret != ERRCODE_SUCC) {
+        ret = -1;
+    } else {
+        ret = 0;
+    }
+    g_tcp_fd = 0;
+    return ret;
+}
+
+errcode_t msg_center_alipay_close(uint8_t cmd_id, uint8_t type, void *usr_data, uint16_t usr_len)
+{
+    uint8_t tl_len = msg_center_get_tlv_tl_len(usr_data);
+    uint16_t payload_len = usr_len - tl_len;
+    uint8_t *tlv_payload = (uint8_t *)msg_center_get_tlv_payload(usr_data);
+
+    return ERRCODE_SUCC;
+}
+
+errcode_t msg_center_alipay_time_sync(uint8_t cmd_id, uint8_t type, void *usr_data, uint16_t usr_len)
+{
+    uint8_t tl_len = msg_center_get_tlv_tl_len(usr_data);
+    uint16_t payload_len = usr_len - tl_len;
+    uint8_t *tlv_payload = (uint8_t *)msg_center_get_tlv_payload(usr_data);
+
+    uint32_t sec = *((uint32_t *)tlv_payload);
+    struct timeval tv = { 0 };
+    struct timezone tz = { 0 };
+
+    tv.tv_sec = sec;
+    tv.tv_usec = 0;
+    tz.tz_dsttime = 0;
+    tz.tz_minuteswest = (8 * 60);
+
+    settimeofday(&tv, &tz);
+    msg_center_alipay_reply_ack(cmd_id, type);
+    return ERRCODE_SUCC;
+}
+
+
+#ifdef __cplusplus
+}
 #endif
\ No newline at end of file
index fd21a24..a67716c 100644 (file)
@@ -1087,8 +1087,8 @@ target = {
             'BOARD_LEINIAO',
             # cat1 相关配置
             'FT_SINGLE_UART',
-            'ENABLE_SLIP',
-            'SUPPORT_CAT1_SERVICE',
+            # 'ENABLE_SLIP',
+            # 'SUPPORT_CAT1_SERVICE',
             'SUPPORT_TCPIP',           
             
             'CONFIG_EFUSE_READ_TO_RAM',
@@ -1149,8 +1149,9 @@ target = {
                           "vibrator_service",
                         #   "-:lwip",
                           "thunderbird_toys",#雷鸟
-                          "cat1Service",
-                          "cat1Test",
+                        #   "cat1Service",
+                        #   "cat1Test",
+                          "ppp",
                           "audio_manager",
                         #   "CustomUIcomponentTool",
                           "nativeabilityfwk",
index 107ee07..e083e5e 100644 (file)
@@ -22,6 +22,40 @@ elseif("freertos" IN_LIST TARGET_COMPONENT)
         ${CMAKE_CURRENT_SOURCE_DIR}/lwip_adapter/freertos)     
 endif()
 
+# PPP
+set(lwipppp_SRCS
+    ${LWIP_DIR}/src/netif/ppp/auth.c
+    ${LWIP_DIR}/src/netif/ppp/ccp.c
+    ${LWIP_DIR}/src/netif/ppp/chap-md5.c
+    ${LWIP_DIR}/src/netif/ppp/chap_ms.c
+    ${LWIP_DIR}/src/netif/ppp/chap-new.c
+    ${LWIP_DIR}/src/netif/ppp/demand.c
+    ${LWIP_DIR}/src/netif/ppp/eap.c
+    ${LWIP_DIR}/src/netif/ppp/ecp.c
+    ${LWIP_DIR}/src/netif/ppp/eui64.c
+    ${LWIP_DIR}/src/netif/ppp/fsm.c
+    ${LWIP_DIR}/src/netif/ppp/ipcp.c
+    ${LWIP_DIR}/src/netif/ppp/ipv6cp.c
+    ${LWIP_DIR}/src/netif/ppp/lcp.c
+    ${LWIP_DIR}/src/netif/ppp/magic.c
+    ${LWIP_DIR}/src/netif/ppp/mppe.c
+    ${LWIP_DIR}/src/netif/ppp/multilink.c
+    ${LWIP_DIR}/src/netif/ppp/ppp.c
+    ${LWIP_DIR}/src/netif/ppp/pppapi.c
+    ${LWIP_DIR}/src/netif/ppp/pppcrypt.c
+    ${LWIP_DIR}/src/netif/ppp/pppoe.c
+    ${LWIP_DIR}/src/netif/ppp/pppol2tp.c
+    ${LWIP_DIR}/src/netif/ppp/pppos.c
+    ${LWIP_DIR}/src/netif/ppp/upap.c
+    ${LWIP_DIR}/src/netif/ppp/utils.c
+    ${LWIP_DIR}/src/netif/ppp/vj.c
+    ${LWIP_DIR}/src/netif/ppp/polarssl/arc4.c
+    ${LWIP_DIR}/src/netif/ppp/polarssl/des.c
+    ${LWIP_DIR}/src/netif/ppp/polarssl/md4.c
+    ${LWIP_DIR}/src/netif/ppp/polarssl/md5.c
+    ${LWIP_DIR}/src/netif/ppp/polarssl/sha1.c
+)
+
 set(SOURCES
     ${CMAKE_LWIP_SOURCE_DIR}/src/core/tcp_out.c
     ${CMAKE_LWIP_SOURCE_DIR}/src/core/inet_chksum.c
@@ -81,6 +115,7 @@ set(SOURCES
     ${CMAKE_LWIP_ADAPTER_DIR}/src/test/socket_test.c
     ${CMAKE_LWIP_ADAPTER_DIR}/src/test/slip_test.c
     ${CMAKE_CURRENT_SOURCE_DIR}/lwip_adapter/lwip_driver_adapter/lwip_driver_adapter.c
+    ${lwipppp_SRCS}
 )
 
 if("__LITEOS__" IN_LIST DEFINES)
@@ -89,6 +124,11 @@ if("__LITEOS__" IN_LIST DEFINES)
     )
 endif()
 
+
+
+
+
+
 set(PUBLIC_HEADER
 )
 
index 2108834..fae1e89 100644 (file)
 #include "errcode.h"
 #include "hal_uart.h"
 
+/* SIO UART buffer configuration */
+#define SIO_UART_RX_BUFFER_SIZE    256
+static uint8_t g_sio_uart_rx_buffer[SIO_UART_RX_BUFFER_SIZE];
 
-
-/* SLIP UART buffer configuration */
-#define SLIP_UART_RX_BUFFER_SIZE    256
-static uint8_t g_slip_uart_rx_buffer[SLIP_UART_RX_BUFFER_SIZE];
-
-/* SLIP UART pin configuration */
-static hal_uart_pin_config_t g_slip_uart_pin_config = {
-    .tx_pin = SLIP_UART_TX_PIN,  /* SLIP UART TX pin */
-    .rx_pin = SLIP_UART_RX_PIN,  /* SLIP UART RX pin */
-    .cts_pin = PIN_NONE, /* SLIP UART CTS pin */
-    .rts_pin = PIN_NONE  /* SLIP UART RTS pin */
+/* SIO UART pin configuration */
+static hal_uart_pin_config_t g_sio_uart_pin_config = {
+    .tx_pin = SIO_UART_TX_PIN,  /* SIO UART TX pin */
+    .rx_pin = SIO_UART_RX_PIN,  /* SIO UART RX pin */
+    .cts_pin = PIN_NONE,        /* SIO UART CTS pin */
+    .rts_pin = PIN_NONE         /* SIO UART RTS pin */
 };
-/* SLIP UART attributes */
-static hal_uart_attr_t g_slip_uart_attr = {
-    .baud_rate = SLIP_UART_BAUDRATE,
+/* SIO UART attributes */
+static hal_uart_attr_t g_sio_uart_attr = {
+    .baud_rate = SIO_UART_BAUDRATE,
     .data_bits = UART_DATA_BIT_8,
     .stop_bits = UART_STOP_BIT_1,
     .parity = UART_PARITY_NONE,
     .pad = 0
 };
 
-/* SLIP UART buffer configuration */
+/* SIO UART buffer configuration */
 static struct {
     void *rx_buffer;
     size_t rx_buffer_size;
-} g_slip_uart_buffer_config = {
-    .rx_buffer = g_slip_uart_rx_buffer,
-    .rx_buffer_size = SLIP_UART_RX_BUFFER_SIZE
+} g_sio_uart_buffer_config = {
+    .rx_buffer = g_sio_uart_rx_buffer,
+    .rx_buffer_size = SIO_UART_RX_BUFFER_SIZE
 };
 
-/* SLIP UART state */
-static bool g_slip_uart_initialized = false;
+/* SIO UART state */
+static bool g_sio_uart_initialized = false;
 
 /* SIO write buffer configuration */
 #define SIO_WRITE_BUFFER_SIZE   1024
@@ -81,52 +79,51 @@ static uint32_t g_sio_write_buffer_pos = 0;
 extern void uart_slip_read_int_handler(const void *buffer, uint16_t length, bool error);
 
 /**
- * Initialize SLIP UART
+ * Initialize SIO UART
  */
-static errcode_t slip_uart_init(void)
+static errcode_t sio_uart_init(void)
 {
     errcode_t ret;
     printf(" %s %d\n", __FUNCTION__, __LINE__);
-    if (g_slip_uart_initialized) {
+    if (g_sio_uart_initialized) {
         return ERRCODE_SUCC;
     }
 
     printf(" %s %d\n", __FUNCTION__, __LINE__);
     /* Initialize UART */
-    ret = uapi_uart_init(SLIP_UART_BUS, (const void*)&g_slip_uart_pin_config, 
-                         (const void*)&g_slip_uart_attr, NULL, (void*)&g_slip_uart_buffer_config);
+    ret = uapi_uart_init(SIO_UART_BUS, (const void*)&g_sio_uart_pin_config, 
+                         (const void*)&g_sio_uart_attr, NULL, (void*)&g_sio_uart_buffer_config);
     if (ret != ERRCODE_SUCC) {
         return ret;
     }
 
-    
-    ret = uapi_uart_register_rx_callback(SLIP_UART_BUS, UART_RX_CONDITION_FULL_OR_SUFFICIENT_DATA_OR_IDLE,
-    SLIP_UART_RX_BUFFER_SIZE, uart_slip_read_int_handler);
-    
+    ret = uapi_uart_register_rx_callback(SIO_UART_BUS, UART_RX_CONDITION_FULL_OR_SUFFICIENT_DATA_OR_IDLE,
+        SIO_UART_RX_BUFFER_SIZE, uart_slip_read_int_handler);
+
     printf("uapi_uart_register_rx_callback ret=%x\r\n", ret);
 
     printf(" %s %d\n", __FUNCTION__, __LINE__);
-    g_slip_uart_initialized = true;
+    g_sio_uart_initialized = true;
     return ERRCODE_SUCC;
 }
 
 /**
  * Opens a serial device for communication.
  *
- * @param devnum device number (not used, always uses SLIP_UART_BUS)
+ * @param devnum device number (not used, always uses SIO_UART_BUS)
  * @return handle to serial device if successful, NULL otherwise
  */
 sio_fd_t sio_open(u8_t devnum)
 {
     (void)devnum; /* Unused parameter */
     
-    if (slip_uart_init() != ERRCODE_SUCC) {
+    if (sio_uart_init() != ERRCODE_SUCC) {
         printf(" %s %d\n", __FUNCTION__, __LINE__);
         return NULL;
     }
     printf(" %s %d\n", __FUNCTION__, __LINE__);    
     /* Return a dummy handle since we use the global UART bus */
-    return 1;// (sio_fd_t)SLIP_UART_BUS;
+    return 1;// (sio_fd_t)SIO_UART_BUS;
 }
 
 /**
@@ -143,7 +140,7 @@ void sio_send(u8_t c, sio_fd_t fd)
 {
     uart_bus_t bus = (uart_bus_t)(uintptr_t)fd;
     
-    if (!g_slip_uart_initialized) {
+    if (!g_sio_uart_initialized) {
         return;
     }
     
@@ -153,7 +150,8 @@ void sio_send(u8_t c, sio_fd_t fd)
     
     /* Write to UART when buffer is full */
     if (g_sio_write_buffer_pos >= SIO_WRITE_BUFFER_SIZE) {
-        uapi_uart_write(UART_BUS_0, g_sio_write_buffer, g_sio_write_buffer_pos, 1000);
+        int32_t result = uapi_uart_write(SIO_UART_BUS, g_sio_write_buffer, g_sio_write_buffer_pos, 1000);
+        (void)result; /* Ignore result in buffered send operation */
         g_sio_write_buffer_pos = 0; /* Reset buffer position */
     }
 }
@@ -170,11 +168,12 @@ u8_t sio_recv(sio_fd_t fd)
     uart_bus_t bus = (uart_bus_t)(uintptr_t)fd;
     u8_t c = 0;
     
-    if (!g_slip_uart_initialized) {
+    if (!g_sio_uart_initialized) {
         return 0;
     }
     
-    if (uapi_uart_read(UART_BUS_0, &c, 1, 1000) > 0) { /* 1 second timeout */
+    int32_t result = uapi_uart_read(SIO_UART_BUS, &c, 1, 1000); /* 1 second timeout */
+    if (result > 0) {
         return c;
     }
     
@@ -196,11 +195,12 @@ u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len)
 {
     uart_bus_t bus = (uart_bus_t)(uintptr_t)fd;
     
-    if (!g_slip_uart_initialized || data == NULL) {
+    if (!g_sio_uart_initialized || data == NULL) {
         return 0;
     }
     
-    return uapi_uart_read(UART_BUS_0, data, len, 1000); /* 1 second timeout */
+    int32_t result = uapi_uart_read(SIO_UART_BUS, data, len, 1000); /* 1 second timeout */
+    return (result > 0) ? (u32_t)result : 0;
 }
 
 /**
@@ -216,12 +216,13 @@ u32_t sio_tryread(sio_fd_t fd, u8_t *data, u32_t len)
 {
     uart_bus_t bus = (uart_bus_t)(uintptr_t)fd;
     
-    if (!g_slip_uart_initialized || data == NULL) {
+    if (!g_sio_uart_initialized || data == NULL) {
         return 0;
     }
     
     /* Use a very short timeout to make it non-blocking */
-    return uapi_uart_read(UART_BUS_0, data, len, 1); /* 1ms timeout */
+    int32_t result = uapi_uart_read(SIO_UART_BUS, data, len, 1); /* 1ms timeout */
+    return (result > 0) ? (u32_t)result : 0;
 }
 
 /**
@@ -238,11 +239,12 @@ u32_t sio_write(sio_fd_t fd, u8_t *data, u32_t len)
 {
     uart_bus_t bus = (uart_bus_t)(uintptr_t)fd;
     
-    if (!g_slip_uart_initialized || data == NULL) {
+    if (!g_sio_uart_initialized || data == NULL) {
         return 0;
     }
     
-    return uapi_uart_write(UART_BUS_0, data, len, 1000); /* 1 second timeout */
+    int32_t result = uapi_uart_write(SIO_UART_BUS, data, len, 1000); /* 1 second timeout */
+    return (result > 0) ? (u32_t)result : 0;
 }
 
 /**
@@ -254,12 +256,12 @@ void sio_read_abort(sio_fd_t fd)
 {
     uart_bus_t bus = (uart_bus_t)(uintptr_t)fd;
     
-    if (!g_slip_uart_initialized) {
+    if (!g_sio_uart_initialized) {
         return;
     }
     
     /* Flush RX data to abort any pending read */
-    uapi_uart_flush_rx_data(UART_BUS_0);
+    uapi_uart_flush_rx_data(SIO_UART_BUS);
 }
 
 /**
@@ -268,13 +270,14 @@ void sio_read_abort(sio_fd_t fd)
  */
 void sio_send_flush(void)
 {
-    if (!g_slip_uart_initialized) {
+    if (!g_sio_uart_initialized) {
         return;
     }
     
     /* Write any remaining data in buffer to UART */
     if (g_sio_write_buffer_pos > 0) {
-        uapi_uart_write(UART_BUS_0, g_sio_write_buffer, g_sio_write_buffer_pos, 1000);
+        int32_t result = uapi_uart_write(SIO_UART_BUS, g_sio_write_buffer, g_sio_write_buffer_pos, 1000);
+        (void)result; /* Ignore result in flush operation */
         g_sio_write_buffer_pos = 0; /* Reset buffer position */
     }
 } 
\ No newline at end of file
index f4190a9..2485b79 100644 (file)
@@ -473,7 +473,11 @@ u32_t sys_now(void)
     /* Lwip docs mentioned like wraparound is not a problem in this funtion */
     return (u32_t)((LOS_TickCountGet() * 1000) / LOSCFG_BASE_CORE_TICK_PER_SECOND);
 }
-
+u32_t sys_jiffies(void)
+{
+    /*sys_jiffies is the same as sys_now*/
+    return (u32_t)((LOS_TickCountGet() * 1000) / LOSCFG_BASE_CORE_TICK_PER_SECOND);
+}
 sys_thread_t sys_thread_new(const char *name, lwip_thread_fn function, void *arg, int stacksize, int prio)
 {
   TSK_INIT_PARAM_S task;
index 4129c6a..2416d01 100644 (file)
@@ -59,26 +59,168 @@ extern "C" {
 #define LWIP_DBG_TYPES_ON       LWIP_DBG_OFF
 #define SOCKETS_DEBUG           LWIP_DBG_OFF 
 
+#ifndef LWIP_SLIP
+#define LWIP_SLIP 0
+#endif
+
+/* ---------- SLIP/PPP Protocol Selection ---------- */
+/* 
+ * SLIP和PPP协议二选一配置
+ * 通过编译时定义SLIP_MODULE_ENABLED或PPP_MODULE_ENABLED来选择协议
+ * 如果两个都定义或都未定义,则默认使用SLIP
+ */
+
+#if defined(SLIP_MODULE_ENABLED) && defined(PPP_MODULE_ENABLED)
+#error "Cannot enable both SLIP and PPP modules simultaneously. Please choose one."
+#endif
+
+#if defined(PPP_MODULE_ENABLED) && !defined(SLIP_MODULE_ENABLED)
+/* PPP模式配置 */
+#undef LWIP_SLIP
+#define LWIP_SLIP 0
+
+/* PPP协议支持 */
+#ifndef PPP_SUPPORT
+#define PPP_SUPPORT 1
+#endif
+
+#ifndef PPPOS_SUPPORT
+#define PPPOS_SUPPORT 1
+#endif
+
+#ifndef PPP_IPV4_SUPPORT
+#define PPP_IPV4_SUPPORT 1
+#endif
+
+#ifndef PAP_SUPPORT
+#define PAP_SUPPORT 1
+#endif
+
+#ifndef CHAP_SUPPORT
+#define CHAP_SUPPORT 1
+#endif
+
+#ifndef LWIP_PPP_API
+#define LWIP_PPP_API 1
+#endif
+
+#ifndef PPP_NOTIFY_PHASE
+#define PPP_NOTIFY_PHASE 1
+#endif
+
+#ifndef MEMP_NUM_PPP_PCB
+#define MEMP_NUM_PPP_PCB 2
+#endif
+
+#ifndef MEMP_NUM_PPPOS_INTERFACES
+#define MEMP_NUM_PPPOS_INTERFACES 2
+#endif
+
+#ifndef PPP_USE_PBUF_RAM
+#define PPP_USE_PBUF_RAM 1
+#endif
+
+#ifndef PPP_FCS_TABLE
+#define PPP_FCS_TABLE 1
+#endif
+
+#ifndef PRINTPKT_SUPPORT
+#define PRINTPKT_SUPPORT 1
+#endif
+
+#ifndef PPP_DEBUG
+#define PPP_DEBUG LWIP_DBG_ON
+#endif
+
+#ifndef PPP_INPROC_IRQ_SAFE
+#define PPP_INPROC_IRQ_SAFE 0
+#endif
+
+#elif defined(SLIP_MODULE_ENABLED) && !defined(PPP_MODULE_ENABLED)
+/* SLIP模式配置 */
+#ifndef LWIP_SLIP
+#define LWIP_SLIP 1
+#endif
+
+/* 确保PPP协议关闭 */
+#ifndef PPP_SUPPORT
+#define PPP_SUPPORT 0
+#endif
+
+#ifndef PPPOS_SUPPORT
+#define PPPOS_SUPPORT 0
+#endif
+
+#else
+/* 默认SLIP模式(向后兼容) */
 #ifndef LWIP_SLIP
 #define LWIP_SLIP 1
 #endif
 
+#ifndef PPP_SUPPORT
+#define PPP_SUPPORT 0
+#endif
+
+#ifndef PPPOS_SUPPORT
+#define PPPOS_SUPPORT 0
+#endif
+
+#endif
+
 #if LWIP_SLIP
 /* SLIP UART configuration */
 // UART_BUS_0 = 0  UART_BUS_1 = 1
+#ifndef SLIP_IFNUM
 #define SLIP_IFNUM              0  
+#endif
+
+#ifndef SLIP_RX_FROM_ISR
 #define SLIP_RX_FROM_ISR        1
+#endif
+
+#ifndef SLIP_USE_RX_THREAD
 #define SLIP_USE_RX_THREAD      1    // 启用接收线程作为主线程处理队列
-#define SLIP_UART_BUS           UART_BUS_0
-#define SLIP_UART_BAUDRATE      921600
-#define SLIP_UART_TX_PIN        S_MGPIO19
-#define SLIP_UART_RX_PIN        S_MGPIO20
-#define SLIP_UART_DATA_BIT      UART_DATA_BIT_8
-#define SLIP_UART_PARITY        UART_PARITY_NONE
-#define SLIP_UART_STOP_BIT      UART_STOP_BIT_1
-#define SLIP_UART_FLOW_CTRL     UART_FLOW_CTRL_NONE
+#endif
+
+#ifndef SLIP_DEBUG
 #define SLIP_DEBUG              LWIP_DBG_ON
 #endif
+#endif
+
+
+/* SIO UART configuration - unified for PPP, SLIP and all serial protocols */
+#ifndef SIO_UART_BUS
+#define SIO_UART_BUS           UART_BUS_0
+#endif
+
+#ifndef SIO_UART_BAUDRATE
+#define SIO_UART_BAUDRATE      921600
+#endif
+
+#ifndef SIO_UART_TX_PIN
+#define SIO_UART_TX_PIN        S_MGPIO19
+#endif
+
+#ifndef SIO_UART_RX_PIN
+#define SIO_UART_RX_PIN        S_MGPIO20
+#endif
+
+#ifndef SIO_UART_DATA_BIT
+#define SIO_UART_DATA_BIT      UART_DATA_BIT_8
+#endif
+
+#ifndef SIO_UART_PARITY
+#define SIO_UART_PARITY        UART_PARITY_NONE
+#endif
+
+#ifndef SIO_UART_STOP_BIT
+#define SIO_UART_STOP_BIT      UART_STOP_BIT_1
+#endif
+
+#ifndef SIO_UART_FLOW_CTRL
+#define SIO_UART_FLOW_CTRL     UART_FLOW_CTRL_NONE
+#endif
+
 
 #define LWIP_HTTPC_HAVE_FILE_IO 1
 /*
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/FILES b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/FILES
new file mode 100644 (file)
index 0000000..0be0741
--- /dev/null
@@ -0,0 +1,15 @@
+api/      - The code for the high-level wrapper API. Not needed if
+            you use the lowel-level call-back/raw API.
+
+apps/     - Higher layer applications that are specifically programmed
+            with the lwIP low-level raw API.
+
+core/     - The core of the TPC/IP stack; protocol implementations,
+            memory and buffer management, and the low-level raw API.
+
+include/  - lwIP include files.
+
+netif/    - Generic network interface device drivers are kept here.
+
+For more information on the various subdirectories, check the FILES
+file in each directory.
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/Filelists.cmake b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/Filelists.cmake
new file mode 100644 (file)
index 0000000..5430b38
--- /dev/null
@@ -0,0 +1,279 @@
+# This file is indended to be included in end-user CMakeLists.txt
+# include(/path/to/Filelists.cmake)
+# It assumes the variable LWIP_DIR is defined pointing to the
+# root path of lwIP sources.
+#
+# This file is NOT designed (on purpose) to be used as cmake
+# subdir via add_subdirectory()
+# The intention is to provide greater flexibility to users to 
+# create their own targets using the *_SRCS variables.
+
+set(LWIP_VERSION_MAJOR    "2")
+set(LWIP_VERSION_MINOR    "1")
+set(LWIP_VERSION_REVISION "3")
+# LWIP_VERSION_RC is set to LWIP_RC_RELEASE for official releases
+# LWIP_VERSION_RC is set to LWIP_RC_DEVELOPMENT for Git versions
+# Numbers 1..31 are reserved for release candidates
+set(LWIP_VERSION_RC       "LWIP_RC_RELEASE")
+
+if ("${LWIP_VERSION_RC}" STREQUAL "LWIP_RC_RELEASE")
+    set(LWIP_VERSION_STRING
+        "${LWIP_VERSION_MAJOR}.${LWIP_VERSION_MINOR}.${LWIP_VERSION_REVISION}"
+    )
+elseif ("${LWIP_VERSION_RC}" STREQUAL "LWIP_RC_DEVELOPMENT")
+    set(LWIP_VERSION_STRING
+        "${LWIP_VERSION_MAJOR}.${LWIP_VERSION_MINOR}.${LWIP_VERSION_REVISION}.dev"
+    )
+else ("${LWIP_VERSION_RC}" STREQUAL "LWIP_RC_RELEASE")
+    set(LWIP_VERSION_STRING
+        "${LWIP_VERSION_MAJOR}.${LWIP_VERSION_MINOR}.${LWIP_VERSION_REVISION}.rc${LWIP_VERSION_RC}"
+    )
+endif ("${LWIP_VERSION_RC}" STREQUAL "LWIP_RC_RELEASE")
+
+# The minimum set of files needed for lwIP.
+set(lwipcore_SRCS
+    ${LWIP_DIR}/src/core/init.c
+    ${LWIP_DIR}/src/core/def.c
+    ${LWIP_DIR}/src/core/dns.c
+    ${LWIP_DIR}/src/core/inet_chksum.c
+    ${LWIP_DIR}/src/core/ip.c
+    ${LWIP_DIR}/src/core/mem.c
+    ${LWIP_DIR}/src/core/memp.c
+    ${LWIP_DIR}/src/core/netif.c
+    ${LWIP_DIR}/src/core/pbuf.c
+    ${LWIP_DIR}/src/core/raw.c
+    ${LWIP_DIR}/src/core/stats.c
+    ${LWIP_DIR}/src/core/sys.c
+    ${LWIP_DIR}/src/core/altcp.c
+    ${LWIP_DIR}/src/core/altcp_alloc.c
+    ${LWIP_DIR}/src/core/altcp_tcp.c
+    ${LWIP_DIR}/src/core/tcp.c
+    ${LWIP_DIR}/src/core/tcp_in.c
+    ${LWIP_DIR}/src/core/tcp_out.c
+    ${LWIP_DIR}/src/core/timeouts.c
+    ${LWIP_DIR}/src/core/udp.c
+)
+set(lwipcore4_SRCS
+    ${LWIP_DIR}/src/core/ipv4/autoip.c
+    ${LWIP_DIR}/src/core/ipv4/dhcp.c
+    ${LWIP_DIR}/src/core/ipv4/etharp.c
+    ${LWIP_DIR}/src/core/ipv4/icmp.c
+    ${LWIP_DIR}/src/core/ipv4/igmp.c
+    ${LWIP_DIR}/src/core/ipv4/ip4_frag.c
+    ${LWIP_DIR}/src/core/ipv4/ip4.c
+    ${LWIP_DIR}/src/core/ipv4/ip4_addr.c
+)
+set(lwipcore6_SRCS
+    ${LWIP_DIR}/src/core/ipv6/dhcp6.c
+    ${LWIP_DIR}/src/core/ipv6/ethip6.c
+    ${LWIP_DIR}/src/core/ipv6/icmp6.c
+    ${LWIP_DIR}/src/core/ipv6/inet6.c
+    ${LWIP_DIR}/src/core/ipv6/ip6.c
+    ${LWIP_DIR}/src/core/ipv6/ip6_addr.c
+    ${LWIP_DIR}/src/core/ipv6/ip6_frag.c
+    ${LWIP_DIR}/src/core/ipv6/mld6.c
+    ${LWIP_DIR}/src/core/ipv6/nd6.c
+)
+
+# APIFILES: The files which implement the sequential and socket APIs.
+set(lwipapi_SRCS
+    ${LWIP_DIR}/src/api/api_lib.c
+    ${LWIP_DIR}/src/api/api_msg.c
+    ${LWIP_DIR}/src/api/err.c
+    ${LWIP_DIR}/src/api/if_api.c
+    ${LWIP_DIR}/src/api/netbuf.c
+    ${LWIP_DIR}/src/api/netdb.c
+    ${LWIP_DIR}/src/api/netifapi.c
+    # ${LWIP_DIR}/src/api/sockets.c
+    ${LWIP_DIR}/src/api/tcpip.c
+)
+
+# Files implementing various generic network interface functions
+set(lwipnetif_SRCS
+    ${LWIP_DIR}/src/netif/ethernet.c
+    ${LWIP_DIR}/src/netif/bridgeif.c
+    ${LWIP_DIR}/src/netif/bridgeif_fdb.c
+    # ${LWIP_DIR}/src/netif/slipif.c
+)
+
+# 6LoWPAN
+set(lwipsixlowpan_SRCS
+    ${LWIP_DIR}/src/netif/lowpan6_common.c
+    ${LWIP_DIR}/src/netif/lowpan6.c
+    ${LWIP_DIR}/src/netif/lowpan6_ble.c
+    ${LWIP_DIR}/src/netif/zepif.c
+)
+
+# PPP
+set(lwipppp_SRCS
+    ${LWIP_DIR}/src/netif/ppp/auth.c
+    ${LWIP_DIR}/src/netif/ppp/ccp.c
+    ${LWIP_DIR}/src/netif/ppp/chap-md5.c
+    ${LWIP_DIR}/src/netif/ppp/chap_ms.c
+    ${LWIP_DIR}/src/netif/ppp/chap-new.c
+    ${LWIP_DIR}/src/netif/ppp/demand.c
+    ${LWIP_DIR}/src/netif/ppp/eap.c
+    ${LWIP_DIR}/src/netif/ppp/ecp.c
+    ${LWIP_DIR}/src/netif/ppp/eui64.c
+    ${LWIP_DIR}/src/netif/ppp/fsm.c
+    ${LWIP_DIR}/src/netif/ppp/ipcp.c
+    ${LWIP_DIR}/src/netif/ppp/ipv6cp.c
+    ${LWIP_DIR}/src/netif/ppp/lcp.c
+    ${LWIP_DIR}/src/netif/ppp/magic.c
+    ${LWIP_DIR}/src/netif/ppp/mppe.c
+    ${LWIP_DIR}/src/netif/ppp/multilink.c
+    ${LWIP_DIR}/src/netif/ppp/ppp.c
+    ${LWIP_DIR}/src/netif/ppp/pppapi.c
+    ${LWIP_DIR}/src/netif/ppp/pppcrypt.c
+    ${LWIP_DIR}/src/netif/ppp/pppoe.c
+    ${LWIP_DIR}/src/netif/ppp/pppol2tp.c
+    ${LWIP_DIR}/src/netif/ppp/pppos.c
+    ${LWIP_DIR}/src/netif/ppp/upap.c
+    ${LWIP_DIR}/src/netif/ppp/utils.c
+    ${LWIP_DIR}/src/netif/ppp/vj.c
+    ${LWIP_DIR}/src/netif/ppp/polarssl/arc4.c
+    ${LWIP_DIR}/src/netif/ppp/polarssl/des.c
+    ${LWIP_DIR}/src/netif/ppp/polarssl/md4.c
+    ${LWIP_DIR}/src/netif/ppp/polarssl/md5.c
+    ${LWIP_DIR}/src/netif/ppp/polarssl/sha1.c
+)
+
+# SNMPv3 agent
+set(lwipsnmp_SRCS
+    ${LWIP_DIR}/src/apps/snmp/snmp_asn1.c
+    ${LWIP_DIR}/src/apps/snmp/snmp_core.c
+    ${LWIP_DIR}/src/apps/snmp/snmp_mib2.c
+    ${LWIP_DIR}/src/apps/snmp/snmp_mib2_icmp.c
+    ${LWIP_DIR}/src/apps/snmp/snmp_mib2_interfaces.c
+    ${LWIP_DIR}/src/apps/snmp/snmp_mib2_ip.c
+    ${LWIP_DIR}/src/apps/snmp/snmp_mib2_snmp.c
+    ${LWIP_DIR}/src/apps/snmp/snmp_mib2_system.c
+    ${LWIP_DIR}/src/apps/snmp/snmp_mib2_tcp.c
+    ${LWIP_DIR}/src/apps/snmp/snmp_mib2_udp.c
+    ${LWIP_DIR}/src/apps/snmp/snmp_snmpv2_framework.c
+    ${LWIP_DIR}/src/apps/snmp/snmp_snmpv2_usm.c
+    ${LWIP_DIR}/src/apps/snmp/snmp_msg.c
+    ${LWIP_DIR}/src/apps/snmp/snmpv3.c
+    ${LWIP_DIR}/src/apps/snmp/snmp_netconn.c
+    ${LWIP_DIR}/src/apps/snmp/snmp_pbuf_stream.c
+    ${LWIP_DIR}/src/apps/snmp/snmp_raw.c
+    ${LWIP_DIR}/src/apps/snmp/snmp_scalar.c
+    ${LWIP_DIR}/src/apps/snmp/snmp_table.c
+    ${LWIP_DIR}/src/apps/snmp/snmp_threadsync.c
+    ${LWIP_DIR}/src/apps/snmp/snmp_traps.c
+)
+
+# HTTP server + client
+set(lwiphttp_SRCS
+    ${LWIP_DIR}/src/apps/http/altcp_proxyconnect.c
+    ${LWIP_DIR}/src/apps/http/fs.c
+    ${LWIP_DIR}/src/apps/http/http_client.c
+    ${LWIP_DIR}/src/apps/http/httpd.c
+)
+
+# MAKEFSDATA HTTP server host utility
+set(lwipmakefsdata_SRCS
+    ${LWIP_DIR}/src/apps/http/makefsdata/makefsdata.c
+)
+
+# IPERF server
+set(lwipiperf_SRCS
+    ${LWIP_DIR}/src/apps/lwiperf/lwiperf.c
+)
+
+# SMTP client
+set(lwipsmtp_SRCS
+    ${LWIP_DIR}/src/apps/smtp/smtp.c
+)
+
+# SNTP client
+set(lwipsntp_SRCS
+    ${LWIP_DIR}/src/apps/sntp/sntp.c
+)
+
+# MDNS responder
+set(lwipmdns_SRCS
+    ${LWIP_DIR}/src/apps/mdns/mdns.c
+)
+
+# NetBIOS name server
+set(lwipnetbios_SRCS
+    ${LWIP_DIR}/src/apps/netbiosns/netbiosns.c
+)
+
+# TFTP server files
+set(lwiptftp_SRCS
+    ${LWIP_DIR}/src/apps/tftp/tftp_server.c
+)
+
+# MQTT client files
+set(lwipmqtt_SRCS
+    ${LWIP_DIR}/src/apps/mqtt/mqtt.c
+)
+
+# ARM MBEDTLS related files of lwIP rep
+set(lwipmbedtls_SRCS
+    ${LWIP_DIR}/src/apps/altcp_tls/altcp_tls_mbedtls.c
+    ${LWIP_DIR}/src/apps/altcp_tls/altcp_tls_mbedtls_mem.c
+    ${LWIP_DIR}/src/apps/snmp/snmpv3_mbedtls.c
+)
+
+# All LWIP files without apps
+set(lwipnoapps_SRCS
+    ${lwipcore_SRCS}
+    ${lwipcore4_SRCS}
+    ${lwipcore6_SRCS}
+    ${lwipapi_SRCS}
+    ${lwipnetif_SRCS}
+    ${lwipsixlowpan_SRCS}
+    ${lwipppp_SRCS}
+)
+
+# LWIPAPPFILES: All LWIP APPs
+set(lwipallapps_SRCS
+    ${lwipsnmp_SRCS}
+    ${lwiphttp_SRCS}
+    ${lwipiperf_SRCS}
+    ${lwipsmtp_SRCS}
+    ${lwipsntp_SRCS}
+    ${lwipmdns_SRCS}
+    ${lwipnetbios_SRCS}
+    ${lwiptftp_SRCS}
+    ${lwipmqtt_SRCS}
+    ${lwipmbedtls_SRCS}
+)
+
+# Generate lwip/init.h (version info)
+configure_file(${LWIP_DIR}/src/include/lwip/init.h.cmake.in ${LWIP_DIR}/src/include/lwip/init.h)
+
+# Documentation
+# set(DOXYGEN_DIR ${LWIP_DIR}/doc/doxygen)
+# set(DOXYGEN_OUTPUT_DIR output)
+# set(DOXYGEN_IN  ${LWIP_DIR}/doc/doxygen/lwip.Doxyfile.cmake.in)
+# set(DOXYGEN_OUT ${LWIP_DIR}/doc/doxygen/lwip.Doxyfile)
+# configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT})
+
+# find_package(Doxygen)
+# if (DOXYGEN_FOUND)
+    # message("Doxygen build started")
+# 
+    # add_custom_target(lwipdocs
+        # COMMAND ${CMAKE_COMMAND} -E remove_directory ${DOXYGEN_DIR}/${DOXYGEN_OUTPUT_DIR}/html
+        # COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT}
+        # WORKING_DIRECTORY ${DOXYGEN_DIR}
+        # COMMENT "Generating API documentation with Doxygen"
+        # VERBATIM)
+# else (DOXYGEN_FOUND)
+    # message("Doxygen needs to be installed to generate the doxygen documentation")
+# endif (DOXYGEN_FOUND)
+
+# lwIP libraries
+# add_library(lwipcore EXCLUDE_FROM_ALL ${lwipnoapps_SRCS})
+# target_compile_options(lwipcore PRIVATE ${LWIP_COMPILER_FLAGS})
+# target_compile_definitions(lwipcore PRIVATE ${LWIP_DEFINITIONS}  ${LWIP_MBEDTLS_DEFINITIONS})
+# target_include_directories(lwipcore PRIVATE ${LWIP_INCLUDE_DIRS} ${LWIP_MBEDTLS_INCLUDE_DIRS})
+
+# add_library(lwipallapps EXCLUDE_FROM_ALL ${lwipallapps_SRCS})
+# target_compile_options(lwipallapps PRIVATE ${LWIP_COMPILER_FLAGS})
+# target_compile_definitions(lwipallapps PRIVATE ${LWIP_DEFINITIONS}  ${LWIP_MBEDTLS_DEFINITIONS})
+# target_include_directories(lwipallapps PRIVATE ${LWIP_INCLUDE_DIRS} ${LWIP_MBEDTLS_INCLUDE_DIRS})
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/Filelists.mk b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/Filelists.mk
new file mode 100644 (file)
index 0000000..828b9f2
--- /dev/null
@@ -0,0 +1,205 @@
+#
+# Copyright (c) 2001, 2002 Swedish Institute of Computer Science.
+# All rights reserved. 
+# 
+# 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.
+# 3. The name of the author may not be used to endorse or promote products
+#    derived from this software without specific prior written permission. 
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+#
+# This file is part of the lwIP TCP/IP stack.
+# 
+# Author: Adam Dunkels <adam@sics.se>
+#
+
+# COREFILES, CORE4FILES: The minimum set of files needed for lwIP.
+COREFILES=$(LWIPDIR)/core/init.c \
+       $(LWIPDIR)/core/def.c \
+       $(LWIPDIR)/core/dns.c \
+       $(LWIPDIR)/core/inet_chksum.c \
+       $(LWIPDIR)/core/ip.c \
+       $(LWIPDIR)/core/mem.c \
+       $(LWIPDIR)/core/memp.c \
+       $(LWIPDIR)/core/netif.c \
+       $(LWIPDIR)/core/pbuf.c \
+       $(LWIPDIR)/core/raw.c \
+       $(LWIPDIR)/core/stats.c \
+       $(LWIPDIR)/core/sys.c \
+       $(LWIPDIR)/core/altcp.c \
+       $(LWIPDIR)/core/altcp_alloc.c \
+       $(LWIPDIR)/core/altcp_tcp.c \
+       $(LWIPDIR)/core/tcp.c \
+       $(LWIPDIR)/core/tcp_in.c \
+       $(LWIPDIR)/core/tcp_out.c \
+       $(LWIPDIR)/core/timeouts.c \
+       $(LWIPDIR)/core/udp.c
+
+CORE4FILES=$(LWIPDIR)/core/ipv4/autoip.c \
+       $(LWIPDIR)/core/ipv4/dhcp.c \
+       $(LWIPDIR)/core/ipv4/etharp.c \
+       $(LWIPDIR)/core/ipv4/icmp.c \
+       $(LWIPDIR)/core/ipv4/igmp.c \
+       $(LWIPDIR)/core/ipv4/ip4_frag.c \
+       $(LWIPDIR)/core/ipv4/ip4.c \
+       $(LWIPDIR)/core/ipv4/ip4_addr.c
+
+CORE6FILES=$(LWIPDIR)/core/ipv6/dhcp6.c \
+       $(LWIPDIR)/core/ipv6/ethip6.c \
+       $(LWIPDIR)/core/ipv6/icmp6.c \
+       $(LWIPDIR)/core/ipv6/inet6.c \
+       $(LWIPDIR)/core/ipv6/ip6.c \
+       $(LWIPDIR)/core/ipv6/ip6_addr.c \
+       $(LWIPDIR)/core/ipv6/ip6_frag.c \
+       $(LWIPDIR)/core/ipv6/mld6.c \
+       $(LWIPDIR)/core/ipv6/nd6.c
+
+# APIFILES: The files which implement the sequential and socket APIs.
+APIFILES=$(LWIPDIR)/api/api_lib.c \
+       $(LWIPDIR)/api/api_msg.c \
+       $(LWIPDIR)/api/err.c \
+       $(LWIPDIR)/api/if_api.c \
+       $(LWIPDIR)/api/netbuf.c \
+       $(LWIPDIR)/api/netdb.c \
+       $(LWIPDIR)/api/netifapi.c \
+       $(LWIPDIR)/api/sockets.c \
+       $(LWIPDIR)/api/tcpip.c
+
+# NETIFFILES: Files implementing various generic network interface functions
+NETIFFILES=$(LWIPDIR)/netif/ethernet.c \
+       $(LWIPDIR)/netif/bridgeif.c \
+       $(LWIPDIR)/netif/bridgeif_fdb.c \
+       $(LWIPDIR)/netif/slipif.c
+
+# SIXLOWPAN: 6LoWPAN
+SIXLOWPAN=$(LWIPDIR)/netif/lowpan6_common.c \
+        $(LWIPDIR)/netif/lowpan6.c \
+       $(LWIPDIR)/netif/lowpan6_ble.c \
+       $(LWIPDIR)/netif/zepif.c
+
+# PPPFILES: PPP
+PPPFILES=$(LWIPDIR)/netif/ppp/auth.c \
+       $(LWIPDIR)/netif/ppp/ccp.c \
+       $(LWIPDIR)/netif/ppp/chap-md5.c \
+       $(LWIPDIR)/netif/ppp/chap_ms.c \
+       $(LWIPDIR)/netif/ppp/chap-new.c \
+       $(LWIPDIR)/netif/ppp/demand.c \
+       $(LWIPDIR)/netif/ppp/eap.c \
+       $(LWIPDIR)/netif/ppp/ecp.c \
+       $(LWIPDIR)/netif/ppp/eui64.c \
+       $(LWIPDIR)/netif/ppp/fsm.c \
+       $(LWIPDIR)/netif/ppp/ipcp.c \
+       $(LWIPDIR)/netif/ppp/ipv6cp.c \
+       $(LWIPDIR)/netif/ppp/lcp.c \
+       $(LWIPDIR)/netif/ppp/magic.c \
+       $(LWIPDIR)/netif/ppp/mppe.c \
+       $(LWIPDIR)/netif/ppp/multilink.c \
+       $(LWIPDIR)/netif/ppp/ppp.c \
+       $(LWIPDIR)/netif/ppp/pppapi.c \
+       $(LWIPDIR)/netif/ppp/pppcrypt.c \
+       $(LWIPDIR)/netif/ppp/pppoe.c \
+       $(LWIPDIR)/netif/ppp/pppol2tp.c \
+       $(LWIPDIR)/netif/ppp/pppos.c \
+       $(LWIPDIR)/netif/ppp/upap.c \
+       $(LWIPDIR)/netif/ppp/utils.c \
+       $(LWIPDIR)/netif/ppp/vj.c \
+       $(LWIPDIR)/netif/ppp/polarssl/arc4.c \
+       $(LWIPDIR)/netif/ppp/polarssl/des.c \
+       $(LWIPDIR)/netif/ppp/polarssl/md4.c \
+       $(LWIPDIR)/netif/ppp/polarssl/md5.c \
+       $(LWIPDIR)/netif/ppp/polarssl/sha1.c
+
+# LWIPNOAPPSFILES: All LWIP files without apps
+LWIPNOAPPSFILES=$(COREFILES) \
+       $(CORE4FILES) \
+       $(CORE6FILES) \
+       $(APIFILES) \
+       $(NETIFFILES) \
+       $(PPPFILES) \
+       $(SIXLOWPAN)
+
+# SNMPFILES: SNMPv2c agent
+SNMPFILES=$(LWIPDIR)/apps/snmp/snmp_asn1.c \
+       $(LWIPDIR)/apps/snmp/snmp_core.c \
+       $(LWIPDIR)/apps/snmp/snmp_mib2.c \
+       $(LWIPDIR)/apps/snmp/snmp_mib2_icmp.c \
+       $(LWIPDIR)/apps/snmp/snmp_mib2_interfaces.c \
+       $(LWIPDIR)/apps/snmp/snmp_mib2_ip.c \
+       $(LWIPDIR)/apps/snmp/snmp_mib2_snmp.c \
+       $(LWIPDIR)/apps/snmp/snmp_mib2_system.c \
+       $(LWIPDIR)/apps/snmp/snmp_mib2_tcp.c \
+       $(LWIPDIR)/apps/snmp/snmp_mib2_udp.c \
+       $(LWIPDIR)/apps/snmp/snmp_snmpv2_framework.c \
+       $(LWIPDIR)/apps/snmp/snmp_snmpv2_usm.c \
+       $(LWIPDIR)/apps/snmp/snmp_msg.c \
+       $(LWIPDIR)/apps/snmp/snmpv3.c \
+       $(LWIPDIR)/apps/snmp/snmp_netconn.c \
+       $(LWIPDIR)/apps/snmp/snmp_pbuf_stream.c \
+       $(LWIPDIR)/apps/snmp/snmp_raw.c \
+       $(LWIPDIR)/apps/snmp/snmp_scalar.c \
+       $(LWIPDIR)/apps/snmp/snmp_table.c \
+       $(LWIPDIR)/apps/snmp/snmp_threadsync.c \
+       $(LWIPDIR)/apps/snmp/snmp_traps.c
+
+# HTTPFILES: HTTP server + client
+HTTPFILES=$(LWIPDIR)/apps/http/altcp_proxyconnect.c \
+       $(LWIPDIR)/apps/http/fs.c \
+       $(LWIPDIR)/apps/http/http_client.c \
+       $(LWIPDIR)/apps/http/httpd.c
+
+# MAKEFSDATA: MAKEFSDATA HTTP server host utility
+MAKEFSDATAFILES=$(LWIPDIR)/apps/http/makefsdata/makefsdata.c
+
+# LWIPERFFILES: IPERF server
+LWIPERFFILES=$(LWIPDIR)/apps/lwiperf/lwiperf.c
+
+# SMTPFILES: SMTP client
+SMTPFILES=$(LWIPDIR)/apps/smtp/smtp.c
+
+# SNTPFILES: SNTP client
+SNTPFILES=$(LWIPDIR)/apps/sntp/sntp.c
+
+# MDNSFILES: MDNS responder
+MDNSFILES=$(LWIPDIR)/apps/mdns/mdns.c
+
+# NETBIOSNSFILES: NetBIOS name server
+NETBIOSNSFILES=$(LWIPDIR)/apps/netbiosns/netbiosns.c
+
+# TFTPFILES: TFTP server files
+TFTPFILES=$(LWIPDIR)/apps/tftp/tftp_server.c
+
+# MQTTFILES: MQTT client files
+MQTTFILES=$(LWIPDIR)/apps/mqtt/mqtt.c
+
+# MBEDTLS_FILES: MBEDTLS related files of lwIP rep
+MBEDTLS_FILES=$(LWIPDIR)/apps/altcp_tls/altcp_tls_mbedtls.c \
+       $(LWIPDIR)/apps/altcp_tls/altcp_tls_mbedtls_mem.c \
+       $(LWIPDIR)/apps/snmp/snmpv3_mbedtls.c
+
+# LWIPAPPFILES: All LWIP APPs
+LWIPAPPFILES=$(SNMPFILES) \
+       $(HTTPFILES) \
+       $(LWIPERFFILES) \
+       $(SMTPFILES) \
+       $(SNTPFILES) \
+       $(MDNSFILES) \
+       $(NETBIOSNSFILES) \
+       $(TFTPFILES) \
+       $(MQTTFILES) \
+       $(MBEDTLS_FILES)
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/altcp_tls/altcp_tls_mbedtls.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/altcp_tls/altcp_tls_mbedtls.c
new file mode 100644 (file)
index 0000000..47d5d22
--- /dev/null
@@ -0,0 +1,1287 @@
+/**
+ * @file
+ * Application layered TCP/TLS connection API (to be used from TCPIP thread)
+ *
+ * This file provides a TLS layer using mbedTLS
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ * Watch out:
+ * - 'sent' is always called with len==0 to the upper layer. This is because keeping
+ *   track of the ratio of application data and TLS overhead would be too much.
+ *
+ * Mandatory security-related configuration:
+ * - ensure to add at least one strong entropy source to your mbedtls port (implement
+ *   mbedtls_platform_entropy_poll or mbedtls_hardware_poll providing strong entropy)
+ * - define ALTCP_MBEDTLS_ENTROPY_PTR and ALTCP_MBEDTLS_ENTROPY_LEN to something providing
+ *   GOOD custom entropy
+ *
+ * Missing things / @todo:
+ * - some unhandled/untested things migh be caught by LWIP_ASSERTs...
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/altcp_tls_mbedtls_opts.h"
+
+#if LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS
+
+#include "lwip/altcp.h"
+#include "lwip/altcp_tls.h"
+#include "lwip/priv/altcp_priv.h"
+
+#include "altcp_tls_mbedtls_structs.h"
+#include "altcp_tls_mbedtls_mem.h"
+#if (MBEDTLS_VERSION_NUMBER == 0x03010000)
+#include "mbedtls/pk.h"
+#include "mbedtls/net_sockets.h"
+#include "mbedtls/x509_crt.h"
+
+#else
+#include "mbedtls/certs.h"
+#include "mbedtls/x509.h"
+#include "mbedtls/net.h"
+#include "mbedtls/ssl_internal.h" /* to call mbedtls_flush_output after ERR_MEM */
+#endif
+/* @todo: which includes are really needed? */
+#include "mbedtls/entropy.h"
+#include "mbedtls/ctr_drbg.h"
+#include "mbedtls/ssl.h"
+#include "mbedtls/error.h"
+#include "mbedtls/debug.h"
+#include "mbedtls/platform.h"
+#include "mbedtls/memory_buffer_alloc.h"
+#include "mbedtls/ssl_cache.h"
+#include "mbedtls/ssl_ticket.h"
+
+
+#include <string.h>
+
+#ifndef ALTCP_MBEDTLS_ENTROPY_PTR
+#define ALTCP_MBEDTLS_ENTROPY_PTR   NULL
+#endif
+#ifndef ALTCP_MBEDTLS_ENTROPY_LEN
+#define ALTCP_MBEDTLS_ENTROPY_LEN   0
+#endif
+
+/* Variable prototype, the actual declaration is at the end of this file
+   since it contains pointers to static functions declared here */
+extern const struct altcp_functions altcp_mbedtls_functions;
+
+/** Our global mbedTLS configuration (server-specific, not connection-specific) */
+struct altcp_tls_config {
+  mbedtls_ssl_config conf;
+  mbedtls_x509_crt *cert;
+  mbedtls_pk_context *pkey;
+  uint8_t cert_count;
+  uint8_t cert_max;
+  uint8_t pkey_count;
+  uint8_t pkey_max;
+  mbedtls_x509_crt *ca;
+#if defined(MBEDTLS_SSL_CACHE_C) && ALTCP_MBEDTLS_USE_SESSION_CACHE
+  /** Inter-connection cache for fast connection startup */
+  struct mbedtls_ssl_cache_context cache;
+#endif
+#if defined(MBEDTLS_SSL_SESSION_TICKETS) && ALTCP_MBEDTLS_USE_SESSION_TICKETS
+  mbedtls_ssl_ticket_context ticket_ctx;
+#endif
+};
+
+/** Entropy and random generator are shared by all mbedTLS configuration */
+struct altcp_tls_entropy_rng {
+  mbedtls_entropy_context entropy;
+  mbedtls_ctr_drbg_context ctr_drbg;
+  int ref;
+};
+static struct altcp_tls_entropy_rng *altcp_tls_entropy_rng;
+
+static err_t altcp_mbedtls_lower_recv(void *arg, struct altcp_pcb *inner_conn, struct pbuf *p, err_t err);
+static err_t altcp_mbedtls_setup(void *conf, struct altcp_pcb *conn, struct altcp_pcb *inner_conn);
+static err_t altcp_mbedtls_lower_recv_process(struct altcp_pcb *conn, altcp_mbedtls_state_t *state);
+static err_t altcp_mbedtls_handle_rx_appldata(struct altcp_pcb *conn, altcp_mbedtls_state_t *state);
+static int altcp_mbedtls_bio_send(void *ctx, const unsigned char *dataptr, size_t size);
+
+
+/* callback functions from inner/lower connection: */
+
+/** Accept callback from lower connection (i.e. TCP)
+ * Allocate one of our structures, assign it to the new connection's 'state' and
+ * call the new connection's 'accepted' callback. If that succeeds, we wait
+ * to receive connection setup handshake bytes from the client.
+ */
+static err_t
+altcp_mbedtls_lower_accept(void *arg, struct altcp_pcb *accepted_conn, err_t err)
+{
+  struct altcp_pcb *listen_conn = (struct altcp_pcb *)arg;
+  if (listen_conn && listen_conn->state && listen_conn->accept) {
+    err_t setup_err;
+    altcp_mbedtls_state_t *listen_state = (altcp_mbedtls_state_t *)listen_conn->state;
+    /* create a new altcp_conn to pass to the next 'accept' callback */
+    struct altcp_pcb *new_conn = altcp_alloc();
+    if (new_conn == NULL) {
+      return ERR_MEM;
+    }
+    setup_err = altcp_mbedtls_setup(listen_state->conf, new_conn, accepted_conn);
+    if (setup_err != ERR_OK) {
+      altcp_free(new_conn);
+      return setup_err;
+    }
+    return listen_conn->accept(listen_conn->arg, new_conn, err);
+  }
+  return ERR_ARG;
+}
+
+/** Connected callback from lower connection (i.e. TCP).
+ * Not really implemented/tested yet...
+ */
+static err_t
+altcp_mbedtls_lower_connected(void *arg, struct altcp_pcb *inner_conn, err_t err)
+{
+  struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+  LWIP_UNUSED_ARG(inner_conn); /* for LWIP_NOASSERT */
+  if (conn && conn->state) {
+    LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
+    /* upper connected is called when handshake is done */
+    if (err != ERR_OK) {
+      if (conn->connected) {
+        return conn->connected(conn->arg, conn, err);
+      }
+    }
+    return altcp_mbedtls_lower_recv_process(conn, (altcp_mbedtls_state_t *)conn->state);
+  }
+  return ERR_VAL;
+}
+
+/* Call recved for possibly more than an u16_t */
+static void
+altcp_mbedtls_lower_recved(struct altcp_pcb *inner_conn, int recvd_cnt)
+{
+  while (recvd_cnt > 0) {
+    u16_t recvd_part = (u16_t)LWIP_MIN(recvd_cnt, 0xFFFF);
+    altcp_recved(inner_conn, recvd_part);
+    recvd_cnt -= recvd_part;
+  }
+}
+
+/** Recv callback from lower connection (i.e. TCP)
+ * This one mainly differs between connection setup/handshake (data is fed into mbedTLS only)
+ * and application phase (data is decoded by mbedTLS and passed on to the application).
+ */
+static err_t
+altcp_mbedtls_lower_recv(void *arg, struct altcp_pcb *inner_conn, struct pbuf *p, err_t err)
+{
+  altcp_mbedtls_state_t *state;
+  struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+
+  LWIP_ASSERT("no err expected", err == ERR_OK);
+  LWIP_UNUSED_ARG(err);
+
+  if (!conn) {
+    /* no connection given as arg? should not happen, but prevent pbuf/conn leaks */
+    if (p != NULL) {
+      pbuf_free(p);
+    }
+    altcp_close(inner_conn);
+    return ERR_CLSD;
+  }
+  state = (altcp_mbedtls_state_t *)conn->state;
+  LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
+  if (!state) {
+    /* already closed */
+    if (p != NULL) {
+      pbuf_free(p);
+    }
+    altcp_close(inner_conn);
+    return ERR_CLSD;
+  }
+
+  /* handle NULL pbuf (inner connection closed) */
+  if (p == NULL) {
+    /* remote host sent FIN, remember this (SSL state is destroyed
+        when both sides are closed only!) */
+    if ((state->flags & (ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE | ALTCP_MBEDTLS_FLAGS_UPPER_CALLED)) ==
+        (ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE | ALTCP_MBEDTLS_FLAGS_UPPER_CALLED)) {
+      /* need to notify upper layer (e.g. 'accept' called or 'connect' succeeded) */
+      if ((state->rx != NULL) || (state->rx_app != NULL)) {
+        state->flags |= ALTCP_MBEDTLS_FLAGS_RX_CLOSE_QUEUED;
+        /* this is a normal close (FIN) but we have unprocessed data, so delay the FIN */
+        altcp_mbedtls_handle_rx_appldata(conn, state);
+        return ERR_OK;
+      }
+      state->flags |= ALTCP_MBEDTLS_FLAGS_RX_CLOSED;
+      if (conn->recv) {
+        return conn->recv(conn->arg, conn, NULL, ERR_OK);
+      }
+    } else {
+      /* before connection setup is done: call 'err' */
+      if (conn->err) {
+        conn->err(conn->arg, ERR_CLSD);
+      }
+      altcp_close(conn);
+    }
+    return ERR_OK;
+  }
+
+  /* If we come here, the connection is in good state (handshake phase or application data phase).
+     Queue up the pbuf for processing as handshake data or application data. */
+  if (state->rx == NULL) {
+    state->rx = p;
+  } else {
+    LWIP_ASSERT("rx pbuf overflow", (int)p->tot_len + (int)p->len <= 0xFFFF);
+    pbuf_cat(state->rx, p);
+  }
+  return altcp_mbedtls_lower_recv_process(conn, state);
+}
+
+static err_t
+altcp_mbedtls_lower_recv_process(struct altcp_pcb *conn, altcp_mbedtls_state_t *state)
+{
+  if (!(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
+    /* handle connection setup (handshake not done) */
+    int ret = mbedtls_ssl_handshake(&state->ssl_context);
+    /* try to send data... */
+    altcp_output(conn->inner_conn);
+    if (state->bio_bytes_read) {
+      /* acknowledge all bytes read */
+      altcp_mbedtls_lower_recved(conn->inner_conn, state->bio_bytes_read);
+      state->bio_bytes_read = 0;
+    }
+
+    if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
+      /* handshake not done, wait for more recv calls */
+      LWIP_ASSERT("in this state, the rx chain should be empty", state->rx == NULL);
+      return ERR_OK;
+    }
+    if (ret != 0) {
+      LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_handshake failed: %d\n", ret));
+      /* handshake failed, connection has to be closed */
+      if (conn->err) {
+        conn->err(conn->arg, ERR_CLSD);
+      }
+
+      if (altcp_close(conn) != ERR_OK) {
+        altcp_abort(conn);
+      }
+      return ERR_OK;
+    }
+    /* If we come here, handshake succeeded. */
+    LWIP_ASSERT("state", state->bio_bytes_read == 0);
+    LWIP_ASSERT("state", state->bio_bytes_appl == 0);
+    state->flags |= ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE;
+    /* issue "connect" callback" to upper connection (this can only happen for active open) */
+    if (conn->connected) {
+      err_t err;
+      err = conn->connected(conn->arg, conn, ERR_OK);
+      if (err != ERR_OK) {
+        return err;
+      }
+    }
+    if (state->rx == NULL) {
+      return ERR_OK;
+    }
+  }
+  /* handle application data */
+  return altcp_mbedtls_handle_rx_appldata(conn, state);
+}
+
+/* Pass queued decoded rx data to application */
+static err_t
+altcp_mbedtls_pass_rx_data(struct altcp_pcb *conn, altcp_mbedtls_state_t *state)
+{
+  err_t err;
+  struct pbuf *buf;
+  LWIP_ASSERT("conn != NULL", conn != NULL);
+  LWIP_ASSERT("state != NULL", state != NULL);
+  buf = state->rx_app;
+  if (buf) {
+    state->rx_app = NULL;
+    if (conn->recv) {
+      u16_t tot_len = buf->tot_len;
+      /* this needs to be increased first because the 'recved' call may come nested */
+      state->rx_passed_unrecved += tot_len;
+      state->flags |= ALTCP_MBEDTLS_FLAGS_UPPER_CALLED;
+      err = conn->recv(conn->arg, conn, buf, ERR_OK);
+      if (err != ERR_OK) {
+        if (err == ERR_ABRT) {
+          return ERR_ABRT;
+        }
+        /* not received, leave the pbuf(s) queued (and decrease 'unrecved' again) */
+        LWIP_ASSERT("state == conn->state", state == conn->state);
+        state->rx_app = buf;
+        state->rx_passed_unrecved -= tot_len;
+        LWIP_ASSERT("state->rx_passed_unrecved >= 0", state->rx_passed_unrecved >= 0);
+        if (state->rx_passed_unrecved < 0) {
+          state->rx_passed_unrecved = 0;
+        }
+        return err;
+      }
+    } else {
+      pbuf_free(buf);
+    }
+  } else if ((state->flags & (ALTCP_MBEDTLS_FLAGS_RX_CLOSE_QUEUED | ALTCP_MBEDTLS_FLAGS_RX_CLOSED)) ==
+             ALTCP_MBEDTLS_FLAGS_RX_CLOSE_QUEUED) {
+    state->flags |= ALTCP_MBEDTLS_FLAGS_RX_CLOSED;
+    if (conn->recv) {
+      return conn->recv(conn->arg, conn, NULL, ERR_OK);
+    }
+  }
+
+  /* application may have close the connection */
+  if (conn->state != state) {
+    /* return error code to ensure altcp_mbedtls_handle_rx_appldata() exits the loop */
+    return ERR_CLSD;
+  }
+  return ERR_OK;
+}
+
+/* Helper function that processes rx application data stored in rx pbuf chain */
+static err_t
+altcp_mbedtls_handle_rx_appldata(struct altcp_pcb *conn, altcp_mbedtls_state_t *state)
+{
+  int ret;
+  LWIP_ASSERT("state != NULL", state != NULL);
+  if (!(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
+    /* handshake not done yet */
+    return ERR_VAL;
+  }
+  do {
+    /* allocate a full-sized unchained PBUF_POOL: this is for RX! */
+    struct pbuf *buf = pbuf_alloc(PBUF_RAW, PBUF_POOL_BUFSIZE, PBUF_POOL);
+    if (buf == NULL) {
+      /* We're short on pbufs, try again later from 'poll' or 'recv' callbacks.
+         @todo: close on excessive allocation failures or leave this up to upper conn? */
+      return ERR_OK;
+    }
+
+    /* decrypt application data, this pulls encrypted RX data off state->rx pbuf chain */
+    ret = mbedtls_ssl_read(&state->ssl_context, (unsigned char *)buf->payload, PBUF_POOL_BUFSIZE);
+    if (ret < 0) {
+      if (ret == MBEDTLS_ERR_SSL_CLIENT_RECONNECT) {
+        /* client is initiating a new connection using the same source port -> close connection or make handshake */
+        LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("new connection on same source port\n"));
+        LWIP_ASSERT("TODO: new connection on same source port, close this connection", 0);
+      } else if ((ret != MBEDTLS_ERR_SSL_WANT_READ) && (ret != MBEDTLS_ERR_SSL_WANT_WRITE)) {
+        if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
+          LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("connection was closed gracefully\n"));
+        } else if (ret == MBEDTLS_ERR_NET_CONN_RESET) {
+          LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("connection was reset by peer\n"));
+        }
+        pbuf_free(buf);
+        return ERR_OK;
+      } else {
+        pbuf_free(buf);
+        return ERR_OK;
+      }
+      pbuf_free(buf);
+      altcp_abort(conn);
+      return ERR_ABRT;
+    } else {
+      err_t err;
+      if (ret) {
+        LWIP_ASSERT("bogus receive length", ret <= PBUF_POOL_BUFSIZE);
+        /* trim pool pbuf to actually decoded length */
+        pbuf_realloc(buf, (u16_t)ret);
+
+        state->bio_bytes_appl += ret;
+        if (mbedtls_ssl_get_bytes_avail(&state->ssl_context) == 0) {
+          /* Record is done, now we know the share between application and protocol bytes
+             and can adjust the RX window by the protocol bytes.
+             The rest is 'recved' by the application calling our 'recved' fn. */
+          int overhead_bytes;
+          LWIP_ASSERT("bogus byte counts", state->bio_bytes_read > state->bio_bytes_appl);
+          overhead_bytes = state->bio_bytes_read - state->bio_bytes_appl;
+          altcp_mbedtls_lower_recved(conn->inner_conn, overhead_bytes);
+          state->bio_bytes_read = 0;
+          state->bio_bytes_appl = 0;
+        }
+
+        if (state->rx_app == NULL) {
+          state->rx_app = buf;
+        } else {
+          pbuf_cat(state->rx_app, buf);
+        }
+      } else {
+        pbuf_free(buf);
+        buf = NULL;
+      }
+      err = altcp_mbedtls_pass_rx_data(conn, state);
+      if (err != ERR_OK) {
+        if (err == ERR_ABRT) {
+          /* recv callback needs to return this as the pcb is deallocated */
+          return ERR_ABRT;
+        }
+        /* we hide all other errors as we retry feeding the pbuf to the app later */
+        return ERR_OK;
+      }
+    }
+  } while (ret > 0);
+  return ERR_OK;
+}
+
+/** Receive callback function called from mbedtls (set via mbedtls_ssl_set_bio)
+ * This function mainly copies data from pbufs and frees the pbufs after copying.
+ */
+static int
+altcp_mbedtls_bio_recv(void *ctx, unsigned char *buf, size_t len)
+{
+  struct altcp_pcb *conn = (struct altcp_pcb *)ctx;
+  altcp_mbedtls_state_t *state;
+  struct pbuf *p;
+  u16_t ret;
+  u16_t copy_len;
+  err_t err;
+
+  LWIP_UNUSED_ARG(err); /* for LWIP_NOASSERT */
+  if ((conn == NULL) || (conn->state == NULL)) {
+    return MBEDTLS_ERR_NET_INVALID_CONTEXT;
+  }
+  state = (altcp_mbedtls_state_t *)conn->state;
+  LWIP_ASSERT("state != NULL", state != NULL);
+  p = state->rx;
+
+  /* @todo: return MBEDTLS_ERR_NET_CONN_RESET/MBEDTLS_ERR_NET_RECV_FAILED? */
+
+  if ((p == NULL) || ((p->len == 0) && (p->next == NULL))) {
+    if (p) {
+      pbuf_free(p);
+    }
+    state->rx = NULL;
+    if ((state->flags & (ALTCP_MBEDTLS_FLAGS_RX_CLOSE_QUEUED | ALTCP_MBEDTLS_FLAGS_RX_CLOSED)) ==
+        ALTCP_MBEDTLS_FLAGS_RX_CLOSE_QUEUED) {
+      /* close queued but not passed up yet */
+      return 0;
+    }
+    return MBEDTLS_ERR_SSL_WANT_READ;
+  }
+  /* limit number of bytes again to copy from first pbuf in a chain only */
+  copy_len = (u16_t)LWIP_MIN(len, p->len);
+  /* copy the data */
+  ret = pbuf_copy_partial(p, buf, copy_len, 0);
+  LWIP_ASSERT("ret == copy_len", ret == copy_len);
+  /* hide the copied bytes from the pbuf */
+  err = pbuf_remove_header(p, ret);
+  LWIP_ASSERT("error", err == ERR_OK);
+  if (p->len == 0) {
+    /* the first pbuf has been fully read, free it */
+    state->rx = p->next;
+    p->next = NULL;
+    pbuf_free(p);
+  }
+
+  state->bio_bytes_read += (int)ret;
+  return ret;
+}
+
+/** Sent callback from lower connection (i.e. TCP)
+ * This only informs the upper layer to try to send more, not about
+ * the number of ACKed bytes.
+ */
+static err_t
+altcp_mbedtls_lower_sent(void *arg, struct altcp_pcb *inner_conn, u16_t len)
+{
+  struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+  LWIP_UNUSED_ARG(inner_conn); /* for LWIP_NOASSERT */
+  LWIP_UNUSED_ARG(len);
+  if (conn) {
+    altcp_mbedtls_state_t *state = (altcp_mbedtls_state_t *)conn->state;
+    LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
+    if (!state || !(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
+      /* @todo: do something here? */
+      return ERR_OK;
+    }
+    /* try to send more if we failed before */
+    mbedtls_ssl_flush_output(&state->ssl_context);
+    /* call upper sent with len==0 if the application already sent data */
+    if ((state->flags & ALTCP_MBEDTLS_FLAGS_APPLDATA_SENT) && conn->sent) {
+      return conn->sent(conn->arg, conn, 0);
+    }
+  }
+  return ERR_OK;
+}
+
+/** Poll callback from lower connection (i.e. TCP)
+ * Just pass this on to the application.
+ * @todo: retry sending?
+ */
+static err_t
+altcp_mbedtls_lower_poll(void *arg, struct altcp_pcb *inner_conn)
+{
+  struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+  LWIP_UNUSED_ARG(inner_conn); /* for LWIP_NOASSERT */
+  if (conn) {
+    LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
+    /* check if there's unreceived rx data */
+    if (conn->state) {
+      altcp_mbedtls_state_t *state = (altcp_mbedtls_state_t *)conn->state;
+      /* try to send more if we failed before */
+      mbedtls_ssl_flush_output(&state->ssl_context);
+      if (altcp_mbedtls_handle_rx_appldata(conn, state) == ERR_ABRT) {
+        return ERR_ABRT;
+      }
+    }
+    if (conn->poll) {
+      return conn->poll(conn->arg, conn);
+    }
+  }
+  return ERR_OK;
+}
+
+static void
+altcp_mbedtls_lower_err(void *arg, err_t err)
+{
+  struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+  if (conn) {
+    conn->inner_conn = NULL; /* already freed */
+    if (conn->err) {
+      conn->err(conn->arg, err);
+    }
+    altcp_free(conn);
+  }
+}
+
+/* setup functions */
+
+static void
+altcp_mbedtls_remove_callbacks(struct altcp_pcb *inner_conn)
+{
+  altcp_arg(inner_conn, NULL);
+  altcp_recv(inner_conn, NULL);
+  altcp_sent(inner_conn, NULL);
+  altcp_err(inner_conn, NULL);
+  altcp_poll(inner_conn, NULL, inner_conn->pollinterval);
+}
+
+static void
+altcp_mbedtls_setup_callbacks(struct altcp_pcb *conn, struct altcp_pcb *inner_conn)
+{
+  altcp_arg(inner_conn, conn);
+  altcp_recv(inner_conn, altcp_mbedtls_lower_recv);
+  altcp_sent(inner_conn, altcp_mbedtls_lower_sent);
+  altcp_err(inner_conn, altcp_mbedtls_lower_err);
+  /* tcp_poll is set when interval is set by application */
+  /* listen is set totally different :-) */
+}
+
+static err_t
+altcp_mbedtls_setup(void *conf, struct altcp_pcb *conn, struct altcp_pcb *inner_conn)
+{
+  int ret;
+  struct altcp_tls_config *config = (struct altcp_tls_config *)conf;
+  altcp_mbedtls_state_t *state;
+  if (!conf) {
+    return ERR_ARG;
+  }
+  LWIP_ASSERT("invalid inner_conn", conn != inner_conn);
+
+  /* allocate mbedtls context */
+  state = altcp_mbedtls_alloc(conf);
+  if (state == NULL) {
+    return ERR_MEM;
+  }
+  /* initialize mbedtls context: */
+  mbedtls_ssl_init(&state->ssl_context);
+  ret = mbedtls_ssl_setup(&state->ssl_context, &config->conf);
+  if (ret != 0) {
+    LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_setup failed\n"));
+    /* @todo: convert 'ret' to err_t */
+    altcp_mbedtls_free(conf, state);
+    return ERR_MEM;
+  }
+  /* tell mbedtls about our I/O functions */
+  mbedtls_ssl_set_bio(&state->ssl_context, conn, altcp_mbedtls_bio_send, altcp_mbedtls_bio_recv, NULL);
+
+  altcp_mbedtls_setup_callbacks(conn, inner_conn);
+  conn->inner_conn = inner_conn;
+  conn->fns = &altcp_mbedtls_functions;
+  conn->state = state;
+  return ERR_OK;
+}
+
+struct altcp_pcb *
+altcp_tls_wrap(struct altcp_tls_config *config, struct altcp_pcb *inner_pcb)
+{
+  struct altcp_pcb *ret;
+  if (inner_pcb == NULL) {
+    return NULL;
+  }
+  ret = altcp_alloc();
+  if (ret != NULL) {
+    if (altcp_mbedtls_setup(config, ret, inner_pcb) != ERR_OK) {
+      altcp_free(ret);
+      return NULL;
+    }
+  }
+  return ret;
+}
+
+void *
+altcp_tls_context(struct altcp_pcb *conn)
+{
+  if (conn && conn->state) {
+    altcp_mbedtls_state_t *state = (altcp_mbedtls_state_t *)conn->state;
+    return &state->ssl_context;
+  }
+  return NULL;
+}
+
+#if ALTCP_MBEDTLS_LIB_DEBUG != LWIP_DBG_OFF
+static void
+altcp_mbedtls_debug(void *ctx, int level, const char *file, int line, const char *str)
+{
+  LWIP_UNUSED_ARG(ctx);
+  LWIP_UNUSED_ARG(file);
+  LWIP_UNUSED_ARG(line);
+  LWIP_UNUSED_ARG(str);
+
+  if (level >= ALTCP_MBEDTLS_LIB_DEBUG_LEVEL_MIN) {
+    LWIP_DEBUGF(ALTCP_MBEDTLS_LIB_DEBUG, ("%s:%04d: %s", file, line, str));
+  }
+}
+#endif
+
+/** Create new TLS configuration
+ * ATTENTION: Server certificate and private key have to be added outside this function!
+ */
+static struct altcp_tls_config *
+altcp_tls_create_config(int is_server, uint8_t cert_count, uint8_t pkey_count, int have_ca)
+{
+  size_t sz;
+  int ret;
+  struct altcp_tls_config *conf;
+  mbedtls_x509_crt *mem;
+
+  if (TCP_WND < MBEDTLS_SSL_IN_CONTENT_LEN) {
+    LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG|LWIP_DBG_LEVEL_SERIOUS,
+      ("altcp_tls: TCP_WND is smaller than the RX decrypion buffer, connection RX might stall!\n"));
+  }
+
+  altcp_mbedtls_mem_init();
+
+  sz = sizeof(struct altcp_tls_config);
+  if (cert_count > 0) {
+    sz += (cert_count * sizeof(mbedtls_x509_crt));
+  }
+  if (have_ca) {
+    sz += sizeof(mbedtls_x509_crt);
+  }
+  if (pkey_count > 0) {
+    sz += (pkey_count * sizeof(mbedtls_pk_context));
+  }
+
+  conf = (struct altcp_tls_config *)altcp_mbedtls_alloc_config(sz);
+  if (conf == NULL) {
+    return NULL;
+  }
+  conf->cert_max = cert_count;
+  mem = (mbedtls_x509_crt *)(conf + 1);
+  if (cert_count > 0) {
+    conf->cert = mem;
+    mem += cert_count;
+  }
+  if (have_ca) {
+    conf->ca = mem;
+    mem++;
+  }
+  conf->pkey_max = pkey_count;
+  if (pkey_count > 0) {
+    conf->pkey = (mbedtls_pk_context *)mem;
+  }
+
+  mbedtls_ssl_config_init(&conf->conf);
+
+  if (!altcp_tls_entropy_rng) {
+    altcp_tls_entropy_rng = (struct altcp_tls_entropy_rng *)altcp_mbedtls_alloc_config(sizeof(struct altcp_tls_entropy_rng));
+    if (altcp_tls_entropy_rng) {
+      altcp_tls_entropy_rng->ref = 1;
+      mbedtls_entropy_init(&altcp_tls_entropy_rng->entropy);
+      mbedtls_ctr_drbg_init(&altcp_tls_entropy_rng->ctr_drbg);
+      /* Seed the RNG, only once */
+      ret = mbedtls_ctr_drbg_seed(&altcp_tls_entropy_rng->ctr_drbg,
+                                  mbedtls_entropy_func, &altcp_tls_entropy_rng->entropy,
+                                  ALTCP_MBEDTLS_ENTROPY_PTR, ALTCP_MBEDTLS_ENTROPY_LEN);
+      if (ret != 0) {
+        LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ctr_drbg_seed failed: %d\n", ret));
+        mbedtls_ctr_drbg_free(&altcp_tls_entropy_rng->ctr_drbg);
+        mbedtls_entropy_free(&altcp_tls_entropy_rng->entropy);
+        altcp_mbedtls_free_config(altcp_tls_entropy_rng);
+        altcp_tls_entropy_rng = NULL;
+        altcp_mbedtls_free_config(conf);
+        return NULL;
+      }
+    } else {
+      altcp_mbedtls_free_config(conf);
+      return NULL;
+    }
+  } else {
+    altcp_tls_entropy_rng->ref++;
+  }
+
+  /* Setup ssl context (@todo: what's different for a client here? -> might better be done on listen/connect) */
+  ret = mbedtls_ssl_config_defaults(&conf->conf, is_server ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT,
+                                    MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
+  if (ret != 0) {
+    LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_config_defaults failed: %d\n", ret));
+    if (altcp_tls_entropy_rng->ref == 1) {
+      mbedtls_ctr_drbg_free(&altcp_tls_entropy_rng->ctr_drbg);
+      mbedtls_entropy_free(&altcp_tls_entropy_rng->entropy);
+      altcp_mbedtls_free_config(altcp_tls_entropy_rng);
+      altcp_tls_entropy_rng = NULL;
+    }
+    altcp_mbedtls_free_config(conf);
+    return NULL;
+  }
+  mbedtls_ssl_conf_authmode(&conf->conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
+
+  mbedtls_ssl_conf_rng(&conf->conf, mbedtls_ctr_drbg_random, &altcp_tls_entropy_rng->ctr_drbg);
+#if ALTCP_MBEDTLS_LIB_DEBUG != LWIP_DBG_OFF
+  mbedtls_ssl_conf_dbg(&conf->conf, altcp_mbedtls_debug, stdout);
+#endif
+#if defined(MBEDTLS_SSL_CACHE_C) && ALTCP_MBEDTLS_USE_SESSION_CACHE
+  mbedtls_ssl_conf_session_cache(&conf->conf, &conf->cache, mbedtls_ssl_cache_get, mbedtls_ssl_cache_set);
+  mbedtls_ssl_cache_set_timeout(&conf->cache, ALTCP_MBEDTLS_SESSION_CACHE_TIMEOUT_SECONDS);
+  mbedtls_ssl_cache_set_max_entries(&conf->cache, ALTCP_MBEDTLS_SESSION_CACHE_SIZE);
+#endif
+
+#if defined(MBEDTLS_SSL_SESSION_TICKETS) && ALTCP_MBEDTLS_USE_SESSION_TICKETS
+  mbedtls_ssl_ticket_init(&conf->ticket_ctx);
+
+  ret = mbedtls_ssl_ticket_setup(&conf->ticket_ctx, mbedtls_ctr_drbg_random, &altcp_tls_entropy_rng->ctr_drbg,
+    ALTCP_MBEDTLS_SESSION_TICKET_CIPHER, ALTCP_MBEDTLS_SESSION_TICKET_TIMEOUT_SECONDS);
+  if (ret) {
+    LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_ticket_setup failed: %d\n", ret));
+    altcp_mbedtls_free_config(conf);
+    return NULL;
+  }
+
+  mbedtls_ssl_conf_session_tickets_cb(&conf->conf, mbedtls_ssl_ticket_write, mbedtls_ssl_ticket_parse,
+    &conf->ticket_ctx);
+#endif
+
+  return conf;
+}
+
+struct altcp_tls_config *altcp_tls_create_config_server(uint8_t cert_count)
+{
+  struct altcp_tls_config *conf = altcp_tls_create_config(1, cert_count, cert_count, 0);
+  if (conf == NULL) {
+    return NULL;
+  }
+
+  mbedtls_ssl_conf_ca_chain(&conf->conf, NULL, NULL);
+  return conf;
+}
+
+err_t altcp_tls_config_server_add_privkey_cert(struct altcp_tls_config *config,
+      const u8_t *privkey, size_t privkey_len,
+      const u8_t *privkey_pass, size_t privkey_pass_len,
+      const u8_t *cert, size_t cert_len)
+{
+  int ret;
+  mbedtls_x509_crt *srvcert;
+  mbedtls_pk_context *pkey;
+
+  if (config->cert_count >= config->cert_max) {
+    return ERR_MEM;
+  }
+  if (config->pkey_count >= config->pkey_max) {
+    return ERR_MEM;
+  }
+
+  srvcert = config->cert + config->cert_count;
+  mbedtls_x509_crt_init(srvcert);
+
+  pkey = config->pkey + config->pkey_count;
+  mbedtls_pk_init(pkey);
+
+  /* Load the certificates and private key */
+  ret = mbedtls_x509_crt_parse(srvcert, cert, cert_len);
+  if (ret != 0) {
+    LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_x509_crt_parse failed: %d\n", ret));
+    return ERR_VAL;
+  }
+
+  ret = mbedtls_pk_parse_key(pkey, (const unsigned char *) privkey, privkey_len, privkey_pass, privkey_pass_len,NULL,NULL);
+  if (ret != 0) {
+    LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_pk_parse_public_key failed: %d\n", ret));
+    mbedtls_x509_crt_free(srvcert);
+    return ERR_VAL;
+  }
+
+  ret = mbedtls_ssl_conf_own_cert(&config->conf, srvcert, pkey);
+  if (ret != 0) {
+    LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_conf_own_cert failed: %d\n", ret));
+    mbedtls_x509_crt_free(srvcert);
+    mbedtls_pk_free(pkey);
+    return ERR_VAL;
+  }
+
+  config->cert_count++;
+  config->pkey_count++;
+  return ERR_OK;
+}
+
+/** Create new TLS configuration
+ * This is a suboptimal version that gets the encrypted private key and its password,
+ * as well as the server certificate.
+ */
+struct altcp_tls_config *
+altcp_tls_create_config_server_privkey_cert(const u8_t *privkey, size_t privkey_len,
+    const u8_t *privkey_pass, size_t privkey_pass_len,
+    const u8_t *cert, size_t cert_len)
+{
+  struct altcp_tls_config *conf = altcp_tls_create_config_server(1);
+  if (conf == NULL) {
+    return NULL;
+  }
+
+  if (altcp_tls_config_server_add_privkey_cert(conf, privkey, privkey_len,
+    privkey_pass, privkey_pass_len, cert, cert_len) != ERR_OK) {
+    altcp_mbedtls_free_config(conf);
+    return NULL;
+  }
+
+  return conf;
+}
+
+static struct altcp_tls_config *
+altcp_tls_create_config_client_common(const u8_t *ca, size_t ca_len, int is_2wayauth)
+{
+  int ret;
+  struct altcp_tls_config *conf = altcp_tls_create_config(0, (is_2wayauth) ? 1 : 0, (is_2wayauth) ? 1 : 0, ca != NULL);
+  if (conf == NULL) {
+    return NULL;
+  }
+
+  /* Initialize the CA certificate if provided
+   * CA certificate is optional (to save memory) but recommended for production environment
+   * Without CA certificate, connection will be prone to man-in-the-middle attacks */
+  if (ca) {
+    mbedtls_x509_crt_init(conf->ca);
+    ret = mbedtls_x509_crt_parse(conf->ca, ca, ca_len);
+    if (ret != 0) {
+      LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_x509_crt_parse ca failed: %d 0x%x", ret, -1*ret));
+      altcp_mbedtls_free_config(conf);
+      return NULL;
+    }
+
+    mbedtls_ssl_conf_ca_chain(&conf->conf, conf->ca, NULL);
+  }
+  return conf;
+}
+
+struct altcp_tls_config *
+altcp_tls_create_config_client(const u8_t *ca, size_t ca_len)
+{
+  return altcp_tls_create_config_client_common(ca, ca_len, 0);
+}
+
+struct altcp_tls_config *
+altcp_tls_create_config_client_2wayauth(const u8_t *ca, size_t ca_len, const u8_t *privkey, size_t privkey_len,
+                                        const u8_t *privkey_pass, size_t privkey_pass_len,
+                                        const u8_t *cert, size_t cert_len)
+{
+  int ret;
+  struct altcp_tls_config *conf;
+
+  if (!cert || !privkey) {
+    LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("altcp_tls_create_config_client_2wayauth: certificate and priv key required"));
+    return NULL;
+  }
+
+  conf = altcp_tls_create_config_client_common(ca, ca_len, 1);
+  if (conf == NULL) {
+    return NULL;
+  }
+
+  /* Initialize the client certificate and corresponding private key */
+  mbedtls_x509_crt_init(conf->cert);
+  ret = mbedtls_x509_crt_parse(conf->cert, cert, cert_len);
+  if (ret != 0) {
+    LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_x509_crt_parse cert failed: %d 0x%x", ret, -1*ret));
+    altcp_mbedtls_free_config(conf->cert);
+    return NULL;
+  }
+
+  mbedtls_pk_init(conf->pkey);
+  ret = mbedtls_pk_parse_key(conf->pkey, privkey, privkey_len, privkey_pass, privkey_pass_len,NULL,NULL);
+  if (ret != 0) {
+    LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_pk_parse_key failed: %d 0x%x", ret, -1*ret));
+    altcp_mbedtls_free_config(conf);
+    return NULL;
+  }
+
+  ret = mbedtls_ssl_conf_own_cert(&conf->conf, conf->cert, conf->pkey);
+  if (ret != 0) {
+    LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_conf_own_cert failed: %d 0x%x", ret, -1*ret));
+    altcp_mbedtls_free_config(conf);
+    return NULL;
+  }
+
+  return conf;
+}
+
+void
+altcp_tls_free_config(struct altcp_tls_config *conf)
+{
+  if (conf->pkey) {
+    mbedtls_pk_free(conf->pkey);
+  }
+  if (conf->cert) {
+    mbedtls_x509_crt_free(conf->cert);
+  }
+  if (conf->ca) {
+    mbedtls_x509_crt_free(conf->ca);
+  }
+  altcp_mbedtls_free_config(conf);
+  if (altcp_tls_entropy_rng && altcp_tls_entropy_rng->ref)
+      altcp_tls_entropy_rng->ref--;
+}
+
+void
+altcp_tls_free_entropy(void)
+{
+  if (altcp_tls_entropy_rng && altcp_tls_entropy_rng->ref == 0) {
+    mbedtls_ctr_drbg_free(&altcp_tls_entropy_rng->ctr_drbg);
+    mbedtls_entropy_free(&altcp_tls_entropy_rng->entropy);
+    altcp_mbedtls_free_config(altcp_tls_entropy_rng);
+    altcp_tls_entropy_rng = NULL;
+  }
+}
+
+/* "virtual" functions */
+static void
+altcp_mbedtls_set_poll(struct altcp_pcb *conn, u8_t interval)
+{
+  if (conn != NULL) {
+    altcp_poll(conn->inner_conn, altcp_mbedtls_lower_poll, interval);
+  }
+}
+
+static void
+altcp_mbedtls_recved(struct altcp_pcb *conn, u16_t len)
+{
+  u16_t lower_recved;
+  altcp_mbedtls_state_t *state;
+  if (conn == NULL) {
+    return;
+  }
+  state = (altcp_mbedtls_state_t *)conn->state;
+  if (state == NULL) {
+    return;
+  }
+  if (!(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
+    return;
+  }
+  lower_recved = len;
+  if (lower_recved > state->rx_passed_unrecved) {
+    LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("bogus recved count (len > state->rx_passed_unrecved / %d / %d)",
+                                      len, state->rx_passed_unrecved));
+    lower_recved = (u16_t)state->rx_passed_unrecved;
+  }
+  state->rx_passed_unrecved -= lower_recved;
+
+  altcp_recved(conn->inner_conn, lower_recved);
+}
+
+static err_t
+altcp_mbedtls_connect(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected)
+{
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+  conn->connected = connected;
+  return altcp_connect(conn->inner_conn, ipaddr, port, altcp_mbedtls_lower_connected);
+}
+
+static struct altcp_pcb *
+altcp_mbedtls_listen(struct altcp_pcb *conn, u8_t backlog, err_t *err)
+{
+  struct altcp_pcb *lpcb;
+  if (conn == NULL) {
+    return NULL;
+  }
+  lpcb = altcp_listen_with_backlog_and_err(conn->inner_conn, backlog, err);
+  if (lpcb != NULL) {
+    altcp_mbedtls_state_t *state = (altcp_mbedtls_state_t *)conn->state;
+    /* Free members of the ssl context (not used on listening pcb). This
+       includes freeing input/output buffers, so saves ~32KByte by default */
+    mbedtls_ssl_free(&state->ssl_context);
+
+    conn->inner_conn = lpcb;
+    altcp_accept(lpcb, altcp_mbedtls_lower_accept);
+    return conn;
+  }
+  return NULL;
+}
+
+static void
+altcp_mbedtls_abort(struct altcp_pcb *conn)
+{
+  if (conn != NULL) {
+    altcp_abort(conn->inner_conn);
+  }
+}
+
+static err_t
+altcp_mbedtls_close(struct altcp_pcb *conn)
+{
+  struct altcp_pcb *inner_conn;
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+  inner_conn = conn->inner_conn;
+  if (inner_conn) {
+    err_t err;
+    altcp_poll_fn oldpoll = inner_conn->poll;
+    altcp_mbedtls_remove_callbacks(conn->inner_conn);
+    err = altcp_close(conn->inner_conn);
+    if (err != ERR_OK) {
+      /* not closed, set up all callbacks again */
+      altcp_mbedtls_setup_callbacks(conn, inner_conn);
+      /* poll callback is not included in the above */
+      altcp_poll(inner_conn, oldpoll, inner_conn->pollinterval);
+      return err;
+    }
+    conn->inner_conn = NULL;
+  }
+  altcp_free(conn);
+  return ERR_OK;
+}
+
+/** Allow caller of altcp_write() to limit to negotiated chunk size
+ *  or remaining sndbuf space of inner_conn.
+ */
+static u16_t
+altcp_mbedtls_sndbuf(struct altcp_pcb *conn)
+{
+  if (conn) {
+    altcp_mbedtls_state_t *state;
+    state = (altcp_mbedtls_state_t*)conn->state;
+    if (!state || !(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
+      return 0;
+    }
+    if (conn->inner_conn) {
+      u16_t sndbuf = altcp_sndbuf(conn->inner_conn);
+      /* Take care of record header, IV, AuthTag */
+      int ssl_expan = mbedtls_ssl_get_record_expansion(&state->ssl_context);
+      if (ssl_expan > 0) {
+        size_t ssl_added = (u16_t)LWIP_MIN(ssl_expan, 0xFFFF);
+        /* internal sndbuf smaller than our offset */
+        if (ssl_added < sndbuf) {
+          size_t max_len = 0xFFFF;
+          size_t ret;
+#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
+          /* @todo: adjust ssl_added to real value related to negociated cipher */
+          #if (MBEDTLS_VERSION_NUMBER == 0x03010000)
+          size_t max_frag_len = mbedtls_ssl_get_max_out_record_payload(&state->ssl_context);
+          #else
+          size_t max_frag_len = mbedtls_ssl_get_max_frag_len(&state->ssl_context);
+          #endif
+          max_len = LWIP_MIN(max_frag_len, max_len);
+#endif
+          /* Adjust sndbuf of inner_conn with what added by SSL */
+          ret = LWIP_MIN(sndbuf - ssl_added, max_len);
+          LWIP_ASSERT("sndbuf overflow", ret <= 0xFFFF);
+          return (u16_t)ret;
+        }
+      }
+    }
+  }
+  /* fallback: use sendbuf of the inner connection */
+  return altcp_default_sndbuf(conn);
+}
+
+/** Write data to a TLS connection. Calls into mbedTLS, which in turn calls into
+ * @ref altcp_mbedtls_bio_send() to send the encrypted data
+ */
+static err_t
+altcp_mbedtls_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags)
+{
+  int ret;
+  altcp_mbedtls_state_t *state;
+
+  LWIP_UNUSED_ARG(apiflags);
+
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+
+  state = (altcp_mbedtls_state_t *)conn->state;
+  if (state == NULL) {
+    /* @todo: which error? */
+    return ERR_CLSD;
+  }
+  if (!(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
+    /* @todo: which error? */
+    return ERR_VAL;
+  }
+
+  /* HACK: if thre is something left to send, try to flush it and only
+     allow sending more if this succeeded (this is a hack because neither
+     returning 0 nor MBEDTLS_ERR_SSL_WANT_WRITE worked for me) */
+  #if (MBEDTLS_VERSION_NUMBER == 0x03010000)
+    if(mbedtls_ssl_flush_output(&state->ssl_context) !=0)
+    {
+      return ERR_MEM;
+    }
+  #else
+  if (state->ssl_context.out_left) {
+    mbedtls_ssl_flush_output(&state->ssl_context);
+    if (state->ssl_context.out_left) {
+      return ERR_MEM;
+    }
+  }
+  #endif
+  ret = mbedtls_ssl_write(&state->ssl_context, (const unsigned char *)dataptr, len);
+  /* try to send data... */
+  altcp_output(conn->inner_conn);
+  if (ret >= 0) {
+    if (ret == len) {
+      state->flags |= ALTCP_MBEDTLS_FLAGS_APPLDATA_SENT;
+      return ERR_OK;
+    } else {
+      /* @todo/@fixme: assumption: either everything sent or error */
+      LWIP_ASSERT("ret <= 0", 0);
+      return ERR_MEM;
+    }
+  } else {
+    if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
+      /* @todo: convert error to err_t */
+      return ERR_MEM;
+    }
+    LWIP_ASSERT("unhandled error", 0);
+    return ERR_VAL;
+  }
+}
+
+/** Send callback function called from mbedtls (set via mbedtls_ssl_set_bio)
+ * This function is either called during handshake or when sending application
+ * data via @ref altcp_mbedtls_write (or altcp_write)
+ */
+static int
+altcp_mbedtls_bio_send(void *ctx, const unsigned char *dataptr, size_t size)
+{
+  struct altcp_pcb *conn = (struct altcp_pcb *) ctx;
+  int written = 0;
+  size_t size_left = size;
+  u8_t apiflags = TCP_WRITE_FLAG_COPY;
+
+  LWIP_ASSERT("conn != NULL", conn != NULL);
+  if ((conn == NULL) || (conn->inner_conn == NULL)) {
+    return MBEDTLS_ERR_NET_INVALID_CONTEXT;
+  }
+
+  while (size_left) {
+    u16_t write_len = (u16_t)LWIP_MIN(size_left, 0xFFFF);
+    err_t err = altcp_write(conn->inner_conn, (const void *)dataptr, write_len, apiflags);
+    if (err == ERR_OK) {
+      written += write_len;
+      size_left -= write_len;
+    } else if (err == ERR_MEM) {
+      if (written) {
+        return written;
+      }
+      return 0; /* MBEDTLS_ERR_SSL_WANT_WRITE; */
+    } else {
+      LWIP_ASSERT("tls_write, tcp_write: err != ERR MEM", 0);
+      /* @todo: return MBEDTLS_ERR_NET_CONN_RESET or MBEDTLS_ERR_NET_SEND_FAILED */
+      return MBEDTLS_ERR_NET_SEND_FAILED;
+    }
+  }
+  return written;
+}
+
+static u16_t
+altcp_mbedtls_mss(struct altcp_pcb *conn)
+{
+  if (conn == NULL) {
+    return 0;
+  }
+  /* @todo: LWIP_MIN(mss, mbedtls_ssl_get_max_frag_len()) ? */
+  return altcp_mss(conn->inner_conn);
+}
+
+static void
+altcp_mbedtls_dealloc(struct altcp_pcb *conn)
+{
+  /* clean up and free tls state */
+  if (conn) {
+    altcp_mbedtls_state_t *state = (altcp_mbedtls_state_t *)conn->state;
+    if (state) {
+      mbedtls_ssl_free(&state->ssl_context);
+      state->flags = 0;
+      if (state->rx) {
+        /* free leftover (unhandled) rx pbufs */
+        pbuf_free(state->rx);
+        state->rx = NULL;
+      }
+      altcp_mbedtls_free(state->conf, state);
+      conn->state = NULL;
+    }
+  }
+}
+
+const struct altcp_functions altcp_mbedtls_functions = {
+  altcp_mbedtls_set_poll,
+  altcp_mbedtls_recved,
+  altcp_default_bind,
+  altcp_mbedtls_connect,
+  altcp_mbedtls_listen,
+  altcp_mbedtls_abort,
+  altcp_mbedtls_close,
+  altcp_default_shutdown,
+  altcp_mbedtls_write,
+  altcp_default_output,
+  altcp_mbedtls_mss,
+  altcp_mbedtls_sndbuf,
+  altcp_default_sndqueuelen,
+  altcp_default_nagle_disable,
+  altcp_default_nagle_enable,
+  altcp_default_nagle_disabled,
+  altcp_default_setprio,
+  altcp_mbedtls_dealloc,
+  altcp_default_get_tcp_addrinfo,
+  altcp_default_get_ip,
+  altcp_default_get_port
+#if LWIP_TCP_KEEPALIVE
+  , altcp_default_keepalive_disable
+  , altcp_default_keepalive_enable
+#endif
+#ifdef LWIP_DEBUG
+  , altcp_default_dbg_get_tcp_state
+#endif
+};
+
+#endif /* LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS */
+#endif /* LWIP_ALTCP */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/altcp_tls/altcp_tls_mbedtls_mem.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/altcp_tls/altcp_tls_mbedtls_mem.c
new file mode 100644 (file)
index 0000000..04d47ae
--- /dev/null
@@ -0,0 +1,210 @@
+/**
+ * @file
+ * Application layered TCP connection API (to be used from TCPIP thread)
+ *
+ * This file contains memory management functions for a TLS layer using mbedTLS.
+ *
+ * ATTENTION: For production usage, you might want to override this file with
+ *            your own implementation since this implementation simply uses the
+ *            lwIP heap without caring for fragmentation or leaving heap for
+ *            other parts of lwIP!
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ * Missing things / @todo:
+ * - RX data is acknowledged after receiving (tcp_recved is called when enqueueing
+ *   the pbuf for mbedTLS receive, not when processed by mbedTLS or the inner
+ *   connection; altcp_recved() from inner connection does nothing)
+ * - TX data is marked as 'sent' (i.e. acknowledged; sent callback is called) right
+ *   after enqueueing for transmission, not when actually ACKed be the remote host.
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/altcp_tls_mbedtls_opts.h"
+
+#if LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS
+
+#include "altcp_tls_mbedtls_mem.h"
+#include "altcp_tls_mbedtls_structs.h"
+#include "lwip/mem.h"
+
+#include "mbedtls/platform.h"
+
+#include <string.h>
+
+#ifndef ALTCP_MBEDTLS_MEM_DEBUG
+#define ALTCP_MBEDTLS_MEM_DEBUG   LWIP_DBG_OFF
+#endif
+
+#if defined(MBEDTLS_PLATFORM_MEMORY) && \
+   (!defined(MBEDTLS_PLATFORM_FREE_MACRO) || \
+    defined(MBEDTLS_PLATFORM_CALLOC_MACRO))
+#define ALTCP_MBEDTLS_PLATFORM_ALLOC 1
+#else
+#define ALTCP_MBEDTLS_PLATFORM_ALLOC 0
+#endif
+
+#if ALTCP_MBEDTLS_PLATFORM_ALLOC
+
+#ifndef ALTCP_MBEDTLS_PLATFORM_ALLOC_STATS
+#define ALTCP_MBEDTLS_PLATFORM_ALLOC_STATS 0
+#endif
+
+/* This is an example/debug implementation of alloc/free functions only */
+typedef struct altcp_mbedtls_malloc_helper_s {
+  size_t c;
+  size_t len;
+} altcp_mbedtls_malloc_helper_t;
+
+#if ALTCP_MBEDTLS_PLATFORM_ALLOC_STATS
+typedef struct altcp_mbedtls_malloc_stats_s {
+  size_t allocedBytes;
+  size_t allocCnt;
+  size_t maxBytes;
+  size_t totalBytes;
+} altcp_mbedtls_malloc_stats_t;
+altcp_mbedtls_malloc_stats_t altcp_mbedtls_malloc_stats;
+volatile int altcp_mbedtls_malloc_clear_stats;
+#endif
+
+static void *
+tls_malloc(size_t c, size_t len)
+{
+  altcp_mbedtls_malloc_helper_t *hlpr;
+  void *ret;
+  size_t alloc_size;
+#if ALTCP_MBEDTLS_PLATFORM_ALLOC_STATS
+  if (altcp_mbedtls_malloc_clear_stats) {
+    altcp_mbedtls_malloc_clear_stats = 0;
+    memset(&altcp_mbedtls_malloc_stats, 0, sizeof(altcp_mbedtls_malloc_stats));
+  }
+#endif
+  alloc_size = sizeof(altcp_mbedtls_malloc_helper_t) + (c * len);
+  /* check for maximum allocation size, mainly to prevent mem_size_t overflow */
+  if (alloc_size > MEM_SIZE) {
+    LWIP_DEBUGF(ALTCP_MBEDTLS_MEM_DEBUG, ("mbedtls allocation too big: %c * %d bytes vs MEM_SIZE=%d",
+                                          (int)c, (int)len, (int)MEM_SIZE));
+    return NULL;
+  }
+  hlpr = (altcp_mbedtls_malloc_helper_t *)mem_malloc((mem_size_t)alloc_size);
+  if (hlpr == NULL) {
+    LWIP_DEBUGF(ALTCP_MBEDTLS_MEM_DEBUG, ("mbedtls alloc callback failed for %c * %d bytes", (int)c, (int)len));
+    return NULL;
+  }
+#if ALTCP_MBEDTLS_PLATFORM_ALLOC_STATS
+  altcp_mbedtls_malloc_stats.allocCnt++;
+  altcp_mbedtls_malloc_stats.allocedBytes += c * len;
+  if (altcp_mbedtls_malloc_stats.allocedBytes > altcp_mbedtls_malloc_stats.maxBytes) {
+    altcp_mbedtls_malloc_stats.maxBytes = altcp_mbedtls_malloc_stats.allocedBytes;
+  }
+  altcp_mbedtls_malloc_stats.totalBytes += c * len;
+#endif
+  hlpr->c = c;
+  hlpr->len = len;
+  ret = hlpr + 1;
+  /* zeroing the allocated chunk is required by mbedTLS! */
+  memset(ret, 0, c * len);
+  return ret;
+}
+
+static void
+tls_free(void *ptr)
+{
+  altcp_mbedtls_malloc_helper_t *hlpr;
+  if (ptr == NULL) {
+    /* this obviously happened in mbedtls... */
+    return;
+  }
+  hlpr = ((altcp_mbedtls_malloc_helper_t *)ptr) - 1;
+#if ALTCP_MBEDTLS_PLATFORM_ALLOC_STATS
+  if (!altcp_mbedtls_malloc_clear_stats) {
+    altcp_mbedtls_malloc_stats.allocedBytes -= hlpr->c * hlpr->len;
+  }
+#endif
+  mem_free(hlpr);
+}
+#endif /* ALTCP_MBEDTLS_PLATFORM_ALLOC*/
+
+void
+altcp_mbedtls_mem_init(void)
+{
+  /* not much to do here when using the heap */
+
+#if ALTCP_MBEDTLS_PLATFORM_ALLOC
+  /* set mbedtls allocation methods */
+  mbedtls_platform_set_calloc_free(&tls_malloc, &tls_free);
+#endif
+}
+
+altcp_mbedtls_state_t *
+altcp_mbedtls_alloc(void *conf)
+{
+  altcp_mbedtls_state_t *ret = (altcp_mbedtls_state_t *)mem_calloc(1, sizeof(altcp_mbedtls_state_t));
+  if (ret != NULL) {
+    ret->conf = conf;
+  }
+  return ret;
+}
+
+void
+altcp_mbedtls_free(void *conf, altcp_mbedtls_state_t *state)
+{
+  LWIP_UNUSED_ARG(conf);
+  LWIP_ASSERT("state != NULL", state != NULL);
+  mem_free(state);
+}
+
+void *
+altcp_mbedtls_alloc_config(size_t size)
+{
+  void *ret;
+  size_t checked_size = (mem_size_t)size;
+  if (size != checked_size) {
+    /* allocation too big (mem_size_t overflow) */
+    return NULL;
+  }
+  ret = (altcp_mbedtls_state_t *)mem_calloc(1, (mem_size_t)size);
+  return ret;
+}
+
+void
+altcp_mbedtls_free_config(void *item)
+{
+  LWIP_ASSERT("item != NULL", item != NULL);
+  mem_free(item);
+}
+
+#endif /* LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS */
+#endif /* LWIP_ALTCP */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/altcp_tls/altcp_tls_mbedtls_mem.h b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/altcp_tls/altcp_tls_mbedtls_mem.h
new file mode 100644 (file)
index 0000000..b391bf8
--- /dev/null
@@ -0,0 +1,72 @@
+/**
+ * @file
+ * Application layered TCP/TLS connection API (to be used from TCPIP thread)
+ *
+ * This file contains memory management function prototypes for a TLS layer using mbedTLS.
+ *
+ * Memory management contains:
+ * - allocating/freeing altcp_mbedtls_state_t
+ * - allocating/freeing memory used in the mbedTLS library
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_ALTCP_MBEDTLS_MEM_H
+#define LWIP_HDR_ALTCP_MBEDTLS_MEM_H
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/altcp_tls_mbedtls_opts.h"
+
+#if LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS
+
+#include "altcp_tls_mbedtls_structs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void altcp_mbedtls_mem_init(void);
+altcp_mbedtls_state_t *altcp_mbedtls_alloc(void *conf);
+void altcp_mbedtls_free(void *conf, altcp_mbedtls_state_t *state);
+void *altcp_mbedtls_alloc_config(size_t size);
+void altcp_mbedtls_free_config(void *item);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS */
+#endif /* LWIP_ALTCP */
+#endif /* LWIP_HDR_ALTCP_MBEDTLS_MEM_H */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/altcp_tls/altcp_tls_mbedtls_structs.h b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/altcp_tls/altcp_tls_mbedtls_structs.h
new file mode 100644 (file)
index 0000000..17efaaf
--- /dev/null
@@ -0,0 +1,83 @@
+/**
+ * @file
+ * Application layered TCP/TLS connection API (to be used from TCPIP thread)
+ *
+ * This file contains structure definitions for a TLS layer using mbedTLS.
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_ALTCP_MBEDTLS_STRUCTS_H
+#define LWIP_HDR_ALTCP_MBEDTLS_STRUCTS_H
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/altcp_tls_mbedtls_opts.h"
+
+#if LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS
+
+#include "lwip/altcp.h"
+#include "lwip/pbuf.h"
+
+#include "mbedtls/ssl.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE    0x01
+#define ALTCP_MBEDTLS_FLAGS_UPPER_CALLED      0x02
+#define ALTCP_MBEDTLS_FLAGS_RX_CLOSE_QUEUED   0x04
+#define ALTCP_MBEDTLS_FLAGS_RX_CLOSED         0x08
+#define ALTCP_MBEDTLS_FLAGS_APPLDATA_SENT     0x10
+
+typedef struct altcp_mbedtls_state_s {
+  void *conf;
+  mbedtls_ssl_context ssl_context;
+  /* chain of rx pbufs (before decryption) */
+  struct pbuf *rx;
+  struct pbuf *rx_app;
+  u8_t flags;
+  int rx_passed_unrecved;
+  int bio_bytes_read;
+  int bio_bytes_appl;
+} altcp_mbedtls_state_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS */
+#endif /* LWIP_ALTCP */
+#endif /* LWIP_HDR_ALTCP_MBEDTLS_STRUCTS_H */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/altcp_proxyconnect.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/altcp_proxyconnect.c
new file mode 100644 (file)
index 0000000..9a0b2ba
--- /dev/null
@@ -0,0 +1,584 @@
+/**
+ * @file
+ * Application layered TCP connection API that executes a proxy-connect.
+ *
+ * This file provides a starting layer that executes a proxy-connect e.g. to
+ * set up TLS connections through a http proxy.
+ */
+
+/*
+ * Copyright (c) 2018 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+
+#include "lwip/apps/altcp_proxyconnect.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/altcp.h"
+#include "lwip/priv/altcp_priv.h"
+
+#include "lwip/altcp_tcp.h"
+#include "lwip/altcp_tls.h"
+
+#include "lwip/mem.h"
+#include "lwip/init.h"
+
+#include <stdio.h>
+
+/** This string is passed in the HTTP header as "User-Agent: " */
+#ifndef ALTCP_PROXYCONNECT_CLIENT_AGENT
+#define ALTCP_PROXYCONNECT_CLIENT_AGENT "lwIP/" LWIP_VERSION_STRING " (http://savannah.nongnu.org/projects/lwip)"
+#endif
+
+#define ALTCP_PROXYCONNECT_FLAGS_CONNECT_STARTED  0x01
+#define ALTCP_PROXYCONNECT_FLAGS_HANDSHAKE_DONE   0x02
+
+typedef struct altcp_proxyconnect_state_s
+{
+  ip_addr_t outer_addr;
+  u16_t outer_port;
+  struct altcp_proxyconnect_config *conf;
+  u8_t flags;
+} altcp_proxyconnect_state_t;
+
+/* Variable prototype, the actual declaration is at the end of this file
+   since it contains pointers to static functions declared here */
+extern const struct altcp_functions altcp_proxyconnect_functions;
+
+/* memory management functions: */
+
+static altcp_proxyconnect_state_t *
+altcp_proxyconnect_state_alloc(void)
+{
+  altcp_proxyconnect_state_t *ret = (altcp_proxyconnect_state_t *)mem_calloc(1, sizeof(altcp_proxyconnect_state_t));
+  return ret;
+}
+
+static void
+altcp_proxyconnect_state_free(altcp_proxyconnect_state_t *state)
+{
+  LWIP_ASSERT("state != NULL", state != NULL);
+  mem_free(state);
+}
+
+/* helper functions */
+
+#define PROXY_CONNECT "CONNECT %s:%d HTTP/1.1\r\n" /* HOST, PORT */ \
+  "User-Agent: %s\r\n" /* User-Agent */\
+  "Proxy-Connection: keep-alive\r\n" \
+  "Connection: keep-alive\r\n" \
+  "\r\n"
+#define PROXY_CONNECT_FORMAT(host, port) PROXY_CONNECT, host, port, ALTCP_PROXYCONNECT_CLIENT_AGENT
+
+/* Format the http proxy connect request via snprintf */
+static int
+altcp_proxyconnect_format_request(char *buffer, size_t bufsize, const char *host, int port)
+{
+  return snprintf(buffer, bufsize, PROXY_CONNECT_FORMAT(host, port));
+}
+
+/* Create and send the http proxy connect request */
+static err_t
+altcp_proxyconnect_send_request(struct altcp_pcb *conn)
+{
+  int len, len2;
+  mem_size_t alloc_len;
+  char *buffer, *host;
+  altcp_proxyconnect_state_t *state = (altcp_proxyconnect_state_t *)conn->state;
+
+  if (!state) {
+    return ERR_VAL;
+  }
+  /* Use printf with zero length to get the required allocation size */
+  len = altcp_proxyconnect_format_request(NULL, 0, "", state->outer_port);
+  if (len < 0) {
+    return ERR_VAL;
+  }
+  /* add allocation size for IP address strings */
+#if LWIP_IPV6
+  len += 40; /* worst-case IPv6 address length */
+#else
+  len += 16; /* worst-case IPv4 address length */
+#endif
+  alloc_len = (mem_size_t)len;
+  if ((len < 0) || (int)alloc_len != len) {
+    /* overflow */
+    return ERR_MEM;
+  }
+  /* Allocate a bufer for the request string */
+  buffer = (char *)mem_malloc(alloc_len);
+  if (buffer == NULL) {
+    return ERR_MEM;
+  }
+  host = ipaddr_ntoa(&state->outer_addr);
+  len2 = altcp_proxyconnect_format_request(buffer, alloc_len, host, state->outer_port);
+  if ((len2 > 0) && (len2 <= len) && (len2 <= 0xFFFF)) {
+    err_t err = altcp_write(conn->inner_conn, buffer, (u16_t)len2, TCP_WRITE_FLAG_COPY);
+    if (err != ERR_OK) {
+      /* @todo: abort? */
+      mem_free(buffer);
+      return err;
+    }
+  }
+  mem_free(buffer);
+  return ERR_OK;
+}
+
+/* callback functions from inner/lower connection: */
+
+/** Connected callback from lower connection (i.e. TCP).
+ * Not really implemented/tested yet...
+ */
+static err_t
+altcp_proxyconnect_lower_connected(void *arg, struct altcp_pcb *inner_conn, err_t err)
+{
+  struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+  if (conn && conn->state) {
+    LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
+    LWIP_UNUSED_ARG(inner_conn); /* for LWIP_NOASSERT */
+    /* upper connected is called when handshake is done */
+    if (err != ERR_OK) {
+      if (conn->connected) {
+        if (conn->connected(conn->arg, conn, err) == ERR_ABRT) {
+          return ERR_ABRT;
+        }
+        return ERR_OK;
+      }
+    }
+    /* send proxy connect request here */
+    return altcp_proxyconnect_send_request(conn);
+  }
+  return ERR_VAL;
+}
+
+/** Recv callback from lower connection (i.e. TCP)
+ * This one mainly differs between connection setup (wait for proxy OK string)
+ * and application phase (data is passed on to the application).
+ */
+static err_t
+altcp_proxyconnect_lower_recv(void *arg, struct altcp_pcb *inner_conn, struct pbuf *p, err_t err)
+{
+  altcp_proxyconnect_state_t *state;
+  struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+
+  LWIP_ASSERT("no err expected", err == ERR_OK);
+  LWIP_UNUSED_ARG(err);
+
+  if (!conn) {
+    /* no connection given as arg? should not happen, but prevent pbuf/conn leaks */
+    if (p != NULL) {
+      pbuf_free(p);
+    }
+    altcp_close(inner_conn);
+    return ERR_CLSD;
+  }
+  state = (altcp_proxyconnect_state_t *)conn->state;
+  LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
+  if (!state) {
+    /* already closed */
+    if (p != NULL) {
+      pbuf_free(p);
+    }
+    altcp_close(inner_conn);
+    return ERR_CLSD;
+  }
+  if (state->flags & ALTCP_PROXYCONNECT_FLAGS_HANDSHAKE_DONE) {
+    /* application phase, just pass this through */
+    if (conn->recv) {
+      return conn->recv(conn->arg, conn, p, err);
+    }
+    pbuf_free(p);
+    return ERR_OK;
+  } else {
+    /* setup phase */
+    /* handle NULL pbuf (inner connection closed) */
+    if (p == NULL) {
+      if (altcp_close(conn) != ERR_OK) {
+        altcp_abort(conn);
+        return ERR_ABRT;
+      }
+      return ERR_OK;
+    } else {
+      /* @todo: parse setup phase rx data
+         for now, we just wait for the end of the header... */
+      u16_t idx = pbuf_memfind(p, "\r\n\r\n", 4, 0);
+      altcp_recved(inner_conn, p->tot_len);
+      pbuf_free(p);
+      if (idx != 0xFFFF) {
+        state->flags |= ALTCP_PROXYCONNECT_FLAGS_HANDSHAKE_DONE;
+        if (conn->connected) {
+          return conn->connected(conn->arg, conn, ERR_OK);
+        }
+      }
+      return ERR_OK;
+    }
+  }
+}
+
+/** Sent callback from lower connection (i.e. TCP)
+ * This only informs the upper layer to try to send more, not about
+ * the number of ACKed bytes.
+ */
+static err_t
+altcp_proxyconnect_lower_sent(void *arg, struct altcp_pcb *inner_conn, u16_t len)
+{
+  struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+  LWIP_UNUSED_ARG(len);
+  if (conn) {
+    altcp_proxyconnect_state_t *state = (altcp_proxyconnect_state_t *)conn->state;
+    LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
+    LWIP_UNUSED_ARG(inner_conn); /* for LWIP_NOASSERT */
+    if (!state || !(state->flags & ALTCP_PROXYCONNECT_FLAGS_HANDSHAKE_DONE)) {
+      /* @todo: do something here? */
+      return ERR_OK;
+    }
+    /* pass this on to upper sent */
+    if (conn->sent) {
+      return conn->sent(conn->arg, conn, len);
+    }
+  }
+  return ERR_OK;
+}
+
+/** Poll callback from lower connection (i.e. TCP)
+ * Just pass this on to the application.
+ * @todo: retry sending?
+ */
+static err_t
+altcp_proxyconnect_lower_poll(void *arg, struct altcp_pcb *inner_conn)
+{
+  struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+  if (conn) {
+    LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
+    LWIP_UNUSED_ARG(inner_conn); /* for LWIP_NOASSERT */
+    if (conn->poll) {
+      return conn->poll(conn->arg, conn);
+    }
+  }
+  return ERR_OK;
+}
+
+static void
+altcp_proxyconnect_lower_err(void *arg, err_t err)
+{
+  struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+  if (conn) {
+    conn->inner_conn = NULL; /* already freed */
+    if (conn->err) {
+      conn->err(conn->arg, err);
+    }
+    altcp_free(conn);
+  }
+}
+
+
+/* setup functions */
+
+static void
+altcp_proxyconnect_setup_callbacks(struct altcp_pcb *conn, struct altcp_pcb *inner_conn)
+{
+  altcp_arg(inner_conn, conn);
+  altcp_recv(inner_conn, altcp_proxyconnect_lower_recv);
+  altcp_sent(inner_conn, altcp_proxyconnect_lower_sent);
+  altcp_err(inner_conn, altcp_proxyconnect_lower_err);
+  /* tcp_poll is set when interval is set by application */
+  /* listen is set totally different :-) */
+}
+
+static err_t
+altcp_proxyconnect_setup(struct altcp_proxyconnect_config *config, struct altcp_pcb *conn, struct altcp_pcb *inner_conn)
+{
+  altcp_proxyconnect_state_t *state;
+  if (!config) {
+    return ERR_ARG;
+  }
+  LWIP_ASSERT("invalid inner_conn", conn != inner_conn);
+
+  /* allocate proxyconnect context */
+  state = altcp_proxyconnect_state_alloc();
+  if (state == NULL) {
+    return ERR_MEM;
+  }
+  state->flags = 0;
+  state->conf = config;
+  altcp_proxyconnect_setup_callbacks(conn, inner_conn);
+  conn->inner_conn = inner_conn;
+  conn->fns = &altcp_proxyconnect_functions;
+  conn->state = state;
+  return ERR_OK;
+}
+
+/** Allocate a new altcp layer connecting through a proxy.
+ * This function gets the inner pcb passed.
+ *
+ * @param config struct altcp_proxyconnect_config that contains the proxy settings
+ * @param inner_pcb pcb that makes the connection to the proxy (i.e. tcp pcb)
+ */
+struct altcp_pcb *
+altcp_proxyconnect_new(struct altcp_proxyconnect_config *config, struct altcp_pcb *inner_pcb)
+{
+  struct altcp_pcb *ret;
+  if (inner_pcb == NULL) {
+    return NULL;
+  }
+  ret = altcp_alloc();
+  if (ret != NULL) {
+    if (altcp_proxyconnect_setup(config, ret, inner_pcb) != ERR_OK) {
+      altcp_free(ret);
+      return NULL;
+    }
+  }
+  return ret;
+}
+
+/** Allocate a new altcp layer connecting through a proxy.
+ * This function allocates the inner pcb as tcp pcb, resulting in a direct tcp
+ * connection to the proxy.
+ *
+ * @param config struct altcp_proxyconnect_config that contains the proxy settings
+ * @param ip_type IP type of the connection (@ref lwip_ip_addr_type)
+ */
+struct altcp_pcb *
+altcp_proxyconnect_new_tcp(struct altcp_proxyconnect_config *config, u8_t ip_type)
+{
+  struct altcp_pcb *inner_pcb, *ret;
+
+  /* inner pcb is tcp */
+  inner_pcb = altcp_tcp_new_ip_type(ip_type);
+  if (inner_pcb == NULL) {
+    return NULL;
+  }
+  ret = altcp_proxyconnect_new(config, inner_pcb);
+  if (ret == NULL) {
+    altcp_close(inner_pcb);
+  }
+  return ret;
+}
+
+/** Allocator function to allocate a proxy connect altcp pcb connecting directly
+ * via tcp to the proxy.
+ *
+ * The returned pcb is a chain: altcp_proxyconnect - altcp_tcp - tcp pcb
+ *
+ * This function is meant for use with @ref altcp_new.
+ *
+ * @param arg struct altcp_proxyconnect_config that contains the proxy settings
+ * @param ip_type IP type of the connection (@ref lwip_ip_addr_type)
+ */
+struct altcp_pcb *
+altcp_proxyconnect_alloc(void *arg, u8_t ip_type)
+{
+  return altcp_proxyconnect_new_tcp((struct altcp_proxyconnect_config *)arg, ip_type);
+}
+
+
+#if LWIP_ALTCP_TLS
+
+/** Allocator function to allocate a TLS connection through a proxy.
+ *
+ * The returned pcb is a chain: altcp_tls - altcp_proxyconnect - altcp_tcp - tcp pcb
+ *
+ * This function is meant for use with @ref altcp_new.
+ *
+ * @param arg struct altcp_proxyconnect_tls_config that contains the proxy settings
+ *        and tls settings
+ * @param ip_type IP type of the connection (@ref lwip_ip_addr_type)
+ */
+struct altcp_pcb *
+altcp_proxyconnect_tls_alloc(void *arg, u8_t ip_type)
+{
+  struct altcp_proxyconnect_tls_config *cfg = (struct altcp_proxyconnect_tls_config *)arg;
+  struct altcp_pcb *proxy_pcb;
+  struct altcp_pcb *tls_pcb;
+
+  proxy_pcb = altcp_proxyconnect_new_tcp(&cfg->proxy, ip_type);
+  tls_pcb = altcp_tls_wrap(cfg->tls_config, proxy_pcb);
+
+  if (tls_pcb == NULL) {
+    altcp_close(proxy_pcb);
+  }
+  return tls_pcb;
+}
+#endif /* LWIP_ALTCP_TLS */
+
+/* "virtual" functions */
+static void
+altcp_proxyconnect_set_poll(struct altcp_pcb *conn, u8_t interval)
+{
+  if (conn != NULL) {
+    altcp_poll(conn->inner_conn, altcp_proxyconnect_lower_poll, interval);
+  }
+}
+
+static void
+altcp_proxyconnect_recved(struct altcp_pcb *conn, u16_t len)
+{
+  altcp_proxyconnect_state_t *state;
+  if (conn == NULL) {
+    return;
+  }
+  state = (altcp_proxyconnect_state_t *)conn->state;
+  if (state == NULL) {
+    return;
+  }
+  if (!(state->flags & ALTCP_PROXYCONNECT_FLAGS_HANDSHAKE_DONE)) {
+    return;
+  }
+  altcp_recved(conn->inner_conn, len);
+}
+
+static err_t
+altcp_proxyconnect_connect(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected)
+{
+  altcp_proxyconnect_state_t *state;
+
+  if ((conn == NULL) || (ipaddr == NULL)) {
+    return ERR_VAL;
+  }
+  state = (altcp_proxyconnect_state_t *)conn->state;
+  if (state == NULL) {
+    return ERR_VAL;
+  }
+  if (state->flags & ALTCP_PROXYCONNECT_FLAGS_CONNECT_STARTED) {
+    return ERR_VAL;
+  }
+  state->flags |= ALTCP_PROXYCONNECT_FLAGS_CONNECT_STARTED;
+
+  conn->connected = connected;
+  /* connect to our proxy instead, but store the requested address and port */
+  ip_addr_copy(state->outer_addr, *ipaddr);
+  state->outer_port = port;
+
+  return altcp_connect(conn->inner_conn, &state->conf->proxy_addr, state->conf->proxy_port, altcp_proxyconnect_lower_connected);
+}
+
+static struct altcp_pcb *
+altcp_proxyconnect_listen(struct altcp_pcb *conn, u8_t backlog, err_t *err)
+{
+  LWIP_UNUSED_ARG(conn);
+  LWIP_UNUSED_ARG(backlog);
+  LWIP_UNUSED_ARG(err);
+  /* listen not supported! */
+  return NULL;
+}
+
+static void
+altcp_proxyconnect_abort(struct altcp_pcb *conn)
+{
+  if (conn != NULL) {
+    if (conn->inner_conn != NULL) {
+      altcp_abort(conn->inner_conn);
+    }
+    altcp_free(conn);
+  }
+}
+
+static err_t
+altcp_proxyconnect_close(struct altcp_pcb *conn)
+{
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+  if (conn->inner_conn != NULL) {
+    err_t err = altcp_close(conn->inner_conn);
+    if (err != ERR_OK) {
+      /* closing inner conn failed, return the error */
+      return err;
+    }
+  }
+  /* no inner conn or closing it succeeded, deallocate myself */
+  altcp_free(conn);
+  return ERR_OK;
+}
+
+static err_t
+altcp_proxyconnect_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags)
+{
+  altcp_proxyconnect_state_t *state;
+
+  LWIP_UNUSED_ARG(apiflags);
+
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+
+  state = (altcp_proxyconnect_state_t *)conn->state;
+  if (state == NULL) {
+    /* @todo: which error? */
+    return ERR_CLSD;
+  }
+  if (!(state->flags & ALTCP_PROXYCONNECT_FLAGS_HANDSHAKE_DONE)) {
+    /* @todo: which error? */
+    return ERR_VAL;
+  }
+  return altcp_write(conn->inner_conn, dataptr, len, apiflags);
+}
+
+static void
+altcp_proxyconnect_dealloc(struct altcp_pcb *conn)
+{
+  /* clean up and free tls state */
+  if (conn) {
+    altcp_proxyconnect_state_t *state = (altcp_proxyconnect_state_t *)conn->state;
+    if (state) {
+      altcp_proxyconnect_state_free(state);
+      conn->state = NULL;
+    }
+  }
+}
+const struct altcp_functions altcp_proxyconnect_functions = {
+  altcp_proxyconnect_set_poll,
+  altcp_proxyconnect_recved,
+  altcp_default_bind,
+  altcp_proxyconnect_connect,
+  altcp_proxyconnect_listen,
+  altcp_proxyconnect_abort,
+  altcp_proxyconnect_close,
+  altcp_default_shutdown,
+  altcp_proxyconnect_write,
+  altcp_default_output,
+  altcp_default_mss,
+  altcp_default_sndbuf,
+  altcp_default_sndqueuelen,
+  altcp_default_nagle_disable,
+  altcp_default_nagle_enable,
+  altcp_default_nagle_disabled,
+  altcp_default_setprio,
+  altcp_proxyconnect_dealloc,
+  altcp_default_get_tcp_addrinfo,
+  altcp_default_get_ip,
+  altcp_default_get_port
+#ifdef LWIP_DEBUG
+  , altcp_default_dbg_get_tcp_state
+#endif
+};
+
+#endif /* LWIP_ALTCP */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/fs.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/fs.c
new file mode 100644 (file)
index 0000000..f15a480
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/apps/httpd_opts.h"
+#include "lwip/def.h"
+#include "lwip/apps/fs.h"
+#include <string.h>
+
+
+#include HTTPD_FSDATA_FILE
+
+/*-----------------------------------------------------------------------------------*/
+
+#if LWIP_HTTPD_CUSTOM_FILES
+int fs_open_custom(struct fs_file *file, const char *name);
+void fs_close_custom(struct fs_file *file);
+#if LWIP_HTTPD_FS_ASYNC_READ
+u8_t fs_canread_custom(struct fs_file *file);
+u8_t fs_wait_read_custom(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg);
+int fs_read_async_custom(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg);
+#else /* LWIP_HTTPD_FS_ASYNC_READ */
+int fs_read_custom(struct fs_file *file, char *buffer, int count);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+
+/*-----------------------------------------------------------------------------------*/
+err_t
+fs_open(struct fs_file *file, const char *name)
+{
+  const struct fsdata_file *f;
+
+  if ((file == NULL) || (name == NULL)) {
+    return ERR_ARG;
+  }
+
+#if LWIP_HTTPD_CUSTOM_FILES
+  if (fs_open_custom(file, name)) {
+    file->is_custom_file = 1;
+    return ERR_OK;
+  }
+  file->is_custom_file = 0;
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+
+  for (f = FS_ROOT; f != NULL; f = f->next) {
+    if (!strcmp(name, (const char *)f->name)) {
+      file->data = (const char *)f->data;
+      file->len = f->len;
+      file->index = f->len;
+      file->pextension = NULL;
+      file->flags = f->flags;
+#if HTTPD_PRECALCULATED_CHECKSUM
+      file->chksum_count = f->chksum_count;
+      file->chksum = f->chksum;
+#endif /* HTTPD_PRECALCULATED_CHECKSUM */
+#if LWIP_HTTPD_FILE_STATE
+      file->state = fs_state_init(file, name);
+#endif /* #if LWIP_HTTPD_FILE_STATE */
+      return ERR_OK;
+    }
+  }
+  /* file not found */
+  return ERR_VAL;
+}
+
+/*-----------------------------------------------------------------------------------*/
+void
+fs_close(struct fs_file *file)
+{
+#if LWIP_HTTPD_CUSTOM_FILES
+  if (file->is_custom_file) {
+    fs_close_custom(file);
+  }
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+#if LWIP_HTTPD_FILE_STATE
+  fs_state_free(file, file->state);
+#endif /* #if LWIP_HTTPD_FILE_STATE */
+  LWIP_UNUSED_ARG(file);
+}
+/*-----------------------------------------------------------------------------------*/
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+#if LWIP_HTTPD_FS_ASYNC_READ
+int
+fs_read_async(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg)
+#else /* LWIP_HTTPD_FS_ASYNC_READ */
+int
+fs_read(struct fs_file *file, char *buffer, int count)
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+{
+  int read;
+  if (file->index == file->len) {
+    return FS_READ_EOF;
+  }
+#if LWIP_HTTPD_FS_ASYNC_READ
+  LWIP_UNUSED_ARG(callback_fn);
+  LWIP_UNUSED_ARG(callback_arg);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+#if LWIP_HTTPD_CUSTOM_FILES
+  if (file->is_custom_file) {
+#if LWIP_HTTPD_FS_ASYNC_READ
+    return fs_read_async_custom(file, buffer, count, callback_fn, callback_arg);
+#else /* LWIP_HTTPD_FS_ASYNC_READ */
+    return fs_read_custom(file, buffer, count);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+  }
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+
+  read = file->len - file->index;
+  if (read > count) {
+    read = count;
+  }
+
+  MEMCPY(buffer, (file->data + file->index), read);
+  file->index += read;
+
+  return (read);
+}
+#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
+/*-----------------------------------------------------------------------------------*/
+#if LWIP_HTTPD_FS_ASYNC_READ
+int
+fs_is_file_ready(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg)
+{
+  if (file != NULL) {
+#if LWIP_HTTPD_FS_ASYNC_READ
+#if LWIP_HTTPD_CUSTOM_FILES
+    if (!fs_canread_custom(file)) {
+      if (fs_wait_read_custom(file, callback_fn, callback_arg)) {
+        return 0;
+      }
+    }
+#else /* LWIP_HTTPD_CUSTOM_FILES */
+    LWIP_UNUSED_ARG(callback_fn);
+    LWIP_UNUSED_ARG(callback_arg);
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+  }
+  return 1;
+}
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+/*-----------------------------------------------------------------------------------*/
+int
+fs_bytes_left(struct fs_file *file)
+{
+  return file->len - file->index;
+}
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/fs/404.html b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/fs/404.html
new file mode 100644 (file)
index 0000000..40b343a
--- /dev/null
@@ -0,0 +1,21 @@
+<html>
+<head><title>lwIP - A Lightweight TCP/IP Stack</title></head>
+<body bgcolor="white" text="black">
+
+    <table width="100%">
+      <tr valign="top"><td width="80">   
+         <a href="http://www.sics.se/"><img src="/img/sics.gif"
+         border="0" alt="SICS logo" title="SICS logo"></a>
+       </td><td width="500">     
+         <h1>lwIP - A Lightweight TCP/IP Stack</h1>
+         <h2>404 - Page not found</h2>
+         <p>
+           Sorry, the page you are requesting was not found on this
+           server. 
+         </p>
+       </td><td>
+         &nbsp;
+       </td></tr>
+      </table>
+</body>
+</html>
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/fs/img/sics.gif b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/fs/img/sics.gif
new file mode 100644 (file)
index 0000000..0a4fc7b
Binary files /dev/null and b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/fs/img/sics.gif differ
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/fs/index.html b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/fs/index.html
new file mode 100644 (file)
index 0000000..ab575ef
--- /dev/null
@@ -0,0 +1,47 @@
+<html>
+<head><title>lwIP - A Lightweight TCP/IP Stack</title></head>
+<body bgcolor="white" text="black">
+
+    <table width="100%">
+      <tr valign="top"><td width="80">   
+         <a href="http://www.sics.se/"><img src="/img/sics.gif"
+         border="0" alt="SICS logo" title="SICS logo"></a>
+       </td><td width="500">     
+         <h1>lwIP - A Lightweight TCP/IP Stack</h1>
+         <p>
+           The web page you are watching was served by a simple web
+           server running on top of the lightweight TCP/IP stack <a
+           href="http://www.sics.se/~adam/lwip/">lwIP</a>.
+         </p>
+         <p>
+           lwIP is an open source implementation of the TCP/IP
+           protocol suite that was originally written by <a
+           href="http://www.sics.se/~adam/lwip/">Adam Dunkels
+           of the Swedish Institute of Computer Science</a> but now is
+           being actively developed by a team of developers
+           distributed world-wide. Since it's release, lwIP has
+           spurred a lot of interest and has been ported to several
+           platforms and operating systems. lwIP can be used either
+           with or without an underlying OS.
+         </p>
+         <p>
+           The focus of the lwIP TCP/IP implementation is to reduce
+           the RAM usage while still having a full scale TCP. This
+           makes lwIP suitable for use in embedded systems with tens
+           of kilobytes of free RAM and room for around 40 kilobytes
+           of code ROM.
+         </p>
+         <p>
+           More information about lwIP can be found at the lwIP
+           homepage at <a
+           href="http://savannah.nongnu.org/projects/lwip/">http://savannah.nongnu.org/projects/lwip/</a>
+           or at the lwIP wiki at <a
+           href="http://lwip.wikia.com/">http://lwip.wikia.com/</a>.
+         </p>
+       </td><td>
+         &nbsp;
+       </td></tr>
+      </table>
+</body>
+</html>
+
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/fsdata.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/fsdata.c
new file mode 100644 (file)
index 0000000..ab9da7e
--- /dev/null
@@ -0,0 +1,337 @@
+#include "lwip/apps/fs.h"
+#include "lwip/def.h"
+
+
+#define file_NULL (struct fsdata_file *) NULL
+
+
+#ifndef FS_FILE_FLAGS_HEADER_INCLUDED
+#define FS_FILE_FLAGS_HEADER_INCLUDED 1
+#endif
+#ifndef FS_FILE_FLAGS_HEADER_PERSISTENT
+#define FS_FILE_FLAGS_HEADER_PERSISTENT 0
+#endif
+/* FSDATA_FILE_ALIGNMENT: 0=off, 1=by variable, 2=by include */
+#ifndef FSDATA_FILE_ALIGNMENT
+#define FSDATA_FILE_ALIGNMENT 0
+#endif
+#ifndef FSDATA_ALIGN_PRE
+#define FSDATA_ALIGN_PRE
+#endif
+#ifndef FSDATA_ALIGN_POST
+#define FSDATA_ALIGN_POST
+#endif
+#if FSDATA_FILE_ALIGNMENT==2
+#include "fsdata_alignment.h"
+#endif
+#if FSDATA_FILE_ALIGNMENT==1
+static const unsigned int dummy_align__img_sics_gif = 0;
+#endif
+static const unsigned char FSDATA_ALIGN_PRE data__img_sics_gif[] FSDATA_ALIGN_POST = {
+/* /img/sics.gif (14 chars) */
+0x2f,0x69,0x6d,0x67,0x2f,0x73,0x69,0x63,0x73,0x2e,0x67,0x69,0x66,0x00,0x00,0x00,
+
+/* HTTP header */
+/* "HTTP/1.0 200 OK
+" (17 bytes) */
+0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x32,0x30,0x30,0x20,0x4f,0x4b,0x0d,
+0x0a,
+/* "Server: lwIP/2.0.3d (http://savannah.nongnu.org/projects/lwip)
+" (64 bytes) */
+0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x32,0x2e,0x30,
+0x2e,0x33,0x64,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,
+0x6e,0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,
+0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a,
+
+/* "Content-Length: 724
+" (18+ bytes) */
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20,
+0x37,0x32,0x34,0x0d,0x0a,
+/* "Content-Type: image/gif
+
+" (27 bytes) */
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,0x79,0x70,0x65,0x3a,0x20,0x69,0x6d,
+0x61,0x67,0x65,0x2f,0x67,0x69,0x66,0x0d,0x0a,0x0d,0x0a,
+/* raw file data (724 bytes) */
+0x47,0x49,0x46,0x38,0x39,0x61,0x46,0x00,0x22,0x00,0xa5,0x00,0x00,0xd9,0x2b,0x39,
+0x6a,0x6a,0x6a,0xbf,0xbf,0xbf,0x93,0x93,0x93,0x0f,0x0f,0x0f,0xb0,0xb0,0xb0,0xa6,
+0xa6,0xa6,0x80,0x80,0x80,0x76,0x76,0x76,0x1e,0x1e,0x1e,0x9d,0x9d,0x9d,0x2e,0x2e,
+0x2e,0x49,0x49,0x49,0x54,0x54,0x54,0x8a,0x8a,0x8a,0x60,0x60,0x60,0xc6,0xa6,0x99,
+0xbd,0xb5,0xb2,0xc2,0xab,0xa1,0xd9,0x41,0x40,0xd5,0x67,0x55,0xc0,0xb0,0xaa,0xd5,
+0x5e,0x4e,0xd6,0x50,0x45,0xcc,0x93,0x7d,0xc8,0xa1,0x90,0xce,0x8b,0x76,0xd2,0x7b,
+0x65,0xd1,0x84,0x6d,0xc9,0x99,0x86,0x3a,0x3a,0x3a,0x00,0x00,0x00,0xb8,0xb8,0xb8,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x2c,0x00,0x00,
+0x00,0x00,0x46,0x00,0x22,0x00,0x00,0x06,0xfe,0x40,0x90,0x70,0x48,0x2c,0x1a,0x8f,
+0xc8,0xa4,0x72,0xc9,0x6c,0x3a,0x9f,0xd0,0xa8,0x74,0x4a,0xad,0x5a,0xaf,0xd8,0xac,
+0x76,0xa9,0x40,0x04,0xbe,0x83,0xe2,0x60,0x3c,0x50,0x20,0x0d,0x8e,0x6f,0x00,0x31,
+0x28,0x1c,0x0d,0x07,0xb5,0xc3,0x60,0x75,0x24,0x3e,0xf8,0xfc,0x87,0x11,0x06,0xe9,
+0x3d,0x46,0x07,0x0b,0x7a,0x7a,0x7c,0x43,0x06,0x1e,0x84,0x78,0x0b,0x07,0x6e,0x51,
+0x01,0x8a,0x84,0x08,0x7e,0x79,0x80,0x87,0x89,0x91,0x7a,0x93,0x0a,0x04,0x99,0x78,
+0x96,0x4f,0x03,0x9e,0x79,0x01,0x94,0x9f,0x43,0x9c,0xa3,0xa4,0x05,0x77,0xa3,0xa0,
+0x4e,0x98,0x79,0x0b,0x1e,0x83,0xa4,0xa6,0x1f,0x96,0x05,0x9d,0xaa,0x78,0x01,0x07,
+0x84,0x04,0x1e,0x1e,0xbb,0xb8,0x51,0x84,0x0e,0x43,0x05,0x07,0x77,0xa5,0x7f,0x42,
+0xb1,0xb2,0x01,0x63,0x08,0x0d,0xbb,0x01,0x0c,0x7a,0x0d,0x44,0x0e,0xd8,0xaf,0x4c,
+0x05,0x7a,0x04,0x47,0x07,0x07,0xb7,0x80,0xa2,0xe1,0x7d,0x44,0x05,0x01,0x04,0x01,
+0xd0,0xea,0x87,0x93,0x4f,0xe0,0x9a,0x49,0xce,0xd8,0x79,0x04,0x66,0x20,0x15,0x10,
+0x10,0x11,0x92,0x29,0x80,0xb6,0xc0,0x91,0x15,0x45,0x1e,0x90,0x19,0x71,0x46,0xa8,
+0x5c,0x04,0x0e,0x00,0x22,0x4e,0xe8,0x40,0x24,0x9f,0x3e,0x04,0x06,0xa7,0x58,0xd4,
+0x93,0xa0,0x1c,0x91,0x3f,0xe8,0xf0,0x88,0x03,0xb1,0x21,0xa2,0x49,0x00,0x19,0x86,
+0xfc,0x52,0x44,0xe0,0x01,0x9d,0x29,0x21,0x15,0x25,0x50,0xf7,0x67,0x25,0x1e,0x06,
+0xfd,0x4e,0x9a,0xb4,0x90,0xac,0x15,0xfa,0xcb,0x52,0x53,0x1e,0x8c,0xf2,0xf8,0x07,
+0x92,0x2d,0x08,0x3a,0x4d,0x12,0x49,0x95,0x49,0xdb,0x14,0x04,0xc4,0x14,0x85,0x29,
+0xaa,0xe7,0x01,0x08,0xa4,0x49,0x01,0x14,0x51,0xe0,0x53,0x91,0xd5,0x29,0x06,0x1a,
+0x64,0x02,0xf4,0xc7,0x81,0x9e,0x05,0x20,0x22,0x64,0xa5,0x30,0xae,0xab,0x9e,0x97,
+0x53,0xd8,0xb9,0xfd,0x50,0xef,0x93,0x02,0x42,0x74,0x34,0xe8,0x9c,0x20,0x21,0xc9,
+0x01,0x68,0x78,0xe6,0x55,0x29,0x20,0x56,0x4f,0x4c,0x40,0x51,0x71,0x82,0xc0,0x70,
+0x21,0x22,0x85,0xbe,0x4b,0x1c,0x44,0x05,0xea,0xa4,0x01,0xbf,0x22,0xb5,0xf0,0x1c,
+0x06,0x51,0x38,0x8f,0xe0,0x22,0xec,0x18,0xac,0x39,0x22,0xd4,0xd6,0x93,0x44,0x01,
+0x32,0x82,0xc8,0xfc,0x61,0xb3,0x01,0x45,0x0c,0x2e,0x83,0x30,0xd0,0x0e,0x17,0x24,
+0x0f,0x70,0x85,0x94,0xee,0x05,0x05,0x53,0x4b,0x32,0x1b,0x3f,0x98,0xd3,0x1d,0x29,
+0x81,0xb0,0xae,0x1e,0x8c,0x7e,0x68,0xe0,0x60,0x5a,0x54,0x8f,0xb0,0x78,0x69,0x73,
+0x06,0xa2,0x00,0x6b,0x57,0xca,0x3d,0x11,0x50,0xbd,0x04,0x30,0x4b,0x3a,0xd4,0xab,
+0x5f,0x1f,0x9b,0x3d,0x13,0x74,0x27,0x88,0x3c,0x25,0xe0,0x17,0xbe,0x7a,0x79,0x45,
+0x0d,0x0c,0xb0,0x8b,0xda,0x90,0xca,0x80,0x06,0x5d,0x17,0x60,0x1c,0x22,0x4c,0xd8,
+0x57,0x22,0x06,0x20,0x00,0x98,0x07,0x08,0xe4,0x56,0x80,0x80,0x1c,0xc5,0xb7,0xc5,
+0x82,0x0c,0x36,0xe8,0xe0,0x83,0x10,0x46,0x28,0xe1,0x84,0x14,0x56,0x68,0xa1,0x10,
+0x41,0x00,0x00,0x3b,};
+
+#if FSDATA_FILE_ALIGNMENT==1
+static const unsigned int dummy_align__404_html = 1;
+#endif
+static const unsigned char FSDATA_ALIGN_PRE data__404_html[] FSDATA_ALIGN_POST = {
+/* /404.html (10 chars) */
+0x2f,0x34,0x30,0x34,0x2e,0x68,0x74,0x6d,0x6c,0x00,0x00,0x00,
+
+/* HTTP header */
+/* "HTTP/1.0 404 File not found
+" (29 bytes) */
+0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x34,0x30,0x34,0x20,0x46,0x69,0x6c,
+0x65,0x20,0x6e,0x6f,0x74,0x20,0x66,0x6f,0x75,0x6e,0x64,0x0d,0x0a,
+/* "Server: lwIP/2.0.3d (http://savannah.nongnu.org/projects/lwip)
+" (64 bytes) */
+0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x32,0x2e,0x30,
+0x2e,0x33,0x64,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,
+0x6e,0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,
+0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a,
+
+/* "Content-Length: 565
+" (18+ bytes) */
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20,
+0x35,0x36,0x35,0x0d,0x0a,
+/* "Content-Type: text/html
+
+" (27 bytes) */
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,0x79,0x70,0x65,0x3a,0x20,0x74,0x65,
+0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x0d,0x0a,0x0d,0x0a,
+/* raw file data (565 bytes) */
+0x3c,0x68,0x74,0x6d,0x6c,0x3e,0x0d,0x0a,0x3c,0x68,0x65,0x61,0x64,0x3e,0x3c,0x74,
+0x69,0x74,0x6c,0x65,0x3e,0x6c,0x77,0x49,0x50,0x20,0x2d,0x20,0x41,0x20,0x4c,0x69,
+0x67,0x68,0x74,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,0x50,
+0x20,0x53,0x74,0x61,0x63,0x6b,0x3c,0x2f,0x74,0x69,0x74,0x6c,0x65,0x3e,0x3c,0x2f,
+0x68,0x65,0x61,0x64,0x3e,0x0d,0x0a,0x3c,0x62,0x6f,0x64,0x79,0x20,0x62,0x67,0x63,
+0x6f,0x6c,0x6f,0x72,0x3d,0x22,0x77,0x68,0x69,0x74,0x65,0x22,0x20,0x74,0x65,0x78,
+0x74,0x3d,0x22,0x62,0x6c,0x61,0x63,0x6b,0x22,0x3e,0x0d,0x0a,0x0d,0x0a,0x20,0x20,
+0x20,0x20,0x3c,0x74,0x61,0x62,0x6c,0x65,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,0x22,
+0x31,0x30,0x30,0x25,0x22,0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x74,
+0x72,0x20,0x76,0x61,0x6c,0x69,0x67,0x6e,0x3d,0x22,0x74,0x6f,0x70,0x22,0x3e,0x3c,
+0x74,0x64,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,0x22,0x38,0x30,0x22,0x3e,0x09,0x20,
+0x20,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x61,0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68,
+0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x73,0x69,0x63,0x73,0x2e,0x73,
+0x65,0x2f,0x22,0x3e,0x3c,0x69,0x6d,0x67,0x20,0x73,0x72,0x63,0x3d,0x22,0x2f,0x69,
+0x6d,0x67,0x2f,0x73,0x69,0x63,0x73,0x2e,0x67,0x69,0x66,0x22,0x0d,0x0a,0x09,0x20,
+0x20,0x62,0x6f,0x72,0x64,0x65,0x72,0x3d,0x22,0x30,0x22,0x20,0x61,0x6c,0x74,0x3d,
+0x22,0x53,0x49,0x43,0x53,0x20,0x6c,0x6f,0x67,0x6f,0x22,0x20,0x74,0x69,0x74,0x6c,
+0x65,0x3d,0x22,0x53,0x49,0x43,0x53,0x20,0x6c,0x6f,0x67,0x6f,0x22,0x3e,0x3c,0x2f,
+0x61,0x3e,0x0d,0x0a,0x09,0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x74,0x64,0x20,0x77,0x69,
+0x64,0x74,0x68,0x3d,0x22,0x35,0x30,0x30,0x22,0x3e,0x09,0x20,0x20,0x0d,0x0a,0x09,
+0x20,0x20,0x3c,0x68,0x31,0x3e,0x6c,0x77,0x49,0x50,0x20,0x2d,0x20,0x41,0x20,0x4c,
+0x69,0x67,0x68,0x74,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,
+0x50,0x20,0x53,0x74,0x61,0x63,0x6b,0x3c,0x2f,0x68,0x31,0x3e,0x0d,0x0a,0x09,0x20,
+0x20,0x3c,0x68,0x32,0x3e,0x34,0x30,0x34,0x20,0x2d,0x20,0x50,0x61,0x67,0x65,0x20,
+0x6e,0x6f,0x74,0x20,0x66,0x6f,0x75,0x6e,0x64,0x3c,0x2f,0x68,0x32,0x3e,0x0d,0x0a,
+0x09,0x20,0x20,0x3c,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x53,0x6f,0x72,
+0x72,0x79,0x2c,0x20,0x74,0x68,0x65,0x20,0x70,0x61,0x67,0x65,0x20,0x79,0x6f,0x75,
+0x20,0x61,0x72,0x65,0x20,0x72,0x65,0x71,0x75,0x65,0x73,0x74,0x69,0x6e,0x67,0x20,
+0x77,0x61,0x73,0x20,0x6e,0x6f,0x74,0x20,0x66,0x6f,0x75,0x6e,0x64,0x20,0x6f,0x6e,
+0x20,0x74,0x68,0x69,0x73,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x73,0x65,0x72,0x76,
+0x65,0x72,0x2e,0x20,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a,0x09,
+0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x74,0x64,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x26,0x6e,
+0x62,0x73,0x70,0x3b,0x0d,0x0a,0x09,0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x2f,0x74,0x72,
+0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x2f,0x74,0x61,0x62,0x6c,0x65,
+0x3e,0x0d,0x0a,0x3c,0x2f,0x62,0x6f,0x64,0x79,0x3e,0x0d,0x0a,0x3c,0x2f,0x68,0x74,
+0x6d,0x6c,0x3e,0x0d,0x0a,};
+
+#if FSDATA_FILE_ALIGNMENT==1
+static const unsigned int dummy_align__index_html = 2;
+#endif
+static const unsigned char FSDATA_ALIGN_PRE data__index_html[] FSDATA_ALIGN_POST = {
+/* /index.html (12 chars) */
+0x2f,0x69,0x6e,0x64,0x65,0x78,0x2e,0x68,0x74,0x6d,0x6c,0x00,
+
+/* HTTP header */
+/* "HTTP/1.0 200 OK
+" (17 bytes) */
+0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x32,0x30,0x30,0x20,0x4f,0x4b,0x0d,
+0x0a,
+/* "Server: lwIP/2.0.3d (http://savannah.nongnu.org/projects/lwip)
+" (64 bytes) */
+0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x32,0x2e,0x30,
+0x2e,0x33,0x64,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,
+0x6e,0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,
+0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a,
+
+/* "Content-Length: 1751
+" (18+ bytes) */
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20,
+0x31,0x37,0x35,0x31,0x0d,0x0a,
+/* "Content-Type: text/html
+
+" (27 bytes) */
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,0x79,0x70,0x65,0x3a,0x20,0x74,0x65,
+0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x0d,0x0a,0x0d,0x0a,
+/* raw file data (1751 bytes) */
+0x3c,0x68,0x74,0x6d,0x6c,0x3e,0x0d,0x0a,0x3c,0x68,0x65,0x61,0x64,0x3e,0x3c,0x74,
+0x69,0x74,0x6c,0x65,0x3e,0x6c,0x77,0x49,0x50,0x20,0x2d,0x20,0x41,0x20,0x4c,0x69,
+0x67,0x68,0x74,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,0x50,
+0x20,0x53,0x74,0x61,0x63,0x6b,0x3c,0x2f,0x74,0x69,0x74,0x6c,0x65,0x3e,0x3c,0x2f,
+0x68,0x65,0x61,0x64,0x3e,0x0d,0x0a,0x3c,0x62,0x6f,0x64,0x79,0x20,0x62,0x67,0x63,
+0x6f,0x6c,0x6f,0x72,0x3d,0x22,0x77,0x68,0x69,0x74,0x65,0x22,0x20,0x74,0x65,0x78,
+0x74,0x3d,0x22,0x62,0x6c,0x61,0x63,0x6b,0x22,0x3e,0x0d,0x0a,0x0d,0x0a,0x20,0x20,
+0x20,0x20,0x3c,0x74,0x61,0x62,0x6c,0x65,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,0x22,
+0x31,0x30,0x30,0x25,0x22,0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x74,
+0x72,0x20,0x76,0x61,0x6c,0x69,0x67,0x6e,0x3d,0x22,0x74,0x6f,0x70,0x22,0x3e,0x3c,
+0x74,0x64,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,0x22,0x38,0x30,0x22,0x3e,0x09,0x20,
+0x20,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x61,0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68,
+0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x73,0x69,0x63,0x73,0x2e,0x73,
+0x65,0x2f,0x22,0x3e,0x3c,0x69,0x6d,0x67,0x20,0x73,0x72,0x63,0x3d,0x22,0x2f,0x69,
+0x6d,0x67,0x2f,0x73,0x69,0x63,0x73,0x2e,0x67,0x69,0x66,0x22,0x0d,0x0a,0x09,0x20,
+0x20,0x62,0x6f,0x72,0x64,0x65,0x72,0x3d,0x22,0x30,0x22,0x20,0x61,0x6c,0x74,0x3d,
+0x22,0x53,0x49,0x43,0x53,0x20,0x6c,0x6f,0x67,0x6f,0x22,0x20,0x74,0x69,0x74,0x6c,
+0x65,0x3d,0x22,0x53,0x49,0x43,0x53,0x20,0x6c,0x6f,0x67,0x6f,0x22,0x3e,0x3c,0x2f,
+0x61,0x3e,0x0d,0x0a,0x09,0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x74,0x64,0x20,0x77,0x69,
+0x64,0x74,0x68,0x3d,0x22,0x35,0x30,0x30,0x22,0x3e,0x09,0x20,0x20,0x0d,0x0a,0x09,
+0x20,0x20,0x3c,0x68,0x31,0x3e,0x6c,0x77,0x49,0x50,0x20,0x2d,0x20,0x41,0x20,0x4c,
+0x69,0x67,0x68,0x74,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,
+0x50,0x20,0x53,0x74,0x61,0x63,0x6b,0x3c,0x2f,0x68,0x31,0x3e,0x0d,0x0a,0x09,0x20,
+0x20,0x3c,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x54,0x68,0x65,0x20,0x77,
+0x65,0x62,0x20,0x70,0x61,0x67,0x65,0x20,0x79,0x6f,0x75,0x20,0x61,0x72,0x65,0x20,
+0x77,0x61,0x74,0x63,0x68,0x69,0x6e,0x67,0x20,0x77,0x61,0x73,0x20,0x73,0x65,0x72,
+0x76,0x65,0x64,0x20,0x62,0x79,0x20,0x61,0x20,0x73,0x69,0x6d,0x70,0x6c,0x65,0x20,
+0x77,0x65,0x62,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x73,0x65,0x72,0x76,0x65,0x72,
+0x20,0x72,0x75,0x6e,0x6e,0x69,0x6e,0x67,0x20,0x6f,0x6e,0x20,0x74,0x6f,0x70,0x20,
+0x6f,0x66,0x20,0x74,0x68,0x65,0x20,0x6c,0x69,0x67,0x68,0x74,0x77,0x65,0x69,0x67,
+0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,0x50,0x20,0x73,0x74,0x61,0x63,0x6b,0x20,
+0x3c,0x61,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68,
+0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x73,0x69,0x63,0x73,0x2e,0x73,
+0x65,0x2f,0x7e,0x61,0x64,0x61,0x6d,0x2f,0x6c,0x77,0x69,0x70,0x2f,0x22,0x3e,0x6c,
+0x77,0x49,0x50,0x3c,0x2f,0x61,0x3e,0x2e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x2f,0x70,
+0x3e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,
+0x6c,0x77,0x49,0x50,0x20,0x69,0x73,0x20,0x61,0x6e,0x20,0x6f,0x70,0x65,0x6e,0x20,
+0x73,0x6f,0x75,0x72,0x63,0x65,0x20,0x69,0x6d,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,
+0x61,0x74,0x69,0x6f,0x6e,0x20,0x6f,0x66,0x20,0x74,0x68,0x65,0x20,0x54,0x43,0x50,
+0x2f,0x49,0x50,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x70,0x72,0x6f,0x74,0x6f,0x63,
+0x6f,0x6c,0x20,0x73,0x75,0x69,0x74,0x65,0x20,0x74,0x68,0x61,0x74,0x20,0x77,0x61,
+0x73,0x20,0x6f,0x72,0x69,0x67,0x69,0x6e,0x61,0x6c,0x6c,0x79,0x20,0x77,0x72,0x69,
+0x74,0x74,0x65,0x6e,0x20,0x62,0x79,0x20,0x3c,0x61,0x0d,0x0a,0x09,0x20,0x20,0x20,
+0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,
+0x77,0x2e,0x73,0x69,0x63,0x73,0x2e,0x73,0x65,0x2f,0x7e,0x61,0x64,0x61,0x6d,0x2f,
+0x6c,0x77,0x69,0x70,0x2f,0x22,0x3e,0x41,0x64,0x61,0x6d,0x20,0x44,0x75,0x6e,0x6b,
+0x65,0x6c,0x73,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x6f,0x66,0x20,0x74,0x68,0x65,
+0x20,0x53,0x77,0x65,0x64,0x69,0x73,0x68,0x20,0x49,0x6e,0x73,0x74,0x69,0x74,0x75,
+0x74,0x65,0x20,0x6f,0x66,0x20,0x43,0x6f,0x6d,0x70,0x75,0x74,0x65,0x72,0x20,0x53,
+0x63,0x69,0x65,0x6e,0x63,0x65,0x3c,0x2f,0x61,0x3e,0x20,0x62,0x75,0x74,0x20,0x6e,
+0x6f,0x77,0x20,0x69,0x73,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x62,0x65,0x69,0x6e,
+0x67,0x20,0x61,0x63,0x74,0x69,0x76,0x65,0x6c,0x79,0x20,0x64,0x65,0x76,0x65,0x6c,
+0x6f,0x70,0x65,0x64,0x20,0x62,0x79,0x20,0x61,0x20,0x74,0x65,0x61,0x6d,0x20,0x6f,
+0x66,0x20,0x64,0x65,0x76,0x65,0x6c,0x6f,0x70,0x65,0x72,0x73,0x0d,0x0a,0x09,0x20,
+0x20,0x20,0x20,0x64,0x69,0x73,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x64,0x20,0x77,
+0x6f,0x72,0x6c,0x64,0x2d,0x77,0x69,0x64,0x65,0x2e,0x20,0x53,0x69,0x6e,0x63,0x65,
+0x20,0x69,0x74,0x27,0x73,0x20,0x72,0x65,0x6c,0x65,0x61,0x73,0x65,0x2c,0x20,0x6c,
+0x77,0x49,0x50,0x20,0x68,0x61,0x73,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x73,0x70,
+0x75,0x72,0x72,0x65,0x64,0x20,0x61,0x20,0x6c,0x6f,0x74,0x20,0x6f,0x66,0x20,0x69,
+0x6e,0x74,0x65,0x72,0x65,0x73,0x74,0x20,0x61,0x6e,0x64,0x20,0x68,0x61,0x73,0x20,
+0x62,0x65,0x65,0x6e,0x20,0x70,0x6f,0x72,0x74,0x65,0x64,0x20,0x74,0x6f,0x20,0x73,
+0x65,0x76,0x65,0x72,0x61,0x6c,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x70,0x6c,0x61,
+0x74,0x66,0x6f,0x72,0x6d,0x73,0x20,0x61,0x6e,0x64,0x20,0x6f,0x70,0x65,0x72,0x61,
+0x74,0x69,0x6e,0x67,0x20,0x73,0x79,0x73,0x74,0x65,0x6d,0x73,0x2e,0x20,0x6c,0x77,
+0x49,0x50,0x20,0x63,0x61,0x6e,0x20,0x62,0x65,0x20,0x75,0x73,0x65,0x64,0x20,0x65,
+0x69,0x74,0x68,0x65,0x72,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x77,0x69,0x74,0x68,
+0x20,0x6f,0x72,0x20,0x77,0x69,0x74,0x68,0x6f,0x75,0x74,0x20,0x61,0x6e,0x20,0x75,
+0x6e,0x64,0x65,0x72,0x6c,0x79,0x69,0x6e,0x67,0x20,0x4f,0x53,0x2e,0x0d,0x0a,0x09,
+0x20,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x70,0x3e,0x0d,0x0a,
+0x09,0x20,0x20,0x20,0x20,0x54,0x68,0x65,0x20,0x66,0x6f,0x63,0x75,0x73,0x20,0x6f,
+0x66,0x20,0x74,0x68,0x65,0x20,0x6c,0x77,0x49,0x50,0x20,0x54,0x43,0x50,0x2f,0x49,
+0x50,0x20,0x69,0x6d,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x61,0x74,0x69,0x6f,0x6e,
+0x20,0x69,0x73,0x20,0x74,0x6f,0x20,0x72,0x65,0x64,0x75,0x63,0x65,0x0d,0x0a,0x09,
+0x20,0x20,0x20,0x20,0x74,0x68,0x65,0x20,0x52,0x41,0x4d,0x20,0x75,0x73,0x61,0x67,
+0x65,0x20,0x77,0x68,0x69,0x6c,0x65,0x20,0x73,0x74,0x69,0x6c,0x6c,0x20,0x68,0x61,
+0x76,0x69,0x6e,0x67,0x20,0x61,0x20,0x66,0x75,0x6c,0x6c,0x20,0x73,0x63,0x61,0x6c,
+0x65,0x20,0x54,0x43,0x50,0x2e,0x20,0x54,0x68,0x69,0x73,0x0d,0x0a,0x09,0x20,0x20,
+0x20,0x20,0x6d,0x61,0x6b,0x65,0x73,0x20,0x6c,0x77,0x49,0x50,0x20,0x73,0x75,0x69,
+0x74,0x61,0x62,0x6c,0x65,0x20,0x66,0x6f,0x72,0x20,0x75,0x73,0x65,0x20,0x69,0x6e,
+0x20,0x65,0x6d,0x62,0x65,0x64,0x64,0x65,0x64,0x20,0x73,0x79,0x73,0x74,0x65,0x6d,
+0x73,0x20,0x77,0x69,0x74,0x68,0x20,0x74,0x65,0x6e,0x73,0x0d,0x0a,0x09,0x20,0x20,
+0x20,0x20,0x6f,0x66,0x20,0x6b,0x69,0x6c,0x6f,0x62,0x79,0x74,0x65,0x73,0x20,0x6f,
+0x66,0x20,0x66,0x72,0x65,0x65,0x20,0x52,0x41,0x4d,0x20,0x61,0x6e,0x64,0x20,0x72,
+0x6f,0x6f,0x6d,0x20,0x66,0x6f,0x72,0x20,0x61,0x72,0x6f,0x75,0x6e,0x64,0x20,0x34,
+0x30,0x20,0x6b,0x69,0x6c,0x6f,0x62,0x79,0x74,0x65,0x73,0x0d,0x0a,0x09,0x20,0x20,
+0x20,0x20,0x6f,0x66,0x20,0x63,0x6f,0x64,0x65,0x20,0x52,0x4f,0x4d,0x2e,0x0d,0x0a,
+0x09,0x20,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x70,0x3e,0x0d,
+0x0a,0x09,0x20,0x20,0x20,0x20,0x4d,0x6f,0x72,0x65,0x20,0x69,0x6e,0x66,0x6f,0x72,
+0x6d,0x61,0x74,0x69,0x6f,0x6e,0x20,0x61,0x62,0x6f,0x75,0x74,0x20,0x6c,0x77,0x49,
+0x50,0x20,0x63,0x61,0x6e,0x20,0x62,0x65,0x20,0x66,0x6f,0x75,0x6e,0x64,0x20,0x61,
+0x74,0x20,0x74,0x68,0x65,0x20,0x6c,0x77,0x49,0x50,0x0d,0x0a,0x09,0x20,0x20,0x20,
+0x20,0x68,0x6f,0x6d,0x65,0x70,0x61,0x67,0x65,0x20,0x61,0x74,0x20,0x3c,0x61,0x0d,
+0x0a,0x09,0x20,0x20,0x20,0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68,0x74,0x74,0x70,
+0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,0x6e,0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,
+0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,
+0x6c,0x77,0x69,0x70,0x2f,0x22,0x3e,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,
+0x76,0x61,0x6e,0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,
+0x67,0x2f,0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x2f,
+0x3c,0x2f,0x61,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x6f,0x72,0x20,0x61,0x74,
+0x20,0x74,0x68,0x65,0x20,0x6c,0x77,0x49,0x50,0x20,0x77,0x69,0x6b,0x69,0x20,0x61,
+0x74,0x20,0x3c,0x61,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x68,0x72,0x65,0x66,0x3d,
+0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x6c,0x77,0x69,0x70,0x2e,0x77,0x69,0x6b,
+0x69,0x61,0x2e,0x63,0x6f,0x6d,0x2f,0x22,0x3e,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,
+0x6c,0x77,0x69,0x70,0x2e,0x77,0x69,0x6b,0x69,0x61,0x2e,0x63,0x6f,0x6d,0x2f,0x3c,
+0x2f,0x61,0x3e,0x2e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a,0x09,
+0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x74,0x64,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x26,0x6e,
+0x62,0x73,0x70,0x3b,0x0d,0x0a,0x09,0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x2f,0x74,0x72,
+0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x2f,0x74,0x61,0x62,0x6c,0x65,
+0x3e,0x0d,0x0a,0x3c,0x2f,0x62,0x6f,0x64,0x79,0x3e,0x0d,0x0a,0x3c,0x2f,0x68,0x74,
+0x6d,0x6c,0x3e,0x0d,0x0a,0x0d,0x0a,};
+
+
+
+const struct fsdata_file file__img_sics_gif[] = { {
+file_NULL,
+data__img_sics_gif,
+data__img_sics_gif + 16,
+sizeof(data__img_sics_gif) - 16,
+FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT,
+}};
+
+const struct fsdata_file file__404_html[] = { {
+file__img_sics_gif,
+data__404_html,
+data__404_html + 12,
+sizeof(data__404_html) - 12,
+FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT,
+}};
+
+const struct fsdata_file file__index_html[] = { {
+file__404_html,
+data__index_html,
+data__index_html + 12,
+sizeof(data__index_html) - 12,
+FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT,
+}};
+
+#define FS_ROOT file__index_html
+#define FS_NUMFILES 3
+
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/fsdata.h b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/fsdata.h
new file mode 100644 (file)
index 0000000..d31550d
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_FSDATA_H
+#define LWIP_FSDATA_H
+
+#include "lwip/apps/httpd_opts.h"
+#include "lwip/apps/fs.h"
+
+/* THIS FILE IS DEPRECATED AND WILL BE REMOVED IN THE FUTURE */
+/* content was moved to fs.h to simplify #include structure */
+
+#endif /* LWIP_FSDATA_H */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/http_client.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/http_client.c
new file mode 100644 (file)
index 0000000..2e767db
--- /dev/null
@@ -0,0 +1,1023 @@
+/**
+ * @file
+ * HTTP client
+ */
+
+/*
+ * Copyright (c) 2018 Simon Goldschmidt <goldsimon@gmx.de>
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ */
+
+/**
+ * @defgroup httpc HTTP client
+ * @ingroup apps
+ * @todo:
+ * - persistent connections
+ * - select outgoing http version
+ * - optionally follow redirect
+ * - check request uri for invalid characters? (e.g. encode spaces)
+ * - IPv6 support
+ */
+
+#include "lwip/apps/http_client.h"
+
+#include "lwip/altcp_tcp.h"
+#include "lwip/dns.h"
+#include "lwip/debug.h"
+#include "lwip/mem.h"
+#include "lwip/altcp_tls.h"
+#include "lwip/init.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if LWIP_TCP && LWIP_CALLBACK_API
+
+/**
+ * HTTPC_DEBUG: Enable debugging for HTTP client.
+ */
+#ifndef HTTPC_DEBUG
+#define HTTPC_DEBUG                 LWIP_DBG_OFF
+#endif
+
+/** Set this to 1 to keep server name and uri in request state */
+#ifndef HTTPC_DEBUG_REQUEST
+#define HTTPC_DEBUG_REQUEST         0
+#endif
+
+/** This string is passed in the HTTP header as "User-Agent: " */
+#ifndef HTTPC_CLIENT_AGENT
+#define HTTPC_CLIENT_AGENT "lwIP/" LWIP_VERSION_STRING " (http://savannah.nongnu.org/projects/lwip)"
+#endif
+
+/* the various debug levels for this file */
+#define HTTPC_DEBUG_TRACE        (HTTPC_DEBUG | LWIP_DBG_TRACE)
+#define HTTPC_DEBUG_STATE        (HTTPC_DEBUG | LWIP_DBG_STATE)
+#define HTTPC_DEBUG_WARN         (HTTPC_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#define HTTPC_DEBUG_WARN_STATE   (HTTPC_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
+#define HTTPC_DEBUG_SERIOUS      (HTTPC_DEBUG | LWIP_DBG_LEVEL_SERIOUS)
+
+#define HTTPC_POLL_INTERVAL     1
+#define HTTPC_POLL_TIMEOUT      80 /* 15 seconds */
+
+#define HTTPC_CONTENT_LEN_INVALID 0xFFFFFFFF
+
+/* GET request basic */
+#define HTTPC_REQ_11 "GET %s HTTP/1.1\r\n" /* URI */\
+    "User-Agent: %s\r\n" /* User-Agent */ \
+    "Accept: */*\r\n" \
+    "Connection: Close\r\n" /* we don't support persistent connections, yet */ \
+    "\r\n"
+#define HTTPC_REQ_11_FORMAT(uri) HTTPC_REQ_11, uri, HTTPC_CLIENT_AGENT
+
+/* GET request with host */
+#define HTTPC_REQ_11_HOST "GET %s HTTP/1.1\r\n" /* URI */\
+    "User-Agent: %s\r\n" /* User-Agent */ \
+    "Accept: */*\r\n" \
+    "Host: %s\r\n" /* server name */ \
+    "Connection: Close\r\n" /* we don't support persistent connections, yet */ \
+    "\r\n"
+#define HTTPC_REQ_11_HOST_FORMAT(uri, srv_name) HTTPC_REQ_11_HOST, uri, HTTPC_CLIENT_AGENT, srv_name
+
+/* GET request with proxy */
+#define HTTPC_REQ_11_PROXY "GET http://%s%s HTTP/1.1\r\n" /* HOST, URI */\
+    "User-Agent: %s\r\n" /* User-Agent */ \
+    "Accept: */*\r\n" \
+    "Host: %s\r\n" /* server name */ \
+    "Connection: Close\r\n" /* we don't support persistent connections, yet */ \
+    "\r\n"
+#define HTTPC_REQ_11_PROXY_FORMAT(host, uri, srv_name) HTTPC_REQ_11_PROXY, host, uri, HTTPC_CLIENT_AGENT, srv_name
+
+/* GET request with proxy (non-default server port) */
+#define HTTPC_REQ_11_PROXY_PORT "GET http://%s:%d%s HTTP/1.1\r\n" /* HOST, host-port, URI */\
+    "User-Agent: %s\r\n" /* User-Agent */ \
+    "Accept: */*\r\n" \
+    "Host: %s\r\n" /* server name */ \
+    "Connection: Close\r\n" /* we don't support persistent connections, yet */ \
+    "\r\n"
+#define HTTPC_REQ_11_PROXY_PORT_FORMAT(host, host_port, uri, srv_name) HTTPC_REQ_11_PROXY_PORT, host, host_port, uri, HTTPC_CLIENT_AGENT, srv_name
+
+//x-www-form-urlencoded
+//application/json
+////////////////////////////////////////post报文///////////////////////////////////////////////////
+/* POST request basic */  
+#define HTTPC_REQ_11_POST "POST %s HTTP/1.1\r\n" /* URI */\
+    "User-Agent: %s\r\n" /* User-Agent */ \
+    "Accept: */*\r\n" \
+    "Content-Type: application/json\r\n" \
+    "Content-Length: %d\r\n" \
+    "Connection: Close\r\n" /* we don't support persistent connections, yet */\
+    "\r\n" \
+    "%s"
+      
+#define HTTPC_REQ_11_FORMAT_POST(uri, arglen, args)  HTTPC_REQ_11_POST, uri, HTTPC_CLIENT_AGENT, arglen, args
+      
+/* POST request with host */
+#define HTTPC_REQ_11_HOST_POST "POST %s HTTP/1.1\r\n" /* URI */\
+    "User-Agent: %s\r\n" /* User-Agent */ \
+    "Accept: */*\r\n" \
+    "Host: %s\r\n" /* server name */ \
+    "Content-Type: application/json\r\n" \
+    "Content-Length: %d\r\n" \
+    "Connection: Close\r\n" /* we don't support persistent connections, yet */ \
+    "\r\n" \
+    "%s"
+      
+#define HTTPC_REQ_11_HOST_FORMAT_POST(uri, srv_name, arglen, args) HTTPC_REQ_11_HOST_POST, uri, HTTPC_CLIENT_AGENT, srv_name, arglen, args
+
+////////////////////////////////////////PUT报文///////////////////////////////////////////////////
+/* PUT request basic */  
+#define HTTPC_REQ_11_PUT "PUT %s HTTP/1.1\r\n" /* URI */\
+    "User-Agent: %s\r\n" /* User-Agent */ \
+    "Accept: */*\r\n" \
+    "Content-Type: application/json\r\n" \
+    "Content-Length: %d\r\n" \
+    "Connection: Close\r\n" /* we don't support persistent connections, yet */\
+    "\r\n" \
+    "%s"
+      
+#define HTTPC_REQ_11_FORMAT_PUT(uri, arglen, args)  HTTPC_REQ_11_PUT, uri, HTTPC_CLIENT_AGENT, arglen, args
+      
+//       PUT /api/user HTTP/1.1
+// Host: example.com
+// Content-Type: application/json
+// Content-Length: 123
+
+// {
+// "id": 123,
+// "name": "John Doe",
+// "email": "johndoe@example.com",
+// "age": 30
+// }
+/* PUT request with host */
+#define HTTPC_REQ_11_HOST_PUT "PUT %s HTTP/1.1\r\n" /* URI */\
+    "User-Agent: %s\r\n" /* User-Agent */ \
+    "Accept: */*\r\n" \
+    "Host: %s\r\n" /* server name */ \
+    "Content-Type: application/json\r\n" \
+    "Content-Length: %d\r\n" \
+    "Connection: Close\r\n" /* we don't support persistent connections, yet */ \
+    "\r\n" \
+    "%s"
+      
+#define HTTPC_REQ_11_HOST_FORMAT_PUT(uri, srv_name, arglen, args) HTTPC_REQ_11_HOST_PUT, uri, HTTPC_CLIENT_AGENT, srv_name, arglen, args
+
+
+
+typedef enum ehttpc_parse_state {
+  HTTPC_PARSE_WAIT_FIRST_LINE = 0,
+  HTTPC_PARSE_WAIT_HEADERS,
+  HTTPC_PARSE_RX_DATA
+} httpc_parse_state_t;
+
+typedef struct _httpc_state
+{
+  struct altcp_pcb* pcb;
+  ip_addr_t remote_addr;
+  u16_t remote_port;
+  int timeout_ticks;
+  struct pbuf *request;
+  struct pbuf *rx_hdrs;
+  u16_t rx_http_version;
+  u16_t rx_status;
+  altcp_recv_fn recv_fn;
+  const httpc_connection_t *conn_settings;
+  void* callback_arg;
+  u32_t rx_content_len;
+  u32_t hdr_content_len;
+  httpc_parse_state_t parse_state;
+#if HTTPC_DEBUG_REQUEST
+  char* server_name;
+  char* uri;
+#endif
+} httpc_state_t;
+
+/** Free http client state and deallocate all resources within */
+static err_t
+httpc_free_state(httpc_state_t* req)
+{
+  struct altcp_pcb* tpcb;
+
+  if (req->request != NULL) {
+    pbuf_free(req->request);
+    req->request = NULL;
+  }
+  if (req->rx_hdrs != NULL) {
+    pbuf_free(req->rx_hdrs);
+    req->rx_hdrs = NULL;
+  }
+
+  tpcb = req->pcb;
+  mem_free(req);
+  req = NULL;
+
+  if (tpcb != NULL) {
+    err_t r;
+    altcp_arg(tpcb, NULL);
+    altcp_recv(tpcb, NULL);
+    altcp_err(tpcb, NULL);
+    altcp_poll(tpcb, NULL, 0);
+    altcp_sent(tpcb, NULL);
+    r = altcp_close(tpcb);
+    if (r != ERR_OK) {
+      altcp_abort(tpcb);
+      return ERR_ABRT;
+    }
+  }
+  return ERR_OK;
+}
+
+/** Close the connection: call finished callback and free the state */
+static err_t
+httpc_close(httpc_state_t* req, httpc_result_t result, u32_t server_response, err_t err)
+{
+  if (req != NULL) {
+    if (req->conn_settings != NULL) {
+      if (req->conn_settings->result_fn != NULL) {
+        req->conn_settings->result_fn(req->callback_arg, result, req->rx_content_len, server_response, err);
+      }
+    }
+    return httpc_free_state(req);
+  }
+  return ERR_OK;
+}
+
+/** Parse http header response line 1 */
+static err_t
+http_parse_response_status(struct pbuf *p, u16_t *http_version, u16_t *http_status, u16_t *http_status_str_offset)
+{
+  u16_t end1 = pbuf_memfind(p, "\r\n", 2, 0);
+  if (end1 != 0xFFFF) {
+    /* get parts of first line */
+    u16_t space1, space2;
+    space1 = pbuf_memfind(p, " ", 1, 0);
+    if (space1 != 0xFFFF) {
+      if ((pbuf_memcmp(p, 0, "HTTP/", 5) == 0)  && (pbuf_get_at(p, 6) == '.')) {
+        char status_num[10];
+        size_t status_num_len;
+        /* parse http version */
+        u16_t version = pbuf_get_at(p, 5) - '0';
+        version <<= 8;
+        version |= pbuf_get_at(p, 7) - '0';
+        *http_version = version;
+
+        /* parse http status number */
+        space2 = pbuf_memfind(p, " ", 1, space1 + 1);
+        if (space2 != 0xFFFF) {
+          *http_status_str_offset = space2 + 1;
+          status_num_len = space2 - space1 - 1;
+        } else {
+          status_num_len = end1 - space1 - 1;
+        }
+        memset(status_num, 0, sizeof(status_num));
+        if (pbuf_copy_partial(p, status_num, (u16_t)status_num_len, space1 + 1) == status_num_len) {
+          int status = atoi(status_num);
+          if ((status > 0) && (status <= 0xFFFF)) {
+            *http_status = (u16_t)status;
+            return ERR_OK;
+          }
+        }
+      }
+    }
+  }
+  return ERR_VAL;
+}
+
+/** Wait for all headers to be received, return its length and content-length (if available) */
+static err_t
+http_wait_headers(struct pbuf *p, u32_t *content_length, u16_t *total_header_len)
+{
+  u16_t end1 = pbuf_memfind(p, "\r\n\r\n", 4, 0);
+  if (end1 < (0xFFFF - 2)) {
+    /* all headers received */
+    /* check if we have a content length (@todo: case insensitive?) */
+    u16_t content_len_hdr;
+    *content_length = HTTPC_CONTENT_LEN_INVALID;
+    *total_header_len = end1 + 4;
+
+    content_len_hdr = pbuf_memfind(p, "Content-Length: ", 16, 0);
+    if (content_len_hdr != 0xFFFF) {
+      u16_t content_len_line_end = pbuf_memfind(p, "\r\n", 2, content_len_hdr);
+      if (content_len_line_end != 0xFFFF) {
+        char content_len_num[16];
+        u16_t content_len_num_len = (u16_t)(content_len_line_end - content_len_hdr - 16);
+        memset(content_len_num, 0, sizeof(content_len_num));
+        if (pbuf_copy_partial(p, content_len_num, content_len_num_len, content_len_hdr + 16) == content_len_num_len) {
+          int len = atoi(content_len_num);
+          if ((len >= 0) && ((u32_t)len < HTTPC_CONTENT_LEN_INVALID)) {
+            *content_length = (u32_t)len;
+          }
+        }
+      }
+    }
+    return ERR_OK;
+  }
+  return ERR_VAL;
+}
+
+/** http client tcp recv callback */
+static err_t
+httpc_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t r)
+{
+  httpc_state_t* req = (httpc_state_t*)arg;
+  LWIP_UNUSED_ARG(r);
+
+  if (p == NULL) {
+    httpc_result_t result;
+    if (req->parse_state != HTTPC_PARSE_RX_DATA) {
+      /* did not get RX data yet */
+      result = HTTPC_RESULT_ERR_CLOSED;
+    } else if ((req->hdr_content_len != HTTPC_CONTENT_LEN_INVALID) &&
+      (req->hdr_content_len != req->rx_content_len)) {
+      /* header has been received with content length but not all data received */
+      result = HTTPC_RESULT_ERR_CONTENT_LEN;
+    } else {
+      /* receiving data and either all data received or no content length header */
+      result = HTTPC_RESULT_OK;
+    }
+    return httpc_close(req, result, req->rx_status, ERR_OK);
+  }
+  req->timeout_ticks = HTTPC_POLL_TIMEOUT;
+  if (req->parse_state != HTTPC_PARSE_RX_DATA) {
+    if (req->rx_hdrs == NULL) {
+      req->rx_hdrs = p;
+    } else {
+      pbuf_cat(req->rx_hdrs, p);
+    }
+    if (req->parse_state == HTTPC_PARSE_WAIT_FIRST_LINE) {
+      u16_t status_str_off;
+      err_t err = http_parse_response_status(req->rx_hdrs, &req->rx_http_version, &req->rx_status, &status_str_off);
+      if (err == ERR_OK) {
+        /* don't care status string */
+        req->parse_state = HTTPC_PARSE_WAIT_HEADERS;
+      }
+    }
+    if (req->parse_state == HTTPC_PARSE_WAIT_HEADERS) {
+      u16_t total_header_len;
+      err_t err = http_wait_headers(req->rx_hdrs, &req->hdr_content_len, &total_header_len);
+      if (err == ERR_OK) {
+        struct pbuf *q;
+        /* full header received, send window update for header bytes and call into client callback */
+        altcp_recved(pcb, total_header_len);
+        if (req->conn_settings) {
+          if (req->conn_settings->headers_done_fn) {
+            err = req->conn_settings->headers_done_fn(req, req->callback_arg, req->rx_hdrs, total_header_len, req->hdr_content_len);
+            if (err != ERR_OK) {
+              return httpc_close(req, HTTPC_RESULT_LOCAL_ABORT, req->rx_status, err);
+            }
+          }
+        }
+        /* hide header bytes in pbuf */
+        q = pbuf_free_header(req->rx_hdrs, total_header_len);
+        p = q;
+        req->rx_hdrs = NULL;
+        /* go on with data */
+        req->parse_state = HTTPC_PARSE_RX_DATA;
+      }
+    }
+  }
+  if ((p != NULL) && (req->parse_state == HTTPC_PARSE_RX_DATA)) {
+    req->rx_content_len += p->tot_len;
+    if (req->recv_fn != NULL) {
+      /* directly return here: the connection migth already be aborted from the callback! */
+      err_t error=req->recv_fn(req->callback_arg, pcb, p, r);
+      if (req->conn_settings && req->conn_settings->file_rx_fn) {
+          req->conn_settings->file_rx_fn(req,p->tot_len,req->rx_content_len); 
+      }
+      return error;
+    } else {
+      altcp_recved(pcb, p->tot_len);
+      pbuf_free(p);
+    }
+  }
+  return ERR_OK;
+}
+
+/** http client tcp err callback */
+static void
+httpc_tcp_err(void *arg, err_t err)
+{
+  httpc_state_t* req = (httpc_state_t*)arg;
+  if (req != NULL) {
+    /* pcb has already been deallocated */
+    req->pcb = NULL;
+    httpc_close(req, HTTPC_RESULT_ERR_CLOSED, 0, err);
+  }
+}
+
+/** http client tcp poll callback */
+static err_t
+httpc_tcp_poll(void *arg, struct altcp_pcb *pcb)
+{
+  /* implement timeout */
+  httpc_state_t* req = (httpc_state_t*)arg;
+  LWIP_UNUSED_ARG(pcb);
+  if (req != NULL) {
+    if (req->timeout_ticks ==  HTTPC_POLL_TIMEOUT)
+    {
+      // printf("=============== timeout_ticks reset to default !\r\n");
+    }
+    if (req->timeout_ticks) {
+      req->timeout_ticks--;
+      // printf("=============== timeout_ticks -- %d !\r\n",req->timeout_ticks);
+    }
+    if (!req->timeout_ticks) {
+      // printf("=============== timeout_ticks HTTPC_RESULT_ERR_TIMEOUT !\r\n");
+
+      return httpc_close(req, HTTPC_RESULT_ERR_TIMEOUT, 0, ERR_OK);
+    }
+  }
+  return ERR_OK;
+}
+
+/** http client tcp sent callback */
+static err_t
+httpc_tcp_sent(void *arg, struct altcp_pcb *pcb, u16_t len)
+{
+  /* nothing to do here for now */
+  LWIP_UNUSED_ARG(arg);
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_UNUSED_ARG(len);
+  return ERR_OK;
+}
+
+/** http client tcp connected callback */
+static err_t
+httpc_tcp_connected(void *arg, struct altcp_pcb *pcb, err_t err)
+{
+  err_t r;
+  httpc_state_t* req = (httpc_state_t*)arg;
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_UNUSED_ARG(err);
+
+  /* send request; last char is zero termination */
+  r = altcp_write(req->pcb, req->request->payload, req->request->len - 1, TCP_WRITE_FLAG_COPY);
+  if (r != ERR_OK) {
+     /* could not write the single small request -> fail, don't retry */
+     return httpc_close(req, HTTPC_RESULT_ERR_MEM, 0, r);
+  }
+  /* everything written, we can free the request */
+  pbuf_free(req->request);
+  req->request = NULL;
+
+  altcp_output(req->pcb);
+  return ERR_OK;
+}
+
+/** Start the http request when the server IP addr is known */
+static err_t
+httpc_get_internal_addr(httpc_state_t* req, const ip_addr_t *ipaddr)
+{
+  err_t err;
+  LWIP_ASSERT("req != NULL", req != NULL);
+
+  if (&req->remote_addr != ipaddr) {
+    /* fill in remote addr if called externally */
+    req->remote_addr = *ipaddr;
+  }
+
+  err = altcp_connect(req->pcb, &req->remote_addr, req->remote_port, httpc_tcp_connected);
+  if (err == ERR_OK) {
+    return ERR_OK;
+  }
+  LWIP_DEBUGF(HTTPC_DEBUG_WARN_STATE, ("tcp_connect failed: %d\n", (int)err));
+  return err;
+}
+
+#if LWIP_DNS
+/** DNS callback
+ * If ipaddr is non-NULL, resolving succeeded and the request can be sent, otherwise it failed.
+ */
+static void
+httpc_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg)
+{
+  httpc_state_t* req = (httpc_state_t*)arg;
+  err_t err;
+  httpc_result_t result;
+
+  LWIP_UNUSED_ARG(hostname);
+
+  if (ipaddr != NULL) {
+    err = httpc_get_internal_addr(req, ipaddr);
+    if (err == ERR_OK) {
+      return;
+    }
+    result = HTTPC_RESULT_ERR_CONNECT;
+  } else {
+    LWIP_DEBUGF(HTTPC_DEBUG_WARN_STATE, ("httpc_dns_found: failed to resolve hostname: %s\n",
+      hostname));
+    result = HTTPC_RESULT_ERR_HOSTNAME;
+    err = ERR_ARG;
+  }
+  httpc_close(req, result, 0, err);
+}
+#endif /* LWIP_DNS */
+
+/** Start the http request after converting 'server_name' to ip address (DNS or address string) */
+static err_t
+httpc_get_internal_dns(httpc_state_t* req, const char* server_name)
+{
+  err_t err;
+  LWIP_ASSERT("req != NULL", req != NULL);
+
+#if LWIP_DNS
+  err = dns_gethostbyname(server_name, &req->remote_addr, httpc_dns_found, req);
+#else
+  err = ipaddr_aton(server_name, &req->remote_addr) ? ERR_OK : ERR_ARG;
+#endif
+
+  if (err == ERR_OK) {
+    /* cached or IP-string */
+    err = httpc_get_internal_addr(req, &req->remote_addr);
+  } else if (err == ERR_INPROGRESS) {
+    return ERR_OK;
+  }
+  return err;
+}
+
+static int
+httpc_create_request_string(const httpc_connection_t *settings, const char* server_name, int server_port, const char* uri,
+                            int use_host, char *buffer, size_t buffer_size)
+{
+  //  printf("httpc_create_request_string  use_post %d use_proxy %d use_host %d \r\n",settings->use_post,settings->use_proxy,use_host);
+  if (settings->use_proxy) {
+    LWIP_ASSERT("server_name != NULL", server_name != NULL);
+    if (server_port != HTTP_DEFAULT_PORT)
+     {
+      return snprintf(buffer, buffer_size, HTTPC_REQ_11_PROXY_PORT_FORMAT(server_name, server_port, uri, server_name));
+    } else {
+      return snprintf(buffer, buffer_size, HTTPC_REQ_11_PROXY_FORMAT(server_name, uri, server_name));
+    }
+  }
+  else if (use_host) 
+  {
+    LWIP_ASSERT("server_name != NULL", server_name != NULL);
+
+    if (settings->use_post)
+    {
+                 return snprintf(buffer, buffer_size, HTTPC_REQ_11_HOST_FORMAT_POST(uri, server_name, settings->arglen, settings->args));
+      // printf("%s\r\n",buffer);
+         }
+    if (settings->use_put)
+    {
+                 return snprintf(buffer, buffer_size, HTTPC_REQ_11_HOST_FORMAT_PUT(uri, server_name, settings->arglen, settings->args));
+      // printf("%s\r\n",buffer);
+         }
+
+    return snprintf(buffer, buffer_size, HTTPC_REQ_11_HOST_FORMAT(uri, server_name));
+  } 
+  else 
+  {
+
+     if (settings->use_put)
+    {
+                 return snprintf(buffer, buffer_size, HTTPC_REQ_11_HOST_FORMAT_PUT(uri, server_name, settings->arglen, settings->args));
+      // printf("%s\r\n",buffer);
+         }
+
+    if (settings->use_post){
+       return snprintf(buffer, buffer_size, HTTPC_REQ_11_FORMAT_POST(uri, settings->arglen, settings->args));
+      
+    }
+    return snprintf(buffer, buffer_size, HTTPC_REQ_11_FORMAT(uri));
+  }
+}
+
+/** Initialize the connection struct */
+static err_t
+httpc_init_connection_common(httpc_state_t **connection, const httpc_connection_t *settings, const char* server_name,
+                      u16_t server_port, const char* uri, altcp_recv_fn recv_fn, void* callback_arg, int use_host)
+{
+  size_t alloc_len;
+  mem_size_t mem_alloc_len;
+  int req_len, req_len2;
+  httpc_state_t *req;
+#if HTTPC_DEBUG_REQUEST
+  size_t server_name_len, uri_len;
+#endif
+
+  LWIP_ASSERT("uri != NULL", uri != NULL);
+
+  /* get request len */
+  req_len = httpc_create_request_string(settings, server_name, server_port, uri, use_host, NULL, 0);
+  if ((req_len < 0) || (req_len > 0xFFFF)) {
+    return ERR_VAL;
+  }
+  /* alloc state and request in one block */
+  alloc_len = sizeof(httpc_state_t);
+#if HTTPC_DEBUG_REQUEST
+  server_name_len = server_name ? strlen(server_name) : 0;
+  uri_len = strlen(uri);
+  alloc_len += server_name_len + 1 + uri_len + 1;
+#endif
+  mem_alloc_len = (mem_size_t)alloc_len;
+  if ((mem_alloc_len < alloc_len) || (req_len + 1 > 0xFFFF)) {
+    return ERR_VAL;
+  }
+
+  req = (httpc_state_t*)mem_malloc((mem_size_t)alloc_len);
+  if(req == NULL) {
+    return ERR_MEM;
+  }
+  memset(req, 0, sizeof(httpc_state_t));
+  req->timeout_ticks = HTTPC_POLL_TIMEOUT;
+  req->request = pbuf_alloc(PBUF_RAW, (u16_t)(req_len + 1), PBUF_RAM);
+  if (req->request == NULL) {
+    httpc_free_state(req);
+    return ERR_MEM;
+  }
+  if (req->request->next != NULL) {
+    /* need a pbuf in one piece */
+    httpc_free_state(req);
+    return ERR_MEM;
+  }
+  req->hdr_content_len = HTTPC_CONTENT_LEN_INVALID;
+#if HTTPC_DEBUG_REQUEST
+  req->server_name = (char*)(req + 1);
+  if (server_name) {
+    memcpy(req->server_name, server_name, server_name_len + 1);
+  }
+  req->uri = req->server_name + server_name_len + 1;
+  memcpy(req->uri, uri, uri_len + 1);
+#endif
+  req->pcb = altcp_new(settings->altcp_allocator);
+  if(req->pcb == NULL) {
+    httpc_free_state(req);
+    return ERR_MEM;
+  }
+  req->remote_port = settings->use_proxy ? settings->proxy_port : server_port;
+  altcp_arg(req->pcb, req);
+  altcp_recv(req->pcb, httpc_tcp_recv);
+  altcp_err(req->pcb, httpc_tcp_err);
+  altcp_poll(req->pcb, httpc_tcp_poll, HTTPC_POLL_INTERVAL);
+  altcp_sent(req->pcb, httpc_tcp_sent);
+
+  /* set up request buffer */
+  req_len2 = httpc_create_request_string(settings, server_name, server_port, uri, use_host,
+    (char *)req->request->payload, req_len + 1);
+  printf("%s\r\n",(char *)req->request->payload);
+
+  if (req_len2 != req_len) {
+    httpc_free_state(req);
+    return ERR_VAL;
+  }
+
+  req->recv_fn = recv_fn;
+  req->conn_settings = settings;
+  req->callback_arg = callback_arg;
+
+  *connection = req;
+  return ERR_OK;
+}
+
+/**
+ * Initialize the connection struct
+ */
+static err_t
+httpc_init_connection(httpc_state_t **connection, const httpc_connection_t *settings, const char* server_name,
+                      u16_t server_port, const char* uri, altcp_recv_fn recv_fn, void* callback_arg)
+{
+  return httpc_init_connection_common(connection, settings, server_name, server_port, uri, recv_fn, callback_arg, 1);
+}
+
+
+/**
+ * Initialize the connection struct (from IP address)
+ */
+static err_t
+httpc_init_connection_addr(httpc_state_t **connection, const httpc_connection_t *settings,
+                           const ip_addr_t* server_addr, u16_t server_port, const char* uri,
+                           altcp_recv_fn recv_fn, void* callback_arg)
+{
+  char *server_addr_str = ipaddr_ntoa(server_addr);
+  if (server_addr_str == NULL) {
+    return ERR_VAL;
+  }
+  return httpc_init_connection_common(connection, settings, server_addr_str, server_port, uri,
+    recv_fn, callback_arg, 1);
+}
+
+/**
+ * @ingroup httpc 
+ * HTTP client API: get a file by passing server IP address
+ *
+ * @param server_addr IP address of the server to connect
+ * @param port tcp port of the server
+ * @param uri uri to get from the server, remember leading "/"!
+ * @param settings connection settings (callbacks, proxy, etc.)
+ * @param recv_fn the http body (not the headers) are passed to this callback
+ * @param callback_arg argument passed to all the callbacks
+ * @param connection retreives the connection handle (to match in callbacks)
+ * @return ERR_OK if starting the request succeeds (callback_fn will be called later)
+ *         or an error code
+ */
+err_t
+httpc_get_file(const ip_addr_t* server_addr, u16_t port, const char* uri, const httpc_connection_t *settings,
+               altcp_recv_fn recv_fn, void* callback_arg, httpc_state_t **connection)
+{
+  err_t err;
+  httpc_state_t* req;
+
+  LWIP_ERROR("invalid parameters", (server_addr != NULL) && (uri != NULL) && (recv_fn != NULL), return ERR_ARG;);
+
+  err = httpc_init_connection_addr(&req, settings, server_addr, port,
+    uri, recv_fn, callback_arg);
+  if (err != ERR_OK) {
+    return err;
+  }
+
+  if (settings->use_proxy) {
+    err = httpc_get_internal_addr(req, &settings->proxy_addr);
+  } else {
+    err = httpc_get_internal_addr(req, server_addr);
+  }
+  if(err != ERR_OK) {
+    httpc_free_state(req);
+    return err;
+  }
+
+  if (connection != NULL) {
+    *connection = req;
+  }
+  return ERR_OK;
+}
+
+/**
+ * @ingroup httpc 
+ * HTTP client API: get a file by passing server name as string (DNS name or IP address string)
+ *
+ * @param server_name server name as string (DNS name or IP address string)
+ * @param port tcp port of the server
+ * @param uri uri to get from the server, remember leading "/"!
+ * @param settings connection settings (callbacks, proxy, etc.)
+ * @param recv_fn the http body (not the headers) are passed to this callback
+ * @param callback_arg argument passed to all the callbacks
+ * @param connection retreives the connection handle (to match in callbacks)
+ * @return ERR_OK if starting the request succeeds (callback_fn will be called later)
+ *         or an error code
+ */
+err_t
+httpc_get_file_dns(const char* server_name, u16_t port, const char* uri, const httpc_connection_t *settings,
+                   altcp_recv_fn recv_fn, void* callback_arg, httpc_state_t **connection)
+{
+  err_t err;
+  httpc_state_t* req;
+
+  LWIP_ERROR("invalid parameters", (server_name != NULL) && (uri != NULL) && (recv_fn != NULL), return ERR_ARG;);
+
+  err = httpc_init_connection(&req, settings, server_name, port, uri, recv_fn, callback_arg);
+  if (err != ERR_OK) {
+    return err;
+  }
+
+  if (settings->use_proxy) {
+    err = httpc_get_internal_addr(req, &settings->proxy_addr);
+  } else {
+    err = httpc_get_internal_dns(req, server_name);
+  }
+  if(err != ERR_OK) {
+    httpc_free_state(req);
+    return err;
+  }
+
+  if (connection != NULL) {
+    *connection = req;
+  }
+  return ERR_OK;
+}
+
+#if LWIP_HTTPC_HAVE_FILE_IO
+/* Implementation to disk via fopen/fwrite/fclose follows */
+
+typedef struct _httpc_filestate
+{
+  const char* local_file_name;
+  FILE *file;
+  httpc_connection_t settings;
+  const httpc_connection_t *client_settings;
+  void *callback_arg;
+} httpc_filestate_t;
+
+static void httpc_fs_result(void *arg, httpc_result_t httpc_result, u32_t rx_content_len,
+  u32_t srv_res, err_t err);
+
+static void httpc_fs_receive_bytes(void *arg,  u32_t rx_received_len, u32_t total_len );
+
+
+/** Initalize http client state for download to file system */
+static err_t
+httpc_fs_init(httpc_filestate_t **filestate_out, const char* local_file_name,
+              const httpc_connection_t *settings, void* callback_arg)
+{
+  httpc_filestate_t *filestate;
+  size_t file_len, alloc_len;
+  FILE *f;
+
+  file_len = strlen(local_file_name);
+  alloc_len = sizeof(httpc_filestate_t) + file_len + 1;
+
+  filestate = (httpc_filestate_t *)mem_malloc((mem_size_t)alloc_len);
+  if (filestate == NULL) {
+    return ERR_MEM;
+  }
+  memset(filestate, 0, sizeof(httpc_filestate_t));
+  filestate->local_file_name = (const char *)(filestate + 1);
+  memcpy((char *)(filestate + 1), local_file_name, file_len + 1);
+  filestate->file = NULL;
+  filestate->client_settings = settings;
+  filestate->callback_arg = callback_arg;
+  /* copy client settings but override result callback */
+  memcpy(&filestate->settings, settings, sizeof(httpc_connection_t));
+  filestate->settings.result_fn = httpc_fs_result;
+  
+  f = fopen(local_file_name, "wb");
+  if(f == NULL) {
+    /* could not open file */
+    mem_free(filestate);
+    return ERR_VAL;
+  }
+  filestate->file = f;
+  *filestate_out = filestate;
+  return ERR_OK;
+}
+
+/** Free http client state for download to file system */
+static void
+httpc_fs_free(httpc_filestate_t *filestate)
+{
+  if (filestate != NULL) {
+    mem_free(filestate);
+  }
+}
+
+/** Connection closed (success or error) */
+static void
+httpc_fs_result(void *arg, httpc_result_t httpc_result, u32_t rx_content_len,
+                u32_t srv_res, err_t err)
+{
+  httpc_filestate_t *filestate = (httpc_filestate_t *)arg;
+  if (filestate != NULL) {
+    if (filestate->file != NULL) {
+      fclose(filestate->file);
+      filestate->file = NULL;
+    }
+    if (filestate->client_settings->result_fn != NULL) {
+      filestate->client_settings->result_fn(filestate->callback_arg, httpc_result, rx_content_len,
+        srv_res, err);
+    }
+    httpc_fs_free(filestate);
+  }
+}
+
+/** tcp recv callback */
+static err_t
+httpc_fs_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+  httpc_filestate_t *filestate = (httpc_filestate_t*)arg;
+  struct pbuf* q;
+  LWIP_UNUSED_ARG(err);
+
+  LWIP_ASSERT("p != NULL", p != NULL);
+
+  for (q = p; q != NULL; q = q->next) {
+    fwrite(q->payload, 1, q->len, filestate->file);
+  }
+    // printf("received file bufsize %d\r\n",p->tot_len);
+  altcp_recved(pcb, p->tot_len);
+  pbuf_free(p);
+  return ERR_OK;
+}
+
+/**
+ * @ingroup httpc 
+ * HTTP client API: get a file to disk by passing server IP address
+ *
+ * @param server_addr IP address of the server to connect
+ * @param port tcp port of the server
+ * @param uri uri to get from the server, remember leading "/"!
+ * @param settings connection settings (callbacks, proxy, etc.)
+ * @param callback_arg argument passed to all the callbacks
+ * @param connection retreives the connection handle (to match in callbacks)
+ * @return ERR_OK if starting the request succeeds (callback_fn will be called later)
+ *         or an error code
+ */
+err_t
+httpc_get_file_to_disk(const ip_addr_t* server_addr, u16_t port, const char* uri, const httpc_connection_t *settings,
+                       void* callback_arg, const char* local_file_name, httpc_state_t **connection)
+{
+  err_t err;
+  httpc_state_t* req;
+  httpc_filestate_t *filestate;
+
+  LWIP_ERROR("invalid parameters", (server_addr != NULL) && (uri != NULL) && (local_file_name != NULL), return ERR_ARG;);
+
+  err = httpc_fs_init(&filestate, local_file_name, settings, callback_arg);
+  if (err != ERR_OK) {
+    return err;
+  }
+
+  err = httpc_init_connection_addr(&req, &filestate->settings, server_addr, port,
+    uri, httpc_fs_tcp_recv, filestate);
+  if (err != ERR_OK) {
+    httpc_fs_free(filestate);
+    return err;
+  }
+
+  if (settings->use_proxy) {
+    err = httpc_get_internal_addr(req, &settings->proxy_addr);
+  } else {
+    err = httpc_get_internal_addr(req, server_addr);
+  }
+  if(err != ERR_OK) {
+    httpc_fs_free(filestate);
+    httpc_free_state(req);
+    return err;
+  }
+
+  if (connection != NULL) {
+    *connection = req;
+  }
+  return ERR_OK;
+}
+
+/**
+ * @ingroup httpc 
+ * HTTP client API: get a file to disk by passing server name as string (DNS name or IP address string)
+ *
+ * @param server_name server name as string (DNS name or IP address string)
+ * @param port tcp port of the server
+ * @param uri uri to get from the server, remember leading "/"!
+ * @param settings connection settings (callbacks, proxy, etc.)
+ * @param callback_arg argument passed to all the callbacks
+ * @param connection retreives the connection handle (to match in callbacks)
+ * @return ERR_OK if starting the request succeeds (callback_fn will be called later)
+ *         or an error code
+ */
+err_t
+httpc_get_file_dns_to_disk(const char* server_name, u16_t port, const char* uri, const httpc_connection_t *settings,
+                           void* callback_arg, const char* local_file_name, httpc_state_t **connection)
+{
+  err_t err;
+  httpc_state_t* req;
+  httpc_filestate_t *filestate;
+
+  LWIP_ERROR("invalid parameters", (server_name != NULL) && (uri != NULL) && (local_file_name != NULL), return ERR_ARG;);
+
+  err = httpc_fs_init(&filestate, local_file_name, settings, callback_arg);
+  if (err != ERR_OK) {
+    return err;
+  }
+
+  err = httpc_init_connection(&req, &filestate->settings, server_name, port,
+    uri, httpc_fs_tcp_recv, filestate);
+  if (err != ERR_OK) {
+    httpc_fs_free(filestate);
+    return err;
+  }
+
+  if (settings->use_proxy) {
+    err = httpc_get_internal_addr(req, &settings->proxy_addr);
+  } else {
+    err = httpc_get_internal_dns(req, server_name);
+  }
+  if(err != ERR_OK) {
+    httpc_fs_free(filestate);
+    httpc_free_state(req);
+    return err;
+  }
+
+  if (connection != NULL) {
+    *connection = req;
+  }
+  return ERR_OK;
+}
+#endif /* LWIP_HTTPC_HAVE_FILE_IO */
+
+#endif /* LWIP_TCP && LWIP_CALLBACK_API */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/httpd.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/httpd.c
new file mode 100644 (file)
index 0000000..fc27e8e
--- /dev/null
@@ -0,0 +1,2762 @@
+/**
+ * @file
+ * LWIP HTTP server implementation
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *         Simon Goldschmidt
+ *
+ */
+
+/**
+ * @defgroup httpd HTTP server
+ * @ingroup apps
+ *
+ * This httpd supports for a
+ * rudimentary server-side-include facility which will replace tags of the form
+ * <!--#tag--> in any file whose extension is .shtml, .shtm or .ssi with
+ * strings provided by an include handler whose pointer is provided to the
+ * module via function http_set_ssi_handler().
+ * Additionally, a simple common
+ * gateway interface (CGI) handling mechanism has been added to allow clients
+ * to hook functions to particular request URIs.
+ *
+ * To enable SSI support, define label LWIP_HTTPD_SSI in lwipopts.h.
+ * To enable CGI support, define label LWIP_HTTPD_CGI in lwipopts.h.
+ *
+ * By default, the server assumes that HTTP headers are already present in
+ * each file stored in the file system.  By defining LWIP_HTTPD_DYNAMIC_HEADERS in
+ * lwipopts.h, this behavior can be changed such that the server inserts the
+ * headers automatically based on the extension of the file being served.  If
+ * this mode is used, be careful to ensure that the file system image used
+ * does not already contain the header information.
+ *
+ * File system images without headers can be created using the makefsfile
+ * tool with the -h command line option.
+ *
+ *
+ * Notes about valid SSI tags
+ * --------------------------
+ *
+ * The following assumptions are made about tags used in SSI markers:
+ *
+ * 1. No tag may contain '-' or whitespace characters within the tag name.
+ * 2. Whitespace is allowed between the tag leadin "<!--#" and the start of
+ *    the tag name and between the tag name and the leadout string "-->".
+ * 3. The maximum tag name length is LWIP_HTTPD_MAX_TAG_NAME_LEN, currently 8 characters.
+ *
+ * Notes on CGI usage
+ * ------------------
+ *
+ * The simple CGI support offered here works with GET method requests only
+ * and can handle up to 16 parameters encoded into the URI. The handler
+ * function may not write directly to the HTTP output but must return a
+ * filename that the HTTP server will send to the browser as a response to
+ * the incoming CGI request.
+ *
+ *
+ *
+ * The list of supported file types is quite short, so if makefsdata complains
+ * about an unknown extension, make sure to add it (and its doctype) to
+ * the 'g_psHTTPHeaders' list.
+ */
+#include "lwip/init.h"
+#include "lwip/apps/httpd.h"
+#include "lwip/debug.h"
+#include "lwip/stats.h"
+#include "lwip/apps/fs.h"
+#include "httpd_structs.h"
+#include "lwip/def.h"
+
+#include "lwip/altcp.h"
+#include "lwip/altcp_tcp.h"
+#if HTTPD_ENABLE_HTTPS
+#include "lwip/altcp_tls.h"
+#endif
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+#if LWIP_HTTPD_TIMING
+#include "lwip/sys.h"
+#endif /* LWIP_HTTPD_TIMING */
+
+#include <string.h> /* memset */
+#include <stdlib.h> /* atoi */
+#include <stdio.h>
+
+#if LWIP_TCP && LWIP_CALLBACK_API
+
+/** Minimum length for a valid HTTP/0.9 request: "GET /\r\n" -> 7 bytes */
+#define MIN_REQ_LEN   7
+
+#define CRLF "\r\n"
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+#define HTTP11_CONNECTIONKEEPALIVE  "Connection: keep-alive"
+#define HTTP11_CONNECTIONKEEPALIVE2 "Connection: Keep-Alive"
+#endif
+
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+#define HTTP_IS_DYNAMIC_FILE(hs) ((hs)->buf != NULL)
+#else
+#define HTTP_IS_DYNAMIC_FILE(hs) 0
+#endif
+
+/* This defines checks whether tcp_write has to copy data or not */
+
+#ifndef HTTP_IS_DATA_VOLATILE
+/** tcp_write does not have to copy data when sent from rom-file-system directly */
+#define HTTP_IS_DATA_VOLATILE(hs)       (HTTP_IS_DYNAMIC_FILE(hs) ? TCP_WRITE_FLAG_COPY : 0)
+#endif
+/** Default: dynamic headers are sent from ROM (non-dynamic headers are handled like file data) */
+#ifndef HTTP_IS_HDR_VOLATILE
+#define HTTP_IS_HDR_VOLATILE(hs, ptr)   0
+#endif
+
+/* Return values for http_send_*() */
+#define HTTP_DATA_TO_SEND_FREED    3
+#define HTTP_DATA_TO_SEND_BREAK    2
+#define HTTP_DATA_TO_SEND_CONTINUE 1
+#define HTTP_NO_DATA_TO_SEND       0
+
+typedef struct {
+  const char *name;
+  u8_t shtml;
+} default_filename;
+
+static const default_filename httpd_default_filenames[] = {
+  {"/index.shtml", 1 },
+  {"/index.ssi",   1 },
+  {"/index.shtm",  1 },
+  {"/index.html",  0 },
+  {"/index.htm",   0 }
+};
+
+#define NUM_DEFAULT_FILENAMES LWIP_ARRAYSIZE(httpd_default_filenames)
+
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+/** HTTP request is copied here from pbufs for simple parsing */
+static char httpd_req_buf[LWIP_HTTPD_MAX_REQ_LENGTH + 1];
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+
+#if LWIP_HTTPD_SUPPORT_POST
+#if LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN > LWIP_HTTPD_MAX_REQUEST_URI_LEN
+#define LWIP_HTTPD_URI_BUF_LEN LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN
+#endif
+#endif
+#ifndef LWIP_HTTPD_URI_BUF_LEN
+#define LWIP_HTTPD_URI_BUF_LEN LWIP_HTTPD_MAX_REQUEST_URI_LEN
+#endif
+#if LWIP_HTTPD_URI_BUF_LEN
+/* Filename for response file to send when POST is finished or
+ * search for default files when a directory is requested. */
+static char http_uri_buf[LWIP_HTTPD_URI_BUF_LEN + 1];
+#endif
+
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+/* The number of individual strings that comprise the headers sent before each
+ * requested file.
+ */
+#define NUM_FILE_HDR_STRINGS 5
+#define HDR_STRINGS_IDX_HTTP_STATUS           0 /* e.g. "HTTP/1.0 200 OK\r\n" */
+#define HDR_STRINGS_IDX_SERVER_NAME           1 /* e.g. "Server: "HTTPD_SERVER_AGENT"\r\n" */
+#define HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE 2 /* e.g. "Content-Length: xy\r\n" and/or "Connection: keep-alive\r\n" */
+#define HDR_STRINGS_IDX_CONTENT_LEN_NR        3 /* the byte count, when content-length is used */
+#define HDR_STRINGS_IDX_CONTENT_TYPE          4 /* the content type (or default answer content type including default document) */
+
+/* The dynamically generated Content-Length buffer needs space for CRLF + NULL */
+#define LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET 3
+#ifndef LWIP_HTTPD_MAX_CONTENT_LEN_SIZE
+/* The dynamically generated Content-Length buffer shall be able to work with
+   ~953 MB (9 digits) */
+#define LWIP_HTTPD_MAX_CONTENT_LEN_SIZE   (9 + LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET)
+#endif
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+
+#if LWIP_HTTPD_SSI
+
+#define HTTPD_LAST_TAG_PART 0xFFFF
+
+enum tag_check_state {
+  TAG_NONE,       /* Not processing an SSI tag */
+  TAG_LEADIN,     /* Tag lead in "<!--#" being processed */
+  TAG_FOUND,      /* Tag name being read, looking for lead-out start */
+  TAG_LEADOUT,    /* Tag lead out "-->" being processed */
+  TAG_SENDING     /* Sending tag replacement string */
+};
+
+struct http_ssi_state {
+  const char *parsed;     /* Pointer to the first unparsed byte in buf. */
+#if !LWIP_HTTPD_SSI_INCLUDE_TAG
+  const char *tag_started;/* Pointer to the first opening '<' of the tag. */
+#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */
+  const char *tag_end;    /* Pointer to char after the closing '>' of the tag. */
+  u32_t parse_left; /* Number of unparsed bytes in buf. */
+  u16_t tag_index;   /* Counter used by tag parsing state machine */
+  u16_t tag_insert_len; /* Length of insert in string tag_insert */
+#if LWIP_HTTPD_SSI_MULTIPART
+  u16_t tag_part; /* Counter passed to and changed by tag insertion function to insert multiple times */
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+  u8_t tag_type; /* index into http_ssi_tag_desc array */
+  u8_t tag_name_len; /* Length of the tag name in string tag_name */
+  char tag_name[LWIP_HTTPD_MAX_TAG_NAME_LEN + 1]; /* Last tag name extracted */
+  char tag_insert[LWIP_HTTPD_MAX_TAG_INSERT_LEN + 1]; /* Insert string for tag_name */
+  enum tag_check_state tag_state; /* State of the tag processor */
+};
+
+struct http_ssi_tag_description {
+  const char *lead_in;
+  const char *lead_out; 
+};
+
+#endif /* LWIP_HTTPD_SSI */
+
+struct http_state {
+#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
+  struct http_state *next;
+#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
+  struct fs_file file_handle;
+  struct fs_file *handle;
+  const char *file;       /* Pointer to first unsent byte in buf. */
+
+  struct altcp_pcb *pcb;
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+  struct pbuf *req;
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+  char *buf;        /* File read buffer. */
+  int buf_len;      /* Size of file read buffer, buf. */
+#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
+  u32_t left;       /* Number of unsent bytes in buf. */
+  u8_t retries;
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+  u8_t keepalive;
+#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
+#if LWIP_HTTPD_SSI
+  struct http_ssi_state *ssi;
+#endif /* LWIP_HTTPD_SSI */
+#if LWIP_HTTPD_CGI
+  char *params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */
+  char *param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for each extracted param */
+#endif /* LWIP_HTTPD_CGI */
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+  const char *hdrs[NUM_FILE_HDR_STRINGS]; /* HTTP headers to be sent. */
+  char hdr_content_len[LWIP_HTTPD_MAX_CONTENT_LEN_SIZE];
+  u16_t hdr_pos;     /* The position of the first unsent header byte in the
+                        current string */
+  u16_t hdr_index;   /* The index of the hdr string currently being sent. */
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+#if LWIP_HTTPD_TIMING
+  u32_t time_started;
+#endif /* LWIP_HTTPD_TIMING */
+#if LWIP_HTTPD_SUPPORT_POST
+  u32_t post_content_len_left;
+#if LWIP_HTTPD_POST_MANUAL_WND
+  u32_t unrecved_bytes;
+  u8_t no_auto_wnd;
+  u8_t post_finished;
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+#endif /* LWIP_HTTPD_SUPPORT_POST*/
+};
+
+#if HTTPD_USE_MEM_POOL
+LWIP_MEMPOOL_DECLARE(HTTPD_STATE,     MEMP_NUM_PARALLEL_HTTPD_CONNS,     sizeof(struct http_state),     "HTTPD_STATE")
+#if LWIP_HTTPD_SSI
+LWIP_MEMPOOL_DECLARE(HTTPD_SSI_STATE, MEMP_NUM_PARALLEL_HTTPD_SSI_CONNS, sizeof(struct http_ssi_state), "HTTPD_SSI_STATE")
+#define HTTP_FREE_SSI_STATE(x)  LWIP_MEMPOOL_FREE(HTTPD_SSI_STATE, (x))
+#define HTTP_ALLOC_SSI_STATE()  (struct http_ssi_state *)LWIP_MEMPOOL_ALLOC(HTTPD_SSI_STATE)
+#endif /* LWIP_HTTPD_SSI */
+#define HTTP_ALLOC_HTTP_STATE() (struct http_state *)LWIP_MEMPOOL_ALLOC(HTTPD_STATE)
+#define HTTP_FREE_HTTP_STATE(x) LWIP_MEMPOOL_FREE(HTTPD_STATE, (x))
+#else /* HTTPD_USE_MEM_POOL */
+#define HTTP_ALLOC_HTTP_STATE() (struct http_state *)mem_malloc(sizeof(struct http_state))
+#define HTTP_FREE_HTTP_STATE(x) mem_free(x)
+#if LWIP_HTTPD_SSI
+#define HTTP_ALLOC_SSI_STATE()  (struct http_ssi_state *)mem_malloc(sizeof(struct http_ssi_state))
+#define HTTP_FREE_SSI_STATE(x)  mem_free(x)
+#endif /* LWIP_HTTPD_SSI */
+#endif /* HTTPD_USE_MEM_POOL */
+
+static err_t http_close_conn(struct altcp_pcb *pcb, struct http_state *hs);
+static err_t http_close_or_abort_conn(struct altcp_pcb *pcb, struct http_state *hs, u8_t abort_conn);
+static err_t http_find_file(struct http_state *hs, const char *uri, int is_09);
+static err_t http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri, u8_t tag_check, char *params);
+static err_t http_poll(void *arg, struct altcp_pcb *pcb);
+static u8_t http_check_eof(struct altcp_pcb *pcb, struct http_state *hs);
+#if LWIP_HTTPD_FS_ASYNC_READ
+static void http_continue(void *connection);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+
+#if LWIP_HTTPD_SSI
+/* SSI insert handler function pointer. */
+static tSSIHandler httpd_ssi_handler;
+#if !LWIP_HTTPD_SSI_RAW
+static int httpd_num_tags;
+static const char **httpd_tags;
+#endif /* !LWIP_HTTPD_SSI_RAW */
+
+/* Define the available tag lead-ins and corresponding lead-outs.
+ * ATTENTION: for the algorithm below using this array, it is essential
+ * that the lead in differs in the first character! */
+const struct http_ssi_tag_description http_ssi_tag_desc[] = {
+  {"<!--#", "-->"},
+  {"/*#", "*/"}
+};
+
+#endif /* LWIP_HTTPD_SSI */
+
+#if LWIP_HTTPD_CGI
+/* CGI handler information */
+static const tCGI *httpd_cgis;
+static int httpd_num_cgis;
+static int http_cgi_paramcount;
+#define http_cgi_params     hs->params
+#define http_cgi_param_vals hs->param_vals
+#elif LWIP_HTTPD_CGI_SSI
+static char *http_cgi_params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */
+static char *http_cgi_param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for each extracted param */
+#endif /* LWIP_HTTPD_CGI */
+
+#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
+/** global list of active HTTP connections, use to kill the oldest when
+    running out of memory */
+static struct http_state *http_connections;
+
+static void
+http_add_connection(struct http_state *hs)
+{
+  /* add the connection to the list */
+  hs->next = http_connections;
+  http_connections = hs;
+}
+
+static void
+http_remove_connection(struct http_state *hs)
+{
+  /* take the connection off the list */
+  if (http_connections) {
+    if (http_connections == hs) {
+      http_connections = hs->next;
+    } else {
+      struct http_state *last;
+      for (last = http_connections; last->next != NULL; last = last->next) {
+        if (last->next == hs) {
+          last->next = hs->next;
+          break;
+        }
+      }
+    }
+  }
+}
+
+static void
+http_kill_oldest_connection(u8_t ssi_required)
+{
+  struct http_state *hs = http_connections;
+  struct http_state *hs_free_next = NULL;
+  while (hs && hs->next) {
+#if LWIP_HTTPD_SSI
+    if (ssi_required) {
+      if (hs->next->ssi != NULL) {
+        hs_free_next = hs;
+      }
+    } else
+#else /* LWIP_HTTPD_SSI */
+    LWIP_UNUSED_ARG(ssi_required);
+#endif /* LWIP_HTTPD_SSI */
+    {
+      hs_free_next = hs;
+    }
+    LWIP_ASSERT("broken list", hs != hs->next);
+    hs = hs->next;
+  }
+  if (hs_free_next != NULL) {
+    LWIP_ASSERT("hs_free_next->next != NULL", hs_free_next->next != NULL);
+    LWIP_ASSERT("hs_free_next->next->pcb != NULL", hs_free_next->next->pcb != NULL);
+    /* send RST when killing a connection because of memory shortage */
+    http_close_or_abort_conn(hs_free_next->next->pcb, hs_free_next->next, 1); /* this also unlinks the http_state from the list */
+  }
+}
+#else /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
+
+#define http_add_connection(hs)
+#define http_remove_connection(hs)
+
+#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
+
+#if LWIP_HTTPD_SSI
+/** Allocate as struct http_ssi_state. */
+static struct http_ssi_state *
+http_ssi_state_alloc(void)
+{
+  struct http_ssi_state *ret = HTTP_ALLOC_SSI_STATE();
+#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
+  if (ret == NULL) {
+    http_kill_oldest_connection(1);
+    ret = HTTP_ALLOC_SSI_STATE();
+  }
+#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
+  if (ret != NULL) {
+    memset(ret, 0, sizeof(struct http_ssi_state));
+  }
+  return ret;
+}
+
+/** Free a struct http_ssi_state. */
+static void
+http_ssi_state_free(struct http_ssi_state *ssi)
+{
+  if (ssi != NULL) {
+    HTTP_FREE_SSI_STATE(ssi);
+  }
+}
+#endif /* LWIP_HTTPD_SSI */
+
+/** Initialize a struct http_state.
+ */
+static void
+http_state_init(struct http_state *hs)
+{
+  /* Initialize the structure. */
+  memset(hs, 0, sizeof(struct http_state));
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+  /* Indicate that the headers are not yet valid */
+  hs->hdr_index = NUM_FILE_HDR_STRINGS;
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+}
+
+/** Allocate a struct http_state. */
+static struct http_state *
+http_state_alloc(void)
+{
+  struct http_state *ret = HTTP_ALLOC_HTTP_STATE();
+#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
+  if (ret == NULL) {
+    http_kill_oldest_connection(0);
+    ret = HTTP_ALLOC_HTTP_STATE();
+  }
+#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
+  if (ret != NULL) {
+    http_state_init(ret);
+    http_add_connection(ret);
+  }
+  return ret;
+}
+
+/** Free a struct http_state.
+ * Also frees the file data if dynamic.
+ */
+static void
+http_state_eof(struct http_state *hs)
+{
+  if (hs->handle) {
+#if LWIP_HTTPD_TIMING
+    u32_t ms_needed = sys_now() - hs->time_started;
+    u32_t needed = LWIP_MAX(1, (ms_needed / 100));
+    LWIP_DEBUGF(HTTPD_DEBUG_TIMING, ("httpd: needed %"U32_F" ms to send file of %d bytes -> %"U32_F" bytes/sec\n",
+                                     ms_needed, hs->handle->len, ((((u32_t)hs->handle->len) * 10) / needed)));
+#endif /* LWIP_HTTPD_TIMING */
+    fs_close(hs->handle);
+    hs->handle = NULL;
+  }
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+  if (hs->buf != NULL) {
+    mem_free(hs->buf);
+    hs->buf = NULL;
+  }
+#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
+#if LWIP_HTTPD_SSI
+  if (hs->ssi) {
+    http_ssi_state_free(hs->ssi);
+    hs->ssi = NULL;
+  }
+#endif /* LWIP_HTTPD_SSI */
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+  if (hs->req) {
+    pbuf_free(hs->req);
+    hs->req = NULL;
+  }
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+}
+
+/** Free a struct http_state.
+ * Also frees the file data if dynamic.
+ */
+static void
+http_state_free(struct http_state *hs)
+{
+  if (hs != NULL) {
+    http_state_eof(hs);
+    http_remove_connection(hs);
+    HTTP_FREE_HTTP_STATE(hs);
+  }
+}
+
+/** Call tcp_write() in a loop trying smaller and smaller length
+ *
+ * @param pcb altcp_pcb to send
+ * @param ptr Data to send
+ * @param length Length of data to send (in/out: on return, contains the
+ *        amount of data sent)
+ * @param apiflags directly passed to tcp_write
+ * @return the return value of tcp_write
+ */
+static err_t
+http_write(struct altcp_pcb *pcb, const void *ptr, u16_t *length, u8_t apiflags)
+{
+  u16_t len, max_len;
+  err_t err;
+  LWIP_ASSERT("length != NULL", length != NULL);
+  len = *length;
+  if (len == 0) {
+    return ERR_OK;
+  }
+  /* We cannot send more data than space available in the send buffer. */
+  max_len = altcp_sndbuf(pcb);
+  if (max_len < len) {
+    len = max_len;
+  }
+#ifdef HTTPD_MAX_WRITE_LEN
+  /* Additional limitation: e.g. don't enqueue more than 2*mss at once */
+  max_len = HTTPD_MAX_WRITE_LEN(pcb);
+  if (len > max_len) {
+    len = max_len;
+  }
+#endif /* HTTPD_MAX_WRITE_LEN */
+  do {
+    LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Trying to send %d bytes\n", len));
+    err = altcp_write(pcb, ptr, len, apiflags);
+    if (err == ERR_MEM) {
+      if ((altcp_sndbuf(pcb) == 0) ||
+          (altcp_sndqueuelen(pcb) >= TCP_SND_QUEUELEN)) {
+        /* no need to try smaller sizes */
+        len = 1;
+      } else {
+        len /= 2;
+      }
+      LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE,
+                  ("Send failed, trying less (%d bytes)\n", len));
+    }
+  } while ((err == ERR_MEM) && (len > 1));
+
+  if (err == ERR_OK) {
+    LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Sent %d bytes\n", len));
+    *length = len;
+  } else {
+    LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Send failed with err %d (\"%s\")\n", err, lwip_strerr(err)));
+    *length = 0;
+  }
+
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+  /* ensure nagle is normally enabled (only disabled for persistent connections
+     when all data has been enqueued but the connection stays open for the next
+     request */
+  altcp_nagle_enable(pcb);
+#endif
+
+  return err;
+}
+
+/**
+ * The connection shall be actively closed (using RST to close from fault states).
+ * Reset the sent- and recv-callbacks.
+ *
+ * @param pcb the tcp pcb to reset callbacks
+ * @param hs connection state to free
+ */
+static err_t
+http_close_or_abort_conn(struct altcp_pcb *pcb, struct http_state *hs, u8_t abort_conn)
+{
+  err_t err;
+  LWIP_DEBUGF(HTTPD_DEBUG, ("Closing connection %p\n", (void *)pcb));
+
+#if LWIP_HTTPD_SUPPORT_POST
+  if (hs != NULL) {
+    if ((hs->post_content_len_left != 0)
+#if LWIP_HTTPD_POST_MANUAL_WND
+        || ((hs->no_auto_wnd != 0) && (hs->unrecved_bytes != 0))
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+       ) {
+      /* make sure the post code knows that the connection is closed */
+      http_uri_buf[0] = 0;
+      httpd_post_finished(hs, http_uri_buf, LWIP_HTTPD_URI_BUF_LEN);
+    }
+  }
+#endif /* LWIP_HTTPD_SUPPORT_POST*/
+
+
+  altcp_arg(pcb, NULL);
+  altcp_recv(pcb, NULL);
+  altcp_err(pcb, NULL);
+  altcp_poll(pcb, NULL, 0);
+  altcp_sent(pcb, NULL);
+  if (hs != NULL) {
+    http_state_free(hs);
+  }
+
+  if (abort_conn) {
+    altcp_abort(pcb);
+    return ERR_OK;
+  }
+  err = altcp_close(pcb);
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(HTTPD_DEBUG, ("Error %d closing %p\n", err, (void *)pcb));
+    /* error closing, try again later in poll */
+    altcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL);
+  }
+  return err;
+}
+
+/**
+ * The connection shall be actively closed.
+ * Reset the sent- and recv-callbacks.
+ *
+ * @param pcb the tcp pcb to reset callbacks
+ * @param hs connection state to free
+ */
+static err_t
+http_close_conn(struct altcp_pcb *pcb, struct http_state *hs)
+{
+  return http_close_or_abort_conn(pcb, hs, 0);
+}
+
+/** End of file: either close the connection (Connection: close) or
+ * close the file (Connection: keep-alive)
+ */
+static void
+http_eof(struct altcp_pcb *pcb, struct http_state *hs)
+{
+  /* HTTP/1.1 persistent connection? (Not supported for SSI) */
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+  if (hs->keepalive) {
+    http_remove_connection(hs);
+
+    http_state_eof(hs);
+    http_state_init(hs);
+    /* restore state: */
+    hs->pcb = pcb;
+    hs->keepalive = 1;
+    http_add_connection(hs);
+    /* ensure nagle doesn't interfere with sending all data as fast as possible: */
+    altcp_nagle_disable(pcb);
+  } else
+#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
+  {
+    http_close_conn(pcb, hs);
+  }
+}
+
+#if LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI
+/**
+ * Extract URI parameters from the parameter-part of an URI in the form
+ * "test.cgi?x=y" @todo: better explanation!
+ * Pointers to the parameters are stored in hs->param_vals.
+ *
+ * @param hs http connection state
+ * @param params pointer to the NULL-terminated parameter string from the URI
+ * @return number of parameters extracted
+ */
+static int
+extract_uri_parameters(struct http_state *hs, char *params)
+{
+  char *pair;
+  char *equals;
+  int loop;
+
+  LWIP_UNUSED_ARG(hs);
+
+  /* If we have no parameters at all, return immediately. */
+  if (!params || (params[0] == '\0')) {
+    return (0);
+  }
+
+  /* Get a pointer to our first parameter */
+  pair = params;
+
+  /* Parse up to LWIP_HTTPD_MAX_CGI_PARAMETERS from the passed string and ignore the
+   * remainder (if any) */
+  for (loop = 0; (loop < LWIP_HTTPD_MAX_CGI_PARAMETERS) && pair; loop++) {
+
+    /* Save the name of the parameter */
+    http_cgi_params[loop] = pair;
+
+    /* Remember the start of this name=value pair */
+    equals = pair;
+
+    /* Find the start of the next name=value pair and replace the delimiter
+     * with a 0 to terminate the previous pair string. */
+    pair = strchr(pair, '&');
+    if (pair) {
+      *pair = '\0';
+      pair++;
+    } else {
+      /* We didn't find a new parameter so find the end of the URI and
+       * replace the space with a '\0' */
+      pair = strchr(equals, ' ');
+      if (pair) {
+        *pair = '\0';
+      }
+
+      /* Revert to NULL so that we exit the loop as expected. */
+      pair = NULL;
+    }
+
+    /* Now find the '=' in the previous pair, replace it with '\0' and save
+     * the parameter value string. */
+    equals = strchr(equals, '=');
+    if (equals) {
+      *equals = '\0';
+      http_cgi_param_vals[loop] = equals + 1;
+    } else {
+      http_cgi_param_vals[loop] = NULL;
+    }
+  }
+
+  return loop;
+}
+#endif /* LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI */
+
+#if LWIP_HTTPD_SSI
+/**
+ * Insert a tag (found in an shtml in the form of "<!--#tagname-->" into the file.
+ * The tag's name is stored in ssi->tag_name (NULL-terminated), the replacement
+ * should be written to hs->tag_insert (up to a length of LWIP_HTTPD_MAX_TAG_INSERT_LEN).
+ * The amount of data written is stored to ssi->tag_insert_len.
+ *
+ * @todo: return tag_insert_len - maybe it can be removed from struct http_state?
+ *
+ * @param hs http connection state
+ */
+static void
+get_tag_insert(struct http_state *hs)
+{
+#if LWIP_HTTPD_SSI_RAW
+  const char *tag;
+#else /* LWIP_HTTPD_SSI_RAW */
+  int tag;
+#endif /* LWIP_HTTPD_SSI_RAW */
+  size_t len;
+  struct http_ssi_state *ssi;
+#if LWIP_HTTPD_SSI_MULTIPART
+  u16_t current_tag_part;
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+
+  LWIP_ASSERT("hs != NULL", hs != NULL);
+  ssi = hs->ssi;
+  LWIP_ASSERT("ssi != NULL", ssi != NULL);
+#if LWIP_HTTPD_SSI_MULTIPART
+  current_tag_part = ssi->tag_part;
+  ssi->tag_part = HTTPD_LAST_TAG_PART;
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+#if LWIP_HTTPD_SSI_RAW
+  tag = ssi->tag_name;
+#endif
+
+  if (httpd_ssi_handler
+#if !LWIP_HTTPD_SSI_RAW
+      && httpd_tags && httpd_num_tags
+#endif /* !LWIP_HTTPD_SSI_RAW */
+     ) {
+
+    /* Find this tag in the list we have been provided. */
+#if LWIP_HTTPD_SSI_RAW
+    {
+#else /* LWIP_HTTPD_SSI_RAW */
+    for (tag = 0; tag < httpd_num_tags; tag++) {
+      if (strcmp(ssi->tag_name, httpd_tags[tag]) == 0)
+#endif /* LWIP_HTTPD_SSI_RAW */
+      {
+        ssi->tag_insert_len = httpd_ssi_handler(tag, ssi->tag_insert,
+                                              LWIP_HTTPD_MAX_TAG_INSERT_LEN
+#if LWIP_HTTPD_SSI_MULTIPART
+                                              , current_tag_part, &ssi->tag_part
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+#if LWIP_HTTPD_FILE_STATE
+                                              , (hs->handle ? hs->handle->state : NULL)
+#endif /* LWIP_HTTPD_FILE_STATE */
+                                             );
+#if LWIP_HTTPD_SSI_RAW
+        if (ssi->tag_insert_len != HTTPD_SSI_TAG_UNKNOWN)
+#endif /* LWIP_HTTPD_SSI_RAW */
+        {
+          return;
+        }
+      }
+    }
+  }
+
+  /* If we drop out, we were asked to serve a page which contains tags that
+   * we don't have a handler for. Merely echo back the tags with an error
+   * marker. */
+#define UNKNOWN_TAG1_TEXT "<b>***UNKNOWN TAG "
+#define UNKNOWN_TAG1_LEN  18
+#define UNKNOWN_TAG2_TEXT "***</b>"
+#define UNKNOWN_TAG2_LEN  7
+  len = LWIP_MIN(sizeof(ssi->tag_name), LWIP_MIN(strlen(ssi->tag_name),
+                 LWIP_HTTPD_MAX_TAG_INSERT_LEN - (UNKNOWN_TAG1_LEN + UNKNOWN_TAG2_LEN)));
+  MEMCPY(ssi->tag_insert, UNKNOWN_TAG1_TEXT, UNKNOWN_TAG1_LEN);
+  MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN], ssi->tag_name, len);
+  MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN + len], UNKNOWN_TAG2_TEXT, UNKNOWN_TAG2_LEN);
+  ssi->tag_insert[UNKNOWN_TAG1_LEN + len + UNKNOWN_TAG2_LEN] = 0;
+
+  len = strlen(ssi->tag_insert);
+  LWIP_ASSERT("len <= 0xffff", len <= 0xffff);
+  ssi->tag_insert_len = (u16_t)len;
+}
+#endif /* LWIP_HTTPD_SSI */
+
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+/**
+ * Generate the relevant HTTP headers for the given filename and write
+ * them into the supplied buffer.
+ */
+static void
+get_http_headers(struct http_state *hs, const char *uri)
+{
+  size_t content_type;
+  char *tmp;
+  char *ext;
+  char *vars;
+
+  /* In all cases, the second header we send is the server identification
+     so set it here. */
+  hs->hdrs[HDR_STRINGS_IDX_SERVER_NAME] = g_psHTTPHeaderStrings[HTTP_HDR_SERVER];
+  hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] = NULL;
+  hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_NR] = NULL;
+
+  /* Is this a normal file or the special case we use to send back the
+     default "404: Page not found" response? */
+  if (uri == NULL) {
+    hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND];
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+    if (hs->keepalive) {
+      hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaderStrings[DEFAULT_404_HTML_PERSISTENT];
+    } else
+#endif
+    {
+      hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaderStrings[DEFAULT_404_HTML];
+    }
+
+    /* Set up to send the first header string. */
+    hs->hdr_index = 0;
+    hs->hdr_pos = 0;
+    return;
+  }
+  /* We are dealing with a particular filename. Look for one other
+      special case.  We assume that any filename with "404" in it must be
+      indicative of a 404 server error whereas all other files require
+      the 200 OK header. */
+  if (strstr(uri, "404")) {
+    hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND];
+  } else if (strstr(uri, "400")) {
+    hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_BAD_REQUEST];
+  } else if (strstr(uri, "501")) {
+    hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_IMPL];
+  } else {
+    hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_OK];
+  }
+
+  /* Determine if the URI has any variables and, if so, temporarily remove
+      them. */
+  vars = strchr(uri, '?');
+  if (vars) {
+    *vars = '\0';
+  }
+
+  /* Get a pointer to the file extension.  We find this by looking for the
+      last occurrence of "." in the filename passed. */
+  ext = NULL;
+  tmp = strchr(uri, '.');
+  while (tmp) {
+    ext = tmp + 1;
+    tmp = strchr(ext, '.');
+  }
+  if (ext != NULL) {
+    /* Now determine the content type and add the relevant header for that. */
+    for (content_type = 0; content_type < NUM_HTTP_HEADERS; content_type++) {
+      /* Have we found a matching extension? */
+      if (!lwip_stricmp(g_psHTTPHeaders[content_type].extension, ext)) {
+        break;
+      }
+    }
+  } else {
+    content_type = NUM_HTTP_HEADERS;
+  }
+
+  /* Reinstate the parameter marker if there was one in the original URI. */
+  if (vars) {
+    *vars = '?';
+  }
+
+#if LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI
+  /* Does the URL passed have any file extension?  If not, we assume it
+     is a special-case URL used for control state notification and we do
+     not send any HTTP headers with the response. */
+  if (!ext) {
+    /* Force the header index to a value indicating that all headers
+       have already been sent. */
+    hs->hdr_index = NUM_FILE_HDR_STRINGS;
+    return;
+  }
+#endif /* LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI */
+  /* Did we find a matching extension? */
+  if (content_type < NUM_HTTP_HEADERS) {
+    /* yes, store it */
+    hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaders[content_type].content_type;
+  } else if (!ext) {
+    /* no, no extension found -> use binary transfer to prevent the browser adding '.txt' on save */
+    hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = HTTP_HDR_APP;
+  } else {
+    /* No - use the default, plain text file type. */
+    hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = HTTP_HDR_DEFAULT_TYPE;
+  }
+  /* Set up to send the first header string. */
+  hs->hdr_index = 0;
+  hs->hdr_pos = 0;
+}
+
+/* Add content-length header? */
+static void
+get_http_content_length(struct http_state *hs)
+{
+  u8_t add_content_len = 0;
+
+  LWIP_ASSERT("already been here?", hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] == NULL);
+
+  add_content_len = 0;
+#if LWIP_HTTPD_SSI
+  if (hs->ssi == NULL) /* @todo: get maximum file length from SSI */
+#endif /* LWIP_HTTPD_SSI */
+  {
+    if ((hs->handle != NULL) && (hs->handle->flags & FS_FILE_FLAGS_HEADER_PERSISTENT)) {
+      add_content_len = 1;
+    }
+  }
+  if (add_content_len) {
+    size_t len;
+    lwip_itoa(hs->hdr_content_len, (size_t)LWIP_HTTPD_MAX_CONTENT_LEN_SIZE,
+              hs->handle->len);
+    len = strlen(hs->hdr_content_len);
+    if (len <= LWIP_HTTPD_MAX_CONTENT_LEN_SIZE - LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET) {
+      SMEMCPY(&hs->hdr_content_len[len], CRLF, 3);
+      hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_NR] = hs->hdr_content_len;
+    } else {
+      add_content_len = 0;
+    }
+  }
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+  if (add_content_len) {
+    hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_KEEPALIVE_LEN];
+  } else {
+    hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_CONN_CLOSE];
+    hs->keepalive = 0;
+  }
+#else /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
+  if (add_content_len) {
+    hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_CONTENT_LENGTH];
+  }
+#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
+}
+
+/** Sub-function of http_send(): send dynamic headers
+ *
+ * @returns: - HTTP_NO_DATA_TO_SEND: no new data has been enqueued
+ *           - HTTP_DATA_TO_SEND_CONTINUE: continue with sending HTTP body
+ *           - HTTP_DATA_TO_SEND_BREAK: data has been enqueued, headers pending,
+ *                                      so don't send HTTP body yet
+ *           - HTTP_DATA_TO_SEND_FREED: http_state and pcb are already freed
+ */
+static u8_t
+http_send_headers(struct altcp_pcb *pcb, struct http_state *hs)
+{
+  err_t err;
+  u16_t len;
+  u8_t data_to_send = HTTP_NO_DATA_TO_SEND;
+  u16_t hdrlen, sendlen;
+
+  if (hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] == NULL) {
+    /* set up "content-length" and "connection:" headers */
+    get_http_content_length(hs);
+  }
+
+  /* How much data can we send? */
+  len = altcp_sndbuf(pcb);
+  sendlen = len;
+
+  while (len && (hs->hdr_index < NUM_FILE_HDR_STRINGS) && sendlen) {
+    const void *ptr;
+    u16_t old_sendlen;
+    u8_t apiflags;
+    /* How much do we have to send from the current header? */
+    hdrlen = (u16_t)strlen(hs->hdrs[hs->hdr_index]);
+
+    /* How much of this can we send? */
+    sendlen = (len < (hdrlen - hs->hdr_pos)) ? len : (hdrlen - hs->hdr_pos);
+
+    /* Send this amount of data or as much as we can given memory
+     * constraints. */
+    ptr = (const void *)(hs->hdrs[hs->hdr_index] + hs->hdr_pos);
+    old_sendlen = sendlen;
+    apiflags = HTTP_IS_HDR_VOLATILE(hs, ptr);
+    if (hs->hdr_index == HDR_STRINGS_IDX_CONTENT_LEN_NR) {
+      /* content-length is always volatile */
+      apiflags |= TCP_WRITE_FLAG_COPY;
+    }
+    if (hs->hdr_index < NUM_FILE_HDR_STRINGS - 1) {
+      apiflags |= TCP_WRITE_FLAG_MORE;
+    }
+    err = http_write(pcb, ptr, &sendlen, apiflags);
+    if ((err == ERR_OK) && (old_sendlen != sendlen)) {
+      /* Remember that we added some more data to be transmitted. */
+      data_to_send = HTTP_DATA_TO_SEND_CONTINUE;
+    } else if (err != ERR_OK) {
+      /* special case: http_write does not try to send 1 byte */
+      sendlen = 0;
+    }
+
+    /* Fix up the header position for the next time round. */
+    hs->hdr_pos += sendlen;
+    len -= sendlen;
+
+    /* Have we finished sending this string? */
+    if (hs->hdr_pos == hdrlen) {
+      /* Yes - move on to the next one */
+      hs->hdr_index++;
+      /* skip headers that are NULL (not all headers are required) */
+      while ((hs->hdr_index < NUM_FILE_HDR_STRINGS) &&
+             (hs->hdrs[hs->hdr_index] == NULL)) {
+        hs->hdr_index++;
+      }
+      hs->hdr_pos = 0;
+    }
+  }
+
+  if ((hs->hdr_index >= NUM_FILE_HDR_STRINGS) && (hs->file == NULL)) {
+    /* When we are at the end of the headers, check for data to send
+     * instead of waiting for ACK from remote side to continue
+     * (which would happen when sending files from async read). */
+    if (http_check_eof(pcb, hs)) {
+      data_to_send = HTTP_DATA_TO_SEND_BREAK;
+    } else {
+      /* At this point, for non-keepalive connections, hs is deallocated an
+         pcb is closed. */
+      return HTTP_DATA_TO_SEND_FREED;
+    }
+  }
+  /* If we get here and there are still header bytes to send, we send
+   * the header information we just wrote immediately. If there are no
+   * more headers to send, but we do have file data to send, drop through
+   * to try to send some file data too. */
+  if ((hs->hdr_index < NUM_FILE_HDR_STRINGS) || !hs->file) {
+    LWIP_DEBUGF(HTTPD_DEBUG, ("tcp_output\n"));
+    return HTTP_DATA_TO_SEND_BREAK;
+  }
+  return data_to_send;
+}
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+
+/** Sub-function of http_send(): end-of-file (or block) is reached,
+ * either close the file or read the next block (if supported).
+ *
+ * @returns: 0 if the file is finished or no data has been read
+ *           1 if the file is not finished and data has been read
+ */
+static u8_t
+http_check_eof(struct altcp_pcb *pcb, struct http_state *hs)
+{
+  int bytes_left;
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+  int count;
+#ifdef HTTPD_MAX_WRITE_LEN
+  int max_write_len;
+#endif /* HTTPD_MAX_WRITE_LEN */
+#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
+
+  /* Do we have a valid file handle? */
+  if (hs->handle == NULL) {
+    /* No - close the connection. */
+    http_eof(pcb, hs);
+    return 0;
+  }
+  bytes_left = fs_bytes_left(hs->handle);
+  if (bytes_left <= 0) {
+    /* We reached the end of the file so this request is done. */
+    LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n"));
+    http_eof(pcb, hs);
+    return 0;
+  }
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+  /* Do we already have a send buffer allocated? */
+  if (hs->buf) {
+    /* Yes - get the length of the buffer */
+    count = LWIP_MIN(hs->buf_len, bytes_left);
+  } else {
+    /* We don't have a send buffer so allocate one now */
+    count = altcp_sndbuf(pcb);
+    if (bytes_left < count) {
+      count = bytes_left;
+    }
+#ifdef HTTPD_MAX_WRITE_LEN
+    /* Additional limitation: e.g. don't enqueue more than 2*mss at once */
+    max_write_len = HTTPD_MAX_WRITE_LEN(pcb);
+    if (count > max_write_len) {
+      count = max_write_len;
+    }
+#endif /* HTTPD_MAX_WRITE_LEN */
+    do {
+      hs->buf = (char *)mem_malloc((mem_size_t)count);
+      if (hs->buf != NULL) {
+        hs->buf_len = count;
+        break;
+      }
+      count = count / 2;
+    } while (count > 100);
+
+    /* Did we get a send buffer? If not, return immediately. */
+    if (hs->buf == NULL) {
+      LWIP_DEBUGF(HTTPD_DEBUG, ("No buff\n"));
+      return 0;
+    }
+  }
+
+  /* Read a block of data from the file. */
+  LWIP_DEBUGF(HTTPD_DEBUG, ("Trying to read %d bytes.\n", count));
+
+#if LWIP_HTTPD_FS_ASYNC_READ
+  count = fs_read_async(hs->handle, hs->buf, count, http_continue, hs);
+#else /* LWIP_HTTPD_FS_ASYNC_READ */
+  count = fs_read(hs->handle, hs->buf, count);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+  if (count < 0) {
+    if (count == FS_READ_DELAYED) {
+      /* Delayed read, wait for FS to unblock us */
+      return 0;
+    }
+    /* We reached the end of the file so this request is done.
+     * @todo: close here for HTTP/1.1 when reading file fails */
+    LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n"));
+    http_eof(pcb, hs);
+    return 0;
+  }
+
+  /* Set up to send the block of data we just read */
+  LWIP_DEBUGF(HTTPD_DEBUG, ("Read %d bytes.\n", count));
+  hs->left = count;
+  hs->file = hs->buf;
+#if LWIP_HTTPD_SSI
+  if (hs->ssi) {
+    hs->ssi->parse_left = count;
+    hs->ssi->parsed = hs->buf;
+  }
+#endif /* LWIP_HTTPD_SSI */
+#else /* LWIP_HTTPD_DYNAMIC_FILE_READ */
+  LWIP_ASSERT("SSI and DYNAMIC_HEADERS turned off but eof not reached", 0);
+#endif /* LWIP_HTTPD_SSI || LWIP_HTTPD_DYNAMIC_HEADERS */
+  return 1;
+}
+
+/** Sub-function of http_send(): This is the normal send-routine for non-ssi files
+ *
+ * @returns: - 1: data has been written (so call tcp_ouput)
+ *           - 0: no data has been written (no need to call tcp_output)
+ */
+static u8_t
+http_send_data_nonssi(struct altcp_pcb *pcb, struct http_state *hs)
+{
+  err_t err;
+  u16_t len;
+  u8_t data_to_send = 0;
+
+  /* We are not processing an SHTML file so no tag checking is necessary.
+   * Just send the data as we received it from the file. */
+  len = (u16_t)LWIP_MIN(hs->left, 0xffff);
+
+  err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
+  if (err == ERR_OK) {
+    data_to_send = 1;
+    hs->file += len;
+    hs->left -= len;
+  }
+
+  return data_to_send;
+}
+
+#if LWIP_HTTPD_SSI
+/** Sub-function of http_send(): This is the send-routine for ssi files
+ *
+ * @returns: - 1: data has been written (so call tcp_ouput)
+ *           - 0: no data has been written (no need to call tcp_output)
+ */
+static u8_t
+http_send_data_ssi(struct altcp_pcb *pcb, struct http_state *hs)
+{
+  err_t err = ERR_OK;
+  u16_t len;
+  u8_t data_to_send = 0;
+  u8_t tag_type;
+
+  struct http_ssi_state *ssi = hs->ssi;
+  LWIP_ASSERT("ssi != NULL", ssi != NULL);
+  /* We are processing an SHTML file so need to scan for tags and replace
+   * them with insert strings. We need to be careful here since a tag may
+   * straddle the boundary of two blocks read from the file and we may also
+   * have to split the insert string between two tcp_write operations. */
+
+  /* How much data could we send? */
+  len = altcp_sndbuf(pcb);
+
+  /* Do we have remaining data to send before parsing more? */
+  if (ssi->parsed > hs->file) {
+    len = (u16_t)LWIP_MIN(ssi->parsed - hs->file, 0xffff);
+
+    err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
+    if (err == ERR_OK) {
+      data_to_send = 1;
+      hs->file += len;
+      hs->left -= len;
+    }
+
+    /* If the send buffer is full, return now. */
+    if (altcp_sndbuf(pcb) == 0) {
+      return data_to_send;
+    }
+  }
+
+  LWIP_DEBUGF(HTTPD_DEBUG, ("State %d, %d left\n", ssi->tag_state, (int)ssi->parse_left));
+
+  /* We have sent all the data that was already parsed so continue parsing
+   * the buffer contents looking for SSI tags. */
+  while (((ssi->tag_state == TAG_SENDING) || ssi->parse_left) && (err == ERR_OK)) {
+    if (len == 0) {
+      return data_to_send;
+    }
+    switch (ssi->tag_state) {
+      case TAG_NONE:
+        /* We are not currently processing an SSI tag so scan for the
+         * start of the lead-in marker. */
+        for (tag_type = 0; tag_type < LWIP_ARRAYSIZE(http_ssi_tag_desc); tag_type++) {
+          if (*ssi->parsed == http_ssi_tag_desc[tag_type].lead_in[0]) {
+            /* We found what could be the lead-in for a new tag so change
+             * state appropriately. */
+            ssi->tag_type = tag_type;
+            ssi->tag_state = TAG_LEADIN;
+            ssi->tag_index = 1;
+  #if !LWIP_HTTPD_SSI_INCLUDE_TAG
+            ssi->tag_started = ssi->parsed;
+  #endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */
+            break;
+          }
+        }
+
+        /* Move on to the next character in the buffer */
+        ssi->parse_left--;
+        ssi->parsed++;
+        break;
+
+      case TAG_LEADIN:
+        /* We are processing the lead-in marker, looking for the start of
+         * the tag name. */
+
+        /* Have we reached the end of the leadin? */
+        if (http_ssi_tag_desc[ssi->tag_type].lead_in[ssi->tag_index] == 0) {
+          ssi->tag_index = 0;
+          ssi->tag_state = TAG_FOUND;
+        } else {
+          /* Have we found the next character we expect for the tag leadin? */
+          if (*ssi->parsed == http_ssi_tag_desc[ssi->tag_type].lead_in[ssi->tag_index]) {
+            /* Yes - move to the next one unless we have found the complete
+             * leadin, in which case we start looking for the tag itself */
+            ssi->tag_index++;
+          } else {
+            /* We found an unexpected character so this is not a tag. Move
+             * back to idle state. */
+            ssi->tag_state = TAG_NONE;
+          }
+
+#if LWIP_HTTPD_DYNAMIC_FILE_READ && !LWIP_HTTPD_SSI_INCLUDE_TAG
+          if ((ssi->tag_state == TAG_NONE) &&
+              (ssi->parsed - hs->file < ssi->tag_index)) {
+            for(u16_t i = 0;i < ssi->tag_index;i++) {
+              ssi->tag_insert[i] = http_ssi_tag_desc[ssi->tag_type].lead_in[i];
+            }
+            ssi->tag_insert_len = ssi->tag_index;
+            hs->file += ssi->parsed - hs->file;
+            hs->left -= ssi->parsed - hs->file;
+            ssi->tag_end = hs->file;
+            ssi->tag_index = 0;
+            ssi->tag_state = TAG_SENDING;
+            break;
+          }
+#endif
+
+          /* Move on to the next character in the buffer */
+          ssi->parse_left--;
+          ssi->parsed++;
+        }
+        break;
+
+      case TAG_FOUND:
+        /* We are reading the tag name, looking for the start of the
+         * lead-out marker and removing any whitespace found. */
+
+        /* Remove leading whitespace between the tag leading and the first
+         * tag name character. */
+        if ((ssi->tag_index == 0) && ((*ssi->parsed == ' ') ||
+                                      (*ssi->parsed == '\t') || (*ssi->parsed == '\n') ||
+                                      (*ssi->parsed == '\r'))) {
+          /* Move on to the next character in the buffer */
+          ssi->parse_left--;
+          ssi->parsed++;
+          break;
+        }
+
+        /* Have we found the end of the tag name? This is signalled by
+         * us finding the first leadout character or whitespace */
+        if ((*ssi->parsed == http_ssi_tag_desc[ssi->tag_type].lead_out[0]) ||
+            (*ssi->parsed == ' ')  || (*ssi->parsed == '\t') ||
+            (*ssi->parsed == '\n') || (*ssi->parsed == '\r')) {
+
+          if (ssi->tag_index == 0) {
+            /* We read a zero length tag so ignore it. */
+            ssi->tag_state = TAG_NONE;
+          } else {
+            /* We read a non-empty tag so go ahead and look for the
+             * leadout string. */
+            ssi->tag_state = TAG_LEADOUT;
+            LWIP_ASSERT("ssi->tag_index <= 0xff", ssi->tag_index <= 0xff);
+            ssi->tag_name_len = (u8_t)ssi->tag_index;
+            ssi->tag_name[ssi->tag_index] = '\0';
+            if (*ssi->parsed == http_ssi_tag_desc[ssi->tag_type].lead_out[0]) {
+              ssi->tag_index = 1;
+            } else {
+              ssi->tag_index = 0;
+            }
+          }
+        } else {
+          /* This character is part of the tag name so save it */
+          if (ssi->tag_index < LWIP_HTTPD_MAX_TAG_NAME_LEN) {
+            ssi->tag_name[ssi->tag_index++] = *ssi->parsed;
+          } else {
+            /* The tag was too long so ignore it. */
+            ssi->tag_state = TAG_NONE;
+          }
+        }
+
+        /* Move on to the next character in the buffer */
+        ssi->parse_left--;
+        ssi->parsed++;
+
+        break;
+
+      /* We are looking for the end of the lead-out marker. */
+      case TAG_LEADOUT:
+        /* Remove leading whitespace between the tag leading and the first
+         * tag leadout character. */
+        if ((ssi->tag_index == 0) && ((*ssi->parsed == ' ') ||
+                                      (*ssi->parsed == '\t') || (*ssi->parsed == '\n') ||
+                                      (*ssi->parsed == '\r'))) {
+          /* Move on to the next character in the buffer */
+          ssi->parse_left--;
+          ssi->parsed++;
+          break;
+        }
+
+        /* Have we found the next character we expect for the tag leadout? */
+        if (*ssi->parsed == http_ssi_tag_desc[ssi->tag_type].lead_out[ssi->tag_index]) {
+          /* Yes - move to the next one unless we have found the complete
+           * leadout, in which case we need to call the client to process
+           * the tag. */
+
+          /* Move on to the next character in the buffer */
+          ssi->parse_left--;
+          ssi->parsed++;
+          ssi->tag_index++;
+
+          if (http_ssi_tag_desc[ssi->tag_type].lead_out[ssi->tag_index] == 0) {
+            /* Call the client to ask for the insert string for the
+             * tag we just found. */
+#if LWIP_HTTPD_SSI_MULTIPART
+            ssi->tag_part = 0; /* start with tag part 0 */
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+            get_tag_insert(hs);
+
+            /* Next time through, we are going to be sending data
+             * immediately, either the end of the block we start
+             * sending here or the insert string. */
+            ssi->tag_index = 0;
+            ssi->tag_state = TAG_SENDING;
+            ssi->tag_end = ssi->parsed;
+#if !LWIP_HTTPD_SSI_INCLUDE_TAG
+            ssi->parsed = ssi->tag_started;
+#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
+
+            /* If there is any unsent data in the buffer prior to the
+             * tag, we need to send it now. */
+            if (ssi->tag_end > hs->file) {
+              /* How much of the data can we send? */
+#if LWIP_HTTPD_SSI_INCLUDE_TAG
+              len = (u16_t)LWIP_MIN(ssi->tag_end - hs->file, 0xffff);
+#else /* LWIP_HTTPD_SSI_INCLUDE_TAG*/
+              /* we would include the tag in sending */
+              len = (u16_t)LWIP_MIN(ssi->tag_started - hs->file, 0xffff);
+#endif /* LWIP_HTTPD_SSI_INCLUDE_TAG*/
+
+              err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
+              if (err == ERR_OK) {
+                data_to_send = 1;
+#if !LWIP_HTTPD_SSI_INCLUDE_TAG
+                if (ssi->tag_started <= hs->file) {
+                  /* pretend to have sent the tag, too */
+                  len += (u16_t)(ssi->tag_end - ssi->tag_started);
+                }
+#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
+                hs->file += len;
+                hs->left -= len;
+              }
+            }
+          }
+        } else {
+          /* We found an unexpected character so this is not a tag. Move
+           * back to idle state. */
+          ssi->parse_left--;
+          ssi->parsed++;
+          ssi->tag_state = TAG_NONE;
+        }
+        break;
+
+      /*
+       * We have found a valid tag and are in the process of sending
+       * data as a result of that discovery. We send either remaining data
+       * from the file prior to the insert point or the insert string itself.
+       */
+      case TAG_SENDING:
+        /* Do we have any remaining file data to send from the buffer prior
+         * to the tag? */
+        if (ssi->tag_end > hs->file) {
+          /* How much of the data can we send? */
+#if LWIP_HTTPD_SSI_INCLUDE_TAG
+          len = (u16_t)LWIP_MIN(ssi->tag_end - hs->file, 0xffff);
+#else /* LWIP_HTTPD_SSI_INCLUDE_TAG*/
+          LWIP_ASSERT("hs->started >= hs->file", ssi->tag_started >= hs->file);
+          /* we would include the tag in sending */
+          len = (u16_t)LWIP_MIN(ssi->tag_started - hs->file, 0xffff);
+#endif /* LWIP_HTTPD_SSI_INCLUDE_TAG*/
+          if (len != 0) {
+            err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
+          } else {
+            err = ERR_OK;
+          }
+          if (err == ERR_OK) {
+            data_to_send = 1;
+#if !LWIP_HTTPD_SSI_INCLUDE_TAG
+            if (ssi->tag_started <= hs->file) {
+              /* pretend to have sent the tag, too */
+              len += (u16_t)(ssi->tag_end - ssi->tag_started);
+            }
+#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
+            hs->file += len;
+            hs->left -= len;
+          }
+        } else {
+#if LWIP_HTTPD_SSI_MULTIPART
+          if (ssi->tag_index >= ssi->tag_insert_len) {
+            /* Did the last SSIHandler have more to send? */
+            if (ssi->tag_part != HTTPD_LAST_TAG_PART) {
+              /* If so, call it again */
+              ssi->tag_index = 0;
+              get_tag_insert(hs);
+            }
+          }
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+
+          /* Do we still have insert data left to send? */
+          if (ssi->tag_index < ssi->tag_insert_len) {
+            /* We are sending the insert string itself. How much of the
+             * insert can we send? */
+            len = (ssi->tag_insert_len - ssi->tag_index);
+
+            /* Note that we set the copy flag here since we only have a
+             * single tag insert buffer per connection. If we don't do
+             * this, insert corruption can occur if more than one insert
+             * is processed before we call tcp_output. */
+            err = http_write(pcb, &(ssi->tag_insert[ssi->tag_index]), &len,
+                             HTTP_IS_TAG_VOLATILE(hs));
+            if (err == ERR_OK) {
+              data_to_send = 1;
+              ssi->tag_index += len;
+              /* Don't return here: keep on sending data */
+            }
+          } else {
+#if LWIP_HTTPD_SSI_MULTIPART
+            if (ssi->tag_part == HTTPD_LAST_TAG_PART)
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+            {
+              /* We have sent all the insert data so go back to looking for
+               * a new tag. */
+              LWIP_DEBUGF(HTTPD_DEBUG, ("Everything sent.\n"));
+              ssi->tag_index = 0;
+              ssi->tag_state = TAG_NONE;
+#if !LWIP_HTTPD_SSI_INCLUDE_TAG
+              ssi->parsed = ssi->tag_end;
+#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
+            }
+          }
+          break;
+        default:
+          break;
+        }
+    }
+  }
+
+  /* If we drop out of the end of the for loop, this implies we must have
+   * file data to send so send it now. In TAG_SENDING state, we've already
+   * handled this so skip the send if that's the case. */
+  if ((ssi->tag_state != TAG_SENDING) && (ssi->parsed > hs->file)) {
+#if LWIP_HTTPD_DYNAMIC_FILE_READ && !LWIP_HTTPD_SSI_INCLUDE_TAG
+    if ((ssi->tag_state != TAG_NONE) && (ssi->tag_started > ssi->tag_end)) {
+      /* If we found tag on the edge of the read buffer: just throw away the first part
+         (we have copied/saved everything required for parsing on later). */
+      len = (u16_t)(ssi->tag_started - hs->file);
+      hs->left -= (ssi->parsed - ssi->tag_started);
+      ssi->parsed = ssi->tag_started;
+      ssi->tag_started = hs->buf;
+    } else
+#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ && !LWIP_HTTPD_SSI_INCLUDE_TAG */
+    {
+      len = (u16_t)LWIP_MIN(ssi->parsed - hs->file, 0xffff);
+    }
+
+    err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
+    if (err == ERR_OK) {
+      data_to_send = 1;
+      hs->file += len;
+      hs->left -= len;
+    }
+  }
+  return data_to_send;
+}
+#endif /* LWIP_HTTPD_SSI */
+
+/**
+ * Try to send more data on this pcb.
+ *
+ * @param pcb the pcb to send data
+ * @param hs connection state
+ */
+static u8_t
+http_send(struct altcp_pcb *pcb, struct http_state *hs)
+{
+  u8_t data_to_send = HTTP_NO_DATA_TO_SEND;
+
+  LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_send: pcb=%p hs=%p left=%d\n", (void *)pcb,
+              (void *)hs, hs != NULL ? (int)hs->left : 0));
+
+#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
+  if (hs->unrecved_bytes != 0) {
+    return 0;
+  }
+#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */
+
+  /* If we were passed a NULL state structure pointer, ignore the call. */
+  if (hs == NULL) {
+    return 0;
+  }
+
+#if LWIP_HTTPD_FS_ASYNC_READ
+  /* Check if we are allowed to read from this file.
+     (e.g. SSI might want to delay sending until data is available) */
+  if (!fs_is_file_ready(hs->handle, http_continue, hs)) {
+    return 0;
+  }
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+  /* Do we have any more header data to send for this file? */
+  if (hs->hdr_index < NUM_FILE_HDR_STRINGS) {
+    data_to_send = http_send_headers(pcb, hs);
+    if ((data_to_send == HTTP_DATA_TO_SEND_FREED) ||
+        ((data_to_send != HTTP_DATA_TO_SEND_CONTINUE) &&
+         (hs->hdr_index < NUM_FILE_HDR_STRINGS))) {
+      return data_to_send;
+    }
+  }
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+
+  /* Have we run out of file data to send? If so, we need to read the next
+   * block from the file. */
+  if (hs->left == 0) {
+    if (!http_check_eof(pcb, hs)) {
+      return 0;
+    }
+  }
+
+#if LWIP_HTTPD_SSI
+  if (hs->ssi) {
+    data_to_send = http_send_data_ssi(pcb, hs);
+  } else
+#endif /* LWIP_HTTPD_SSI */
+  {
+    data_to_send = http_send_data_nonssi(pcb, hs);
+  }
+
+  if ((hs->left == 0) && (fs_bytes_left(hs->handle) <= 0)) {
+    /* We reached the end of the file so this request is done.
+     * This adds the FIN flag right into the last data segment. */
+    LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n"));
+    http_eof(pcb, hs);
+    return 0;
+  }
+  LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("send_data end.\n"));
+  return data_to_send;
+}
+
+#if LWIP_HTTPD_SUPPORT_EXTSTATUS
+/** Initialize a http connection with a file to send for an error message
+ *
+ * @param hs http connection state
+ * @param error_nr HTTP error number
+ * @return ERR_OK if file was found and hs has been initialized correctly
+ *         another err_t otherwise
+ */
+static err_t
+http_find_error_file(struct http_state *hs, u16_t error_nr)
+{
+  const char *uri, *uri1, *uri2, *uri3;
+
+  if (error_nr == 501) {
+    uri1 = "/501.html";
+    uri2 = "/501.htm";
+    uri3 = "/501.shtml";
+  } else {
+    /* 400 (bad request is the default) */
+    uri1 = "/400.html";
+    uri2 = "/400.htm";
+    uri3 = "/400.shtml";
+  }
+  if (fs_open(&hs->file_handle, uri1) == ERR_OK) {
+    uri = uri1;
+  } else if (fs_open(&hs->file_handle, uri2) == ERR_OK) {
+    uri = uri2;
+  } else if (fs_open(&hs->file_handle, uri3) == ERR_OK) {
+    uri = uri3;
+  } else {
+    LWIP_DEBUGF(HTTPD_DEBUG, ("Error page for error %"U16_F" not found\n",
+                              error_nr));
+    return ERR_ARG;
+  }
+  return http_init_file(hs, &hs->file_handle, 0, uri, 0, NULL);
+}
+#else /* LWIP_HTTPD_SUPPORT_EXTSTATUS */
+#define http_find_error_file(hs, error_nr) ERR_ARG
+#endif /* LWIP_HTTPD_SUPPORT_EXTSTATUS */
+
+/**
+ * Get the file struct for a 404 error page.
+ * Tries some file names and returns NULL if none found.
+ *
+ * @param uri pointer that receives the actual file name URI
+ * @return file struct for the error page or NULL no matching file was found
+ */
+static struct fs_file *
+http_get_404_file(struct http_state *hs, const char **uri)
+{
+  err_t err;
+
+  *uri = "/404.html";
+  err = fs_open(&hs->file_handle, *uri);
+  if (err != ERR_OK) {
+    /* 404.html doesn't exist. Try 404.htm instead. */
+    *uri = "/404.htm";
+    err = fs_open(&hs->file_handle, *uri);
+    if (err != ERR_OK) {
+      /* 404.htm doesn't exist either. Try 404.shtml instead. */
+      *uri = "/404.shtml";
+      err = fs_open(&hs->file_handle, *uri);
+      if (err != ERR_OK) {
+        /* 404.htm doesn't exist either. Indicate to the caller that it should
+         * send back a default 404 page.
+         */
+        *uri = NULL;
+        return NULL;
+      }
+    }
+  }
+
+  return &hs->file_handle;
+}
+
+#if LWIP_HTTPD_SUPPORT_POST
+static err_t
+http_handle_post_finished(struct http_state *hs)
+{
+#if LWIP_HTTPD_POST_MANUAL_WND
+  /* Prevent multiple calls to httpd_post_finished, since it might have already
+     been called before from httpd_post_data_recved(). */
+  if (hs->post_finished) {
+    return ERR_OK;
+  }
+  hs->post_finished = 1;
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+  /* application error or POST finished */
+  /* NULL-terminate the buffer */
+  http_uri_buf[0] = 0;
+  httpd_post_finished(hs, http_uri_buf, LWIP_HTTPD_URI_BUF_LEN);
+  return http_find_file(hs, http_uri_buf, 0);
+}
+
+/** Pass received POST body data to the application and correctly handle
+ * returning a response document or closing the connection.
+ * ATTENTION: The application is responsible for the pbuf now, so don't free it!
+ *
+ * @param hs http connection state
+ * @param p pbuf to pass to the application
+ * @return ERR_OK if passed successfully, another err_t if the response file
+ *         hasn't been found (after POST finished)
+ */
+static err_t
+http_post_rxpbuf(struct http_state *hs, struct pbuf *p)
+{
+  err_t err;
+
+  if (p != NULL) {
+    /* adjust remaining Content-Length */
+    if (hs->post_content_len_left < p->tot_len) {
+      hs->post_content_len_left = 0;
+    } else {
+      hs->post_content_len_left -= p->tot_len;
+    }
+  }
+#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
+  /* prevent connection being closed if httpd_post_data_recved() is called nested */
+  hs->unrecved_bytes++;
+#endif
+  if (p != NULL) {
+    err = httpd_post_receive_data(hs, p);
+  } else {
+    err = ERR_OK;
+  }
+#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
+  hs->unrecved_bytes--;
+#endif
+  if (err != ERR_OK) {
+    /* Ignore remaining content in case of application error */
+    hs->post_content_len_left = 0;
+  }
+  if (hs->post_content_len_left == 0) {
+#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
+    if (hs->unrecved_bytes != 0) {
+      return ERR_OK;
+    }
+#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */
+    /* application error or POST finished */
+    return http_handle_post_finished(hs);
+  }
+
+  return ERR_OK;
+}
+
+/** Handle a post request. Called from http_parse_request when method 'POST'
+ * is found.
+ *
+ * @param p The input pbuf (containing the POST header and body).
+ * @param hs The http connection state.
+ * @param data HTTP request (header and part of body) from input pbuf(s).
+ * @param data_len Size of 'data'.
+ * @param uri The HTTP URI parsed from input pbuf(s).
+ * @param uri_end Pointer to the end of 'uri' (here, the rest of the HTTP
+ *                header starts).
+ * @return ERR_OK: POST correctly parsed and accepted by the application.
+ *         ERR_INPROGRESS: POST not completely parsed (no error yet)
+ *         another err_t: Error parsing POST or denied by the application
+ */
+static err_t
+http_post_request(struct pbuf *inp, struct http_state *hs,
+                  char *data, u16_t data_len, char *uri, char *uri_end)
+{
+  err_t err;
+  /* search for end-of-header (first double-CRLF) */
+  char *crlfcrlf = lwip_strnstr(uri_end + 1, CRLF CRLF, data_len - (uri_end + 1 - data));
+
+  if (crlfcrlf != NULL) {
+    /* search for "Content-Length: " */
+#define HTTP_HDR_CONTENT_LEN                "Content-Length: "
+#define HTTP_HDR_CONTENT_LEN_LEN            16
+#define HTTP_HDR_CONTENT_LEN_DIGIT_MAX_LEN  10
+    char *scontent_len = lwip_strnstr(uri_end + 1, HTTP_HDR_CONTENT_LEN, crlfcrlf - (uri_end + 1));
+    if (scontent_len != NULL) {
+      char *scontent_len_end = lwip_strnstr(scontent_len + HTTP_HDR_CONTENT_LEN_LEN, CRLF, HTTP_HDR_CONTENT_LEN_DIGIT_MAX_LEN);
+      if (scontent_len_end != NULL) {
+        int content_len;
+        char *content_len_num = scontent_len + HTTP_HDR_CONTENT_LEN_LEN;
+        content_len = atoi(content_len_num);
+        if (content_len == 0) {
+          /* if atoi returns 0 on error, fix this */
+          if ((content_len_num[0] != '0') || (content_len_num[1] != '\r')) {
+            content_len = -1;
+          }
+        }
+        if (content_len >= 0) {
+          /* adjust length of HTTP header passed to application */
+          const char *hdr_start_after_uri = uri_end + 1;
+          u16_t hdr_len = (u16_t)LWIP_MIN(data_len, crlfcrlf + 4 - data);
+          u16_t hdr_data_len = (u16_t)LWIP_MIN(data_len, crlfcrlf + 4 - hdr_start_after_uri);
+          u8_t post_auto_wnd = 1;
+          http_uri_buf[0] = 0;
+          /* trim http header */
+          *crlfcrlf = 0;
+          err = httpd_post_begin(hs, uri, hdr_start_after_uri, hdr_data_len, content_len,
+                                 http_uri_buf, LWIP_HTTPD_URI_BUF_LEN, &post_auto_wnd);
+          if (err == ERR_OK) {
+            /* try to pass in data of the first pbuf(s) */
+            struct pbuf *q = inp;
+            u16_t start_offset = hdr_len;
+#if LWIP_HTTPD_POST_MANUAL_WND
+            hs->no_auto_wnd = !post_auto_wnd;
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+            /* set the Content-Length to be received for this POST */
+            hs->post_content_len_left = (u32_t)content_len;
+
+            /* get to the pbuf where the body starts */
+            while ((q != NULL) && (q->len <= start_offset)) {
+              start_offset -= q->len;
+              q = q->next;
+            }
+            if (q != NULL) {
+              /* hide the remaining HTTP header */
+              pbuf_remove_header(q, start_offset);
+#if LWIP_HTTPD_POST_MANUAL_WND
+              if (!post_auto_wnd) {
+                /* already tcp_recved() this data... */
+                hs->unrecved_bytes = q->tot_len;
+              }
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+              pbuf_ref(q);
+              return http_post_rxpbuf(hs, q);
+            } else if (hs->post_content_len_left == 0) {
+              q = pbuf_alloc(PBUF_RAW, 0, PBUF_REF);
+              return http_post_rxpbuf(hs, q);
+            } else {
+              return ERR_OK;
+            }
+          } else {
+            /* return file passed from application */
+            return http_find_file(hs, http_uri_buf, 0);
+          }
+        } else {
+          LWIP_DEBUGF(HTTPD_DEBUG, ("POST received invalid Content-Length: %s\n",
+                                    content_len_num));
+          return ERR_ARG;
+        }
+      }
+    }
+    /* If we come here, headers are fully received (double-crlf), but Content-Length
+       was not included. Since this is currently the only supported method, we have
+       to fail in this case! */
+    LWIP_DEBUGF(HTTPD_DEBUG, ("Error when parsing Content-Length\n"));
+    return ERR_ARG;
+  }
+  /* if we come here, the POST is incomplete */
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+  return ERR_INPROGRESS;
+#else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+  return ERR_ARG;
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+}
+
+#if LWIP_HTTPD_POST_MANUAL_WND
+/**
+ * @ingroup httpd
+ * A POST implementation can call this function to update the TCP window.
+ * This can be used to throttle data reception (e.g. when received data is
+ * programmed to flash and data is received faster than programmed).
+ *
+ * @param connection A connection handle passed to httpd_post_begin for which
+ *        httpd_post_finished has *NOT* been called yet!
+ * @param recved_len Length of data received (for window update)
+ */
+void httpd_post_data_recved(void *connection, u16_t recved_len)
+{
+  struct http_state *hs = (struct http_state *)connection;
+  if (hs != NULL) {
+    if (hs->no_auto_wnd) {
+      u16_t len = recved_len;
+      if (hs->unrecved_bytes >= recved_len) {
+        hs->unrecved_bytes -= recved_len;
+      } else {
+        LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("httpd_post_data_recved: recved_len too big\n"));
+        len = (u16_t)hs->unrecved_bytes;
+        hs->unrecved_bytes = 0;
+      }
+      if (hs->pcb != NULL) {
+        if (len != 0) {
+          altcp_recved(hs->pcb, len);
+        }
+        if ((hs->post_content_len_left == 0) && (hs->unrecved_bytes == 0)) {
+          /* finished handling POST */
+          http_handle_post_finished(hs);
+          http_send(hs->pcb, hs);
+        }
+      }
+    }
+  }
+}
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+
+#if LWIP_HTTPD_FS_ASYNC_READ
+/** Try to send more data if file has been blocked before
+ * This is a callback function passed to fs_read_async().
+ */
+static void
+http_continue(void *connection)
+{
+  struct http_state *hs = (struct http_state *)connection;
+  LWIP_ASSERT_CORE_LOCKED();
+  if (hs && (hs->pcb) && (hs->handle)) {
+    LWIP_ASSERT("hs->pcb != NULL", hs->pcb != NULL);
+    LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("httpd_continue: try to send more data\n"));
+    if (http_send(hs->pcb, hs)) {
+      /* If we wrote anything to be sent, go ahead and send it now. */
+      LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n"));
+      altcp_output(hs->pcb);
+    }
+  }
+}
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+
+/**
+ * When data has been received in the correct state, try to parse it
+ * as a HTTP request.
+ *
+ * @param inp the received pbuf
+ * @param hs the connection state
+ * @param pcb the altcp_pcb which received this packet
+ * @return ERR_OK if request was OK and hs has been initialized correctly
+ *         ERR_INPROGRESS if request was OK so far but not fully received
+ *         another err_t otherwise
+ */
+static err_t
+http_parse_request(struct pbuf *inp, struct http_state *hs, struct altcp_pcb *pcb)
+{
+  char *data;
+  char *crlf;
+  u16_t data_len;
+  struct pbuf *p = inp;
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+  u16_t clen;
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+#if LWIP_HTTPD_SUPPORT_POST
+  err_t err;
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+
+  LWIP_UNUSED_ARG(pcb); /* only used for post */
+  LWIP_ASSERT("p != NULL", p != NULL);
+  LWIP_ASSERT("hs != NULL", hs != NULL);
+
+  if ((hs->handle != NULL) || (hs->file != NULL)) {
+    LWIP_DEBUGF(HTTPD_DEBUG, ("Received data while sending a file\n"));
+    /* already sending a file */
+    /* @todo: abort? */
+    return ERR_USE;
+  }
+
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+
+  LWIP_DEBUGF(HTTPD_DEBUG, ("Received %"U16_F" bytes\n", p->tot_len));
+
+  /* first check allowed characters in this pbuf? */
+
+  /* enqueue the pbuf */
+  if (hs->req == NULL) {
+    LWIP_DEBUGF(HTTPD_DEBUG, ("First pbuf\n"));
+    hs->req = p;
+  } else {
+    LWIP_DEBUGF(HTTPD_DEBUG, ("pbuf enqueued\n"));
+    pbuf_cat(hs->req, p);
+  }
+  /* increase pbuf ref counter as it is freed when we return but we want to
+     keep it on the req list */
+  pbuf_ref(p);
+
+  if (hs->req->next != NULL) {
+    data_len = LWIP_MIN(hs->req->tot_len, LWIP_HTTPD_MAX_REQ_LENGTH);
+    pbuf_copy_partial(hs->req, httpd_req_buf, data_len, 0);
+    data = httpd_req_buf;
+  } else
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+  {
+    data = (char *)p->payload;
+    data_len = p->len;
+    if (p->len != p->tot_len) {
+      LWIP_DEBUGF(HTTPD_DEBUG, ("Warning: incomplete header due to chained pbufs\n"));
+    }
+  }
+
+  /* received enough data for minimal request? */
+  if (data_len >= MIN_REQ_LEN) {
+    /* wait for CRLF before parsing anything */
+    crlf = lwip_strnstr(data, CRLF, data_len);
+    if (crlf != NULL) {
+#if LWIP_HTTPD_SUPPORT_POST
+      int is_post = 0;
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+      int is_09 = 0;
+      char *sp1, *sp2;
+      u16_t left_len, uri_len;
+      LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("CRLF received, parsing request\n"));
+      /* parse method */
+      if (!strncmp(data, "GET ", 4)) {
+        sp1 = data + 3;
+        /* received GET request */
+        LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Received GET request\"\n"));
+#if LWIP_HTTPD_SUPPORT_POST
+      } else if (!strncmp(data, "POST ", 5)) {
+        /* store request type */
+        is_post = 1;
+        sp1 = data + 4;
+        /* received GET request */
+        LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Received POST request\n"));
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+      } else {
+        /* null-terminate the METHOD (pbuf is freed anyway wen returning) */
+        data[4] = 0;
+        /* unsupported method! */
+        LWIP_DEBUGF(HTTPD_DEBUG, ("Unsupported request method (not implemented): \"%s\"\n",
+                                  data));
+        return http_find_error_file(hs, 501);
+      }
+      /* if we come here, method is OK, parse URI */
+      left_len = (u16_t)(data_len - ((sp1 + 1) - data));
+      sp2 = lwip_strnstr(sp1 + 1, " ", left_len);
+#if LWIP_HTTPD_SUPPORT_V09
+      if (sp2 == NULL) {
+        /* HTTP 0.9: respond with correct protocol version */
+        sp2 = lwip_strnstr(sp1 + 1, CRLF, left_len);
+        is_09 = 1;
+#if LWIP_HTTPD_SUPPORT_POST
+        if (is_post) {
+          /* HTTP/0.9 does not support POST */
+          goto badrequest;
+        }
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+      }
+#endif /* LWIP_HTTPD_SUPPORT_V09 */
+      uri_len = (u16_t)(sp2 - (sp1 + 1));
+      if ((sp2 != 0) && (sp2 > sp1)) {
+        /* wait for CRLFCRLF (indicating end of HTTP headers) before parsing anything */
+        if (lwip_strnstr(data, CRLF CRLF, data_len) != NULL) {
+          char *uri = sp1 + 1;
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+          /* This is HTTP/1.0 compatible: for strict 1.1, a connection
+             would always be persistent unless "close" was specified. */
+          if (!is_09 && (lwip_strnstr(data, HTTP11_CONNECTIONKEEPALIVE, data_len) ||
+                         lwip_strnstr(data, HTTP11_CONNECTIONKEEPALIVE2, data_len))) {
+            hs->keepalive = 1;
+          } else {
+            hs->keepalive = 0;
+          }
+#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
+          /* null-terminate the METHOD (pbuf is freed anyway wen returning) */
+          *sp1 = 0;
+          uri[uri_len] = 0;
+          LWIP_DEBUGF(HTTPD_DEBUG, ("Received \"%s\" request for URI: \"%s\"\n",
+                                    data, uri));
+#if LWIP_HTTPD_SUPPORT_POST
+          if (is_post) {
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+            struct pbuf *q = hs->req;
+#else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+            struct pbuf *q = inp;
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+            err = http_post_request(q, hs, data, data_len, uri, sp2);
+            if (err != ERR_OK) {
+              /* restore header for next try */
+              *sp1 = ' ';
+              *sp2 = ' ';
+              uri[uri_len] = ' ';
+            }
+            if (err == ERR_ARG) {
+              goto badrequest;
+            }
+            return err;
+          } else
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+          {
+            return http_find_file(hs, uri, is_09);
+          }
+        }
+      } else {
+        LWIP_DEBUGF(HTTPD_DEBUG, ("invalid URI\n"));
+      }
+    }
+  }
+
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+  clen = pbuf_clen(hs->req);
+  if ((hs->req->tot_len <= LWIP_HTTPD_REQ_BUFSIZE) &&
+      (clen <= LWIP_HTTPD_REQ_QUEUELEN)) {
+    /* request not fully received (too short or CRLF is missing) */
+    return ERR_INPROGRESS;
+  } else
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+  {
+#if LWIP_HTTPD_SUPPORT_POST
+badrequest:
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+    LWIP_DEBUGF(HTTPD_DEBUG, ("bad request\n"));
+    /* could not parse request */
+    return http_find_error_file(hs, 400);
+  }
+}
+
+#if LWIP_HTTPD_SSI && (LWIP_HTTPD_SSI_BY_FILE_EXTENSION == 1)
+/* Check if SSI should be parsed for this file/URL
+ * (With LWIP_HTTPD_SSI_BY_FILE_EXTENSION == 2, this function can be
+ * overridden by an external implementation.)
+ *
+ * @return 1 for SSI, 0 for standard files
+ */
+static u8_t
+http_uri_is_ssi(struct fs_file *file, const char *uri)
+{
+  size_t loop;
+  u8_t tag_check = 0;
+  if (file != NULL) {
+    /* See if we have been asked for an shtml file and, if so,
+        enable tag checking. */
+    const char *ext = NULL, *sub;
+    char *param = (char *)strstr(uri, "?");
+    if (param != NULL) {
+      /* separate uri from parameters for now, set back later */
+      *param = 0;
+    }
+    sub = uri;
+    ext = uri;
+    for (sub = strstr(sub, "."); sub != NULL; sub = strstr(sub, ".")) {
+      ext = sub;
+      sub++;
+    }
+    for (loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) {
+      if (!lwip_stricmp(ext, g_pcSSIExtensions[loop])) {
+        tag_check = 1;
+        break;
+      }
+    }
+    if (param != NULL) {
+      *param = '?';
+    }
+  }
+  return tag_check;
+}
+#endif /* LWIP_HTTPD_SSI */
+
+/** Try to find the file specified by uri and, if found, initialize hs
+ * accordingly.
+ *
+ * @param hs the connection state
+ * @param uri the HTTP header URI
+ * @param is_09 1 if the request is HTTP/0.9 (no HTTP headers in response)
+ * @return ERR_OK if file was found and hs has been initialized correctly
+ *         another err_t otherwise
+ */
+static err_t
+http_find_file(struct http_state *hs, const char *uri, int is_09)
+{
+  size_t loop;
+  struct fs_file *file = NULL;
+  char *params = NULL;
+  err_t err;
+#if LWIP_HTTPD_CGI
+  int i;
+#endif /* LWIP_HTTPD_CGI */
+#if !LWIP_HTTPD_SSI
+  const
+#endif /* !LWIP_HTTPD_SSI */
+  /* By default, assume we will not be processing server-side-includes tags */
+  u8_t tag_check = 0;
+
+  /* Have we been asked for the default file (in root or a directory) ? */
+#if LWIP_HTTPD_MAX_REQUEST_URI_LEN
+  size_t uri_len = strlen(uri);
+  if ((uri_len > 0) && (uri[uri_len - 1] == '/') &&
+      ((uri != http_uri_buf) || (uri_len == 1))) {
+    size_t copy_len = LWIP_MIN(sizeof(http_uri_buf) - 1, uri_len - 1);
+    if (copy_len > 0) {
+      MEMCPY(http_uri_buf, uri, copy_len);
+      http_uri_buf[copy_len] = 0;
+    }
+#else /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */
+  if ((uri[0] == '/') &&  (uri[1] == 0)) {
+#endif /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */
+    /* Try each of the configured default filenames until we find one
+       that exists. */
+    for (loop = 0; loop < NUM_DEFAULT_FILENAMES; loop++) {
+      const char *file_name;
+#if LWIP_HTTPD_MAX_REQUEST_URI_LEN
+      if (copy_len > 0) {
+        size_t len_left = sizeof(http_uri_buf) - copy_len - 1;
+        if (len_left > 0) {
+          size_t name_len = strlen(httpd_default_filenames[loop].name);
+          size_t name_copy_len = LWIP_MIN(len_left, name_len);
+          MEMCPY(&http_uri_buf[copy_len], httpd_default_filenames[loop].name, name_copy_len);
+          http_uri_buf[copy_len + name_copy_len] = 0;
+        }
+        file_name = http_uri_buf;
+      } else
+#endif /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */
+      {
+        file_name = httpd_default_filenames[loop].name;
+      }
+      LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Looking for %s...\n", file_name));
+      err = fs_open(&hs->file_handle, file_name);
+      if (err == ERR_OK) {
+        uri = file_name;
+        file = &hs->file_handle;
+        LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opened.\n"));
+#if LWIP_HTTPD_SSI
+        tag_check = httpd_default_filenames[loop].shtml;
+#endif /* LWIP_HTTPD_SSI */
+        break;
+      }
+    }
+  }
+  if (file == NULL) {
+    /* No - we've been asked for a specific file. */
+    /* First, isolate the base URI (without any parameters) */
+    params = (char *)strchr(uri, '?');
+    if (params != NULL) {
+      /* URI contains parameters. NULL-terminate the base URI */
+      *params = '\0';
+      params++;
+    }
+
+#if LWIP_HTTPD_CGI
+    http_cgi_paramcount = -1;
+    /* Does the base URI we have isolated correspond to a CGI handler? */
+    if (httpd_num_cgis && httpd_cgis) {
+      for (i = 0; i < httpd_num_cgis; i++) {
+        if (strcmp(uri, httpd_cgis[i].pcCGIName) == 0) {
+          /*
+           * We found a CGI that handles this URI so extract the
+           * parameters and call the handler.
+           */
+          http_cgi_paramcount = extract_uri_parameters(hs, params);
+          uri = httpd_cgis[i].pfnCGIHandler(i, http_cgi_paramcount, hs->params,
+                                         hs->param_vals);
+          break;
+        }
+      }
+    }
+#endif /* LWIP_HTTPD_CGI */
+
+    LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opening %s\n", uri));
+
+    err = fs_open(&hs->file_handle, uri);
+    if (err == ERR_OK) {
+      file = &hs->file_handle;
+    } else {
+      file = http_get_404_file(hs, &uri);
+    }
+#if LWIP_HTTPD_SSI
+    if (file != NULL) {
+      if (file->flags & FS_FILE_FLAGS_SSI) {
+        tag_check = 1;
+      } else {
+#if LWIP_HTTPD_SSI_BY_FILE_EXTENSION
+        tag_check = http_uri_is_ssi(file, uri);
+#endif /* LWIP_HTTPD_SSI_BY_FILE_EXTENSION */
+      }
+    }
+#endif /* LWIP_HTTPD_SSI */
+  }
+  if (file == NULL) {
+    /* None of the default filenames exist so send back a 404 page */
+    file = http_get_404_file(hs, &uri);
+  }
+  return http_init_file(hs, file, is_09, uri, tag_check, params);
+}
+
+/** Initialize a http connection with a file to send (if found).
+ * Called by http_find_file and http_find_error_file.
+ *
+ * @param hs http connection state
+ * @param file file structure to send (or NULL if not found)
+ * @param is_09 1 if the request is HTTP/0.9 (no HTTP headers in response)
+ * @param uri the HTTP header URI
+ * @param tag_check enable SSI tag checking
+ * @param params != NULL if URI has parameters (separated by '?')
+ * @return ERR_OK if file was found and hs has been initialized correctly
+ *         another err_t otherwise
+ */
+static err_t
+http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri,
+               u8_t tag_check, char *params)
+{
+#if !LWIP_HTTPD_SUPPORT_V09
+  LWIP_UNUSED_ARG(is_09);
+#endif
+  if (file != NULL) {
+    /* file opened, initialise struct http_state */
+#if !LWIP_HTTPD_DYNAMIC_FILE_READ
+    /* If dynamic read is disabled, file data must be in one piece and available now */
+    LWIP_ASSERT("file->data != NULL", file->data != NULL);
+#endif
+
+#if LWIP_HTTPD_SSI
+    if (tag_check) {
+      struct http_ssi_state *ssi = http_ssi_state_alloc();
+      if (ssi != NULL) {
+        ssi->tag_index = 0;
+        ssi->tag_state = TAG_NONE;
+        ssi->parsed = file->data;
+        ssi->parse_left = file->len;
+        ssi->tag_end = file->data;
+        hs->ssi = ssi;
+      }
+    }
+#else /* LWIP_HTTPD_SSI */
+    LWIP_UNUSED_ARG(tag_check);
+#endif /* LWIP_HTTPD_SSI */
+    hs->handle = file;
+#if LWIP_HTTPD_CGI_SSI
+    if (params != NULL) {
+      /* URI contains parameters, call generic CGI handler */
+      int count;
+#if LWIP_HTTPD_CGI
+      if (http_cgi_paramcount >= 0) {
+        count = http_cgi_paramcount;
+      } else
+#endif
+      {
+        count = extract_uri_parameters(hs, params);
+      }
+      httpd_cgi_handler(file, uri, count, http_cgi_params, http_cgi_param_vals
+#if defined(LWIP_HTTPD_FILE_STATE) && LWIP_HTTPD_FILE_STATE
+                        , file->state
+#endif /* LWIP_HTTPD_FILE_STATE */
+                       );
+    }
+#else /* LWIP_HTTPD_CGI_SSI */
+    LWIP_UNUSED_ARG(params);
+#endif /* LWIP_HTTPD_CGI_SSI */
+    hs->file = file->data;
+    LWIP_ASSERT("File length must be positive!", (file->len >= 0));
+#if LWIP_HTTPD_CUSTOM_FILES
+    if (file->is_custom_file && (file->data == NULL)) {
+      /* custom file, need to read data first (via fs_read_custom) */
+      hs->left = 0;
+    } else
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+    {
+      hs->left = (u32_t)file->len;
+    }
+    hs->retries = 0;
+#if LWIP_HTTPD_TIMING
+    hs->time_started = sys_now();
+#endif /* LWIP_HTTPD_TIMING */
+#if !LWIP_HTTPD_DYNAMIC_HEADERS
+    LWIP_ASSERT("HTTP headers not included in file system",
+                (hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) != 0);
+#endif /* !LWIP_HTTPD_DYNAMIC_HEADERS */
+#if LWIP_HTTPD_SUPPORT_V09
+    if (is_09 && ((hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) != 0)) {
+      /* HTTP/0.9 responses are sent without HTTP header,
+         search for the end of the header. */
+      char *file_start = lwip_strnstr(hs->file, CRLF CRLF, hs->left);
+      if (file_start != NULL) {
+        int diff = file_start + 4 - hs->file;
+        hs->file += diff;
+        hs->left -= (u32_t)diff;
+      }
+    }
+#endif /* LWIP_HTTPD_SUPPORT_V09*/
+  } else {
+    hs->handle = NULL;
+    hs->file = NULL;
+    hs->left = 0;
+    hs->retries = 0;
+  }
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+  /* Determine the HTTP headers to send based on the file extension of
+   * the requested URI. */
+  if ((hs->handle == NULL) || ((hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) == 0)) {
+    get_http_headers(hs, uri);
+  }
+#else /* LWIP_HTTPD_DYNAMIC_HEADERS */
+  LWIP_UNUSED_ARG(uri);
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+  if (hs->keepalive) {
+#if LWIP_HTTPD_SSI
+    if (hs->ssi != NULL) {
+      hs->keepalive = 0;
+    } else
+#endif /* LWIP_HTTPD_SSI */
+    {
+      if ((hs->handle != NULL) &&
+          ((hs->handle->flags & (FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT)) == FS_FILE_FLAGS_HEADER_INCLUDED)) {
+        hs->keepalive = 0;
+      }
+    }
+  }
+#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
+  return ERR_OK;
+}
+
+/**
+ * The pcb had an error and is already deallocated.
+ * The argument might still be valid (if != NULL).
+ */
+static void
+http_err(void *arg, err_t err)
+{
+  struct http_state *hs = (struct http_state *)arg;
+  LWIP_UNUSED_ARG(err);
+
+  LWIP_DEBUGF(HTTPD_DEBUG, ("http_err: %s", lwip_strerr(err)));
+
+  if (hs != NULL) {
+    http_state_free(hs);
+  }
+}
+
+/**
+ * Data has been sent and acknowledged by the remote host.
+ * This means that more data can be sent.
+ */
+static err_t
+http_sent(void *arg, struct altcp_pcb *pcb, u16_t len)
+{
+  struct http_state *hs = (struct http_state *)arg;
+
+  LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_sent %p\n", (void *)pcb));
+
+  LWIP_UNUSED_ARG(len);
+
+  if (hs == NULL) {
+    return ERR_OK;
+  }
+
+  hs->retries = 0;
+
+  http_send(pcb, hs);
+
+  return ERR_OK;
+}
+
+/**
+ * The poll function is called every 2nd second.
+ * If there has been no data sent (which resets the retries) in 8 seconds, close.
+ * If the last portion of a file has not been sent in 2 seconds, close.
+ *
+ * This could be increased, but we don't want to waste resources for bad connections.
+ */
+static err_t
+http_poll(void *arg, struct altcp_pcb *pcb)
+{
+  struct http_state *hs = (struct http_state *)arg;
+  LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: pcb=%p hs=%p pcb_state=%s\n",
+              (void *)pcb, (void *)hs, tcp_debug_state_str(altcp_dbg_get_tcp_state(pcb))));
+
+  if (hs == NULL) {
+    err_t closed;
+    /* arg is null, close. */
+    LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: arg is NULL, close\n"));
+    closed = http_close_conn(pcb, NULL);
+    LWIP_UNUSED_ARG(closed);
+#if LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR
+    if (closed == ERR_MEM) {
+      altcp_abort(pcb);
+      return ERR_ABRT;
+    }
+#endif /* LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR */
+    return ERR_OK;
+  } else {
+    hs->retries++;
+    if (hs->retries == HTTPD_MAX_RETRIES) {
+      LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: too many retries, close\n"));
+      http_close_conn(pcb, hs);
+      return ERR_OK;
+    }
+
+    /* If this connection has a file open, try to send some more data. If
+     * it has not yet received a GET request, don't do this since it will
+     * cause the connection to close immediately. */
+    if (hs->handle) {
+      LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: try to send more data\n"));
+      if (http_send(pcb, hs)) {
+        /* If we wrote anything to be sent, go ahead and send it now. */
+        LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n"));
+        altcp_output(pcb);
+      }
+    }
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Data has been received on this pcb.
+ * For HTTP 1.0, this should normally only happen once (if the request fits in one packet).
+ */
+static err_t
+http_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+  struct http_state *hs = (struct http_state *)arg;
+  LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: pcb=%p pbuf=%p err=%s\n", (void *)pcb,
+              (void *)p, lwip_strerr(err)));
+
+  if ((err != ERR_OK) || (p == NULL) || (hs == NULL)) {
+    /* error or closed by other side? */
+    if (p != NULL) {
+      /* Inform TCP that we have taken the data. */
+      altcp_recved(pcb, p->tot_len);
+      pbuf_free(p);
+    }
+    if (hs == NULL) {
+      /* this should not happen, only to be robust */
+      LWIP_DEBUGF(HTTPD_DEBUG, ("Error, http_recv: hs is NULL, close\n"));
+    }
+    http_close_conn(pcb, hs);
+    return ERR_OK;
+  }
+
+#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
+  if (hs->no_auto_wnd) {
+    hs->unrecved_bytes += p->tot_len;
+  } else
+#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */
+  {
+    /* Inform TCP that we have taken the data. */
+    altcp_recved(pcb, p->tot_len);
+  }
+
+#if LWIP_HTTPD_SUPPORT_POST
+  if (hs->post_content_len_left > 0) {
+    /* reset idle counter when POST data is received */
+    hs->retries = 0;
+    /* this is data for a POST, pass the complete pbuf to the application */
+    http_post_rxpbuf(hs, p);
+    /* pbuf is passed to the application, don't free it! */
+    if (hs->post_content_len_left == 0) {
+      /* all data received, send response or close connection */
+      http_send(pcb, hs);
+    }
+    return ERR_OK;
+  } else
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+  {
+    if (hs->handle == NULL) {
+      err_t parsed = http_parse_request(p, hs, pcb);
+      LWIP_ASSERT("http_parse_request: unexpected return value", parsed == ERR_OK
+                  || parsed == ERR_INPROGRESS || parsed == ERR_ARG || parsed == ERR_USE);
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+      if (parsed != ERR_INPROGRESS) {
+        /* request fully parsed or error */
+        if (hs->req != NULL) {
+          pbuf_free(hs->req);
+          hs->req = NULL;
+        }
+      }
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+      pbuf_free(p);
+      if (parsed == ERR_OK) {
+#if LWIP_HTTPD_SUPPORT_POST
+        if (hs->post_content_len_left == 0)
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+        {
+          LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: data %p len %"S32_F"\n", (const void *)hs->file, hs->left));
+          http_send(pcb, hs);
+        }
+      } else if (parsed == ERR_ARG) {
+        /* @todo: close on ERR_USE? */
+        http_close_conn(pcb, hs);
+      }
+    } else {
+      LWIP_DEBUGF(HTTPD_DEBUG, ("http_recv: already sending data\n"));
+      /* already sending but still receiving data, we might want to RST here? */
+      pbuf_free(p);
+    }
+  }
+  return ERR_OK;
+}
+
+/**
+ * A new incoming connection has been accepted.
+ */
+static err_t
+http_accept(void *arg, struct altcp_pcb *pcb, err_t err)
+{
+  struct http_state *hs;
+  LWIP_UNUSED_ARG(err);
+  LWIP_UNUSED_ARG(arg);
+  LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept %p / %p\n", (void *)pcb, arg));
+
+  if ((err != ERR_OK) || (pcb == NULL)) {
+    return ERR_VAL;
+  }
+
+  /* Set priority */
+  altcp_setprio(pcb, HTTPD_TCP_PRIO);
+
+  /* Allocate memory for the structure that holds the state of the
+     connection - initialized by that function. */
+  hs = http_state_alloc();
+  if (hs == NULL) {
+    LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept: Out of memory, RST\n"));
+    return ERR_MEM;
+  }
+  hs->pcb = pcb;
+
+  /* Tell TCP that this is the structure we wish to be passed for our
+     callbacks. */
+  altcp_arg(pcb, hs);
+
+  /* Set up the various callback functions */
+  altcp_recv(pcb, http_recv);
+  altcp_err(pcb, http_err);
+  altcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL);
+  altcp_sent(pcb, http_sent);
+
+  return ERR_OK;
+}
+
+static void
+httpd_init_pcb(struct altcp_pcb *pcb, u16_t port)
+{
+  err_t err;
+
+  if (pcb) {
+    altcp_setprio(pcb, HTTPD_TCP_PRIO);
+    /* set SOF_REUSEADDR here to explicitly bind httpd to multiple interfaces */
+    err = altcp_bind(pcb, IP_ANY_TYPE, port);
+    LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
+    LWIP_ASSERT("httpd_init: tcp_bind failed", err == ERR_OK);
+    pcb = altcp_listen(pcb);
+    LWIP_ASSERT("httpd_init: tcp_listen failed", pcb != NULL);
+    altcp_accept(pcb, http_accept);
+  }
+}
+
+/**
+ * @ingroup httpd
+ * Initialize the httpd: set up a listening PCB and bind it to the defined port
+ */
+void
+httpd_init(void)
+{
+  struct altcp_pcb *pcb;
+
+#if HTTPD_USE_MEM_POOL
+  LWIP_MEMPOOL_INIT(HTTPD_STATE);
+#if LWIP_HTTPD_SSI
+  LWIP_MEMPOOL_INIT(HTTPD_SSI_STATE);
+#endif
+#endif
+  LWIP_DEBUGF(HTTPD_DEBUG, ("httpd_init\n"));
+
+  /* LWIP_ASSERT_CORE_LOCKED(); is checked by tcp_new() */
+
+  pcb = altcp_tcp_new_ip_type(IPADDR_TYPE_ANY);
+  LWIP_ASSERT("httpd_init: tcp_new failed", pcb != NULL);
+  httpd_init_pcb(pcb, HTTPD_SERVER_PORT);
+}
+
+#if HTTPD_ENABLE_HTTPS
+/**
+ * @ingroup httpd
+ * Initialize the httpd: set up a listening PCB and bind it to the defined port.
+ * Also set up TLS connection handling (HTTPS).
+ */
+void
+httpd_inits(struct altcp_tls_config *conf)
+{
+#if LWIP_ALTCP_TLS
+  struct altcp_pcb *pcb_tls = altcp_tls_new(conf, IPADDR_TYPE_ANY);
+  LWIP_ASSERT("httpd_init: altcp_tls_new failed", pcb_tls != NULL);
+  httpd_init_pcb(pcb_tls, HTTPD_SERVER_PORT_HTTPS);
+#else /* LWIP_ALTCP_TLS */
+  LWIP_UNUSED_ARG(conf);
+#endif /* LWIP_ALTCP_TLS */
+}
+#endif /* HTTPD_ENABLE_HTTPS */
+
+#if LWIP_HTTPD_SSI
+/**
+ * @ingroup httpd
+ * Set the SSI handler function.
+ *
+ * @param ssi_handler the SSI handler function
+ * @param tags an array of SSI tag strings to search for in SSI-enabled files
+ * @param num_tags number of tags in the 'tags' array
+ */
+void
+http_set_ssi_handler(tSSIHandler ssi_handler, const char **tags, int num_tags)
+{
+  LWIP_DEBUGF(HTTPD_DEBUG, ("http_set_ssi_handler\n"));
+
+  LWIP_ASSERT("no ssi_handler given", ssi_handler != NULL);
+  httpd_ssi_handler = ssi_handler;
+
+#if LWIP_HTTPD_SSI_RAW
+  LWIP_UNUSED_ARG(tags);
+  LWIP_UNUSED_ARG(num_tags);
+#else /* LWIP_HTTPD_SSI_RAW */
+  LWIP_ASSERT("no tags given", tags != NULL);
+  LWIP_ASSERT("invalid number of tags", num_tags > 0);
+
+  httpd_tags = tags;
+  httpd_num_tags = num_tags;
+#endif /* !LWIP_HTTPD_SSI_RAW */
+}
+#endif /* LWIP_HTTPD_SSI */
+
+#if LWIP_HTTPD_CGI
+/**
+ * @ingroup httpd
+ * Set an array of CGI filenames/handler functions
+ *
+ * @param cgis an array of CGI filenames/handler functions
+ * @param num_handlers number of elements in the 'cgis' array
+ */
+void
+http_set_cgi_handlers(const tCGI *cgis, int num_handlers)
+{
+  LWIP_ASSERT("no cgis given", cgis != NULL);
+  LWIP_ASSERT("invalid number of handlers", num_handlers > 0);
+
+  httpd_cgis = cgis;
+  httpd_num_cgis = num_handlers;
+}
+#endif /* LWIP_HTTPD_CGI */
+
+#endif /* LWIP_TCP && LWIP_CALLBACK_API */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/httpd_structs.h b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/httpd_structs.h
new file mode 100644 (file)
index 0000000..56b97a3
--- /dev/null
@@ -0,0 +1,123 @@
+#ifndef LWIP_HTTPD_STRUCTS_H
+#define LWIP_HTTPD_STRUCTS_H
+
+#include "lwip/apps/httpd.h"
+
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+/** This struct is used for a list of HTTP header strings for various
+ * filename extensions. */
+typedef struct {
+  const char *extension;
+  const char *content_type;
+} tHTTPHeader;
+
+/** A list of strings used in HTTP headers (see RFC 1945 HTTP/1.0 and
+ * RFC 2616 HTTP/1.1 for header field definitions) */
+static const char *const g_psHTTPHeaderStrings[] = {
+  "HTTP/1.0 200 OK\r\n",
+  "HTTP/1.0 404 File not found\r\n",
+  "HTTP/1.0 400 Bad Request\r\n",
+  "HTTP/1.0 501 Not Implemented\r\n",
+  "HTTP/1.1 200 OK\r\n",
+  "HTTP/1.1 404 File not found\r\n",
+  "HTTP/1.1 400 Bad Request\r\n",
+  "HTTP/1.1 501 Not Implemented\r\n",
+  "Content-Length: ",
+  "Connection: Close\r\n",
+  "Connection: keep-alive\r\n",
+  "Connection: keep-alive\r\nContent-Length: ",
+  "Server: "HTTPD_SERVER_AGENT"\r\n",
+  "\r\n<html><body><h2>404: The requested file cannot be found.</h2></body></html>\r\n"
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+  , "Connection: keep-alive\r\nContent-Length: 77\r\n\r\n<html><body><h2>404: The requested file cannot be found.</h2></body></html>\r\n"
+#endif
+};
+
+/* Indexes into the g_psHTTPHeaderStrings array */
+#define HTTP_HDR_OK             0 /* 200 OK */
+#define HTTP_HDR_NOT_FOUND      1 /* 404 File not found */
+#define HTTP_HDR_BAD_REQUEST    2 /* 400 Bad request */
+#define HTTP_HDR_NOT_IMPL       3 /* 501 Not Implemented */
+#define HTTP_HDR_OK_11          4 /* 200 OK */
+#define HTTP_HDR_NOT_FOUND_11   5 /* 404 File not found */
+#define HTTP_HDR_BAD_REQUEST_11 6 /* 400 Bad request */
+#define HTTP_HDR_NOT_IMPL_11    7 /* 501 Not Implemented */
+#define HTTP_HDR_CONTENT_LENGTH 8 /* Content-Length: (HTTP 1.0)*/
+#define HTTP_HDR_CONN_CLOSE     9 /* Connection: Close (HTTP 1.1) */
+#define HTTP_HDR_CONN_KEEPALIVE 10 /* Connection: keep-alive (HTTP 1.1) */
+#define HTTP_HDR_KEEPALIVE_LEN  11 /* Connection: keep-alive + Content-Length: (HTTP 1.1)*/
+#define HTTP_HDR_SERVER         12 /* Server: HTTPD_SERVER_AGENT */
+#define DEFAULT_404_HTML        13 /* default 404 body */
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+#define DEFAULT_404_HTML_PERSISTENT 14 /* default 404 body, but including Connection: keep-alive */
+#endif
+
+#define HTTP_CONTENT_TYPE(contenttype) "Content-Type: "contenttype"\r\n\r\n"
+#define HTTP_CONTENT_TYPE_ENCODING(contenttype, encoding) "Content-Type: "contenttype"\r\nContent-Encoding: "encoding"\r\n\r\n"
+
+#define HTTP_HDR_HTML           HTTP_CONTENT_TYPE("text/html")
+#define HTTP_HDR_SSI            HTTP_CONTENT_TYPE("text/html\r\nExpires: Fri, 10 Apr 2008 14:00:00 GMT\r\nPragma: no-cache")
+#define HTTP_HDR_GIF            HTTP_CONTENT_TYPE("image/gif")
+#define HTTP_HDR_PNG            HTTP_CONTENT_TYPE("image/png")
+#define HTTP_HDR_JPG            HTTP_CONTENT_TYPE("image/jpeg")
+#define HTTP_HDR_BMP            HTTP_CONTENT_TYPE("image/bmp")
+#define HTTP_HDR_ICO            HTTP_CONTENT_TYPE("image/x-icon")
+#define HTTP_HDR_APP            HTTP_CONTENT_TYPE("application/octet-stream")
+#define HTTP_HDR_JS             HTTP_CONTENT_TYPE("application/javascript")
+#define HTTP_HDR_RA             HTTP_CONTENT_TYPE("application/javascript")
+#define HTTP_HDR_CSS            HTTP_CONTENT_TYPE("text/css")
+#define HTTP_HDR_SWF            HTTP_CONTENT_TYPE("application/x-shockwave-flash")
+#define HTTP_HDR_XML            HTTP_CONTENT_TYPE("text/xml")
+#define HTTP_HDR_PDF            HTTP_CONTENT_TYPE("application/pdf")
+#define HTTP_HDR_JSON           HTTP_CONTENT_TYPE("application/json")
+#define HTTP_HDR_CSV            HTTP_CONTENT_TYPE("text/csv")
+#define HTTP_HDR_TSV            HTTP_CONTENT_TYPE("text/tsv")
+#define HTTP_HDR_SVG            HTTP_CONTENT_TYPE("image/svg+xml")
+#define HTTP_HDR_SVGZ           HTTP_CONTENT_TYPE_ENCODING("image/svg+xml", "gzip")
+
+#define HTTP_HDR_DEFAULT_TYPE   HTTP_CONTENT_TYPE("text/plain")
+
+/** A list of extension-to-HTTP header strings (see outdated RFC 1700 MEDIA TYPES
+ * and http://www.iana.org/assignments/media-types for registered content types
+ * and subtypes) */
+static const tHTTPHeader g_psHTTPHeaders[] = {
+  { "html", HTTP_HDR_HTML},
+  { "htm",  HTTP_HDR_HTML},
+  { "shtml", HTTP_HDR_SSI},
+  { "shtm", HTTP_HDR_SSI},
+  { "ssi",  HTTP_HDR_SSI},
+  { "gif",  HTTP_HDR_GIF},
+  { "png",  HTTP_HDR_PNG},
+  { "jpg",  HTTP_HDR_JPG},
+  { "bmp",  HTTP_HDR_BMP},
+  { "ico",  HTTP_HDR_ICO},
+  { "class", HTTP_HDR_APP},
+  { "cls",  HTTP_HDR_APP},
+  { "js",   HTTP_HDR_JS},
+  { "ram",  HTTP_HDR_RA},
+  { "css",  HTTP_HDR_CSS},
+  { "swf",  HTTP_HDR_SWF},
+  { "xml",  HTTP_HDR_XML},
+  { "xsl",  HTTP_HDR_XML},
+  { "pdf",  HTTP_HDR_PDF},
+  { "json", HTTP_HDR_JSON}
+#ifdef HTTPD_ADDITIONAL_CONTENT_TYPES
+  /* If you need to add content types not listed here:
+   * #define HTTPD_ADDITIONAL_CONTENT_TYPES {"ct1", HTTP_CONTENT_TYPE("text/ct1")}, {"exe", HTTP_CONTENT_TYPE("application/exe")}
+   */
+  , HTTPD_ADDITIONAL_CONTENT_TYPES
+#endif
+};
+
+#define NUM_HTTP_HEADERS LWIP_ARRAYSIZE(g_psHTTPHeaders)
+
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+
+#if LWIP_HTTPD_SSI
+static const char *const g_pcSSIExtensions[] = {
+  ".shtml", ".shtm", ".ssi", ".xml", ".json"
+};
+#define NUM_SHTML_EXTENSIONS LWIP_ARRAYSIZE(g_pcSSIExtensions)
+#endif /* LWIP_HTTPD_SSI */
+
+#endif /* LWIP_HTTPD_STRUCTS_H */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/makefsdata/makefsdata b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/makefsdata/makefsdata
new file mode 100644 (file)
index 0000000..37b4203
--- /dev/null
@@ -0,0 +1,97 @@
+#!/usr/bin/perl
+
+open(OUTPUT, "> fsdata.c");
+
+chdir("fs");
+open(FILES, "find . -type f |");
+
+while($file = <FILES>) {
+
+    # Do not include files in CVS directories nor backup files.
+    if($file =~ /(CVS|~)/) {
+       next;
+    }
+    
+    chop($file);
+    
+    open(HEADER, "> /tmp/header") || die $!;
+    if($file =~ /404/) {
+       print(HEADER "HTTP/1.0 404 File not found\r\n");
+    } else {
+       print(HEADER "HTTP/1.0 200 OK\r\n");
+    }
+    print(HEADER "Server: lwIP/pre-0.6 (http://www.sics.se/~adam/lwip/)\r\n");
+    if($file =~ /\.html$/) {
+       print(HEADER "Content-type: text/html\r\n");
+    } elsif($file =~ /\.gif$/) {
+       print(HEADER "Content-type: image/gif\r\n");
+    } elsif($file =~ /\.png$/) {
+       print(HEADER "Content-type: image/png\r\n");
+    } elsif($file =~ /\.jpg$/) {
+       print(HEADER "Content-type: image/jpeg\r\n");
+    } elsif($file =~ /\.class$/) {
+       print(HEADER "Content-type: application/octet-stream\r\n");
+    } elsif($file =~ /\.ram$/) {
+       print(HEADER "Content-type: audio/x-pn-realaudio\r\n");    
+    } else {
+       print(HEADER "Content-type: text/plain\r\n");
+    }
+    print(HEADER "\r\n");
+    close(HEADER);
+
+    unless($file =~ /\.plain$/ || $file =~ /cgi/) {
+       system("cat /tmp/header $file > /tmp/file");
+    } else {
+       system("cp $file /tmp/file");
+    }
+    
+    open(FILE, "/tmp/file");
+    unlink("/tmp/file");
+    unlink("/tmp/header");
+
+    $file =~ s/\.//;
+    $fvar = $file;
+    $fvar =~ s-/-_-g;
+    $fvar =~ s-\.-_-g;
+    print(OUTPUT "static const unsigned char data".$fvar."[] = {\n");
+    print(OUTPUT "\t/* $file */\n\t");
+    for($j = 0; $j < length($file); $j++) {
+       printf(OUTPUT "%#02x, ", unpack("C", substr($file, $j, 1)));
+    }
+    printf(OUTPUT "0,\n");
+    
+    
+    $i = 0;
+    while(read(FILE, $data, 1)) {
+        if($i == 0) {
+            print(OUTPUT "\t");
+        }
+        printf(OUTPUT "%#02x, ", unpack("C", $data));
+        $i++;
+        if($i == 10) {
+            print(OUTPUT "\n");
+            $i = 0;
+        }
+    }
+    print(OUTPUT "};\n\n");
+    close(FILE);
+    push(@fvars, $fvar);
+    push(@files, $file);
+}
+
+for($i = 0; $i < @fvars; $i++) {
+    $file = $files[$i];
+    $fvar = $fvars[$i];
+
+    if($i == 0) {
+        $prevfile = "NULL";
+    } else {
+        $prevfile = "file" . $fvars[$i - 1];
+    }
+    print(OUTPUT "const struct fsdata_file file".$fvar."[] = {{$prevfile, data$fvar, ");
+    print(OUTPUT "data$fvar + ". (length($file) + 1) .", ");
+    print(OUTPUT "sizeof(data$fvar) - ". (length($file) + 1) ."}};\n\n");
+}
+
+print(OUTPUT "#define FS_ROOT file$fvars[$i - 1]\n\n");
+print(OUTPUT "#define FS_NUMFILES $i\n");
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/makefsdata/makefsdata.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/makefsdata/makefsdata.c
new file mode 100644 (file)
index 0000000..695e94f
--- /dev/null
@@ -0,0 +1,1251 @@
+/**
+ * makefsdata: Converts a directory structure for use with the lwIP httpd.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Jim Pettinato
+ *         Simon Goldschmidt
+ *
+ * @todo:
+ * - take TCP_MSS, LWIP_TCP_TIMESTAMPS and
+ *   PAYLOAD_ALIGN_TYPE/PAYLOAD_ALIGNMENT as arguments
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/stat.h>
+
+#include "tinydir.h"
+
+/** Makefsdata can generate *all* files deflate-compressed (where file size shrinks).
+ * Since nearly all browsers support this, this is a good way to reduce ROM size.
+ * To compress the files, "miniz.c" must be downloaded seperately.
+ */
+#ifndef MAKEFS_SUPPORT_DEFLATE
+#define MAKEFS_SUPPORT_DEFLATE 0
+#endif
+
+#define COPY_BUFSIZE (1024*1024) /* 1 MByte */
+
+#if MAKEFS_SUPPORT_DEFLATE
+#include "../miniz.c"
+
+typedef unsigned char uint8;
+typedef unsigned short uint16;
+typedef unsigned int uint;
+
+#define my_max(a,b) (((a) > (b)) ? (a) : (b))
+#define my_min(a,b) (((a) < (b)) ? (a) : (b))
+
+/* COMP_OUT_BUF_SIZE is the size of the output buffer used during compression.
+   COMP_OUT_BUF_SIZE must be >= 1 and <= OUT_BUF_SIZE */
+#define COMP_OUT_BUF_SIZE COPY_BUFSIZE
+
+/* OUT_BUF_SIZE is the size of the output buffer used during decompression.
+   OUT_BUF_SIZE must be a power of 2 >= TINFL_LZ_DICT_SIZE (because the low-level decompressor not only writes, but reads from the output buffer as it decompresses) */
+#define OUT_BUF_SIZE COPY_BUFSIZE
+static uint8 s_outbuf[OUT_BUF_SIZE];
+static uint8 s_checkbuf[OUT_BUF_SIZE];
+
+/* tdefl_compressor contains all the state needed by the low-level compressor so it's a pretty big struct (~300k).
+   This example makes it a global vs. putting it on the stack, of course in real-world usage you'll probably malloc() or new it. */
+tdefl_compressor g_deflator;
+tinfl_decompressor g_inflator;
+
+int deflate_level = 10; /* default compression level, can be changed via command line */
+#define USAGE_ARG_DEFLATE " [-defl<:compr_level>]"
+#else /* MAKEFS_SUPPORT_DEFLATE */
+#define USAGE_ARG_DEFLATE ""
+#endif /* MAKEFS_SUPPORT_DEFLATE */
+
+#ifdef WIN32
+
+#define GETCWD(path, len)             GetCurrentDirectoryA(len, path)
+#define CHDIR(path)                   SetCurrentDirectoryA(path)
+#define CHDIR_SUCCEEDED(ret)          (ret == TRUE)
+
+#elif __linux__
+
+#define GETCWD(path, len)             getcwd(path, len)
+#define CHDIR(path)                   chdir(path)
+#define CHDIR_SUCCEEDED(ret)          (ret == 0)
+
+#else
+
+#error makefsdata not supported on this platform
+
+#endif
+
+#define NEWLINE     "\r\n"
+#define NEWLINE_LEN 2
+
+/* define this to get the header variables we use to build HTTP headers */
+#define LWIP_HTTPD_DYNAMIC_HEADERS 1
+#define LWIP_HTTPD_SSI             1
+#include "lwip/init.h"
+#include "../httpd_structs.h"
+#include "lwip/apps/fs.h"
+
+#include "../core/inet_chksum.c"
+#include "../core/def.c"
+
+/** (Your server name here) */
+const char *serverID = "Server: "HTTPD_SERVER_AGENT"\r\n";
+char serverIDBuffer[1024];
+
+/* change this to suit your MEM_ALIGNMENT */
+#define PAYLOAD_ALIGNMENT 4
+/* set this to 0 to prevent aligning payload */
+#define ALIGN_PAYLOAD 1
+/* define this to a type that has the required alignment */
+#define PAYLOAD_ALIGN_TYPE "unsigned int"
+static int payload_alingment_dummy_counter = 0;
+
+#define HEX_BYTES_PER_LINE 16
+
+#define MAX_PATH_LEN 256
+
+struct file_entry {
+  struct file_entry *next;
+  const char *filename_c;
+};
+
+int process_sub(FILE *data_file, FILE *struct_file);
+int process_file(FILE *data_file, FILE *struct_file, const char *filename);
+int file_write_http_header(FILE *data_file, const char *filename, int file_size, u16_t *http_hdr_len,
+                           u16_t *http_hdr_chksum, u8_t provide_content_len, int is_compressed);
+int file_put_ascii(FILE *file, const char *ascii_string, int len, int *i);
+int s_put_ascii(char *buf, const char *ascii_string, int len, int *i);
+void concat_files(const char *file1, const char *file2, const char *targetfile);
+int check_path(char *path, size_t size);
+static int checkSsiByFilelist(const char* filename_listfile);
+static int ext_in_list(const char* filename, const char *ext_list);
+static int file_to_exclude(const char* filename);
+static int file_can_be_compressed(const char* filename);
+
+/* 5 bytes per char + 3 bytes per line */
+static char file_buffer_c[COPY_BUFSIZE * 5 + ((COPY_BUFSIZE / HEX_BYTES_PER_LINE) * 3)];
+
+char curSubdir[MAX_PATH_LEN];
+char lastFileVar[MAX_PATH_LEN];
+char hdr_buf[4096];
+
+unsigned char processSubs = 1;
+unsigned char includeHttpHeader = 1;
+unsigned char useHttp11 = 0;
+unsigned char supportSsi = 1;
+unsigned char precalcChksum = 0;
+unsigned char includeLastModified = 0;
+#if MAKEFS_SUPPORT_DEFLATE
+unsigned char deflateNonSsiFiles = 0;
+size_t deflatedBytesReduced = 0;
+size_t overallDataBytes = 0;
+#endif
+const char *exclude_list = NULL;
+const char *ncompress_list = NULL;
+
+struct file_entry *first_file = NULL;
+struct file_entry *last_file = NULL;
+
+static char *ssi_file_buffer;
+static char **ssi_file_lines;
+static size_t ssi_file_num_lines;
+
+static void print_usage(void)
+{
+  printf(" Usage: htmlgen [targetdir] [-s] [-e] [-11] [-nossi] [-ssi:<filename>] [-c] [-f:<filename>] [-m] [-svr:<name>] [-x:<ext_list>] [-xc:<ext_list>" USAGE_ARG_DEFLATE NEWLINE NEWLINE);
+  printf("   targetdir: relative or absolute path to files to convert" NEWLINE);
+  printf("   switch -s: toggle processing of subdirectories (default is on)" NEWLINE);
+  printf("   switch -e: exclude HTTP header from file (header is created at runtime, default is off)" NEWLINE);
+  printf("   switch -11: include HTTP 1.1 header (1.0 is default)" NEWLINE);
+  printf("   switch -nossi: no support for SSI (cannot calculate Content-Length for SSI)" NEWLINE);
+  printf("   switch -ssi: ssi filename (ssi support controlled by file list, not by extension)" NEWLINE);
+  printf("   switch -c: precalculate checksums for all pages (default is off)" NEWLINE);
+  printf("   switch -f: target filename (default is \"fsdata.c\")" NEWLINE);
+  printf("   switch -m: include \"Last-Modified\" header based on file time" NEWLINE);
+  printf("   switch -svr: server identifier sent in HTTP response header ('Server' field)" NEWLINE);
+  printf("   switch -x: comma separated list of extensions of files to exclude (e.g., -x:json,txt)" NEWLINE);
+  printf("   switch -xc: comma separated list of extensions of files to not compress (e.g., -xc:mp3,jpg)" NEWLINE);
+#if MAKEFS_SUPPORT_DEFLATE
+  printf("   switch -defl: deflate-compress all non-SSI files (with opt. compr.-level, default=10)" NEWLINE);
+  printf("                 ATTENTION: browser has to support \"Content-Encoding: deflate\"!" NEWLINE);
+#endif
+  printf("   if targetdir not specified, htmlgen will attempt to" NEWLINE);
+  printf("   process files in subdirectory 'fs'" NEWLINE);
+}
+
+int main(int argc, char *argv[])
+{
+  char path[MAX_PATH_LEN];
+  char appPath[MAX_PATH_LEN];
+  FILE *data_file;
+  FILE *struct_file;
+  int filesProcessed;
+  int i;
+  char targetfile[MAX_PATH_LEN];
+  strcpy(targetfile, "fsdata.c");
+
+  memset(path, 0, sizeof(path));
+  memset(appPath, 0, sizeof(appPath));
+
+  printf(NEWLINE " makefsdata - HTML to C source converter" NEWLINE);
+  printf("     by Jim Pettinato               - circa 2003 " NEWLINE);
+  printf("     extended by Simon Goldschmidt  - 2009 " NEWLINE NEWLINE);
+
+  LWIP_ASSERT("sizeof(hdr_buf) must fit into an u16_t", sizeof(hdr_buf) <= 0xffff);
+
+  strcpy(path, "fs");
+  for (i = 1; i < argc; i++) {
+    if (argv[i] == NULL) {
+      continue;
+    }
+    if (argv[i][0] == '-') {
+      if (strstr(argv[i], "-svr:") == argv[i]) {
+        snprintf(serverIDBuffer, sizeof(serverIDBuffer), "Server: %s\r\n", &argv[i][5]);
+        serverID = serverIDBuffer;
+        printf("Using Server-ID: \"%s\"\n", serverID);
+      } else if (!strcmp(argv[i], "-s")) {
+        processSubs = 0;
+      } else if (!strcmp(argv[i], "-e")) {
+        includeHttpHeader = 0;
+      } else if (!strcmp(argv[i], "-11")) {
+        useHttp11 = 1;
+      } else if (!strcmp(argv[i], "-nossi")) {
+        supportSsi = 0;
+      } else if (strstr(argv[i], "-ssi:") == argv[i]) {
+        const char* ssi_list_filename = &argv[i][5];
+        if (checkSsiByFilelist(ssi_list_filename)) {
+          printf("Reading list of SSI files from \"%s\"\n", ssi_list_filename);
+        } else {
+          printf("Failed to load list of SSI files from \"%s\"\n", ssi_list_filename);
+        }
+      } else if (!strcmp(argv[i], "-c")) {
+        precalcChksum = 1;
+      } else if (strstr(argv[i], "-f:") == argv[i]) {
+        strncpy(targetfile, &argv[i][3], sizeof(targetfile) - 1);
+        targetfile[sizeof(targetfile) - 1] = 0;
+        printf("Writing to file \"%s\"\n", targetfile);
+      } else if (!strcmp(argv[i], "-m")) {
+        includeLastModified = 1;
+      } else if (!strcmp(argv[i], "-defl")) {
+#if MAKEFS_SUPPORT_DEFLATE
+        char *colon = strstr(argv[i], ":");
+        if (colon) {
+          if (colon[1] != 0) {
+            int defl_level = atoi(&colon[1]);
+            if ((defl_level >= 0) && (defl_level <= 10)) {
+              deflate_level = defl_level;
+            } else {
+              printf("ERROR: deflate level must be [0..10]" NEWLINE);
+              exit(0);
+            }
+          }
+        }
+        deflateNonSsiFiles = 1;
+        printf("Deflating all non-SSI files with level %d (but only if size is reduced)" NEWLINE, deflate_level);
+#else
+        printf("WARNING: Deflate support is disabled\n");
+#endif
+      } else if (strstr(argv[i], "-x:") == argv[i]) {
+        exclude_list = &argv[i][3];
+        printf("Excluding files with extensions %s" NEWLINE, exclude_list);
+      } else if (strstr(argv[i], "-xc:") == argv[i]) {
+        ncompress_list = &argv[i][4];
+        printf("Skipping compresion for files with extensions %s" NEWLINE, ncompress_list);
+      } else if ((strstr(argv[i], "-?")) || (strstr(argv[i], "-h"))) {
+        print_usage();
+        exit(0);
+      }
+    } else if ((argv[i][0] == '/') && (argv[i][1] == '?') && (argv[i][2] == 0)) {
+      print_usage();
+      exit(0);
+    } else {
+      strncpy(path, argv[i], sizeof(path) - 1);
+      path[sizeof(path) - 1] = 0;
+    }
+  }
+
+  if (!check_path(path, sizeof(path))) {
+    printf("Invalid path: \"%s\"." NEWLINE, path);
+    exit(-1);
+  }
+
+  GETCWD(appPath, MAX_PATH_LEN);
+  /* if command line param or subdir named 'fs' not found spout usage verbiage */
+  if (!CHDIR_SUCCEEDED(CHDIR(path))) {
+    /* if no subdir named 'fs' (or the one which was given) exists, spout usage verbiage */
+    printf(" Failed to open directory \"%s\"." NEWLINE NEWLINE, path);
+    print_usage();
+    exit(-1);
+  }
+  CHDIR(appPath);
+
+  printf("HTTP %sheader will %s statically included." NEWLINE,
+         (includeHttpHeader ? (useHttp11 ? "1.1 " : "1.0 ") : ""),
+         (includeHttpHeader ? "be" : "not be"));
+
+  curSubdir[0] = '\0'; /* start off in web page's root directory - relative paths */
+  printf("  Processing all files in directory %s", path);
+  if (processSubs) {
+    printf(" and subdirectories..." NEWLINE NEWLINE);
+  } else {
+    printf("..." NEWLINE NEWLINE);
+  }
+
+  data_file = fopen("fsdata.tmp", "wb");
+  if (data_file == NULL) {
+    printf("Failed to create file \"fsdata.tmp\"\n");
+    exit(-1);
+  }
+  struct_file = fopen("fshdr.tmp", "wb");
+  if (struct_file == NULL) {
+    printf("Failed to create file \"fshdr.tmp\"\n");
+    fclose(data_file);
+    exit(-1);
+  }
+
+  CHDIR(path);
+
+  fprintf(data_file, "#include \"lwip/apps/fs.h\"" NEWLINE);
+  fprintf(data_file, "#include \"lwip/def.h\"" NEWLINE NEWLINE NEWLINE);
+
+  fprintf(data_file, "#define file_NULL (struct fsdata_file *) NULL" NEWLINE NEWLINE NEWLINE);
+  /* define FS_FILE_FLAGS_HEADER_INCLUDED to 1 if not defined (compatibility with older httpd/fs) */
+  fprintf(data_file, "#ifndef FS_FILE_FLAGS_HEADER_INCLUDED" NEWLINE "#define FS_FILE_FLAGS_HEADER_INCLUDED 1" NEWLINE "#endif" NEWLINE);
+  /* define FS_FILE_FLAGS_HEADER_PERSISTENT to 0 if not defined (compatibility with older httpd/fs: wasn't supported back then) */
+  fprintf(data_file, "#ifndef FS_FILE_FLAGS_HEADER_PERSISTENT" NEWLINE "#define FS_FILE_FLAGS_HEADER_PERSISTENT 0" NEWLINE "#endif" NEWLINE);
+
+  /* define alignment defines */
+#if ALIGN_PAYLOAD
+  fprintf(data_file, "/* FSDATA_FILE_ALIGNMENT: 0=off, 1=by variable, 2=by include */" NEWLINE "#ifndef FSDATA_FILE_ALIGNMENT" NEWLINE "#define FSDATA_FILE_ALIGNMENT 0" NEWLINE "#endif" NEWLINE);
+#endif
+  fprintf(data_file, "#ifndef FSDATA_ALIGN_PRE"  NEWLINE "#define FSDATA_ALIGN_PRE"  NEWLINE "#endif" NEWLINE);
+  fprintf(data_file, "#ifndef FSDATA_ALIGN_POST" NEWLINE "#define FSDATA_ALIGN_POST" NEWLINE "#endif" NEWLINE);
+#if ALIGN_PAYLOAD
+  fprintf(data_file, "#if FSDATA_FILE_ALIGNMENT==2" NEWLINE "#include \"fsdata_alignment.h\"" NEWLINE "#endif" NEWLINE);
+#endif
+
+  sprintf(lastFileVar, "NULL");
+
+  filesProcessed = process_sub(data_file, struct_file);
+
+  /* data_file now contains all of the raw data.. now append linked list of
+   * file header structs to allow embedded app to search for a file name */
+  fprintf(data_file, NEWLINE NEWLINE);
+  fprintf(struct_file, "#define FS_ROOT file_%s" NEWLINE, lastFileVar);
+  fprintf(struct_file, "#define FS_NUMFILES %d" NEWLINE NEWLINE, filesProcessed);
+
+  fclose(data_file);
+  fclose(struct_file);
+
+  CHDIR(appPath);
+  /* append struct_file to data_file */
+  printf(NEWLINE "Creating target file..." NEWLINE NEWLINE);
+  concat_files("fsdata.tmp", "fshdr.tmp", targetfile);
+
+  /* if succeeded, delete the temporary files */
+  if (remove("fsdata.tmp") != 0) {
+    printf("Warning: failed to delete fsdata.tmp\n");
+  }
+  if (remove("fshdr.tmp") != 0) {
+    printf("Warning: failed to delete fshdr.tmp\n");
+  }
+
+  printf(NEWLINE "Processed %d files - done." NEWLINE, filesProcessed);
+#if MAKEFS_SUPPORT_DEFLATE
+  if (deflateNonSsiFiles) {
+    printf("(Deflated total byte reduction: %d bytes -> %d bytes (%.02f%%)" NEWLINE,
+           (int)overallDataBytes, (int)deflatedBytesReduced, (float)((deflatedBytesReduced * 100.0) / overallDataBytes));
+  }
+#endif
+  printf(NEWLINE);
+
+  while (first_file != NULL) {
+    struct file_entry *fe = first_file;
+    first_file = fe->next;
+    free(fe);
+  }
+
+  if (ssi_file_buffer) {
+    free(ssi_file_buffer);
+  }
+  if (ssi_file_lines) {
+    free(ssi_file_lines);
+  }
+
+  return 0;
+}
+
+int check_path(char *path, size_t size)
+{
+  size_t slen;
+  if (path[0] == 0) {
+    /* empty */
+    return 0;
+  }
+  slen = strlen(path);
+  if (slen >= size) {
+    /* not NULL-terminated */
+    return 0;
+  }
+  while ((slen > 0) && ((path[slen] == '\\') || (path[slen] == '/'))) {
+    /* path should not end with trailing backslash */
+    path[slen] = 0;
+    slen--;
+  }
+  if (slen == 0) {
+    return 0;
+  }
+  return 1;
+}
+
+static void copy_file(const char *filename_in, FILE *fout)
+{
+  FILE *fin;
+  size_t len;
+  void *buf;
+  fin = fopen(filename_in, "rb");
+  if (fin == NULL) {
+    printf("Failed to open file \"%s\"\n", filename_in);
+    exit(-1);
+  }
+  buf = malloc(COPY_BUFSIZE);
+  while ((len = fread(buf, 1, COPY_BUFSIZE, fin)) > 0) {
+    fwrite(buf, 1, len, fout);
+  }
+  free(buf);
+  fclose(fin);
+}
+
+void concat_files(const char *file1, const char *file2, const char *targetfile)
+{
+  FILE *fout;
+  fout = fopen(targetfile, "wb");
+  if (fout == NULL) {
+    printf("Failed to open file \"%s\"\n", targetfile);
+    exit(-1);
+  }
+  copy_file(file1, fout);
+  copy_file(file2, fout);
+  fclose(fout);
+}
+
+int process_sub(FILE *data_file, FILE *struct_file)
+{
+  tinydir_dir dir;
+  int filesProcessed = 0;
+
+  if (processSubs) {
+    /* process subs recursively */
+    size_t sublen = strlen(curSubdir);
+    size_t freelen = sizeof(curSubdir) - sublen - 1;
+    int ret;
+    LWIP_ASSERT("sublen < sizeof(curSubdir)", sublen < sizeof(curSubdir));
+
+    ret = tinydir_open_sorted(&dir, TINYDIR_STRING("."));
+
+    if (ret == 0) {
+      unsigned int i;
+      for (i = 0; i < dir.n_files; i++) {
+        tinydir_file file;
+
+        ret = tinydir_readfile_n(&dir, &file, i);
+
+        if (ret == 0) {
+#if (defined _MSC_VER || defined __MINGW32__) && (defined _UNICODE)
+          size_t num_char_converted;
+          char currName[256];
+          wcstombs_s(&num_char_converted, currName, sizeof(currName), file.name, sizeof(currName));
+#else
+          const char *currName = file.name;
+#endif
+
+          if (currName[0] == '.') {
+            continue;
+          }
+          if (!file.is_dir) {
+            continue;
+          }
+          if (freelen > 0) {
+            CHDIR(currName);
+            strncat(curSubdir, "/", freelen);
+            strncat(curSubdir, currName, freelen - 1);
+            curSubdir[sizeof(curSubdir) - 1] = 0;
+            printf("processing subdirectory %s/..." NEWLINE, curSubdir);
+            filesProcessed += process_sub(data_file, struct_file);
+            CHDIR("..");
+            curSubdir[sublen] = 0;
+          } else {
+            printf("WARNING: cannot process sub due to path length restrictions: \"%s/%s\"\n", curSubdir, currName);
+          }
+        }
+      }
+    }
+
+    ret = tinydir_open_sorted(&dir, TINYDIR_STRING("."));
+    if (ret == 0) {
+      unsigned int i;
+      for (i = 0; i < dir.n_files; i++) {
+        tinydir_file file;
+
+        ret = tinydir_readfile_n(&dir, &file, i);
+
+        if (ret == 0) {
+          if (!file.is_dir) {
+#if (defined _MSC_VER || defined __MINGW32__) && (defined _UNICODE)
+            size_t num_char_converted;
+            char curName[256];
+            wcstombs_s(&num_char_converted, curName, sizeof(curName), file.name, sizeof(curName));
+#else
+            const char *curName = file.name;
+#endif
+
+            if (strcmp(curName, "fsdata.tmp") == 0) {
+              continue;
+            }
+            if (strcmp(curName, "fshdr.tmp") == 0) {
+              continue;
+            }
+            if (file_to_exclude(curName)) {
+              printf("skipping %s/%s by exclude list (-x option)..." NEWLINE, curSubdir, curName);
+              continue;
+            }
+
+            printf("processing %s/%s..." NEWLINE, curSubdir, curName);
+
+            if (process_file(data_file, struct_file, curName) < 0) {
+              printf(NEWLINE "Error... aborting" NEWLINE);
+              return -1;
+            }
+            filesProcessed++;
+          }
+        }
+      }
+    }
+  }
+
+  return filesProcessed;
+}
+
+static u8_t *get_file_data(const char *filename, int *file_size, int can_be_compressed, int *is_compressed)
+{
+  FILE *inFile;
+  size_t fsize = 0;
+  u8_t *buf;
+  size_t r;
+  int rs;
+  LWIP_UNUSED_ARG(r); /* for LWIP_NOASSERT */
+  inFile = fopen(filename, "rb");
+  if (inFile == NULL) {
+    printf("Failed to open file \"%s\"\n", filename);
+    exit(-1);
+  }
+  fseek(inFile, 0, SEEK_END);
+  rs = ftell(inFile);
+  if (rs < 0) {
+    printf("ftell failed with %d\n", errno);
+    exit(-1);
+  }
+  fsize = (size_t)rs;
+  fseek(inFile, 0, SEEK_SET);
+  buf = (u8_t *)malloc(fsize);
+  LWIP_ASSERT("buf != NULL", buf != NULL);
+  r = fread(buf, 1, fsize, inFile);
+  LWIP_ASSERT("r == fsize", r == fsize);
+  *file_size = fsize;
+  *is_compressed = 0;
+#if MAKEFS_SUPPORT_DEFLATE
+  overallDataBytes += fsize;
+  if (deflateNonSsiFiles) {
+    if (can_be_compressed) {
+      if (fsize < OUT_BUF_SIZE) {
+        u8_t *ret_buf;
+        tdefl_status status;
+        size_t in_bytes = fsize;
+        size_t out_bytes = OUT_BUF_SIZE;
+        const void *next_in = buf;
+        void *next_out = s_outbuf;
+        /* create tdefl() compatible flags (we have to compose the low-level flags ourselves, or use tdefl_create_comp_flags_from_zip_params() but that means MINIZ_NO_ZLIB_APIS can't be defined). */
+        mz_uint comp_flags = s_tdefl_num_probes[MZ_MIN(10, deflate_level)] | ((deflate_level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0);
+        if (!deflate_level) {
+          comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS;
+        }
+        status = tdefl_init(&g_deflator, NULL, NULL, comp_flags);
+        if (status != TDEFL_STATUS_OKAY) {
+          printf("tdefl_init() failed!\n");
+          exit(-1);
+        }
+        memset(s_outbuf, 0, sizeof(s_outbuf));
+        status = tdefl_compress(&g_deflator, next_in, &in_bytes, next_out, &out_bytes, TDEFL_FINISH);
+        if (status != TDEFL_STATUS_DONE) {
+          printf("deflate failed: %d\n", status);
+          exit(-1);
+        }
+        LWIP_ASSERT("out_bytes <= COPY_BUFSIZE", out_bytes <= OUT_BUF_SIZE);
+        if (out_bytes < fsize) {
+          ret_buf = (u8_t *)malloc(out_bytes);
+          LWIP_ASSERT("ret_buf != NULL", ret_buf != NULL);
+          memcpy(ret_buf, s_outbuf, out_bytes);
+          {
+            /* sanity-check compression be inflating and comparing to the original */
+            tinfl_status dec_status;
+            tinfl_decompressor inflator;
+            size_t dec_in_bytes = out_bytes;
+            size_t dec_out_bytes = OUT_BUF_SIZE;
+            next_out = s_checkbuf;
+
+            tinfl_init(&inflator);
+            memset(s_checkbuf, 0, sizeof(s_checkbuf));
+            dec_status = tinfl_decompress(&inflator, (const mz_uint8 *)ret_buf, &dec_in_bytes, s_checkbuf, (mz_uint8 *)next_out, &dec_out_bytes, 0);
+            LWIP_ASSERT("tinfl_decompress failed", dec_status == TINFL_STATUS_DONE);
+            LWIP_ASSERT("tinfl_decompress size mismatch", fsize == dec_out_bytes);
+            LWIP_ASSERT("decompressed memcmp failed", !memcmp(s_checkbuf, buf, fsize));
+          }
+          /* free original buffer, use compressed data + size */
+          free(buf);
+          buf = ret_buf;
+          *file_size = out_bytes;
+          printf(" - deflate: %d bytes -> %d bytes (%.02f%%)" NEWLINE, (int)fsize, (int)out_bytes, (float)((out_bytes * 100.0) / fsize));
+          deflatedBytesReduced += (size_t)(fsize - out_bytes);
+          *is_compressed = 1;
+        } else {
+          printf(" - uncompressed: (would be %d bytes larger using deflate)" NEWLINE, (int)(out_bytes - fsize));
+        }
+      } else {
+        printf(" - uncompressed: (file is larger than deflate bufer)" NEWLINE);
+      }
+    } else {
+      printf(" - cannot be compressed" NEWLINE);
+    }
+  }
+#else
+  LWIP_UNUSED_ARG(can_be_compressed);
+#endif
+  fclose(inFile);
+  return buf;
+}
+
+static void process_file_data(FILE *data_file, u8_t *file_data, size_t file_size)
+{
+  size_t written, i, src_off = 0;
+  size_t off = 0;
+  LWIP_UNUSED_ARG(written); /* for LWIP_NOASSERT */
+  for (i = 0; i < file_size; i++) {
+    LWIP_ASSERT("file_buffer_c overflow", off < sizeof(file_buffer_c) - 5);
+    sprintf(&file_buffer_c[off], "0x%02x,", file_data[i]);
+    off += 5;
+    if ((++src_off % HEX_BYTES_PER_LINE) == 0) {
+      LWIP_ASSERT("file_buffer_c overflow", off < sizeof(file_buffer_c) - NEWLINE_LEN);
+      memcpy(&file_buffer_c[off], NEWLINE, NEWLINE_LEN);
+      off += NEWLINE_LEN;
+    }
+    if (off + 20 >= sizeof(file_buffer_c)) {
+      written = fwrite(file_buffer_c, 1, off, data_file);
+      LWIP_ASSERT("written == off", written == off);
+      off = 0;
+    }
+  }
+  written = fwrite(file_buffer_c, 1, off, data_file);
+  LWIP_ASSERT("written == off", written == off);
+}
+
+static int write_checksums(FILE *struct_file, const char *varname,
+                           u16_t hdr_len, u16_t hdr_chksum, const u8_t *file_data, size_t file_size)
+{
+  int chunk_size = TCP_MSS;
+  int offset, src_offset;
+  size_t len;
+  int i = 0;
+#if LWIP_TCP_TIMESTAMPS
+  /* when timestamps are used, usable space is 12 bytes less per segment */
+  chunk_size -= 12;
+#endif
+
+  fprintf(struct_file, "#if HTTPD_PRECALCULATED_CHECKSUM" NEWLINE);
+  fprintf(struct_file, "const struct fsdata_chksum chksums_%s[] = {" NEWLINE, varname);
+
+  if (hdr_len > 0) {
+    /* add checksum for HTTP header */
+    fprintf(struct_file, "{%d, 0x%04x, %d}," NEWLINE, 0, hdr_chksum, hdr_len);
+    i++;
+  }
+  src_offset = 0;
+  for (offset = hdr_len; ; offset += len) {
+    unsigned short chksum;
+    const void *data = (const void *)&file_data[src_offset];
+    len = LWIP_MIN(chunk_size, (int)file_size - src_offset);
+    if (len == 0) {
+      break;
+    }
+    chksum = ~inet_chksum(data, (u16_t)len);
+    /* add checksum for data */
+    fprintf(struct_file, "{%d, 0x%04x, %"SZT_F"}," NEWLINE, offset, chksum, len);
+    i++;
+  }
+  fprintf(struct_file, "};" NEWLINE);
+  fprintf(struct_file, "#endif /* HTTPD_PRECALCULATED_CHECKSUM */" NEWLINE);
+  return i;
+}
+
+static int is_valid_char_for_c_var(char x)
+{
+  if (((x >= 'A') && (x <= 'Z')) ||
+      ((x >= 'a') && (x <= 'z')) ||
+      ((x >= '0') && (x <= '9')) ||
+      (x == '_')) {
+    return 1;
+  }
+  return 0;
+}
+
+static void fix_filename_for_c(char *qualifiedName, size_t max_len)
+{
+  struct file_entry *f;
+  size_t len = strlen(qualifiedName);
+  char *new_name = (char *)malloc(len + 2);
+  int filename_ok;
+  int cnt = 0;
+  size_t i;
+  if (len + 3 == max_len) {
+    printf("File name too long: \"%s\"\n", qualifiedName);
+    exit(-1);
+  }
+  strcpy(new_name, qualifiedName);
+  for (i = 0; i < len; i++) {
+    if (!is_valid_char_for_c_var(new_name[i])) {
+      new_name[i] = '_';
+    }
+  }
+  do {
+    filename_ok = 1;
+    for (f = first_file; f != NULL; f = f->next) {
+      if (!strcmp(f->filename_c, new_name)) {
+        filename_ok = 0;
+        cnt++;
+        /* try next unique file name */
+        sprintf(&new_name[len], "%d", cnt);
+        break;
+      }
+    }
+  } while (!filename_ok && (cnt < 999));
+  if (!filename_ok) {
+    printf("Failed to get unique file name: \"%s\"\n", qualifiedName);
+    exit(-1);
+  }
+  strcpy(qualifiedName, new_name);
+  free(new_name);
+}
+
+static void register_filename(const char *qualifiedName)
+{
+  struct file_entry *fe = (struct file_entry *)malloc(sizeof(struct file_entry));
+  fe->filename_c = strdup(qualifiedName);
+  fe->next = NULL;
+  if (first_file == NULL) {
+    first_file = last_file = fe;
+  } else {
+    last_file->next = fe;
+    last_file = fe;
+  }
+}
+
+static int checkSsiByFilelist(const char* filename_listfile)
+{
+  FILE *f = fopen(filename_listfile, "r");
+  if (f != NULL) {
+    char *buf;
+    long rs;
+    size_t fsize, readcount;
+    size_t i, l, num_lines;
+    char **lines;
+    int state;
+
+    fseek(f, 0, SEEK_END);
+    rs = ftell(f);
+    if (rs < 0) {
+      printf("ftell failed with %d\n", errno);
+      fclose(f);
+      return 0;
+    }
+    fsize = (size_t)rs;
+    fseek(f, 0, SEEK_SET);
+    buf = (char*)malloc(fsize);
+    if (!buf) {
+      printf("failed to allocate ssi file buffer\n");
+      fclose(f);
+      return 0;
+    }
+    memset(buf, 0, fsize);
+    readcount = fread(buf, 1, fsize, f);
+    fclose(f);
+    if ((readcount > fsize) || !readcount) {
+      printf("failed to read data from ssi file\n");
+      free(buf);
+      return 0;
+    }
+
+    /* first pass: get the number of lines (and convert newlines to '0') */
+    num_lines = 1;
+    for (i = 0; i < readcount; i++) {
+      if (buf[i] == '\n') {
+        num_lines++;
+        buf[i] = 0;
+      } else if (buf[i] == '\r') {
+        buf[i] = 0;
+      }
+    }
+    /* allocate the line pointer array */
+    lines = (char**)malloc(sizeof(char*) * num_lines);
+    if (!lines) {
+      printf("failed to allocate ssi line buffer\n");
+      free(buf);
+      return 0;
+    }
+    memset(lines, 0, sizeof(char*) * num_lines);
+    l = 0;
+    state = 0;
+    for (i = 0; i < readcount; i++) {
+      if (state) {
+        /* waiting for null */
+        if (buf[i] == 0) {
+          state = 0;
+        }
+      } else {
+        /* waiting for beginning of new string */
+        if (buf[i] != 0) {
+          LWIP_ASSERT("lines array overflow", l < num_lines);
+          lines[l] = &buf[i];
+          state = 1;
+          l++;
+        }
+      }
+    }
+    LWIP_ASSERT("lines array overflow", l < num_lines);
+
+    ssi_file_buffer = buf;
+    ssi_file_lines = lines;
+    ssi_file_num_lines = l;
+  }
+  return 0;
+}
+
+static int is_ssi_file(const char *filename)
+{
+  if (supportSsi) {
+    if (ssi_file_buffer) {
+      /* compare by list */
+      size_t i;
+      int ret = 0;
+      /* build up the relative path to this file */
+      size_t sublen = strlen(curSubdir);
+      size_t freelen = sizeof(curSubdir) - sublen - 1;
+      strncat(curSubdir, "/", freelen);
+      strncat(curSubdir, filename, freelen - 1);
+      curSubdir[sizeof(curSubdir) - 1] = 0;
+      for (i = 0; i < ssi_file_num_lines; i++) {
+        const char *listed_file = ssi_file_lines[i];
+        /* compare without the leading '/' */
+        if (!strcmp(&curSubdir[1], listed_file)) {
+          ret = 1;
+        }
+      }
+      curSubdir[sublen] = 0;
+      return ret;
+    } else {
+      /* check file extension */
+      size_t loop;
+      for (loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) {
+        if (strstr(filename, g_pcSSIExtensions[loop])) {
+          return 1;
+        }
+      }
+    }
+  }
+  return 0;
+}
+
+static int ext_in_list(const char* filename, const char *ext_list)
+{
+  int found = 0;
+  const char *ext = ext_list;
+  if (ext_list == NULL) {
+    return 0;
+  }
+  while(*ext != '\0') {
+    const char *comma = strchr(ext, ',');
+    size_t ext_size;
+    size_t filename_size = strlen(filename);
+    if (comma == NULL) {
+      comma = strchr(ext, '\0');
+    }
+    ext_size = comma - ext;
+    if ((filename[filename_size - ext_size - 1] == '.') &&
+      !strncmp(&filename[filename_size - ext_size], ext, ext_size)) {
+        found = 1;
+        break;
+    }
+    ext = comma + 1;
+  }
+
+  return found;
+}
+
+static int file_to_exclude(const char *filename)
+{
+    return (exclude_list != NULL) && ext_in_list(filename, exclude_list);
+}
+
+static int file_can_be_compressed(const char *filename)
+{
+    return (ncompress_list == NULL) || !ext_in_list(filename, ncompress_list);
+}
+
+int process_file(FILE *data_file, FILE *struct_file, const char *filename)
+{
+  char varname[MAX_PATH_LEN];
+  int i = 0;
+  char qualifiedName[MAX_PATH_LEN];
+  int file_size;
+  u16_t http_hdr_chksum = 0;
+  u16_t http_hdr_len = 0;
+  int chksum_count = 0;
+  u8_t flags = 0;
+  u8_t has_content_len;
+  u8_t *file_data;
+  int is_ssi;
+  int can_be_compressed;
+  int is_compressed = 0;
+  int flags_printed;
+
+  /* create qualified name (@todo: prepend slash or not?) */
+  sprintf(qualifiedName, "%s/%s", curSubdir, filename);
+  /* create C variable name */
+  strcpy(varname, qualifiedName);
+  /* convert slashes & dots to underscores */
+  fix_filename_for_c(varname, MAX_PATH_LEN);
+  register_filename(varname);
+#if ALIGN_PAYLOAD
+  /* to force even alignment of array, type 1 */
+  fprintf(data_file, "#if FSDATA_FILE_ALIGNMENT==1" NEWLINE);
+  fprintf(data_file, "static const " PAYLOAD_ALIGN_TYPE " dummy_align_%s = %d;" NEWLINE, varname, payload_alingment_dummy_counter++);
+  fprintf(data_file, "#endif" NEWLINE);
+#endif /* ALIGN_PAYLOAD */
+  fprintf(data_file, "static const unsigned char FSDATA_ALIGN_PRE data_%s[] FSDATA_ALIGN_POST = {" NEWLINE, varname);
+  /* encode source file name (used by file system, not returned to browser) */
+  fprintf(data_file, "/* %s (%"SZT_F" chars) */" NEWLINE, qualifiedName, strlen(qualifiedName) + 1);
+  file_put_ascii(data_file, qualifiedName, strlen(qualifiedName) + 1, &i);
+#if ALIGN_PAYLOAD
+  /* pad to even number of bytes to assure payload is on aligned boundary */
+  while (i % PAYLOAD_ALIGNMENT != 0) {
+    fprintf(data_file, "0x%02x,", 0);
+    i++;
+  }
+#endif /* ALIGN_PAYLOAD */
+  fprintf(data_file, NEWLINE);
+
+  is_ssi = is_ssi_file(filename);
+  if (is_ssi) {
+    flags |= FS_FILE_FLAGS_SSI;
+  }
+  has_content_len = !is_ssi;
+  can_be_compressed = includeHttpHeader && !is_ssi && file_can_be_compressed(filename);
+  file_data = get_file_data(filename, &file_size, can_be_compressed, &is_compressed);
+  if (includeHttpHeader) {
+    file_write_http_header(data_file, filename, file_size, &http_hdr_len, &http_hdr_chksum, has_content_len, is_compressed);
+    flags |= FS_FILE_FLAGS_HEADER_INCLUDED;
+    if (has_content_len) {
+      flags |= FS_FILE_FLAGS_HEADER_PERSISTENT;
+      if (useHttp11) {
+        flags |= FS_FILE_FLAGS_HEADER_HTTPVER_1_1;
+      }
+    }
+  }
+  if (precalcChksum) {
+    chksum_count = write_checksums(struct_file, varname, http_hdr_len, http_hdr_chksum, file_data, file_size);
+  }
+
+  /* build declaration of struct fsdata_file in temp file */
+  fprintf(struct_file, "const struct fsdata_file file_%s[] = { {" NEWLINE, varname);
+  fprintf(struct_file, "file_%s," NEWLINE, lastFileVar);
+  fprintf(struct_file, "data_%s," NEWLINE, varname);
+  fprintf(struct_file, "data_%s + %d," NEWLINE, varname, i);
+  fprintf(struct_file, "sizeof(data_%s) - %d," NEWLINE, varname, i);
+
+  flags_printed = 0;
+  if (flags & FS_FILE_FLAGS_HEADER_INCLUDED) {
+    fputs("FS_FILE_FLAGS_HEADER_INCLUDED", struct_file);
+    flags_printed = 1;
+  }
+  if (flags & FS_FILE_FLAGS_HEADER_PERSISTENT) {
+    if (flags_printed) {
+      fputs(" | ", struct_file);
+    }
+    fputs("FS_FILE_FLAGS_HEADER_PERSISTENT", struct_file);
+    flags_printed = 1;
+  }
+  if (flags & FS_FILE_FLAGS_HEADER_HTTPVER_1_1) {
+    if (flags_printed) {
+      fputs(" | ", struct_file);
+    }
+    fputs("FS_FILE_FLAGS_HEADER_HTTPVER_1_1", struct_file);
+    flags_printed = 1;
+  }
+  if (flags & FS_FILE_FLAGS_SSI) {
+    if (flags_printed) {
+      fputs(" | ", struct_file);
+    }
+    fputs("FS_FILE_FLAGS_SSI", struct_file);
+    flags_printed = 1;
+  }
+  if (!flags_printed) {
+    fputs("0", struct_file);
+  }
+  fputs("," NEWLINE, struct_file);
+  if (precalcChksum) {
+    fprintf(struct_file, "#if HTTPD_PRECALCULATED_CHECKSUM" NEWLINE);
+    fprintf(struct_file, "%d, chksums_%s," NEWLINE, chksum_count, varname);
+    fprintf(struct_file, "#endif /* HTTPD_PRECALCULATED_CHECKSUM */" NEWLINE);
+  }
+  fprintf(struct_file, "}};" NEWLINE NEWLINE);
+  strcpy(lastFileVar, varname);
+
+  /* write actual file contents */
+  i = 0;
+  fprintf(data_file, NEWLINE "/* raw file data (%d bytes) */" NEWLINE, file_size);
+  process_file_data(data_file, file_data, file_size);
+  fprintf(data_file, "};" NEWLINE NEWLINE);
+  free(file_data);
+  return 0;
+}
+
+int file_write_http_header(FILE *data_file, const char *filename, int file_size, u16_t *http_hdr_len,
+                           u16_t *http_hdr_chksum, u8_t provide_content_len, int is_compressed)
+{
+  int i = 0;
+  int response_type = HTTP_HDR_OK;
+  const char *file_type;
+  const char *cur_string;
+  size_t cur_len;
+  int written = 0;
+  size_t hdr_len = 0;
+  u16_t acc;
+  const char *file_ext;
+  size_t j;
+  u8_t provide_last_modified = includeLastModified;
+
+  memset(hdr_buf, 0, sizeof(hdr_buf));
+
+  if (useHttp11) {
+    response_type = HTTP_HDR_OK_11;
+  }
+
+  fprintf(data_file, NEWLINE "/* HTTP header */");
+  if (strstr(filename, "404") == filename) {
+    response_type = HTTP_HDR_NOT_FOUND;
+    if (useHttp11) {
+      response_type = HTTP_HDR_NOT_FOUND_11;
+    }
+  } else if (strstr(filename, "400") == filename) {
+    response_type = HTTP_HDR_BAD_REQUEST;
+    if (useHttp11) {
+      response_type = HTTP_HDR_BAD_REQUEST_11;
+    }
+  } else if (strstr(filename, "501") == filename) {
+    response_type = HTTP_HDR_NOT_IMPL;
+    if (useHttp11) {
+      response_type = HTTP_HDR_NOT_IMPL_11;
+    }
+  }
+  cur_string = g_psHTTPHeaderStrings[response_type];
+  cur_len = strlen(cur_string);
+  fprintf(data_file, NEWLINE "/* \"%s\" (%"SZT_F" bytes) */" NEWLINE, cur_string, cur_len);
+  written += file_put_ascii(data_file, cur_string, cur_len, &i);
+  i = 0;
+  if (precalcChksum) {
+    memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+    hdr_len += cur_len;
+  }
+
+  cur_string = serverID;
+  cur_len = strlen(cur_string);
+  fprintf(data_file, NEWLINE "/* \"%s\" (%"SZT_F" bytes) */" NEWLINE, cur_string, cur_len);
+  written += file_put_ascii(data_file, cur_string, cur_len, &i);
+  i = 0;
+  if (precalcChksum) {
+    memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+    hdr_len += cur_len;
+  }
+
+  file_ext = filename;
+  if (file_ext != NULL) {
+    while (strstr(file_ext, ".") != NULL) {
+      file_ext = strstr(file_ext, ".");
+      file_ext++;
+    }
+  }
+  if ((file_ext == NULL) || (*file_ext == 0)) {
+    printf("failed to get extension for file \"%s\", using default.\n", filename);
+    file_type = HTTP_HDR_DEFAULT_TYPE;
+  } else {
+    file_type = NULL;
+    for (j = 0; j < NUM_HTTP_HEADERS; j++) {
+      if (!strcmp(file_ext, g_psHTTPHeaders[j].extension)) {
+        file_type = g_psHTTPHeaders[j].content_type;
+        break;
+      }
+    }
+    if (file_type == NULL) {
+      printf("failed to get file type for extension \"%s\", using default.\n", file_ext);
+      file_type = HTTP_HDR_DEFAULT_TYPE;
+    }
+  }
+
+  /* Content-Length is used for persistent connections in HTTP/1.1 but also for
+     download progress in older versions
+     @todo: just use a big-enough buffer and let the HTTPD send spaces? */
+  if (provide_content_len) {
+    char intbuf[MAX_PATH_LEN];
+    int content_len = file_size;
+    memset(intbuf, 0, sizeof(intbuf));
+    cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONTENT_LENGTH];
+    cur_len = strlen(cur_string);
+    fprintf(data_file, NEWLINE "/* \"%s%d\r\n\" (%"SZT_F"+ bytes) */" NEWLINE, cur_string, content_len, cur_len + 2);
+    written += file_put_ascii(data_file, cur_string, cur_len, &i);
+    if (precalcChksum) {
+      memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+      hdr_len += cur_len;
+    }
+
+    lwip_itoa(intbuf, sizeof(intbuf), content_len);
+    strcat(intbuf, "\r\n");
+    cur_len = strlen(intbuf);
+    written += file_put_ascii(data_file, intbuf, cur_len, &i);
+    i = 0;
+    if (precalcChksum) {
+      memcpy(&hdr_buf[hdr_len], intbuf, cur_len);
+      hdr_len += cur_len;
+    }
+  }
+  if (provide_last_modified) {
+    char modbuf[256];
+    struct stat stat_data;
+    struct tm *t;
+    memset(modbuf, 0, sizeof(modbuf));
+    memset(&stat_data, 0, sizeof(stat_data));
+    cur_string = modbuf;
+    strcpy(modbuf, "Last-Modified: ");
+    if (stat(filename, &stat_data) != 0) {
+      printf("stat(%s) failed with error %d\n", filename, errno);
+      exit(-1);
+    }
+    t = gmtime(&stat_data.st_mtime);
+    if (t == NULL) {
+      printf("gmtime() failed with error %d\n", errno);
+      exit(-1);
+    }
+    strftime(&modbuf[15], sizeof(modbuf) - 15, "%a, %d %b %Y %H:%M:%S GMT", t);
+    cur_len = strlen(cur_string);
+    fprintf(data_file, NEWLINE "/* \"%s\"\r\n\" (%"SZT_F"+ bytes) */" NEWLINE, cur_string, cur_len + 2);
+    written += file_put_ascii(data_file, cur_string, cur_len, &i);
+    if (precalcChksum) {
+      memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+      hdr_len += cur_len;
+    }
+
+    modbuf[0] = 0;
+    strcat(modbuf, "\r\n");
+    cur_len = strlen(modbuf);
+    written += file_put_ascii(data_file, modbuf, cur_len, &i);
+    i = 0;
+    if (precalcChksum) {
+      memcpy(&hdr_buf[hdr_len], modbuf, cur_len);
+      hdr_len += cur_len;
+    }
+  }
+
+  /* HTTP/1.1 implements persistent connections */
+  if (useHttp11) {
+    if (provide_content_len) {
+      cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONN_KEEPALIVE];
+    } else {
+      /* no Content-Length available, so a persistent connection is no possible
+         because the client does not know the data length */
+      cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONN_CLOSE];
+    }
+    cur_len = strlen(cur_string);
+    fprintf(data_file, NEWLINE "/* \"%s\" (%"SZT_F" bytes) */" NEWLINE, cur_string, cur_len);
+    written += file_put_ascii(data_file, cur_string, cur_len, &i);
+    i = 0;
+    if (precalcChksum) {
+      memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+      hdr_len += cur_len;
+    }
+  }
+
+#if MAKEFS_SUPPORT_DEFLATE
+  if (is_compressed) {
+    /* tell the client about the deflate encoding */
+    LWIP_ASSERT("error", deflateNonSsiFiles);
+    cur_string = "Content-Encoding: deflate\r\n";
+    cur_len = strlen(cur_string);
+    fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len);
+    written += file_put_ascii(data_file, cur_string, cur_len, &i);
+    i = 0;
+  }
+#else
+  LWIP_UNUSED_ARG(is_compressed);
+#endif
+
+  /* write content-type, ATTENTION: this includes the double-CRLF! */
+  cur_string = file_type;
+  cur_len = strlen(cur_string);
+  fprintf(data_file, NEWLINE "/* \"%s\" (%"SZT_F" bytes) */" NEWLINE, cur_string, cur_len);
+  written += file_put_ascii(data_file, cur_string, cur_len, &i);
+  i = 0;
+
+  /* ATTENTION: headers are done now (double-CRLF has been written!) */
+
+  if (precalcChksum) {
+    LWIP_ASSERT("hdr_len + cur_len <= sizeof(hdr_buf)", hdr_len + cur_len <= sizeof(hdr_buf));
+    memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+    hdr_len += cur_len;
+
+    LWIP_ASSERT("strlen(hdr_buf) == hdr_len", strlen(hdr_buf) == hdr_len);
+    acc = ~inet_chksum(hdr_buf, (u16_t)hdr_len);
+    *http_hdr_len = (u16_t)hdr_len;
+    *http_hdr_chksum = acc;
+  }
+
+  return written;
+}
+
+int file_put_ascii(FILE *file, const char *ascii_string, int len, int *i)
+{
+  int x;
+  for (x = 0; x < len; x++) {
+    unsigned char cur = ascii_string[x];
+    fprintf(file, "0x%02x,", cur);
+    if ((++(*i) % HEX_BYTES_PER_LINE) == 0) {
+      fprintf(file, NEWLINE);
+    }
+  }
+  return len;
+}
+
+int s_put_ascii(char *buf, const char *ascii_string, int len, int *i)
+{
+  int x;
+  int idx = 0;
+  for (x = 0; x < len; x++) {
+    unsigned char cur = ascii_string[x];
+    sprintf(&buf[idx], "0x%02x,", cur);
+    idx += 5;
+    if ((++(*i) % HEX_BYTES_PER_LINE) == 0) {
+      sprintf(&buf[idx], NEWLINE);
+      idx += NEWLINE_LEN;
+    }
+  }
+  return len;
+}
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/makefsdata/readme.txt b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/makefsdata/readme.txt
new file mode 100644 (file)
index 0000000..3768585
--- /dev/null
@@ -0,0 +1,13 @@
+This directory contains a script ('makefsdata') to create C code suitable for
+httpd for given html pages (or other files) in a directory.
+
+There is also a plain C console application doing the same and extended a bit.
+
+Usage: htmlgen [targetdir] [-s] [-i]s
+   targetdir: relative or absolute path to files to convert
+   switch -s: toggle processing of subdirectories (default is on)
+   switch -e: exclude HTTP header from file (header is created at runtime, default is on)
+   switch -11: include HTTP 1.1 header (1.0 is default)
+
+  if targetdir not specified, makefsdata will attempt to
+  process files in subdirectory 'fs'.
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/makefsdata/tinydir.h b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/http/makefsdata/tinydir.h
new file mode 100644 (file)
index 0000000..32ae5e8
--- /dev/null
@@ -0,0 +1,808 @@
+/*
+Copyright (c) 2013-2017, tinydir authors:
+- Cong Xu
+- Lautis Sun
+- Baudouin Feildel
+- Andargor <andargor@yahoo.com>
+All rights reserved.
+
+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 OWNER 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.
+*/
+#ifndef TINYDIR_H
+#define TINYDIR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if ((defined _UNICODE) && !(defined UNICODE))
+#define UNICODE
+#endif
+
+#if ((defined UNICODE) && !(defined _UNICODE))
+#define _UNICODE
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _MSC_VER
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+# include <tchar.h>
+# pragma warning(push)
+# pragma warning (disable : 4996)
+#else
+# include <dirent.h>
+# include <libgen.h>
+# include <sys/stat.h>
+# include <stddef.h>
+#endif
+#ifdef __MINGW32__
+# include <tchar.h>
+#endif
+
+
+/* types */
+
+/* Windows UNICODE wide character support */
+#if defined _MSC_VER || defined __MINGW32__
+#define _tinydir_char_t TCHAR
+#define TINYDIR_STRING(s) _TEXT(s)
+#define _tinydir_strlen _tcslen
+#define _tinydir_strcpy _tcscpy
+#define _tinydir_strcat _tcscat
+#define _tinydir_strcmp _tcscmp
+#define _tinydir_strrchr _tcsrchr
+#define _tinydir_strncmp _tcsncmp
+#else
+#define _tinydir_char_t char
+#define TINYDIR_STRING(s) s
+#define _tinydir_strlen strlen
+#define _tinydir_strcpy strcpy
+#define _tinydir_strcat strcat
+#define _tinydir_strcmp strcmp
+#define _tinydir_strrchr strrchr
+#define _tinydir_strncmp strncmp
+#endif
+
+#if (defined _MSC_VER || defined __MINGW32__)
+#include <windows.h>
+#define _TINYDIR_PATH_MAX MAX_PATH
+#elif defined  __linux__
+#include <linux/limits.h>
+#define _TINYDIR_PATH_MAX PATH_MAX
+#else
+#define _TINYDIR_PATH_MAX 4096
+#endif
+
+#ifdef _MSC_VER
+/* extra chars for the "\\*" mask */
+# define _TINYDIR_PATH_EXTRA 2
+#else
+# define _TINYDIR_PATH_EXTRA 0
+#endif
+
+#define _TINYDIR_FILENAME_MAX 256
+
+#if (defined _MSC_VER || defined __MINGW32__)
+#define _TINYDIR_DRIVE_MAX 3
+#endif
+
+#ifdef _MSC_VER
+# define _TINYDIR_FUNC static __inline
+#elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L
+# define _TINYDIR_FUNC static __inline__
+#else
+# define _TINYDIR_FUNC static inline
+#endif
+
+/* readdir_r usage; define TINYDIR_USE_READDIR_R to use it (if supported) */
+#ifdef TINYDIR_USE_READDIR_R
+
+/* readdir_r is a POSIX-only function, and may not be available under various
+ * environments/settings, e.g. MinGW. Use readdir fallback */
+#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE ||\
+       _POSIX_SOURCE
+# define _TINYDIR_HAS_READDIR_R
+#endif
+#if _POSIX_C_SOURCE >= 200112L
+# define _TINYDIR_HAS_FPATHCONF
+# include <unistd.h>
+#endif
+#if _BSD_SOURCE || _SVID_SOURCE || \
+       (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
+# define _TINYDIR_HAS_DIRFD
+# include <sys/types.h>
+#endif
+#if defined _TINYDIR_HAS_FPATHCONF && defined _TINYDIR_HAS_DIRFD &&\
+       defined _PC_NAME_MAX
+# define _TINYDIR_USE_FPATHCONF
+#endif
+#if defined __MINGW32__ || !defined _TINYDIR_HAS_READDIR_R ||\
+       !(defined _TINYDIR_USE_FPATHCONF || defined NAME_MAX)
+# define _TINYDIR_USE_READDIR
+#endif
+
+/* Use readdir by default */
+#else
+# define _TINYDIR_USE_READDIR
+#endif
+
+/* MINGW32 has two versions of dirent, ASCII and UNICODE*/
+#ifndef _MSC_VER
+#if (defined __MINGW32__) && (defined _UNICODE)
+#define _TINYDIR_DIR _WDIR
+#define _tinydir_dirent _wdirent
+#define _tinydir_opendir _wopendir
+#define _tinydir_readdir _wreaddir
+#define _tinydir_closedir _wclosedir
+#else
+#define _TINYDIR_DIR DIR
+#define _tinydir_dirent dirent
+#define _tinydir_opendir opendir
+#define _tinydir_readdir readdir
+#define _tinydir_closedir closedir
+#endif
+#endif
+
+/* Allow user to use a custom allocator by defining _TINYDIR_MALLOC and _TINYDIR_FREE. */
+#if    defined(_TINYDIR_MALLOC) &&  defined(_TINYDIR_FREE)
+#elif !defined(_TINYDIR_MALLOC) && !defined(_TINYDIR_FREE)
+#else
+#error "Either define both alloc and free or none of them!"
+#endif
+
+#if !defined(_TINYDIR_MALLOC)
+       #define _TINYDIR_MALLOC(_size) malloc(_size)
+       #define _TINYDIR_FREE(_ptr)    free(_ptr)
+#endif /* !defined(_TINYDIR_MALLOC) */
+
+typedef struct tinydir_file
+{
+       _tinydir_char_t path[_TINYDIR_PATH_MAX];
+       _tinydir_char_t name[_TINYDIR_FILENAME_MAX];
+       _tinydir_char_t *extension;
+       int is_dir;
+       int is_reg;
+
+#ifndef _MSC_VER
+#ifdef __MINGW32__
+       struct _stat _s;
+#else
+       struct stat _s;
+#endif
+#endif
+} tinydir_file;
+
+typedef struct tinydir_dir
+{
+       _tinydir_char_t path[_TINYDIR_PATH_MAX];
+       int has_next;
+       size_t n_files;
+
+       tinydir_file *_files;
+#ifdef _MSC_VER
+       HANDLE _h;
+       WIN32_FIND_DATA _f;
+#else
+       _TINYDIR_DIR *_d;
+       struct _tinydir_dirent *_e;
+#ifndef _TINYDIR_USE_READDIR
+       struct _tinydir_dirent *_ep;
+#endif
+#endif
+} tinydir_dir;
+
+
+/* declarations */
+
+_TINYDIR_FUNC
+int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path);
+_TINYDIR_FUNC
+int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path);
+_TINYDIR_FUNC
+void tinydir_close(tinydir_dir *dir);
+
+_TINYDIR_FUNC
+int tinydir_next(tinydir_dir *dir);
+_TINYDIR_FUNC
+int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file);
+_TINYDIR_FUNC
+int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i);
+_TINYDIR_FUNC
+int tinydir_open_subdir_n(tinydir_dir *dir, size_t i);
+
+_TINYDIR_FUNC
+int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path);
+_TINYDIR_FUNC
+void _tinydir_get_ext(tinydir_file *file);
+_TINYDIR_FUNC
+int _tinydir_file_cmp(const void *a, const void *b);
+#ifndef _MSC_VER
+#ifndef _TINYDIR_USE_READDIR
+_TINYDIR_FUNC
+size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp);
+#endif
+#endif
+
+
+/* definitions*/
+
+_TINYDIR_FUNC
+int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path)
+{
+#ifndef _MSC_VER
+#ifndef _TINYDIR_USE_READDIR
+       int error;
+       int size;       /* using int size */
+#endif
+#else
+       _tinydir_char_t path_buf[_TINYDIR_PATH_MAX];
+#endif
+       _tinydir_char_t *pathp;
+
+       if (dir == NULL || path == NULL || _tinydir_strlen(path) == 0)
+       {
+               errno = EINVAL;
+               return -1;
+       }
+       if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
+       {
+               errno = ENAMETOOLONG;
+               return -1;
+       }
+
+       /* initialise dir */
+       dir->_files = NULL;
+#ifdef _MSC_VER
+       dir->_h = INVALID_HANDLE_VALUE;
+#else
+       dir->_d = NULL;
+#ifndef _TINYDIR_USE_READDIR
+       dir->_ep = NULL;
+#endif
+#endif
+       tinydir_close(dir);
+
+       _tinydir_strcpy(dir->path, path);
+       /* Remove trailing slashes */
+       pathp = &dir->path[_tinydir_strlen(dir->path) - 1];
+       while (pathp != dir->path && (*pathp == TINYDIR_STRING('\\') || *pathp == TINYDIR_STRING('/')))
+       {
+               *pathp = TINYDIR_STRING('\0');
+               pathp++;
+       }
+#ifdef _MSC_VER
+       _tinydir_strcpy(path_buf, dir->path);
+       _tinydir_strcat(path_buf, TINYDIR_STRING("\\*"));
+#if (defined WINAPI_FAMILY) && (WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP)
+       dir->_h = FindFirstFileEx(path_buf, FindExInfoStandard, &dir->_f, FindExSearchNameMatch, NULL, 0);
+#else
+       dir->_h = FindFirstFile(path_buf, &dir->_f);
+#endif
+       if (dir->_h == INVALID_HANDLE_VALUE)
+       {
+               errno = ENOENT;
+#else
+       dir->_d = _tinydir_opendir(path);
+       if (dir->_d == NULL)
+       {
+#endif
+               goto bail;
+       }
+
+       /* read first file */
+       dir->has_next = 1;
+#ifndef _MSC_VER
+#ifdef _TINYDIR_USE_READDIR
+       dir->_e = _tinydir_readdir(dir->_d);
+#else
+       /* allocate dirent buffer for readdir_r */
+       size = _tinydir_dirent_buf_size(dir->_d); /* conversion to int */
+       if (size == -1) return -1;
+       dir->_ep = (struct _tinydir_dirent*)_TINYDIR_MALLOC(size);
+       if (dir->_ep == NULL) return -1;
+
+       error = readdir_r(dir->_d, dir->_ep, &dir->_e);
+       if (error != 0) return -1;
+#endif
+       if (dir->_e == NULL)
+       {
+               dir->has_next = 0;
+       }
+#endif
+
+       return 0;
+
+bail:
+       tinydir_close(dir);
+       return -1;
+}
+
+_TINYDIR_FUNC
+int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path)
+{
+       /* Count the number of files first, to pre-allocate the files array */
+       size_t n_files = 0;
+       if (tinydir_open(dir, path) == -1)
+       {
+               return -1;
+       }
+       while (dir->has_next)
+       {
+               n_files++;
+               if (tinydir_next(dir) == -1)
+               {
+                       goto bail;
+               }
+       }
+       tinydir_close(dir);
+
+       if (tinydir_open(dir, path) == -1)
+       {
+               return -1;
+       }
+
+       dir->n_files = 0;
+       dir->_files = (tinydir_file *)_TINYDIR_MALLOC(sizeof *dir->_files * n_files);
+       if (dir->_files == NULL)
+       {
+               goto bail;
+       }
+       while (dir->has_next)
+       {
+               tinydir_file *p_file;
+               dir->n_files++;
+
+               p_file = &dir->_files[dir->n_files - 1];
+               if (tinydir_readfile(dir, p_file) == -1)
+               {
+                       goto bail;
+               }
+
+               if (tinydir_next(dir) == -1)
+               {
+                       goto bail;
+               }
+
+               /* Just in case the number of files has changed between the first and
+               second reads, terminate without writing into unallocated memory */
+               if (dir->n_files == n_files)
+               {
+                       break;
+               }
+       }
+
+       qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp);
+
+       return 0;
+
+bail:
+       tinydir_close(dir);
+       return -1;
+}
+
+_TINYDIR_FUNC
+void tinydir_close(tinydir_dir *dir)
+{
+       if (dir == NULL)
+       {
+               return;
+       }
+
+       memset(dir->path, 0, sizeof(dir->path));
+       dir->has_next = 0;
+       dir->n_files = 0;
+       _TINYDIR_FREE(dir->_files);
+       dir->_files = NULL;
+#ifdef _MSC_VER
+       if (dir->_h != INVALID_HANDLE_VALUE)
+       {
+               FindClose(dir->_h);
+       }
+       dir->_h = INVALID_HANDLE_VALUE;
+#else
+       if (dir->_d)
+       {
+               _tinydir_closedir(dir->_d);
+       }
+       dir->_d = NULL;
+       dir->_e = NULL;
+#ifndef _TINYDIR_USE_READDIR
+       _TINYDIR_FREE(dir->_ep);
+       dir->_ep = NULL;
+#endif
+#endif
+}
+
+_TINYDIR_FUNC
+int tinydir_next(tinydir_dir *dir)
+{
+       if (dir == NULL)
+       {
+               errno = EINVAL;
+               return -1;
+       }
+       if (!dir->has_next)
+       {
+               errno = ENOENT;
+               return -1;
+       }
+
+#ifdef _MSC_VER
+       if (FindNextFile(dir->_h, &dir->_f) == 0)
+#else
+#ifdef _TINYDIR_USE_READDIR
+       dir->_e = _tinydir_readdir(dir->_d);
+#else
+       if (dir->_ep == NULL)
+       {
+               return -1;
+       }
+       if (readdir_r(dir->_d, dir->_ep, &dir->_e) != 0)
+       {
+               return -1;
+       }
+#endif
+       if (dir->_e == NULL)
+#endif
+       {
+               dir->has_next = 0;
+#ifdef _MSC_VER
+               if (GetLastError() != ERROR_SUCCESS &&
+                       GetLastError() != ERROR_NO_MORE_FILES)
+               {
+                       tinydir_close(dir);
+                       errno = EIO;
+                       return -1;
+               }
+#endif
+       }
+
+       return 0;
+}
+
+_TINYDIR_FUNC
+int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file)
+{
+       if (dir == NULL || file == NULL)
+       {
+               errno = EINVAL;
+               return -1;
+       }
+#ifdef _MSC_VER
+       if (dir->_h == INVALID_HANDLE_VALUE)
+#else
+       if (dir->_e == NULL)
+#endif
+       {
+               errno = ENOENT;
+               return -1;
+       }
+       if (_tinydir_strlen(dir->path) +
+               _tinydir_strlen(
+#ifdef _MSC_VER
+                       dir->_f.cFileName
+#else
+                       dir->_e->d_name
+#endif
+               ) + 1 + _TINYDIR_PATH_EXTRA >=
+               _TINYDIR_PATH_MAX)
+       {
+               /* the path for the file will be too long */
+               errno = ENAMETOOLONG;
+               return -1;
+       }
+       if (_tinydir_strlen(
+#ifdef _MSC_VER
+                       dir->_f.cFileName
+#else
+                       dir->_e->d_name
+#endif
+               ) >= _TINYDIR_FILENAME_MAX)
+       {
+               errno = ENAMETOOLONG;
+               return -1;
+       }
+
+       _tinydir_strcpy(file->path, dir->path);
+       _tinydir_strcat(file->path, TINYDIR_STRING("/"));
+       _tinydir_strcpy(file->name,
+#ifdef _MSC_VER
+               dir->_f.cFileName
+#else
+               dir->_e->d_name
+#endif
+       );
+       _tinydir_strcat(file->path, file->name);
+#ifndef _MSC_VER
+#ifdef __MINGW32__
+       if (_tstat(
+#else
+       if (stat(
+#endif
+               file->path, &file->_s) == -1)
+       {
+               return -1;
+       }
+#endif
+       _tinydir_get_ext(file);
+
+       file->is_dir =
+#ifdef _MSC_VER
+               !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
+#else
+               S_ISDIR(file->_s.st_mode);
+#endif
+       file->is_reg =
+#ifdef _MSC_VER
+               !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
+               (
+                       !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) &&
+                       !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
+                       !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
+#ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM
+                       !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) &&
+#endif
+#ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA
+                       !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) &&
+#endif
+                       !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) &&
+                       !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY));
+#else
+               S_ISREG(file->_s.st_mode);
+#endif
+
+       return 0;
+}
+
+_TINYDIR_FUNC
+int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i)
+{
+       if (dir == NULL || file == NULL)
+       {
+               errno = EINVAL;
+               return -1;
+       }
+       if (i >= dir->n_files)
+       {
+               errno = ENOENT;
+               return -1;
+       }
+
+       memcpy(file, &dir->_files[i], sizeof(tinydir_file));
+       _tinydir_get_ext(file);
+
+       return 0;
+}
+
+_TINYDIR_FUNC
+int tinydir_open_subdir_n(tinydir_dir *dir, size_t i)
+{
+       _tinydir_char_t path[_TINYDIR_PATH_MAX];
+       if (dir == NULL)
+       {
+               errno = EINVAL;
+               return -1;
+       }
+       if (i >= dir->n_files || !dir->_files[i].is_dir)
+       {
+               errno = ENOENT;
+               return -1;
+       }
+
+       _tinydir_strcpy(path, dir->_files[i].path);
+       tinydir_close(dir);
+       if (tinydir_open_sorted(dir, path) == -1)
+       {
+               return -1;
+       }
+
+       return 0;
+}
+
+/* Open a single file given its path */
+_TINYDIR_FUNC
+int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path)
+{
+       tinydir_dir dir;
+       int result = 0;
+       int found = 0;
+       _tinydir_char_t dir_name_buf[_TINYDIR_PATH_MAX];
+       _tinydir_char_t file_name_buf[_TINYDIR_FILENAME_MAX];
+       _tinydir_char_t *dir_name;
+       _tinydir_char_t *base_name;
+#if (defined _MSC_VER || defined __MINGW32__)
+       _tinydir_char_t drive_buf[_TINYDIR_PATH_MAX];
+       _tinydir_char_t ext_buf[_TINYDIR_FILENAME_MAX];
+#endif
+
+       if (file == NULL || path == NULL || _tinydir_strlen(path) == 0)
+       {
+               errno = EINVAL;
+               return -1;
+       }
+       if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
+       {
+               errno = ENAMETOOLONG;
+               return -1;
+       }
+
+       /* Get the parent path */
+#if (defined _MSC_VER || defined __MINGW32__)
+#if ((defined _MSC_VER) && (_MSC_VER >= 1400))
+               _tsplitpath_s(
+                       path,
+                       drive_buf, _TINYDIR_DRIVE_MAX,
+                       dir_name_buf, _TINYDIR_FILENAME_MAX,
+                       file_name_buf, _TINYDIR_FILENAME_MAX,
+                       ext_buf, _TINYDIR_FILENAME_MAX);
+#else
+               _tsplitpath(
+                       path,
+                       drive_buf,
+                       dir_name_buf,
+                       file_name_buf,
+                       ext_buf);
+#endif
+
+/* _splitpath_s not work fine with only filename and widechar support */
+#ifdef _UNICODE
+               if (drive_buf[0] == L'\xFEFE')
+                       drive_buf[0] = '\0';
+               if (dir_name_buf[0] == L'\xFEFE')
+                       dir_name_buf[0] = '\0';
+#endif
+
+       if (errno)
+       {
+               errno = EINVAL;
+               return -1;
+       }
+       /* Emulate the behavior of dirname by returning "." for dir name if it's
+       empty */
+       if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0')
+       {
+               _tinydir_strcpy(dir_name_buf, TINYDIR_STRING("."));
+       }
+       /* Concatenate the drive letter and dir name to form full dir name */
+       _tinydir_strcat(drive_buf, dir_name_buf);
+       dir_name = drive_buf;
+       /* Concatenate the file name and extension to form base name */
+       _tinydir_strcat(file_name_buf, ext_buf);
+       base_name = file_name_buf;
+#else
+       _tinydir_strcpy(dir_name_buf, path);
+       dir_name = dirname(dir_name_buf);
+       _tinydir_strcpy(file_name_buf, path);
+       base_name =basename(file_name_buf);
+#endif
+
+       /* Open the parent directory */
+       if (tinydir_open(&dir, dir_name) == -1)
+       {
+               return -1;
+       }
+
+       /* Read through the parent directory and look for the file */
+       while (dir.has_next)
+       {
+               if (tinydir_readfile(&dir, file) == -1)
+               {
+                       result = -1;
+                       goto bail;
+               }
+               if (_tinydir_strcmp(file->name, base_name) == 0)
+               {
+                       /* File found */
+                       found = 1;
+                       break;
+               }
+               tinydir_next(&dir);
+       }
+       if (!found)
+       {
+               result = -1;
+               errno = ENOENT;
+       }
+
+bail:
+       tinydir_close(&dir);
+       return result;
+}
+
+_TINYDIR_FUNC
+void _tinydir_get_ext(tinydir_file *file)
+{
+       _tinydir_char_t *period = _tinydir_strrchr(file->name, TINYDIR_STRING('.'));
+       if (period == NULL)
+       {
+               file->extension = &(file->name[_tinydir_strlen(file->name)]);
+       }
+       else
+       {
+               file->extension = period + 1;
+       }
+}
+
+_TINYDIR_FUNC
+int _tinydir_file_cmp(const void *a, const void *b)
+{
+       const tinydir_file *fa = (const tinydir_file *)a;
+       const tinydir_file *fb = (const tinydir_file *)b;
+       if (fa->is_dir != fb->is_dir)
+       {
+               return -(fa->is_dir - fb->is_dir);
+       }
+       return _tinydir_strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX);
+}
+
+#ifndef _MSC_VER
+#ifndef _TINYDIR_USE_READDIR
+/*
+The following authored by Ben Hutchings <ben@decadent.org.uk>
+from https://womble.decadent.org.uk/readdir_r-advisory.html
+*/
+/* Calculate the required buffer size (in bytes) for directory      *
+* entries read from the given directory handle.  Return -1 if this  *
+* this cannot be done.                                              *
+*                                                                   *
+* This code does not trust values of NAME_MAX that are less than    *
+* 255, since some systems (including at least HP-UX) incorrectly    *
+* define it to be a smaller value.                                  */
+_TINYDIR_FUNC
+size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp)
+{
+       long name_max;
+       size_t name_end;
+       /* parameter may be unused */
+       (void)dirp;
+
+#if defined _TINYDIR_USE_FPATHCONF
+       name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
+       if (name_max == -1)
+#if defined(NAME_MAX)
+               name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
+#else
+               return (size_t)(-1);
+#endif
+#elif defined(NAME_MAX)
+       name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
+#else
+#error "buffer size for readdir_r cannot be determined"
+#endif
+       name_end = (size_t)offsetof(struct _tinydir_dirent, d_name) + name_max + 1;
+       return (name_end > sizeof(struct _tinydir_dirent) ?
+               name_end : sizeof(struct _tinydir_dirent));
+}
+#endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+# if defined (_MSC_VER)
+# pragma warning(pop)
+# endif
+
+#endif
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/lwiperf/lwiperf.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/lwiperf/lwiperf.c
new file mode 100644 (file)
index 0000000..dbec4da
--- /dev/null
@@ -0,0 +1,841 @@
+/**
+ * @file
+ * lwIP iPerf server implementation
+ */
+
+/**
+ * @defgroup iperf Iperf server
+ * @ingroup apps
+ *
+ * This is a simple performance measuring client/server to check your bandwith using
+ * iPerf2 on a PC as server/client.
+ * It is currently a minimal implementation providing a TCP client/server only.
+ *
+ * @todo:
+ * - implement UDP mode
+ * - protect combined sessions handling (via 'related_master_state') against reallocation
+ *   (this is a pointer address, currently, so if the same memory is allocated again,
+ *    session pairs (tx/rx) can be confused on reallocation)
+ */
+
+/*
+ * Copyright (c) 2014 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ */
+
+#include "lwip/apps/lwiperf.h"
+
+#include "lwip/tcp.h"
+#include "lwip/sys.h"
+
+#include <string.h>
+
+/* Currently, only TCP is implemented */
+#if LWIP_TCP && LWIP_CALLBACK_API
+
+/** Specify the idle timeout (in seconds) after that the test fails */
+#ifndef LWIPERF_TCP_MAX_IDLE_SEC
+#define LWIPERF_TCP_MAX_IDLE_SEC    10U
+#endif
+#if LWIPERF_TCP_MAX_IDLE_SEC > 255
+#error LWIPERF_TCP_MAX_IDLE_SEC must fit into an u8_t
+#endif
+
+/** Change this if you don't want to lwiperf to listen to any IP version */
+#ifndef LWIPERF_SERVER_IP_TYPE
+#define LWIPERF_SERVER_IP_TYPE      IPADDR_TYPE_ANY
+#endif
+
+/* File internal memory allocation (struct lwiperf_*): this defaults to
+   the heap */
+#ifndef LWIPERF_ALLOC
+#define LWIPERF_ALLOC(type)         mem_malloc(sizeof(type))
+#define LWIPERF_FREE(type, item)    mem_free(item)
+#endif
+
+/** If this is 1, check that received data has the correct format */
+#ifndef LWIPERF_CHECK_RX_DATA
+#define LWIPERF_CHECK_RX_DATA       0
+#endif
+
+/** This is the Iperf settings struct sent from the client */
+typedef struct _lwiperf_settings {
+#define LWIPERF_FLAGS_ANSWER_TEST 0x80000000
+#define LWIPERF_FLAGS_ANSWER_NOW  0x00000001
+  u32_t flags;
+  u32_t num_threads; /* unused for now */
+  u32_t remote_port;
+  u32_t buffer_len; /* unused for now */
+  u32_t win_band; /* TCP window / UDP rate: unused for now */
+  u32_t amount; /* pos. value: bytes?; neg. values: time (unit is 10ms: 1/100 second) */
+} lwiperf_settings_t;
+
+/** Basic connection handle */
+struct _lwiperf_state_base;
+typedef struct _lwiperf_state_base lwiperf_state_base_t;
+struct _lwiperf_state_base {
+  /* linked list */
+  lwiperf_state_base_t *next;
+  /* 1=tcp, 0=udp */
+  u8_t tcp;
+  /* 1=server, 0=client */
+  u8_t server;
+  /* master state used to abort sessions (e.g. listener, main client) */
+  lwiperf_state_base_t *related_master_state;
+};
+
+/** Connection handle for a TCP iperf session */
+typedef struct _lwiperf_state_tcp {
+  lwiperf_state_base_t base;
+  struct tcp_pcb *server_pcb;
+  struct tcp_pcb *conn_pcb;
+  u32_t time_started;
+  lwiperf_report_fn report_fn;
+  void *report_arg;
+  u8_t poll_count;
+  u8_t next_num;
+  /* 1=start server when client is closed */
+  u8_t client_tradeoff_mode;
+  u32_t bytes_transferred;
+  lwiperf_settings_t settings;
+  u8_t have_settings_buf;
+  u8_t specific_remote;
+  ip_addr_t remote_addr;
+} lwiperf_state_tcp_t;
+
+/** List of active iperf sessions */
+static lwiperf_state_base_t *lwiperf_all_connections;
+/** A const buffer to send from: we want to measure sending, not copying! */
+static const u8_t lwiperf_txbuf_const[1600] = {
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+};
+
+static err_t lwiperf_tcp_poll(void *arg, struct tcp_pcb *tpcb);
+static void lwiperf_tcp_err(void *arg, err_t err);
+static err_t lwiperf_start_tcp_server_impl(const ip_addr_t *local_addr, u16_t local_port,
+                                           lwiperf_report_fn report_fn, void *report_arg,
+                                           lwiperf_state_base_t *related_master_state, lwiperf_state_tcp_t **state);
+
+
+/** Add an iperf session to the 'active' list */
+static void
+lwiperf_list_add(lwiperf_state_base_t *item)
+{
+  item->next = lwiperf_all_connections;
+  lwiperf_all_connections = item;
+}
+
+/** Remove an iperf session from the 'active' list */
+static void
+lwiperf_list_remove(lwiperf_state_base_t *item)
+{
+  lwiperf_state_base_t *prev = NULL;
+  lwiperf_state_base_t *iter;
+  for (iter = lwiperf_all_connections; iter != NULL; prev = iter, iter = iter->next) {
+    if (iter == item) {
+      if (prev == NULL) {
+        lwiperf_all_connections = iter->next;
+      } else {
+        prev->next = iter->next;
+      }
+      /* @debug: ensure this item is listed only once */
+      for (iter = iter->next; iter != NULL; iter = iter->next) {
+        LWIP_ASSERT("duplicate entry", iter != item);
+      }
+      break;
+    }
+  }
+}
+
+static lwiperf_state_base_t *
+lwiperf_list_find(lwiperf_state_base_t *item)
+{
+  lwiperf_state_base_t *iter;
+  for (iter = lwiperf_all_connections; iter != NULL; iter = iter->next) {
+    if (iter == item) {
+      return item;
+    }
+  }
+  return NULL;
+}
+
+/** Call the report function of an iperf tcp session */
+static void
+lwip_tcp_conn_report(lwiperf_state_tcp_t *conn, enum lwiperf_report_type report_type)
+{
+  if ((conn != NULL) && (conn->report_fn != NULL)) {
+    u32_t now, duration_ms, bandwidth_kbitpsec;
+    now = sys_now();
+    duration_ms = now - conn->time_started;
+    if (duration_ms == 0) {
+      bandwidth_kbitpsec = 0;
+    } else {
+      bandwidth_kbitpsec = (conn->bytes_transferred / duration_ms) * 8U;
+    }
+    conn->report_fn(conn->report_arg, report_type,
+                    &conn->conn_pcb->local_ip, conn->conn_pcb->local_port,
+                    &conn->conn_pcb->remote_ip, conn->conn_pcb->remote_port,
+                    conn->bytes_transferred, duration_ms, bandwidth_kbitpsec);
+  }
+}
+
+/** Close an iperf tcp session */
+static void
+lwiperf_tcp_close(lwiperf_state_tcp_t *conn, enum lwiperf_report_type report_type)
+{
+  err_t err;
+
+  lwiperf_list_remove(&conn->base);
+  lwip_tcp_conn_report(conn, report_type);
+  if (conn->conn_pcb != NULL) {
+    tcp_arg(conn->conn_pcb, NULL);
+    tcp_poll(conn->conn_pcb, NULL, 0);
+    tcp_sent(conn->conn_pcb, NULL);
+    tcp_recv(conn->conn_pcb, NULL);
+    tcp_err(conn->conn_pcb, NULL);
+    err = tcp_close(conn->conn_pcb);
+    if (err != ERR_OK) {
+      /* don't want to wait for free memory here... */
+      tcp_abort(conn->conn_pcb);
+    }
+  } else {
+    /* no conn pcb, this is the listener pcb */
+    err = tcp_close(conn->server_pcb);
+    LWIP_ASSERT("error", err == ERR_OK);
+  }
+  LWIPERF_FREE(lwiperf_state_tcp_t, conn);
+}
+
+/** Try to send more data on an iperf tcp session */
+static err_t
+lwiperf_tcp_client_send_more(lwiperf_state_tcp_t *conn)
+{
+  int send_more;
+  err_t err;
+  u16_t txlen;
+  u16_t txlen_max;
+  void *txptr;
+  u8_t apiflags;
+
+  LWIP_ASSERT("conn invalid", (conn != NULL) && conn->base.tcp && (conn->base.server == 0));
+
+  do {
+    send_more = 0;
+    if (conn->settings.amount & PP_HTONL(0x80000000)) {
+      /* this session is time-limited */
+      u32_t now = sys_now();
+      u32_t diff_ms = now - conn->time_started;
+      u32_t time = (u32_t) - (s32_t)lwip_htonl(conn->settings.amount);
+      u32_t time_ms = time * 10;
+      if (diff_ms >= time_ms) {
+        /* time specified by the client is over -> close the connection */
+        lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_CLIENT);
+        return ERR_OK;
+      }
+    } else {
+      /* this session is byte-limited */
+      u32_t amount_bytes = lwip_htonl(conn->settings.amount);
+      /* @todo: this can send up to 1*MSS more than requested... */
+      if (amount_bytes >= conn->bytes_transferred) {
+        /* all requested bytes transferred -> close the connection */
+        lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_CLIENT);
+        return ERR_OK;
+      }
+    }
+
+    if (conn->bytes_transferred < 24) {
+      /* transmit the settings a first time */
+      txptr = &((u8_t *)&conn->settings)[conn->bytes_transferred];
+      txlen_max = (u16_t)(24 - conn->bytes_transferred);
+      apiflags = TCP_WRITE_FLAG_COPY;
+    } else if (conn->bytes_transferred < 48) {
+      /* transmit the settings a second time */
+      txptr = &((u8_t *)&conn->settings)[conn->bytes_transferred - 24];
+      txlen_max = (u16_t)(48 - conn->bytes_transferred);
+      apiflags = TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE;
+      send_more = 1;
+    } else {
+      /* transmit data */
+      /* @todo: every x bytes, transmit the settings again */
+      txptr = LWIP_CONST_CAST(void *, &lwiperf_txbuf_const[conn->bytes_transferred % 10]);
+      txlen_max = TCP_MSS;
+      if (conn->bytes_transferred == 48) { /* @todo: fix this for intermediate settings, too */
+        txlen_max = TCP_MSS - 24;
+      }
+      apiflags = 0; /* no copying needed */
+      send_more = 1;
+    }
+    txlen = txlen_max;
+    do {
+      err = tcp_write(conn->conn_pcb, txptr, txlen, apiflags);
+      if (err ==  ERR_MEM) {
+        txlen /= 2;
+      }
+    } while ((err == ERR_MEM) && (txlen >= (TCP_MSS / 2)));
+
+    if (err == ERR_OK) {
+      conn->bytes_transferred += txlen;
+    } else {
+      send_more = 0;
+    }
+  } while (send_more);
+
+  tcp_output(conn->conn_pcb);
+  return ERR_OK;
+}
+
+/** TCP sent callback, try to send more data */
+static err_t
+lwiperf_tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
+{
+  lwiperf_state_tcp_t *conn = (lwiperf_state_tcp_t *)arg;
+  /* @todo: check 'len' (e.g. to time ACK of all data)? for now, we just send more... */
+  LWIP_ASSERT("invalid conn", conn->conn_pcb == tpcb);
+  LWIP_UNUSED_ARG(tpcb);
+  LWIP_UNUSED_ARG(len);
+
+  conn->poll_count = 0;
+
+  return lwiperf_tcp_client_send_more(conn);
+}
+
+/** TCP connected callback (active connection), send data now */
+static err_t
+lwiperf_tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
+{
+  lwiperf_state_tcp_t *conn = (lwiperf_state_tcp_t *)arg;
+  LWIP_ASSERT("invalid conn", conn->conn_pcb == tpcb);
+  LWIP_UNUSED_ARG(tpcb);
+  if (err != ERR_OK) {
+    lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE);
+    return ERR_OK;
+  }
+  conn->poll_count = 0;
+  conn->time_started = sys_now();
+  return lwiperf_tcp_client_send_more(conn);
+}
+
+/** Start TCP connection back to the client (either parallel or after the
+ * receive test has finished.
+ */
+static err_t
+lwiperf_tx_start_impl(const ip_addr_t *remote_ip, u16_t remote_port, lwiperf_settings_t *settings, lwiperf_report_fn report_fn,
+                      void *report_arg, lwiperf_state_base_t *related_master_state, lwiperf_state_tcp_t **new_conn)
+{
+  err_t err;
+  lwiperf_state_tcp_t *client_conn;
+  struct tcp_pcb *newpcb;
+  ip_addr_t remote_addr;
+
+  LWIP_ASSERT("remote_ip != NULL", remote_ip != NULL);
+  LWIP_ASSERT("remote_ip != NULL", settings != NULL);
+  LWIP_ASSERT("new_conn != NULL", new_conn != NULL);
+  *new_conn = NULL;
+
+  client_conn = (lwiperf_state_tcp_t *)LWIPERF_ALLOC(lwiperf_state_tcp_t);
+  if (client_conn == NULL) {
+    return ERR_MEM;
+  }
+  newpcb = tcp_new_ip_type(IP_GET_TYPE(remote_ip));
+  if (newpcb == NULL) {
+    LWIPERF_FREE(lwiperf_state_tcp_t, client_conn);
+    return ERR_MEM;
+  }
+  memset(client_conn, 0, sizeof(lwiperf_state_tcp_t));
+  client_conn->base.tcp = 1;
+  client_conn->base.related_master_state = related_master_state;
+  client_conn->conn_pcb = newpcb;
+  client_conn->time_started = sys_now(); /* @todo: set this again on 'connected' */
+  client_conn->report_fn = report_fn;
+  client_conn->report_arg = report_arg;
+  client_conn->next_num = 4; /* initial nr is '4' since the header has 24 byte */
+  client_conn->bytes_transferred = 0;
+  memcpy(&client_conn->settings, settings, sizeof(*settings));
+  client_conn->have_settings_buf = 1;
+
+  tcp_arg(newpcb, client_conn);
+  tcp_sent(newpcb, lwiperf_tcp_client_sent);
+  tcp_poll(newpcb, lwiperf_tcp_poll, 2U);
+  tcp_err(newpcb, lwiperf_tcp_err);
+
+  ip_addr_copy(remote_addr, *remote_ip);
+
+  err = tcp_connect(newpcb, &remote_addr, remote_port, lwiperf_tcp_client_connected);
+  if (err != ERR_OK) {
+    lwiperf_tcp_close(client_conn, LWIPERF_TCP_ABORTED_LOCAL);
+    return err;
+  }
+  lwiperf_list_add(&client_conn->base);
+  *new_conn = client_conn;
+  return ERR_OK;
+}
+
+static err_t
+lwiperf_tx_start_passive(lwiperf_state_tcp_t *conn)
+{
+  err_t ret;
+  lwiperf_state_tcp_t *new_conn = NULL;
+  u16_t remote_port = (u16_t)lwip_htonl(conn->settings.remote_port);
+
+  ret = lwiperf_tx_start_impl(&conn->conn_pcb->remote_ip, remote_port, &conn->settings, conn->report_fn, conn->report_arg,
+    conn->base.related_master_state, &new_conn);
+  if (ret == ERR_OK) {
+    LWIP_ASSERT("new_conn != NULL", new_conn != NULL);
+    new_conn->settings.flags = 0; /* prevent the remote side starting back as client again */
+  }
+  return ret;
+}
+
+/** Receive data on an iperf tcp session */
+static err_t
+lwiperf_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
+{
+  u8_t tmp;
+  u16_t tot_len;
+  u32_t packet_idx;
+  struct pbuf *q;
+  lwiperf_state_tcp_t *conn = (lwiperf_state_tcp_t *)arg;
+
+  LWIP_ASSERT("pcb mismatch", conn->conn_pcb == tpcb);
+  LWIP_UNUSED_ARG(tpcb);
+
+  if (err != ERR_OK) {
+    lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE);
+    return ERR_OK;
+  }
+  if (p == NULL) {
+    /* connection closed -> test done */
+    if (conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST)) {
+      if ((conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_NOW)) == 0) {
+        /* client requested transmission after end of test */
+        lwiperf_tx_start_passive(conn);
+      }
+    }
+    lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_SERVER);
+    return ERR_OK;
+  }
+  tot_len = p->tot_len;
+
+  conn->poll_count = 0;
+
+  if ((!conn->have_settings_buf) || ((conn->bytes_transferred - 24) % (1024 * 128) == 0)) {
+    /* wait for 24-byte header */
+    if (p->tot_len < sizeof(lwiperf_settings_t)) {
+      lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR);
+      pbuf_free(p);
+      return ERR_OK;
+    }
+    if (!conn->have_settings_buf) {
+      if (pbuf_copy_partial(p, &conn->settings, sizeof(lwiperf_settings_t), 0) != sizeof(lwiperf_settings_t)) {
+        lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL);
+        pbuf_free(p);
+        return ERR_OK;
+      }
+      conn->have_settings_buf = 1;
+      if (conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST)) {
+        if (conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_NOW)) {
+          /* client requested parallel transmission test */
+          err_t err2 = lwiperf_tx_start_passive(conn);
+          if (err2 != ERR_OK) {
+            lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_TXERROR);
+            pbuf_free(p);
+            return ERR_OK;
+          }
+        }
+      }
+    } else {
+      if (conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST)) {
+        if (pbuf_memcmp(p, 0, &conn->settings, sizeof(lwiperf_settings_t)) != 0) {
+          lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR);
+          pbuf_free(p);
+          return ERR_OK;
+        }
+      }
+    }
+    conn->bytes_transferred += sizeof(lwiperf_settings_t);
+    if (conn->bytes_transferred <= 24) {
+      conn->time_started = sys_now();
+      tcp_recved(tpcb, p->tot_len);
+      pbuf_free(p);
+      return ERR_OK;
+    }
+    conn->next_num = 4; /* 24 bytes received... */
+    tmp = pbuf_remove_header(p, 24);
+    LWIP_ASSERT("pbuf_remove_header failed", tmp == 0);
+    LWIP_UNUSED_ARG(tmp); /* for LWIP_NOASSERT */
+  }
+
+  packet_idx = 0;
+  for (q = p; q != NULL; q = q->next) {
+#if LWIPERF_CHECK_RX_DATA
+    const u8_t *payload = (const u8_t *)q->payload;
+    u16_t i;
+    for (i = 0; i < q->len; i++) {
+      u8_t val = payload[i];
+      u8_t num = val - '0';
+      if (num == conn->next_num) {
+        conn->next_num++;
+        if (conn->next_num == 10) {
+          conn->next_num = 0;
+        }
+      } else {
+        lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR);
+        pbuf_free(p);
+        return ERR_OK;
+      }
+    }
+#endif
+    packet_idx += q->len;
+  }
+  LWIP_ASSERT("count mismatch", packet_idx == p->tot_len);
+  conn->bytes_transferred += packet_idx;
+  tcp_recved(tpcb, tot_len);
+  pbuf_free(p);
+  return ERR_OK;
+}
+
+/** Error callback, iperf tcp session aborted */
+static void
+lwiperf_tcp_err(void *arg, err_t err)
+{
+  lwiperf_state_tcp_t *conn = (lwiperf_state_tcp_t *)arg;
+  LWIP_UNUSED_ARG(err);
+  lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE);
+}
+
+/** TCP poll callback, try to send more data */
+static err_t
+lwiperf_tcp_poll(void *arg, struct tcp_pcb *tpcb)
+{
+  lwiperf_state_tcp_t *conn = (lwiperf_state_tcp_t *)arg;
+  LWIP_ASSERT("pcb mismatch", conn->conn_pcb == tpcb);
+  LWIP_UNUSED_ARG(tpcb);
+  if (++conn->poll_count >= LWIPERF_TCP_MAX_IDLE_SEC) {
+    lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL);
+    return ERR_OK; /* lwiperf_tcp_close frees conn */
+  }
+
+  if (!conn->base.server) {
+    lwiperf_tcp_client_send_more(conn);
+  }
+
+  return ERR_OK;
+}
+
+/** This is called when a new client connects for an iperf tcp session */
+static err_t
+lwiperf_tcp_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
+{
+  lwiperf_state_tcp_t *s, *conn;
+  if ((err != ERR_OK) || (newpcb == NULL) || (arg == NULL)) {
+    return ERR_VAL;
+  }
+
+  s = (lwiperf_state_tcp_t *)arg;
+  LWIP_ASSERT("invalid session", s->base.server);
+  LWIP_ASSERT("invalid listen pcb", s->server_pcb != NULL);
+  LWIP_ASSERT("invalid conn pcb", s->conn_pcb == NULL);
+  if (s->specific_remote) {
+    LWIP_ASSERT("s->base.related_master_state != NULL", s->base.related_master_state != NULL);
+    if (!ip_addr_cmp(&newpcb->remote_ip, &s->remote_addr)) {
+      /* this listener belongs to a client session, and this is not the correct remote */
+      return ERR_VAL;
+    }
+  } else {
+    LWIP_ASSERT("s->base.related_master_state == NULL", s->base.related_master_state == NULL);
+  }
+
+  conn = (lwiperf_state_tcp_t *)LWIPERF_ALLOC(lwiperf_state_tcp_t);
+  if (conn == NULL) {
+    return ERR_MEM;
+  }
+  memset(conn, 0, sizeof(lwiperf_state_tcp_t));
+  conn->base.tcp = 1;
+  conn->base.server = 1;
+  conn->base.related_master_state = &s->base;
+  conn->conn_pcb = newpcb;
+  conn->time_started = sys_now();
+  conn->report_fn = s->report_fn;
+  conn->report_arg = s->report_arg;
+
+  /* setup the tcp rx connection */
+  tcp_arg(newpcb, conn);
+  tcp_recv(newpcb, lwiperf_tcp_recv);
+  tcp_poll(newpcb, lwiperf_tcp_poll, 2U);
+  tcp_err(conn->conn_pcb, lwiperf_tcp_err);
+
+  if (s->specific_remote) {
+    /* this listener belongs to a client, so make the client the master of the newly created connection */
+    conn->base.related_master_state = s->base.related_master_state;
+    /* if dual mode or (tradeoff mode AND client is done): close the listener */
+    if (!s->client_tradeoff_mode || !lwiperf_list_find(s->base.related_master_state)) {
+      /* prevent report when closing: this is expected */
+      s->report_fn = NULL;
+      lwiperf_tcp_close(s, LWIPERF_TCP_ABORTED_LOCAL);
+    }
+  }
+  lwiperf_list_add(&conn->base);
+  return ERR_OK;
+}
+
+/**
+ * @ingroup iperf
+ * Start a TCP iperf server on the default TCP port (5001) and listen for
+ * incoming connections from iperf clients.
+ *
+ * @returns a connection handle that can be used to abort the server
+ *          by calling @ref lwiperf_abort()
+ */
+void *
+lwiperf_start_tcp_server_default(lwiperf_report_fn report_fn, void *report_arg)
+{
+  return lwiperf_start_tcp_server(IP_ADDR_ANY, LWIPERF_TCP_PORT_DEFAULT,
+                                  report_fn, report_arg);
+}
+
+/**
+ * @ingroup iperf
+ * Start a TCP iperf server on a specific IP address and port and listen for
+ * incoming connections from iperf clients.
+ *
+ * @returns a connection handle that can be used to abort the server
+ *          by calling @ref lwiperf_abort()
+ */
+void *
+lwiperf_start_tcp_server(const ip_addr_t *local_addr, u16_t local_port,
+                         lwiperf_report_fn report_fn, void *report_arg)
+{
+  err_t err;
+  lwiperf_state_tcp_t *state = NULL;
+
+  err = lwiperf_start_tcp_server_impl(local_addr, local_port, report_fn, report_arg,
+    NULL, &state);
+  if (err == ERR_OK) {
+    return state;
+  }
+  return NULL;
+}
+
+static err_t lwiperf_start_tcp_server_impl(const ip_addr_t *local_addr, u16_t local_port,
+                                           lwiperf_report_fn report_fn, void *report_arg,
+                                           lwiperf_state_base_t *related_master_state, lwiperf_state_tcp_t **state)
+{
+  err_t err;
+  struct tcp_pcb *pcb;
+  lwiperf_state_tcp_t *s;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ASSERT("state != NULL", state != NULL);
+
+  if (local_addr == NULL) {
+    return ERR_ARG;
+  }
+
+  s = (lwiperf_state_tcp_t *)LWIPERF_ALLOC(lwiperf_state_tcp_t);
+  if (s == NULL) {
+    return ERR_MEM;
+  }
+  memset(s, 0, sizeof(lwiperf_state_tcp_t));
+  s->base.tcp = 1;
+  s->base.server = 1;
+  s->base.related_master_state = related_master_state;
+  s->report_fn = report_fn;
+  s->report_arg = report_arg;
+
+  pcb = tcp_new_ip_type(LWIPERF_SERVER_IP_TYPE);
+  if (pcb == NULL) {
+    return ERR_MEM;
+  }
+  err = tcp_bind(pcb, local_addr, local_port);
+  if (err != ERR_OK) {
+    return err;
+  }
+  s->server_pcb = tcp_listen_with_backlog(pcb, 1);
+  if (s->server_pcb == NULL) {
+    if (pcb != NULL) {
+      tcp_close(pcb);
+    }
+    LWIPERF_FREE(lwiperf_state_tcp_t, s);
+    return ERR_MEM;
+  }
+  pcb = NULL;
+
+  tcp_arg(s->server_pcb, s);
+  tcp_accept(s->server_pcb, lwiperf_tcp_accept);
+
+  lwiperf_list_add(&s->base);
+  *state = s;
+  return ERR_OK;
+}
+
+/**
+ * @ingroup iperf
+ * Start a TCP iperf client to the default TCP port (5001).
+ *
+ * @returns a connection handle that can be used to abort the client
+ *          by calling @ref lwiperf_abort()
+ */
+void* lwiperf_start_tcp_client_default(const ip_addr_t* remote_addr,
+                               lwiperf_report_fn report_fn, void* report_arg)
+{
+  return lwiperf_start_tcp_client(remote_addr, LWIPERF_TCP_PORT_DEFAULT, LWIPERF_CLIENT,
+                                  report_fn, report_arg);
+}
+
+/**
+ * @ingroup iperf
+ * Start a TCP iperf client to a specific IP address and port.
+ *
+ * @returns a connection handle that can be used to abort the client
+ *          by calling @ref lwiperf_abort()
+ */
+void* lwiperf_start_tcp_client(const ip_addr_t* remote_addr, u16_t remote_port,
+  enum lwiperf_client_type type, lwiperf_report_fn report_fn, void* report_arg)
+{
+  err_t ret;
+  lwiperf_settings_t settings;
+  lwiperf_state_tcp_t *state = NULL;
+
+  memset(&settings, 0, sizeof(settings));
+  switch (type) {
+  case LWIPERF_CLIENT:
+    /* Unidirectional tx only test */
+    settings.flags = 0;
+    break;
+  case LWIPERF_DUAL:
+    /* Do a bidirectional test simultaneously */
+    settings.flags = htonl(LWIPERF_FLAGS_ANSWER_TEST | LWIPERF_FLAGS_ANSWER_NOW);
+    break;
+  case LWIPERF_TRADEOFF:
+    /* Do a bidirectional test individually */
+    settings.flags = htonl(LWIPERF_FLAGS_ANSWER_TEST);
+    break;
+  default:
+    /* invalid argument */
+    return NULL;
+  }
+  settings.num_threads = htonl(1);
+  settings.remote_port = htonl(LWIPERF_TCP_PORT_DEFAULT);
+  /* TODO: implement passing duration/amount of bytes to transfer */
+  settings.amount = htonl((u32_t)-1000);
+
+  ret = lwiperf_tx_start_impl(remote_addr, remote_port, &settings, report_fn, report_arg, NULL, &state);
+  if (ret == ERR_OK) {
+    LWIP_ASSERT("state != NULL", state != NULL);
+    if (type != LWIPERF_CLIENT) {
+      /* start corresponding server now */
+      lwiperf_state_tcp_t *server = NULL;
+      ret = lwiperf_start_tcp_server_impl(&state->conn_pcb->local_ip, LWIPERF_TCP_PORT_DEFAULT,
+        report_fn, report_arg, (lwiperf_state_base_t *)state, &server);
+      if (ret != ERR_OK) {
+        /* starting server failed, abort client */
+        lwiperf_abort(state);
+        return NULL;
+      }
+      /* make this server accept one connection only */
+      server->specific_remote = 1;
+      server->remote_addr = state->conn_pcb->remote_ip;
+      if (type == LWIPERF_TRADEOFF) {
+        /* tradeoff means that the remote host connects only after the client is done,
+           so keep the listen pcb open until the client is done */
+        server->client_tradeoff_mode = 1;
+      }
+    }
+    return state;
+  }
+  return NULL;
+}
+
+/**
+ * @ingroup iperf
+ * Abort an iperf session (handle returned by lwiperf_start_tcp_server*())
+ */
+void
+lwiperf_abort(void *lwiperf_session)
+{
+  lwiperf_state_base_t *i, *dealloc, *last = NULL;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  for (i = lwiperf_all_connections; i != NULL; ) {
+    if ((i == lwiperf_session) || (i->related_master_state == lwiperf_session)) {
+      dealloc = i;
+      i = i->next;
+      if (last != NULL) {
+        last->next = i;
+      }
+      LWIPERF_FREE(lwiperf_state_tcp_t, dealloc); /* @todo: type? */
+    } else {
+      last = i;
+      i = i->next;
+    }
+  }
+}
+
+#endif /* LWIP_TCP && LWIP_CALLBACK_API */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/mdns/mdns.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/mdns/mdns.c
new file mode 100644 (file)
index 0000000..aad2728
--- /dev/null
@@ -0,0 +1,2407 @@
+/**
+ * @file
+ * MDNS responder implementation
+ *
+ * @defgroup mdns MDNS
+ * @ingroup apps
+ *
+ * RFC 6762 - Multicast DNS\n
+ * RFC 6763 - DNS-Based Service Discovery\n
+ *
+ * @verbinclude mdns.txt
+ *
+ * Things left to implement:
+ * -------------------------
+ *
+ * - Tiebreaking for simultaneous probing
+ * - Sending goodbye messages (zero ttl) - shutdown, DHCP lease about to expire, DHCP turned off...
+ * - Checking that source address of unicast requests are on the same network
+ * - Limiting multicast responses to 1 per second per resource record
+ * - Fragmenting replies if required
+ * - Handling multi-packet known answers
+ * - Individual known answer detection for all local IPv6 addresses
+ * - Dynamic size of outgoing packet
+ */
+
+/*
+ * Copyright (c) 2015 Verisure Innovation AB
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Erik Ekman <erik@kryo.se>
+ *
+ */
+
+#include "lwip/apps/mdns.h"
+#include "lwip/apps/mdns_priv.h"
+#include "lwip/netif.h"
+#include "lwip/udp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/mem.h"
+#include "lwip/prot/dns.h"
+#include "lwip/prot/iana.h"
+#include "lwip/timeouts.h"
+
+#include <string.h>
+
+#if LWIP_MDNS_RESPONDER
+
+#if (LWIP_IPV4 && !LWIP_IGMP)
+#error "If you want to use MDNS with IPv4, you have to define LWIP_IGMP=1 in your lwipopts.h"
+#endif
+#if (LWIP_IPV6 && !LWIP_IPV6_MLD)
+#error "If you want to use MDNS with IPv6, you have to define LWIP_IPV6_MLD=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP)
+#error "If you want to use MDNS, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+
+#if LWIP_IPV4
+#include "lwip/igmp.h"
+/* IPv4 multicast group 224.0.0.251 */
+static const ip_addr_t v4group = DNS_MQUERY_IPV4_GROUP_INIT;
+#endif
+
+#if LWIP_IPV6
+#include "lwip/mld6.h"
+/* IPv6 multicast group FF02::FB */
+static const ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT;
+#endif
+
+#define MDNS_TTL  255
+
+/* Stored offsets to beginning of domain names
+ * Used for compression.
+ */
+#define NUM_DOMAIN_OFFSETS 10
+#define DOMAIN_JUMP_SIZE 2
+#define DOMAIN_JUMP 0xc000
+
+static u8_t mdns_netif_client_id;
+static struct udp_pcb *mdns_pcb;
+#if MDNS_RESP_USENETIF_EXTCALLBACK
+NETIF_DECLARE_EXT_CALLBACK(netif_callback)
+#endif
+static mdns_name_result_cb_t mdns_name_result_cb;
+
+#define NETIF_TO_HOST(netif) (struct mdns_host*)(netif_get_client_data(netif, mdns_netif_client_id))
+
+#define TOPDOMAIN_LOCAL "local"
+
+#define REVERSE_PTR_TOPDOMAIN "arpa"
+#define REVERSE_PTR_V4_DOMAIN "in-addr"
+#define REVERSE_PTR_V6_DOMAIN "ip6"
+
+#define SRV_PRIORITY 0
+#define SRV_WEIGHT   0
+
+/* Payload size allocated for each outgoing UDP packet */
+#define OUTPACKET_SIZE 500
+
+/* Lookup from hostname -> IPv4 */
+#define REPLY_HOST_A            0x01
+/* Lookup from IPv4/v6 -> hostname */
+#define REPLY_HOST_PTR_V4       0x02
+/* Lookup from hostname -> IPv6 */
+#define REPLY_HOST_AAAA         0x04
+/* Lookup from hostname -> IPv6 */
+#define REPLY_HOST_PTR_V6       0x08
+
+/* Lookup for service types */
+#define REPLY_SERVICE_TYPE_PTR  0x10
+/* Lookup for instances of service */
+#define REPLY_SERVICE_NAME_PTR  0x20
+/* Lookup for location of service instance */
+#define REPLY_SERVICE_SRV       0x40
+/* Lookup for text info on service instance */
+#define REPLY_SERVICE_TXT       0x80
+
+#define MDNS_PROBE_DELAY_MS       250
+#define MDNS_PROBE_COUNT          3
+#ifdef LWIP_RAND
+/* first probe timeout SHOULD be random 0-250 ms*/
+#define MDNS_INITIAL_PROBE_DELAY_MS (LWIP_RAND() % MDNS_PROBE_DELAY_MS)
+#else
+#define MDNS_INITIAL_PROBE_DELAY_MS MDNS_PROBE_DELAY_MS
+#endif
+
+#define MDNS_PROBING_NOT_STARTED  0
+#define MDNS_PROBING_ONGOING      1
+#define MDNS_PROBING_COMPLETE     2
+
+static const char *dnssd_protos[] = {
+  "_udp", /* DNSSD_PROTO_UDP */
+  "_tcp", /* DNSSD_PROTO_TCP */
+};
+
+/** Description of a service */
+struct mdns_service {
+  /** TXT record to answer with */
+  struct mdns_domain txtdata;
+  /** Name of service, like 'myweb' */
+  char name[MDNS_LABEL_MAXLEN + 1];
+  /** Type of service, like '_http' */
+  char service[MDNS_LABEL_MAXLEN + 1];
+  /** Callback function and userdata
+   * to update txtdata buffer */
+  service_get_txt_fn_t txt_fn;
+  void *txt_userdata;
+  /** TTL in seconds of SRV/TXT replies */
+  u32_t dns_ttl;
+  /** Protocol, TCP or UDP */
+  u16_t proto;
+  /** Port of the service */
+  u16_t port;
+};
+
+/** Description of a host/netif */
+struct mdns_host {
+  /** Hostname */
+  char name[MDNS_LABEL_MAXLEN + 1];
+  /** Pointer to services */
+  struct mdns_service *services[MDNS_MAX_SERVICES];
+  /** TTL in seconds of A/AAAA/PTR replies */
+  u32_t dns_ttl;
+  /** Number of probes sent for the current name */
+  u8_t probes_sent;
+  /** State in probing sequence */
+  u8_t probing_state;
+};
+
+/** Information about received packet */
+struct mdns_packet {
+  /** Sender IP/port */
+  ip_addr_t source_addr;
+  u16_t source_port;
+  /** If packet was received unicast */
+  u16_t recv_unicast;
+  /** Netif that received the packet */
+  struct netif *netif;
+  /** Packet data */
+  struct pbuf *pbuf;
+  /** Current parsing offset in packet */
+  u16_t parse_offset;
+  /** Identifier. Used in legacy queries */
+  u16_t tx_id;
+  /** Number of questions in packet,
+   *  read from packet header */
+  u16_t questions;
+  /** Number of unparsed questions */
+  u16_t questions_left;
+  /** Number of answers in packet,
+   *  (sum of normal, authoritative and additional answers)
+   *  read from packet header */
+  u16_t answers;
+  /** Number of unparsed answers */
+  u16_t answers_left;
+};
+
+/** Information about outgoing packet */
+struct mdns_outpacket {
+  /** Netif to send the packet on */
+  struct netif *netif;
+  /** Packet data */
+  struct pbuf *pbuf;
+  /** Current write offset in packet */
+  u16_t write_offset;
+  /** Identifier. Used in legacy queries */
+  u16_t tx_id;
+  /** Destination IP/port if sent unicast */
+  ip_addr_t dest_addr;
+  u16_t dest_port;
+  /** Number of questions written */
+  u16_t questions;
+  /** Number of normal answers written */
+  u16_t answers;
+  /** Number of authoritative answers written */
+  u16_t authoritative;
+  /** Number of additional answers written */
+  u16_t additional;
+  /** Offsets for written domain names in packet.
+   *  Used for compression */
+  u16_t domain_offsets[NUM_DOMAIN_OFFSETS];
+  /** If all answers in packet should set cache_flush bit */
+  u8_t cache_flush;
+  /** If reply should be sent unicast */
+  u8_t unicast_reply;
+  /** If legacy query. (tx_id needed, and write
+   *  question again in reply before answer) */
+  u8_t legacy_query;
+  /* Reply bitmask for host information */
+  u8_t host_replies;
+  /* Bitmask for which reverse IPv6 hosts to answer */
+  u8_t host_reverse_v6_replies;
+  /* Reply bitmask per service */
+  u8_t serv_replies[MDNS_MAX_SERVICES];
+};
+
+/** Domain, type and class.
+ *  Shared between questions and answers */
+struct mdns_rr_info {
+  struct mdns_domain domain;
+  u16_t type;
+  u16_t klass;
+};
+
+struct mdns_question {
+  struct mdns_rr_info info;
+  /** unicast reply requested */
+  u16_t unicast;
+};
+
+struct mdns_answer {
+  struct mdns_rr_info info;
+  /** cache flush command bit */
+  u16_t cache_flush;
+  /* Validity time in seconds */
+  u32_t ttl;
+  /** Length of variable answer */
+  u16_t rd_length;
+  /** Offset of start of variable answer in packet */
+  u16_t rd_offset;
+};
+
+static err_t mdns_send_outpacket(struct mdns_outpacket *outpkt, u8_t flags);
+static void mdns_probe(void* arg);
+
+static err_t
+mdns_domain_add_label_base(struct mdns_domain *domain, u8_t len)
+{
+  if (len > MDNS_LABEL_MAXLEN) {
+    return ERR_VAL;
+  }
+  if (len > 0 && (1 + len + domain->length >= MDNS_DOMAIN_MAXLEN)) {
+    return ERR_VAL;
+  }
+  /* Allow only zero marker on last byte */
+  if (len == 0 && (1 + domain->length > MDNS_DOMAIN_MAXLEN)) {
+    return ERR_VAL;
+  }
+  domain->name[domain->length] = len;
+  domain->length++;
+  return ERR_OK;
+}
+
+/**
+ * Add a label part to a domain
+ * @param domain The domain to add a label to
+ * @param label The label to add, like &lt;hostname&gt;, 'local', 'com' or ''
+ * @param len The length of the label
+ * @return ERR_OK on success, an err_t otherwise if label too long
+ */
+err_t
+mdns_domain_add_label(struct mdns_domain *domain, const char *label, u8_t len)
+{
+  err_t err = mdns_domain_add_label_base(domain, len);
+  if (err != ERR_OK) {
+    return err;
+  }
+  if (len) {
+    MEMCPY(&domain->name[domain->length], label, len);
+    domain->length += len;
+  }
+  return ERR_OK;
+}
+
+/**
+ * Add a label part to a domain (@see mdns_domain_add_label but copy directly from pbuf)
+ */
+static err_t
+mdns_domain_add_label_pbuf(struct mdns_domain *domain, const struct pbuf *p, u16_t offset, u8_t len)
+{
+  err_t err = mdns_domain_add_label_base(domain, len);
+  if (err != ERR_OK) {
+    return err;
+  }
+  if (len) {
+    if (pbuf_copy_partial(p, &domain->name[domain->length], len, offset) != len) {
+      /* take back the ++ done before */
+      domain->length--;
+      return ERR_ARG;
+    }
+    domain->length += len;
+  }
+  return ERR_OK;
+}
+
+/**
+ * Internal readname function with max 6 levels of recursion following jumps
+ * while decompressing name
+ */
+static u16_t
+mdns_readname_loop(struct pbuf *p, u16_t offset, struct mdns_domain *domain, unsigned depth)
+{
+  u8_t c;
+
+  do {
+    if (depth > 5) {
+      /* Too many jumps */
+      return MDNS_READNAME_ERROR;
+    }
+
+    c = pbuf_get_at(p, offset);
+    offset++;
+
+    /* is this a compressed label? */
+    if ((c & 0xc0) == 0xc0) {
+      u16_t jumpaddr;
+      if (offset >= p->tot_len) {
+        /* Make sure both jump bytes fit in the packet */
+        return MDNS_READNAME_ERROR;
+      }
+      jumpaddr = (((c & 0x3f) << 8) | (pbuf_get_at(p, offset) & 0xff));
+      offset++;
+      if (jumpaddr >= SIZEOF_DNS_HDR && jumpaddr < p->tot_len) {
+        u16_t res;
+        /* Recursive call, maximum depth will be checked */
+        res = mdns_readname_loop(p, jumpaddr, domain, depth + 1);
+        /* Dont return offset since new bytes were not read (jumped to somewhere in packet) */
+        if (res == MDNS_READNAME_ERROR) {
+          return res;
+        }
+      } else {
+        return MDNS_READNAME_ERROR;
+      }
+      break;
+    }
+
+    /* normal label */
+    if (c <= MDNS_LABEL_MAXLEN) {
+      err_t res;
+
+      if (c + domain->length >= MDNS_DOMAIN_MAXLEN) {
+        return MDNS_READNAME_ERROR;
+      }
+      res = mdns_domain_add_label_pbuf(domain, p, offset, c);
+      if (res != ERR_OK) {
+        return MDNS_READNAME_ERROR;
+      }
+      offset += c;
+    } else {
+      /* bad length byte */
+      return MDNS_READNAME_ERROR;
+    }
+  } while (c != 0);
+
+  return offset;
+}
+
+/**
+ * Read possibly compressed domain name from packet buffer
+ * @param p The packet
+ * @param offset start position of domain name in packet
+ * @param domain The domain name destination
+ * @return The new offset after the domain, or MDNS_READNAME_ERROR
+ *         if reading failed
+ */
+u16_t
+mdns_readname(struct pbuf *p, u16_t offset, struct mdns_domain *domain)
+{
+  memset(domain, 0, sizeof(struct mdns_domain));
+  return mdns_readname_loop(p, offset, domain, 0);
+}
+
+/**
+ * Print domain name to debug output
+ * @param domain The domain name
+ */
+static void
+mdns_domain_debug_print(struct mdns_domain *domain)
+{
+  u8_t *src = domain->name;
+  u8_t i;
+
+  while (*src) {
+    u8_t label_len = *src;
+    src++;
+    for (i = 0; i < label_len; i++) {
+      LWIP_DEBUGF(MDNS_DEBUG, ("%c", src[i]));
+    }
+    src += label_len;
+    LWIP_DEBUGF(MDNS_DEBUG, ("."));
+  }
+}
+
+/**
+ * Return 1 if contents of domains match (case-insensitive)
+ * @param a Domain name to compare 1
+ * @param b Domain name to compare 2
+ * @return 1 if domains are equal ignoring case, 0 otherwise
+ */
+int
+mdns_domain_eq(struct mdns_domain *a, struct mdns_domain *b)
+{
+  u8_t *ptra, *ptrb;
+  u8_t len;
+  int res;
+
+  if (a->length != b->length) {
+    return 0;
+  }
+
+  ptra = a->name;
+  ptrb = b->name;
+  while (*ptra && *ptrb && ptra < &a->name[a->length]) {
+    if (*ptra != *ptrb) {
+      return 0;
+    }
+    len = *ptra;
+    ptra++;
+    ptrb++;
+    res = lwip_strnicmp((char *) ptra, (char *) ptrb, len);
+    if (res != 0) {
+      return 0;
+    }
+    ptra += len;
+    ptrb += len;
+  }
+  if (*ptra != *ptrb && ptra < &a->name[a->length]) {
+    return 0;
+  }
+  return 1;
+}
+
+/**
+ * Call user supplied function to setup TXT data
+ * @param service The service to build TXT record for
+ */
+static void
+mdns_prepare_txtdata(struct mdns_service *service)
+{
+  memset(&service->txtdata, 0, sizeof(struct mdns_domain));
+  if (service->txt_fn) {
+    service->txt_fn(service, service->txt_userdata);
+  }
+}
+
+#if LWIP_IPV4
+/**
+ * Build domain for reverse lookup of IPv4 address
+ * like 12.0.168.192.in-addr.arpa. for 192.168.0.12
+ * @param domain Where to write the domain name
+ * @param addr Pointer to an IPv4 address to encode
+ * @return ERR_OK if domain was written, an err_t otherwise
+ */
+static err_t
+mdns_build_reverse_v4_domain(struct mdns_domain *domain, const ip4_addr_t *addr)
+{
+  int i;
+  err_t res;
+  const u8_t *ptr;
+
+  LWIP_UNUSED_ARG(res);
+  if (!domain || !addr) {
+    return ERR_ARG;
+  }
+  memset(domain, 0, sizeof(struct mdns_domain));
+  ptr = (const u8_t *) addr;
+  for (i = sizeof(ip4_addr_t) - 1; i >= 0; i--) {
+    char buf[4];
+    u8_t val = ptr[i];
+
+    lwip_itoa(buf, sizeof(buf), val);
+    res = mdns_domain_add_label(domain, buf, (u8_t)strlen(buf));
+    LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
+  }
+  res = mdns_domain_add_label(domain, REVERSE_PTR_V4_DOMAIN, (u8_t)(sizeof(REVERSE_PTR_V4_DOMAIN) - 1));
+  LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
+  res = mdns_domain_add_label(domain, REVERSE_PTR_TOPDOMAIN, (u8_t)(sizeof(REVERSE_PTR_TOPDOMAIN) - 1));
+  LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
+  res = mdns_domain_add_label(domain, NULL, 0);
+  LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
+
+  return ERR_OK;
+}
+#endif
+
+#if LWIP_IPV6
+/**
+ * Build domain for reverse lookup of IP address
+ * like b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. for 2001:db8::567:89ab
+ * @param domain Where to write the domain name
+ * @param addr Pointer to an IPv6 address to encode
+ * @return ERR_OK if domain was written, an err_t otherwise
+ */
+static err_t
+mdns_build_reverse_v6_domain(struct mdns_domain *domain, const ip6_addr_t *addr)
+{
+  int i;
+  err_t res;
+  const u8_t *ptr;
+  LWIP_UNUSED_ARG(res);
+  if (!domain || !addr) {
+    return ERR_ARG;
+  }
+  memset(domain, 0, sizeof(struct mdns_domain));
+  ptr = (const u8_t *) addr;
+  for (i = sizeof(ip6_addr_p_t) - 1; i >= 0; i--) {
+    char buf;
+    u8_t byte = ptr[i];
+    int j;
+    for (j = 0; j < 2; j++) {
+      if ((byte & 0x0F) < 0xA) {
+        buf = '0' + (byte & 0x0F);
+      } else {
+        buf = 'a' + (byte & 0x0F) - 0xA;
+      }
+      res = mdns_domain_add_label(domain, &buf, sizeof(buf));
+      LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
+      byte >>= 4;
+    }
+  }
+  res = mdns_domain_add_label(domain, REVERSE_PTR_V6_DOMAIN, (u8_t)(sizeof(REVERSE_PTR_V6_DOMAIN) - 1));
+  LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
+  res = mdns_domain_add_label(domain, REVERSE_PTR_TOPDOMAIN, (u8_t)(sizeof(REVERSE_PTR_TOPDOMAIN) - 1));
+  LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
+  res = mdns_domain_add_label(domain, NULL, 0);
+  LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
+
+  return ERR_OK;
+}
+#endif
+
+/* Add .local. to domain */
+static err_t
+mdns_add_dotlocal(struct mdns_domain *domain)
+{
+  err_t res = mdns_domain_add_label(domain, TOPDOMAIN_LOCAL, (u8_t)(sizeof(TOPDOMAIN_LOCAL) - 1));
+  LWIP_UNUSED_ARG(res);
+  LWIP_ERROR("mdns_add_dotlocal: Failed to add label", (res == ERR_OK), return res);
+  return mdns_domain_add_label(domain, NULL, 0);
+}
+
+/**
+ * Build the <hostname>.local. domain name
+ * @param domain Where to write the domain name
+ * @param mdns TMDNS netif descriptor.
+ * @return ERR_OK if domain <hostname>.local. was written, an err_t otherwise
+ */
+static err_t
+mdns_build_host_domain(struct mdns_domain *domain, struct mdns_host *mdns)
+{
+  err_t res;
+  LWIP_UNUSED_ARG(res);
+  memset(domain, 0, sizeof(struct mdns_domain));
+  LWIP_ERROR("mdns_build_host_domain: mdns != NULL", (mdns != NULL), return ERR_VAL);
+  res = mdns_domain_add_label(domain, mdns->name, (u8_t)strlen(mdns->name));
+  LWIP_ERROR("mdns_build_host_domain: Failed to add label", (res == ERR_OK), return res);
+  return mdns_add_dotlocal(domain);
+}
+
+/**
+ * Build the lookup-all-services special DNS-SD domain name
+ * @param domain Where to write the domain name
+ * @return ERR_OK if domain _services._dns-sd._udp.local. was written, an err_t otherwise
+ */
+static err_t
+mdns_build_dnssd_domain(struct mdns_domain *domain)
+{
+  err_t res;
+  LWIP_UNUSED_ARG(res);
+  memset(domain, 0, sizeof(struct mdns_domain));
+  res = mdns_domain_add_label(domain, "_services", (u8_t)(sizeof("_services") - 1));
+  LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
+  res = mdns_domain_add_label(domain, "_dns-sd", (u8_t)(sizeof("_dns-sd") - 1));
+  LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
+  res = mdns_domain_add_label(domain, dnssd_protos[DNSSD_PROTO_UDP], (u8_t)strlen(dnssd_protos[DNSSD_PROTO_UDP]));
+  LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
+  return mdns_add_dotlocal(domain);
+}
+
+/**
+ * Build domain name for a service
+ * @param domain Where to write the domain name
+ * @param service The service struct, containing service name, type and protocol
+ * @param include_name Whether to include the service name in the domain
+ * @return ERR_OK if domain was written. If service name is included,
+ *         <name>.<type>.<proto>.local. will be written, otherwise <type>.<proto>.local.
+ *         An err_t is returned on error.
+ */
+static err_t
+mdns_build_service_domain(struct mdns_domain *domain, struct mdns_service *service, int include_name)
+{
+  err_t res;
+  LWIP_UNUSED_ARG(res);
+  memset(domain, 0, sizeof(struct mdns_domain));
+  if (include_name) {
+    res = mdns_domain_add_label(domain, service->name, (u8_t)strlen(service->name));
+    LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
+  }
+  res = mdns_domain_add_label(domain, service->service, (u8_t)strlen(service->service));
+  LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
+  res = mdns_domain_add_label(domain, dnssd_protos[service->proto], (u8_t)strlen(dnssd_protos[service->proto]));
+  LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
+  return mdns_add_dotlocal(domain);
+}
+
+/**
+ * Check which replies we should send for a host/netif based on question
+ * @param netif The network interface that received the question
+ * @param rr Domain/type/class from a question
+ * @param reverse_v6_reply Bitmask of which IPv6 addresses to send reverse PTRs for
+ *                         if reply bit has REPLY_HOST_PTR_V6 set
+ * @return Bitmask of which replies to send
+ */
+static int
+check_host(struct netif *netif, struct mdns_rr_info *rr, u8_t *reverse_v6_reply)
+{
+  err_t res;
+  int replies = 0;
+  struct mdns_domain mydomain;
+
+  LWIP_UNUSED_ARG(reverse_v6_reply); /* if ipv6 is disabled */
+
+  if (rr->klass != DNS_RRCLASS_IN && rr->klass != DNS_RRCLASS_ANY) {
+    /* Invalid class */
+    return replies;
+  }
+
+  /* Handle PTR for our addresses */
+  if (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY) {
+#if LWIP_IPV6
+    int i;
+    for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+      if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
+        res = mdns_build_reverse_v6_domain(&mydomain, netif_ip6_addr(netif, i));
+        if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
+          replies |= REPLY_HOST_PTR_V6;
+          /* Mark which addresses where requested */
+          if (reverse_v6_reply) {
+            *reverse_v6_reply |= (1 << i);
+          }
+        }
+      }
+    }
+#endif
+#if LWIP_IPV4
+    if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+      res = mdns_build_reverse_v4_domain(&mydomain, netif_ip4_addr(netif));
+      if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
+        replies |= REPLY_HOST_PTR_V4;
+      }
+    }
+#endif
+  }
+
+  res = mdns_build_host_domain(&mydomain, NETIF_TO_HOST(netif));
+  /* Handle requests for our hostname */
+  if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
+    /* TODO return NSEC if unsupported protocol requested */
+#if LWIP_IPV4
+    if (!ip4_addr_isany_val(*netif_ip4_addr(netif))
+        && (rr->type == DNS_RRTYPE_A || rr->type == DNS_RRTYPE_ANY)) {
+      replies |= REPLY_HOST_A;
+    }
+#endif
+#if LWIP_IPV6
+    if (rr->type == DNS_RRTYPE_AAAA || rr->type == DNS_RRTYPE_ANY) {
+      replies |= REPLY_HOST_AAAA;
+    }
+#endif
+  }
+
+  return replies;
+}
+
+/**
+ * Check which replies we should send for a service based on question
+ * @param service A registered MDNS service
+ * @param rr Domain/type/class from a question
+ * @return Bitmask of which replies to send
+ */
+static int
+check_service(struct mdns_service *service, struct mdns_rr_info *rr)
+{
+  err_t res;
+  int replies = 0;
+  struct mdns_domain mydomain;
+
+  if (rr->klass != DNS_RRCLASS_IN && rr->klass != DNS_RRCLASS_ANY) {
+    /* Invalid class */
+    return 0;
+  }
+
+  res = mdns_build_dnssd_domain(&mydomain);
+  if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain) &&
+      (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY)) {
+    /* Request for all service types */
+    replies |= REPLY_SERVICE_TYPE_PTR;
+  }
+
+  res = mdns_build_service_domain(&mydomain, service, 0);
+  if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain) &&
+      (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY)) {
+    /* Request for the instance of my service */
+    replies |= REPLY_SERVICE_NAME_PTR;
+  }
+
+  res = mdns_build_service_domain(&mydomain, service, 1);
+  if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
+    /* Request for info about my service */
+    if (rr->type == DNS_RRTYPE_SRV || rr->type == DNS_RRTYPE_ANY) {
+      replies |= REPLY_SERVICE_SRV;
+    }
+    if (rr->type == DNS_RRTYPE_TXT || rr->type == DNS_RRTYPE_ANY) {
+      replies |= REPLY_SERVICE_TXT;
+    }
+  }
+
+  return replies;
+}
+
+/**
+ * Return bytes needed to write before jump for best result of compressing supplied domain
+ * against domain in outpacket starting at specified offset.
+ * If a match is found, offset is updated to where to jump to
+ * @param pbuf Pointer to pbuf with the partially constructed DNS packet
+ * @param offset Start position of a domain written earlier. If this location is suitable
+ *               for compression, the pointer is updated to where in the domain to jump to.
+ * @param domain The domain to write
+ * @return Number of bytes to write of the new domain before writing a jump to the offset.
+ *         If compression can not be done against this previous domain name, the full new
+ *         domain length is returned.
+ */
+u16_t
+mdns_compress_domain(struct pbuf *pbuf, u16_t *offset, struct mdns_domain *domain)
+{
+  struct mdns_domain target;
+  u16_t target_end;
+  u8_t target_len;
+  u8_t writelen = 0;
+  u8_t *ptr;
+  if (pbuf == NULL) {
+    return domain->length;
+  }
+  target_end = mdns_readname(pbuf, *offset, &target);
+  if (target_end == MDNS_READNAME_ERROR) {
+    return domain->length;
+  }
+  target_len = (u8_t)(target_end - *offset);
+  ptr = domain->name;
+  while (writelen < domain->length) {
+    u8_t domainlen = (u8_t)(domain->length - writelen);
+    u8_t labellen;
+    if (domainlen <= target.length && domainlen > DOMAIN_JUMP_SIZE) {
+      /* Compare domains if target is long enough, and we have enough left of the domain */
+      u8_t targetpos = (u8_t)(target.length - domainlen);
+      if ((targetpos + DOMAIN_JUMP_SIZE) >= target_len) {
+        /* We are checking at or beyond a jump in the original, stop looking */
+        break;
+      }
+      if (target.length >= domainlen &&
+          memcmp(&domain->name[writelen], &target.name[targetpos], domainlen) == 0) {
+        *offset += targetpos;
+        return writelen;
+      }
+    }
+    /* Skip to next label in domain */
+    labellen = *ptr;
+    writelen += 1 + labellen;
+    ptr += 1 + labellen;
+  }
+  /* Nothing found */
+  return domain->length;
+}
+
+/**
+ * Write domain to outpacket. Compression will be attempted,
+ * unless domain->skip_compression is set.
+ * @param outpkt The outpacket to write to
+ * @param domain The domain name to write
+ * @return ERR_OK on success, an err_t otherwise
+ */
+static err_t
+mdns_write_domain(struct mdns_outpacket *outpkt, struct mdns_domain *domain)
+{
+  int i;
+  err_t res;
+  u16_t writelen = domain->length;
+  u16_t jump_offset = 0;
+  u16_t jump;
+
+  if (!domain->skip_compression) {
+    for (i = 0; i < NUM_DOMAIN_OFFSETS; i++) {
+      u16_t offset = outpkt->domain_offsets[i];
+      if (offset) {
+        u16_t len = mdns_compress_domain(outpkt->pbuf, &offset, domain);
+        if (len < writelen) {
+          writelen = len;
+          jump_offset = offset;
+        }
+      }
+    }
+  }
+
+  if (writelen) {
+    /* Write uncompressed part of name */
+    res = pbuf_take_at(outpkt->pbuf, domain->name, writelen, outpkt->write_offset);
+    if (res != ERR_OK) {
+      return res;
+    }
+
+    /* Store offset of this new domain */
+    for (i = 0; i < NUM_DOMAIN_OFFSETS; i++) {
+      if (outpkt->domain_offsets[i] == 0) {
+        outpkt->domain_offsets[i] = outpkt->write_offset;
+        break;
+      }
+    }
+
+    outpkt->write_offset += writelen;
+  }
+  if (jump_offset) {
+    /* Write jump */
+    jump = lwip_htons(DOMAIN_JUMP | jump_offset);
+    res = pbuf_take_at(outpkt->pbuf, &jump, DOMAIN_JUMP_SIZE, outpkt->write_offset);
+    if (res != ERR_OK) {
+      return res;
+    }
+    outpkt->write_offset += DOMAIN_JUMP_SIZE;
+  }
+  return ERR_OK;
+}
+
+/**
+ * Write a question to an outpacket
+ * A question contains domain, type and class. Since an answer also starts with these fields this function is also
+ * called from mdns_add_answer().
+ * @param outpkt The outpacket to write to
+ * @param domain The domain name the answer is for
+ * @param type The DNS type of the answer (like 'AAAA', 'SRV')
+ * @param klass The DNS type of the answer (like 'IN')
+ * @param unicast If highest bit in class should be set, to instruct the responder to
+ *                reply with a unicast packet
+ * @return ERR_OK on success, an err_t otherwise
+ */
+static err_t
+mdns_add_question(struct mdns_outpacket *outpkt, struct mdns_domain *domain, u16_t type, u16_t klass, u16_t unicast)
+{
+  u16_t question_len;
+  u16_t field16;
+  err_t res;
+
+  if (!outpkt->pbuf) {
+    /* If no pbuf is active, allocate one */
+    outpkt->pbuf = pbuf_alloc(PBUF_TRANSPORT, OUTPACKET_SIZE, PBUF_RAM);
+    if (!outpkt->pbuf) {
+      return ERR_MEM;
+    }
+    outpkt->write_offset = SIZEOF_DNS_HDR;
+  }
+
+  /* Worst case calculation. Domain string might be compressed */
+  question_len = domain->length + sizeof(type) + sizeof(klass);
+  if (outpkt->write_offset + question_len > outpkt->pbuf->tot_len) {
+    /* No space */
+    return ERR_MEM;
+  }
+
+  /* Write name */
+  res = mdns_write_domain(outpkt, domain);
+  if (res != ERR_OK) {
+    return res;
+  }
+
+  /* Write type */
+  field16 = lwip_htons(type);
+  res = pbuf_take_at(outpkt->pbuf, &field16, sizeof(field16), outpkt->write_offset);
+  if (res != ERR_OK) {
+    return res;
+  }
+  outpkt->write_offset += sizeof(field16);
+
+  /* Write class */
+  if (unicast) {
+    klass |= 0x8000;
+  }
+  field16 = lwip_htons(klass);
+  res = pbuf_take_at(outpkt->pbuf, &field16, sizeof(field16), outpkt->write_offset);
+  if (res != ERR_OK) {
+    return res;
+  }
+  outpkt->write_offset += sizeof(field16);
+
+  return ERR_OK;
+}
+
+/**
+ * Write answer to reply packet.
+ * buf or answer_domain can be null. The rd_length written will be buf_length +
+ * size of (compressed) domain. Most uses will need either buf or answer_domain,
+ * special case is SRV that starts with 3 u16 and then a domain name.
+ * @param reply The outpacket to write to
+ * @param domain The domain name the answer is for
+ * @param type The DNS type of the answer (like 'AAAA', 'SRV')
+ * @param klass The DNS type of the answer (like 'IN')
+ * @param cache_flush If highest bit in class should be set, to instruct receiver that
+ *                    this reply replaces any earlier answer for this domain/type/class
+ * @param ttl Validity time in seconds to send out for IP address data in DNS replies
+ * @param buf Pointer to buffer of answer data
+ * @param buf_length Length of variable data
+ * @param answer_domain A domain to write after any buffer data as answer
+ * @return ERR_OK on success, an err_t otherwise
+ */
+static err_t
+mdns_add_answer(struct mdns_outpacket *reply, struct mdns_domain *domain, u16_t type, u16_t klass, u16_t cache_flush,
+                u32_t ttl, const u8_t *buf, size_t buf_length, struct mdns_domain *answer_domain)
+{
+  u16_t answer_len;
+  u16_t field16;
+  u16_t rdlen_offset;
+  u16_t answer_offset;
+  u32_t field32;
+  err_t res;
+
+  if (!reply->pbuf) {
+    /* If no pbuf is active, allocate one */
+    reply->pbuf = pbuf_alloc(PBUF_TRANSPORT, OUTPACKET_SIZE, PBUF_RAM);
+    if (!reply->pbuf) {
+      return ERR_MEM;
+    }
+    reply->write_offset = SIZEOF_DNS_HDR;
+  }
+
+  /* Worst case calculation. Domain strings might be compressed */
+  answer_len = domain->length + sizeof(type) + sizeof(klass) + sizeof(ttl) + sizeof(field16)/*rd_length*/;
+  if (buf) {
+    answer_len += (u16_t)buf_length;
+  }
+  if (answer_domain) {
+    answer_len += answer_domain->length;
+  }
+  if (reply->write_offset + answer_len > reply->pbuf->tot_len) {
+    /* No space */
+    return ERR_MEM;
+  }
+
+  /* Answer starts with same data as question, then more fields */
+  mdns_add_question(reply, domain, type, klass, cache_flush);
+
+  /* Write TTL */
+  field32 = lwip_htonl(ttl);
+  res = pbuf_take_at(reply->pbuf, &field32, sizeof(field32), reply->write_offset);
+  if (res != ERR_OK) {
+    return res;
+  }
+  reply->write_offset += sizeof(field32);
+
+  /* Store offsets and skip forward to the data */
+  rdlen_offset = reply->write_offset;
+  reply->write_offset += sizeof(field16);
+  answer_offset = reply->write_offset;
+
+  if (buf) {
+    /* Write static data */
+    res = pbuf_take_at(reply->pbuf, buf, (u16_t)buf_length, reply->write_offset);
+    if (res != ERR_OK) {
+      return res;
+    }
+    reply->write_offset += (u16_t)buf_length;
+  }
+
+  if (answer_domain) {
+    /* Write name answer (compressed if possible) */
+    res = mdns_write_domain(reply, answer_domain);
+    if (res != ERR_OK) {
+      return res;
+    }
+  }
+
+  /* Write rd_length after when we know the answer size */
+  field16 = lwip_htons(reply->write_offset - answer_offset);
+  res = pbuf_take_at(reply->pbuf, &field16, sizeof(field16), rdlen_offset);
+
+  return res;
+}
+
+/**
+ * Helper function for mdns_read_question/mdns_read_answer
+ * Reads a domain, type and class from the packet
+ * @param pkt The MDNS packet to read from. The parse_offset field will be
+ *            incremented to point to the next unparsed byte.
+ * @param info The struct to fill with domain, type and class
+ * @return ERR_OK on success, an err_t otherwise
+ */
+static err_t
+mdns_read_rr_info(struct mdns_packet *pkt, struct mdns_rr_info *info)
+{
+  u16_t field16, copied;
+  pkt->parse_offset = mdns_readname(pkt->pbuf, pkt->parse_offset, &info->domain);
+  if (pkt->parse_offset == MDNS_READNAME_ERROR) {
+    return ERR_VAL;
+  }
+
+  copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset);
+  if (copied != sizeof(field16)) {
+    return ERR_VAL;
+  }
+  pkt->parse_offset += copied;
+  info->type = lwip_ntohs(field16);
+
+  copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset);
+  if (copied != sizeof(field16)) {
+    return ERR_VAL;
+  }
+  pkt->parse_offset += copied;
+  info->klass = lwip_ntohs(field16);
+
+  return ERR_OK;
+}
+
+/**
+ * Read a question from the packet.
+ * All questions have to be read before the answers.
+ * @param pkt The MDNS packet to read from. The questions_left field will be decremented
+ *            and the parse_offset will be updated.
+ * @param question The struct to fill with question data
+ * @return ERR_OK on success, an err_t otherwise
+ */
+static err_t
+mdns_read_question(struct mdns_packet *pkt, struct mdns_question *question)
+{
+  /* Safety check */
+  if (pkt->pbuf->tot_len < pkt->parse_offset) {
+    return ERR_VAL;
+  }
+
+  if (pkt->questions_left) {
+    err_t res;
+    pkt->questions_left--;
+
+    memset(question, 0, sizeof(struct mdns_question));
+    res = mdns_read_rr_info(pkt, &question->info);
+    if (res != ERR_OK) {
+      return res;
+    }
+
+    /* Extract unicast flag from class field */
+    question->unicast = question->info.klass & 0x8000;
+    question->info.klass &= 0x7FFF;
+
+    return ERR_OK;
+  }
+  return ERR_VAL;
+}
+
+/**
+ * Read an answer from the packet
+ * The variable length reply is not copied, its pbuf offset and length is stored instead.
+ * @param pkt The MDNS packet to read. The answers_left field will be decremented and
+ *            the parse_offset will be updated.
+ * @param answer The struct to fill with answer data
+ * @return ERR_OK on success, an err_t otherwise
+ */
+static err_t
+mdns_read_answer(struct mdns_packet *pkt, struct mdns_answer *answer)
+{
+  /* Read questions first */
+  if (pkt->questions_left) {
+    return ERR_VAL;
+  }
+
+  /* Safety check */
+  if (pkt->pbuf->tot_len < pkt->parse_offset) {
+    return ERR_VAL;
+  }
+
+  if (pkt->answers_left) {
+    u16_t copied, field16;
+    u32_t ttl;
+    err_t res;
+    pkt->answers_left--;
+
+    memset(answer, 0, sizeof(struct mdns_answer));
+    res = mdns_read_rr_info(pkt, &answer->info);
+    if (res != ERR_OK) {
+      return res;
+    }
+
+    /* Extract cache_flush flag from class field */
+    answer->cache_flush = answer->info.klass & 0x8000;
+    answer->info.klass &= 0x7FFF;
+
+    copied = pbuf_copy_partial(pkt->pbuf, &ttl, sizeof(ttl), pkt->parse_offset);
+    if (copied != sizeof(ttl)) {
+      return ERR_VAL;
+    }
+    pkt->parse_offset += copied;
+    answer->ttl = lwip_ntohl(ttl);
+
+    copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset);
+    if (copied != sizeof(field16)) {
+      return ERR_VAL;
+    }
+    pkt->parse_offset += copied;
+    answer->rd_length = lwip_ntohs(field16);
+
+    answer->rd_offset = pkt->parse_offset;
+    pkt->parse_offset += answer->rd_length;
+
+    return ERR_OK;
+  }
+  return ERR_VAL;
+}
+
+#if LWIP_IPV4
+/** Write an IPv4 address (A) RR to outpacket */
+static err_t
+mdns_add_a_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif)
+{
+  struct mdns_domain host;
+  mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with A record\n"));
+  return mdns_add_answer(reply, &host, DNS_RRTYPE_A, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, (const u8_t *) netif_ip4_addr(netif), sizeof(ip4_addr_t), NULL);
+}
+
+/** Write a 4.3.2.1.in-addr.arpa -> hostname.local PTR RR to outpacket */
+static err_t
+mdns_add_hostv4_ptr_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif)
+{
+  struct mdns_domain host, revhost;
+  mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
+  mdns_build_reverse_v4_domain(&revhost, netif_ip4_addr(netif));
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with v4 PTR record\n"));
+  return mdns_add_answer(reply, &revhost, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, NULL, 0, &host);
+}
+#endif
+
+#if LWIP_IPV6
+/** Write an IPv6 address (AAAA) RR to outpacket */
+static err_t
+mdns_add_aaaa_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif, int addrindex)
+{
+  struct mdns_domain host;
+  mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with AAAA record\n"));
+  return mdns_add_answer(reply, &host, DNS_RRTYPE_AAAA, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, (const u8_t *) netif_ip6_addr(netif, addrindex), sizeof(ip6_addr_p_t), NULL);
+}
+
+/** Write a x.y.z.ip6.arpa -> hostname.local PTR RR to outpacket */
+static err_t
+mdns_add_hostv6_ptr_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif, int addrindex)
+{
+  struct mdns_domain host, revhost;
+  mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
+  mdns_build_reverse_v6_domain(&revhost, netif_ip6_addr(netif, addrindex));
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with v6 PTR record\n"));
+  return mdns_add_answer(reply, &revhost, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, NULL, 0, &host);
+}
+#endif
+
+/** Write an all-services -> servicetype PTR RR to outpacket */
+static err_t
+mdns_add_servicetype_ptr_answer(struct mdns_outpacket *reply, struct mdns_service *service)
+{
+  struct mdns_domain service_type, service_dnssd;
+  mdns_build_service_domain(&service_type, service, 0);
+  mdns_build_dnssd_domain(&service_dnssd);
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with service type PTR record\n"));
+  return mdns_add_answer(reply, &service_dnssd, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, 0, service->dns_ttl, NULL, 0, &service_type);
+}
+
+/** Write a servicetype -> servicename PTR RR to outpacket */
+static err_t
+mdns_add_servicename_ptr_answer(struct mdns_outpacket *reply, struct mdns_service *service)
+{
+  struct mdns_domain service_type, service_instance;
+  mdns_build_service_domain(&service_type, service, 0);
+  mdns_build_service_domain(&service_instance, service, 1);
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with service name PTR record\n"));
+  return mdns_add_answer(reply, &service_type, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, 0, service->dns_ttl, NULL, 0, &service_instance);
+}
+
+/** Write a SRV RR to outpacket */
+static err_t
+mdns_add_srv_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct mdns_host *mdns, struct mdns_service *service)
+{
+  struct mdns_domain service_instance, srvhost;
+  u16_t srvdata[3];
+  mdns_build_service_domain(&service_instance, service, 1);
+  mdns_build_host_domain(&srvhost, mdns);
+  if (reply->legacy_query) {
+    /* RFC 6762 section 18.14:
+     * In legacy unicast responses generated to answer legacy queries,
+     * name compression MUST NOT be performed on SRV records.
+     */
+    srvhost.skip_compression = 1;
+  }
+  srvdata[0] = lwip_htons(SRV_PRIORITY);
+  srvdata[1] = lwip_htons(SRV_WEIGHT);
+  srvdata[2] = lwip_htons(service->port);
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with SRV record\n"));
+  return mdns_add_answer(reply, &service_instance, DNS_RRTYPE_SRV, DNS_RRCLASS_IN, cache_flush, service->dns_ttl,
+                         (const u8_t *) &srvdata, sizeof(srvdata), &srvhost);
+}
+
+/** Write a TXT RR to outpacket */
+static err_t
+mdns_add_txt_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct mdns_service *service)
+{
+  struct mdns_domain service_instance;
+  mdns_build_service_domain(&service_instance, service, 1);
+  mdns_prepare_txtdata(service);
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with TXT record\n"));
+  return mdns_add_answer(reply, &service_instance, DNS_RRTYPE_TXT, DNS_RRCLASS_IN, cache_flush, service->dns_ttl,
+                         (u8_t *) &service->txtdata.name, service->txtdata.length, NULL);
+}
+
+/**
+ * Setup outpacket as a reply to the incoming packet
+ */
+static void
+mdns_init_outpacket(struct mdns_outpacket *out, struct mdns_packet *in)
+{
+  memset(out, 0, sizeof(struct mdns_outpacket));
+  out->cache_flush = 1;
+  out->netif = in->netif;
+
+  /* Copy source IP/port to use when responding unicast, or to choose
+   * which pcb to use for multicast (IPv4/IPv6)
+   */
+  SMEMCPY(&out->dest_addr, &in->source_addr, sizeof(ip_addr_t));
+  out->dest_port = in->source_port;
+
+  if (in->source_port != LWIP_IANA_PORT_MDNS) {
+    out->unicast_reply = 1;
+    out->cache_flush = 0;
+    if (in->questions == 1) {
+      out->legacy_query = 1;
+      out->tx_id = in->tx_id;
+    }
+  }
+
+  if (in->recv_unicast) {
+    out->unicast_reply = 1;
+  }
+}
+
+/**
+ * Send chosen answers as a reply
+ *
+ * Add all selected answers (first write will allocate pbuf)
+ * Add additional answers based on the selected answers
+ * Send the packet
+ */
+static err_t
+mdns_send_outpacket(struct mdns_outpacket *outpkt, u8_t flags)
+{
+  struct mdns_service *service;
+  err_t res = ERR_ARG;
+  int i;
+  struct mdns_host *mdns = NETIF_TO_HOST(outpkt->netif);
+  u16_t answers = 0;
+
+  /* Write answers to host questions */
+#if LWIP_IPV4
+  if (outpkt->host_replies & REPLY_HOST_A) {
+    res = mdns_add_a_answer(outpkt, outpkt->cache_flush, outpkt->netif);
+    if (res != ERR_OK) {
+      goto cleanup;
+    }
+    answers++;
+  }
+  if (outpkt->host_replies & REPLY_HOST_PTR_V4) {
+    res = mdns_add_hostv4_ptr_answer(outpkt, outpkt->cache_flush, outpkt->netif);
+    if (res != ERR_OK) {
+      goto cleanup;
+    }
+    answers++;
+  }
+#endif
+#if LWIP_IPV6
+  if (outpkt->host_replies & REPLY_HOST_AAAA) {
+    int addrindex;
+    for (addrindex = 0; addrindex < LWIP_IPV6_NUM_ADDRESSES; addrindex++) {
+      if (ip6_addr_isvalid(netif_ip6_addr_state(outpkt->netif, addrindex))) {
+        res = mdns_add_aaaa_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex);
+        if (res != ERR_OK) {
+          goto cleanup;
+        }
+        answers++;
+      }
+    }
+  }
+  if (outpkt->host_replies & REPLY_HOST_PTR_V6) {
+    u8_t rev_addrs = outpkt->host_reverse_v6_replies;
+    int addrindex = 0;
+    while (rev_addrs) {
+      if (rev_addrs & 1) {
+        res = mdns_add_hostv6_ptr_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex);
+        if (res != ERR_OK) {
+          goto cleanup;
+        }
+        answers++;
+      }
+      addrindex++;
+      rev_addrs >>= 1;
+    }
+  }
+#endif
+
+  /* Write answers to service questions */
+  for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+    service = mdns->services[i];
+    if (!service) {
+      continue;
+    }
+
+    if (outpkt->serv_replies[i] & REPLY_SERVICE_TYPE_PTR) {
+      res = mdns_add_servicetype_ptr_answer(outpkt, service);
+      if (res != ERR_OK) {
+        goto cleanup;
+      }
+      answers++;
+    }
+
+    if (outpkt->serv_replies[i] & REPLY_SERVICE_NAME_PTR) {
+      res = mdns_add_servicename_ptr_answer(outpkt, service);
+      if (res != ERR_OK) {
+        goto cleanup;
+      }
+      answers++;
+    }
+
+    if (outpkt->serv_replies[i] & REPLY_SERVICE_SRV) {
+      res = mdns_add_srv_answer(outpkt, outpkt->cache_flush, mdns, service);
+      if (res != ERR_OK) {
+        goto cleanup;
+      }
+      answers++;
+    }
+
+    if (outpkt->serv_replies[i] & REPLY_SERVICE_TXT) {
+      res = mdns_add_txt_answer(outpkt, outpkt->cache_flush, service);
+      if (res != ERR_OK) {
+        goto cleanup;
+      }
+      answers++;
+    }
+  }
+
+  /* if this is a response, the data above is anwers, else this is a probe and the answers above goes into auth section */
+  if (flags & DNS_FLAG1_RESPONSE) {
+    outpkt->answers += answers;
+  } else {
+    outpkt->authoritative += answers;
+  }
+
+  /* All answers written, add additional RRs */
+  for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+    service = mdns->services[i];
+    if (!service) {
+      continue;
+    }
+
+    if (outpkt->serv_replies[i] & REPLY_SERVICE_NAME_PTR) {
+      /* Our service instance requested, include SRV & TXT
+       * if they are already not requested. */
+      if (!(outpkt->serv_replies[i] & REPLY_SERVICE_SRV)) {
+        res = mdns_add_srv_answer(outpkt, outpkt->cache_flush, mdns, service);
+        if (res != ERR_OK) {
+          goto cleanup;
+        }
+        outpkt->additional++;
+      }
+
+      if (!(outpkt->serv_replies[i] & REPLY_SERVICE_TXT)) {
+        res = mdns_add_txt_answer(outpkt, outpkt->cache_flush, service);
+        if (res != ERR_OK) {
+          goto cleanup;
+        }
+        outpkt->additional++;
+      }
+    }
+
+    /* If service instance, SRV, record or an IP address is requested,
+     * supply all addresses for the host
+     */
+    if ((outpkt->serv_replies[i] & (REPLY_SERVICE_NAME_PTR | REPLY_SERVICE_SRV)) ||
+        (outpkt->host_replies & (REPLY_HOST_A | REPLY_HOST_AAAA))) {
+#if LWIP_IPV6
+      if (!(outpkt->host_replies & REPLY_HOST_AAAA)) {
+        int addrindex;
+        for (addrindex = 0; addrindex < LWIP_IPV6_NUM_ADDRESSES; addrindex++) {
+          if (ip6_addr_isvalid(netif_ip6_addr_state(outpkt->netif, addrindex))) {
+            res = mdns_add_aaaa_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex);
+            if (res != ERR_OK) {
+              goto cleanup;
+            }
+            outpkt->additional++;
+          }
+        }
+      }
+#endif
+#if LWIP_IPV4
+      if (!(outpkt->host_replies & REPLY_HOST_A) &&
+          !ip4_addr_isany_val(*netif_ip4_addr(outpkt->netif))) {
+        res = mdns_add_a_answer(outpkt, outpkt->cache_flush, outpkt->netif);
+        if (res != ERR_OK) {
+          goto cleanup;
+        }
+        outpkt->additional++;
+      }
+#endif
+    }
+  }
+
+  if (outpkt->pbuf) {
+    const ip_addr_t *mcast_destaddr;
+    struct dns_hdr hdr;
+
+    /* Write header */
+    memset(&hdr, 0, sizeof(hdr));
+    hdr.flags1 = flags;
+    hdr.numquestions = lwip_htons(outpkt->questions);
+    hdr.numanswers = lwip_htons(outpkt->answers);
+    hdr.numauthrr = lwip_htons(outpkt->authoritative);
+    hdr.numextrarr = lwip_htons(outpkt->additional);
+    hdr.id = lwip_htons(outpkt->tx_id);
+    pbuf_take(outpkt->pbuf, &hdr, sizeof(hdr));
+
+    /* Shrink packet */
+    pbuf_realloc(outpkt->pbuf, outpkt->write_offset);
+
+    if (IP_IS_V6_VAL(outpkt->dest_addr)) {
+#if LWIP_IPV6
+      mcast_destaddr = &v6group;
+#endif
+    } else {
+#if LWIP_IPV4
+      mcast_destaddr = &v4group;
+#endif
+    }
+    /* Send created packet */
+    LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Sending packet, len=%d, unicast=%d\n", outpkt->write_offset, outpkt->unicast_reply));
+    if (outpkt->unicast_reply) {
+      res = udp_sendto_if(mdns_pcb, outpkt->pbuf, &outpkt->dest_addr, outpkt->dest_port, outpkt->netif);
+    } else {
+      res = udp_sendto_if(mdns_pcb, outpkt->pbuf, mcast_destaddr, LWIP_IANA_PORT_MDNS, outpkt->netif);
+    }
+  }
+
+cleanup:
+  if (outpkt->pbuf) {
+    pbuf_free(outpkt->pbuf);
+    outpkt->pbuf = NULL;
+  }
+  return res;
+}
+
+/**
+ * Send unsolicited answer containing all our known data
+ * @param netif The network interface to send on
+ * @param destination The target address to send to (usually multicast address)
+ */
+static void
+mdns_announce(struct netif *netif, const ip_addr_t *destination)
+{
+  struct mdns_outpacket announce;
+  int i;
+  struct mdns_host *mdns = NETIF_TO_HOST(netif);
+
+  memset(&announce, 0, sizeof(announce));
+  announce.netif = netif;
+  announce.cache_flush = 1;
+#if LWIP_IPV4
+  if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+    announce.host_replies = REPLY_HOST_A | REPLY_HOST_PTR_V4;
+  }
+#endif
+#if LWIP_IPV6
+  for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+    if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
+      announce.host_replies |= REPLY_HOST_AAAA | REPLY_HOST_PTR_V6;
+      announce.host_reverse_v6_replies |= (1 << i);
+    }
+  }
+#endif
+
+  for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+    struct mdns_service *serv = mdns->services[i];
+    if (serv) {
+      announce.serv_replies[i] = REPLY_SERVICE_TYPE_PTR | REPLY_SERVICE_NAME_PTR |
+                                 REPLY_SERVICE_SRV | REPLY_SERVICE_TXT;
+    }
+  }
+
+  announce.dest_port = LWIP_IANA_PORT_MDNS;
+  SMEMCPY(&announce.dest_addr, destination, sizeof(announce.dest_addr));
+  mdns_send_outpacket(&announce, DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE);
+}
+
+/**
+ * Handle question MDNS packet
+ * 1. Parse all questions and set bits what answers to send
+ * 2. Clear pending answers if known answers are supplied
+ * 3. Put chosen answers in new packet and send as reply
+ */
+static void
+mdns_handle_question(struct mdns_packet *pkt)
+{
+  struct mdns_service *service;
+  struct mdns_outpacket reply;
+  int replies = 0;
+  int i;
+  err_t res;
+  struct mdns_host *mdns = NETIF_TO_HOST(pkt->netif);
+
+  if (mdns->probing_state != MDNS_PROBING_COMPLETE) {
+    /* Don't answer questions until we've verified our domains via probing */
+    /* @todo we should check incoming questions during probing for tiebreaking */
+    return;
+  }
+
+  mdns_init_outpacket(&reply, pkt);
+
+  while (pkt->questions_left) {
+    struct mdns_question q;
+
+    res = mdns_read_question(pkt, &q);
+    if (res != ERR_OK) {
+      LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse question, skipping query packet\n"));
+      return;
+    }
+
+    LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Query for domain "));
+    mdns_domain_debug_print(&q.info.domain);
+    LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", q.info.type, q.info.klass));
+
+    if (q.unicast) {
+      /* Reply unicast if any question is unicast */
+      reply.unicast_reply = 1;
+    }
+
+    reply.host_replies |= check_host(pkt->netif, &q.info, &reply.host_reverse_v6_replies);
+    replies |= reply.host_replies;
+
+    for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+      service = mdns->services[i];
+      if (!service) {
+        continue;
+      }
+      reply.serv_replies[i] |= check_service(service, &q.info);
+      replies |= reply.serv_replies[i];
+    }
+
+    if (replies && reply.legacy_query) {
+      /* Add question to reply packet (legacy packet only has 1 question) */
+      res = mdns_add_question(&reply, &q.info.domain, q.info.type, q.info.klass, 0);
+      reply.questions = 1;
+      if (res != ERR_OK) {
+        goto cleanup;
+      }
+    }
+  }
+
+  /* Handle known answers */
+  while (pkt->answers_left) {
+    struct mdns_answer ans;
+    u8_t rev_v6;
+    int match;
+
+    res = mdns_read_answer(pkt, &ans);
+    if (res != ERR_OK) {
+      LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping query packet\n"));
+      goto cleanup;
+    }
+
+    LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Known answer for domain "));
+    mdns_domain_debug_print(&ans.info.domain);
+    LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass));
+
+
+    if (ans.info.type == DNS_RRTYPE_ANY || ans.info.klass == DNS_RRCLASS_ANY) {
+      /* Skip known answers for ANY type & class */
+      continue;
+    }
+
+    rev_v6 = 0;
+    match = reply.host_replies & check_host(pkt->netif, &ans.info, &rev_v6);
+    if (match && (ans.ttl > (mdns->dns_ttl / 2))) {
+      /* The RR in the known answer matches an RR we are planning to send,
+       * and the TTL is less than half gone.
+       * If the payload matches we should not send that answer.
+       */
+      if (ans.info.type == DNS_RRTYPE_PTR) {
+        /* Read domain and compare */
+        struct mdns_domain known_ans, my_ans;
+        u16_t len;
+        len = mdns_readname(pkt->pbuf, ans.rd_offset, &known_ans);
+        res = mdns_build_host_domain(&my_ans, mdns);
+        if (len != MDNS_READNAME_ERROR && res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
+#if LWIP_IPV4
+          if (match & REPLY_HOST_PTR_V4) {
+            LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v4 PTR\n"));
+            reply.host_replies &= ~REPLY_HOST_PTR_V4;
+          }
+#endif
+#if LWIP_IPV6
+          if (match & REPLY_HOST_PTR_V6) {
+            LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v6 PTR\n"));
+            reply.host_reverse_v6_replies &= ~rev_v6;
+            if (reply.host_reverse_v6_replies == 0) {
+              reply.host_replies &= ~REPLY_HOST_PTR_V6;
+            }
+          }
+#endif
+        }
+      } else if (match & REPLY_HOST_A) {
+#if LWIP_IPV4
+        if (ans.rd_length == sizeof(ip4_addr_t) &&
+            pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip4_addr(pkt->netif), ans.rd_length) == 0) {
+          LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: A\n"));
+          reply.host_replies &= ~REPLY_HOST_A;
+        }
+#endif
+      } else if (match & REPLY_HOST_AAAA) {
+#if LWIP_IPV6
+        if (ans.rd_length == sizeof(ip6_addr_p_t) &&
+            /* TODO this clears all AAAA responses if first addr is set as known */
+            pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip6_addr(pkt->netif, 0), ans.rd_length) == 0) {
+          LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: AAAA\n"));
+          reply.host_replies &= ~REPLY_HOST_AAAA;
+        }
+#endif
+      }
+    }
+
+    for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+      service = mdns->services[i];
+      if (!service) {
+        continue;
+      }
+      match = reply.serv_replies[i] & check_service(service, &ans.info);
+      if (match && (ans.ttl > (service->dns_ttl / 2))) {
+        /* The RR in the known answer matches an RR we are planning to send,
+         * and the TTL is less than half gone.
+         * If the payload matches we should not send that answer.
+         */
+        if (ans.info.type == DNS_RRTYPE_PTR) {
+          /* Read domain and compare */
+          struct mdns_domain known_ans, my_ans;
+          u16_t len;
+          len = mdns_readname(pkt->pbuf, ans.rd_offset, &known_ans);
+          if (len != MDNS_READNAME_ERROR) {
+            if (match & REPLY_SERVICE_TYPE_PTR) {
+              res = mdns_build_service_domain(&my_ans, service, 0);
+              if (res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
+                LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: service type PTR\n"));
+                reply.serv_replies[i] &= ~REPLY_SERVICE_TYPE_PTR;
+              }
+            }
+            if (match & REPLY_SERVICE_NAME_PTR) {
+              res = mdns_build_service_domain(&my_ans, service, 1);
+              if (res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
+                LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: service name PTR\n"));
+                reply.serv_replies[i] &= ~REPLY_SERVICE_NAME_PTR;
+              }
+            }
+          }
+        } else if (match & REPLY_SERVICE_SRV) {
+          /* Read and compare to my SRV record */
+          u16_t field16, len, read_pos;
+          struct mdns_domain known_ans, my_ans;
+          read_pos = ans.rd_offset;
+          do {
+            /* Check priority field */
+            len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos);
+            if (len != sizeof(field16) || lwip_ntohs(field16) != SRV_PRIORITY) {
+              break;
+            }
+            read_pos += len;
+            /* Check weight field */
+            len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos);
+            if (len != sizeof(field16) || lwip_ntohs(field16) != SRV_WEIGHT) {
+              break;
+            }
+            read_pos += len;
+            /* Check port field */
+            len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos);
+            if (len != sizeof(field16) || lwip_ntohs(field16) != service->port) {
+              break;
+            }
+            read_pos += len;
+            /* Check host field */
+            len = mdns_readname(pkt->pbuf, read_pos, &known_ans);
+            mdns_build_host_domain(&my_ans, mdns);
+            if (len == MDNS_READNAME_ERROR || !mdns_domain_eq(&known_ans, &my_ans)) {
+              break;
+            }
+            LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: SRV\n"));
+            reply.serv_replies[i] &= ~REPLY_SERVICE_SRV;
+          } while (0);
+        } else if (match & REPLY_SERVICE_TXT) {
+          mdns_prepare_txtdata(service);
+          if (service->txtdata.length == ans.rd_length &&
+              pbuf_memcmp(pkt->pbuf, ans.rd_offset, service->txtdata.name, ans.rd_length) == 0) {
+            LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: TXT\n"));
+            reply.serv_replies[i] &= ~REPLY_SERVICE_TXT;
+          }
+        }
+      }
+    }
+  }
+
+  mdns_send_outpacket(&reply, DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE);
+
+cleanup:
+  if (reply.pbuf) {
+    /* This should only happen if we fail to alloc/write question for legacy query */
+    pbuf_free(reply.pbuf);
+    reply.pbuf = NULL;
+  }
+}
+
+/**
+ * Handle response MDNS packet
+ * Only prints debug for now. Will need more code to do conflict resolution.
+ */
+static void
+mdns_handle_response(struct mdns_packet *pkt)
+{
+  struct mdns_host* mdns = NETIF_TO_HOST(pkt->netif);
+
+  /* Ignore all questions */
+  while (pkt->questions_left) {
+    struct mdns_question q;
+    err_t res;
+
+    res = mdns_read_question(pkt, &q);
+    if (res != ERR_OK) {
+      LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse question, skipping response packet\n"));
+      return;
+    }
+  }
+
+  while (pkt->answers_left) {
+    struct mdns_answer ans;
+    err_t res;
+
+    res = mdns_read_answer(pkt, &ans);
+    if (res != ERR_OK) {
+      LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping response packet\n"));
+      return;
+    }
+
+    LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Answer for domain "));
+    mdns_domain_debug_print(&ans.info.domain);
+    LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass));
+
+    /*"Apparently conflicting Multicast DNS responses received *before* the first probe packet is sent MUST
+      be silently ignored" so drop answer if we haven't started probing yet*/
+    if ((mdns->probing_state == MDNS_PROBING_ONGOING) && (mdns->probes_sent > 0)) {
+      struct mdns_domain domain;
+      u8_t i;
+      u8_t conflict = 0;
+
+      res = mdns_build_host_domain(&domain, mdns);
+      if (res == ERR_OK && mdns_domain_eq(&ans.info.domain, &domain)) {
+        LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Probe response matches host domain!"));
+        conflict = 1;
+      }
+
+      for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+        struct mdns_service* service = mdns->services[i];
+        if (!service) {
+          continue;
+        }
+        res = mdns_build_service_domain(&domain, service, 1);
+        if ((res == ERR_OK) && mdns_domain_eq(&ans.info.domain, &domain)) {
+          LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Probe response matches service domain!"));
+          conflict = 1;
+        }
+      }
+
+      if (conflict != 0) {
+        sys_untimeout(mdns_probe, pkt->netif);
+        if (mdns_name_result_cb != NULL) {
+          mdns_name_result_cb(pkt->netif, MDNS_PROBING_CONFLICT);
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Receive input function for MDNS packets.
+ * Handles both IPv4 and IPv6 UDP pcbs.
+ */
+static void
+mdns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+  struct dns_hdr hdr;
+  struct mdns_packet packet;
+  struct netif *recv_netif = ip_current_input_netif();
+  u16_t offset = 0;
+
+  LWIP_UNUSED_ARG(arg);
+  LWIP_UNUSED_ARG(pcb);
+
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Received IPv%d MDNS packet, len %d\n", IP_IS_V6(addr) ? 6 : 4, p->tot_len));
+
+  if (NETIF_TO_HOST(recv_netif) == NULL) {
+    /* From netif not configured for MDNS */
+    goto dealloc;
+  }
+
+  if (pbuf_copy_partial(p, &hdr, SIZEOF_DNS_HDR, offset) < SIZEOF_DNS_HDR) {
+    /* Too small */
+    goto dealloc;
+  }
+  offset += SIZEOF_DNS_HDR;
+
+  if (DNS_HDR_GET_OPCODE(&hdr)) {
+    /* Ignore non-standard queries in multicast packets (RFC 6762, section 18.3) */
+    goto dealloc;
+  }
+
+  memset(&packet, 0, sizeof(packet));
+  SMEMCPY(&packet.source_addr, addr, sizeof(packet.source_addr));
+  packet.source_port = port;
+  packet.netif = recv_netif;
+  packet.pbuf = p;
+  packet.parse_offset = offset;
+  packet.tx_id = lwip_ntohs(hdr.id);
+  packet.questions = packet.questions_left = lwip_ntohs(hdr.numquestions);
+  packet.answers = packet.answers_left = lwip_ntohs(hdr.numanswers) + lwip_ntohs(hdr.numauthrr) + lwip_ntohs(hdr.numextrarr);
+
+#if LWIP_IPV6
+  if (IP_IS_V6(ip_current_dest_addr())) {
+    /* instead of having one 'v6group' per netif, just compare zoneless here */
+    if (!ip_addr_cmp_zoneless(ip_current_dest_addr(), &v6group)) {
+      packet.recv_unicast = 1;
+    }
+  }
+#endif
+#if LWIP_IPV4
+  if (!IP_IS_V6(ip_current_dest_addr())) {
+    if (!ip_addr_cmp(ip_current_dest_addr(), &v4group)) {
+      packet.recv_unicast = 1;
+    }
+  }
+#endif
+
+  if (hdr.flags1 & DNS_FLAG1_RESPONSE) {
+    mdns_handle_response(&packet);
+  } else {
+    mdns_handle_question(&packet);
+  }
+
+dealloc:
+  pbuf_free(p);
+}
+
+#if LWIP_NETIF_EXT_STATUS_CALLBACK && MDNS_RESP_USENETIF_EXTCALLBACK
+static void
+mdns_netif_ext_status_callback(struct netif *netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t *args)
+{
+  LWIP_UNUSED_ARG(args);
+
+  /* MDNS enabled on netif? */
+  if (NETIF_TO_HOST(netif) == NULL) {
+    return;
+  }
+
+  if (reason & LWIP_NSC_STATUS_CHANGED) {
+    if (args->status_changed.state != 0) {
+      mdns_resp_restart(netif);
+    }
+    /* TODO: send goodbye message */
+  }
+  if (reason & LWIP_NSC_LINK_CHANGED) {
+    if (args->link_changed.state != 0) {
+      mdns_resp_restart(netif);
+    }
+  }
+  if (reason & (LWIP_NSC_IPV4_ADDRESS_CHANGED | LWIP_NSC_IPV4_GATEWAY_CHANGED |
+      LWIP_NSC_IPV4_NETMASK_CHANGED | LWIP_NSC_IPV4_SETTINGS_CHANGED |
+      LWIP_NSC_IPV6_SET | LWIP_NSC_IPV6_ADDR_STATE_CHANGED)) {
+    mdns_resp_announce(netif);
+  }
+}
+#endif /* LWIP_NETIF_EXT_STATUS_CALLBACK && MDNS_RESP_USENETIF_EXTCALLBACK */
+
+static err_t
+mdns_send_probe(struct netif* netif, const ip_addr_t *destination)
+{
+  struct mdns_host* mdns;
+  struct mdns_outpacket pkt;
+  struct mdns_domain domain;
+  u8_t i;
+  err_t res;
+
+  mdns = NETIF_TO_HOST(netif);
+
+  memset(&pkt, 0, sizeof(pkt));
+  pkt.netif = netif;
+
+  /* Add unicast questions with rtype ANY for all our desired records */
+  mdns_build_host_domain(&domain, mdns);
+  res = mdns_add_question(&pkt, &domain, DNS_RRTYPE_ANY, DNS_RRCLASS_IN, 1);
+  if (res != ERR_OK) {
+    goto cleanup;
+  }
+  pkt.questions++;
+  for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+    struct mdns_service* service = mdns->services[i];
+    if (!service) {
+      continue;
+    }
+    mdns_build_service_domain(&domain, service, 1);
+    res = mdns_add_question(&pkt, &domain, DNS_RRTYPE_ANY, DNS_RRCLASS_IN, 1);
+    if (res != ERR_OK) {
+      goto cleanup;
+    }
+    pkt.questions++;
+  }
+
+  /* Add answers to the questions above into the authority section for tiebreaking */
+#if LWIP_IPV4
+  if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+    pkt.host_replies = REPLY_HOST_A;
+  }
+#endif
+#if LWIP_IPV6
+  for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+    if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
+      pkt.host_replies |= REPLY_HOST_AAAA;
+    }
+  }
+#endif
+
+  for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+    struct mdns_service *serv = mdns->services[i];
+    if (serv) {
+      pkt.serv_replies[i] = REPLY_SERVICE_SRV | REPLY_SERVICE_TXT;
+    }
+  }
+
+  pkt.tx_id = 0;
+  pkt.dest_port = LWIP_IANA_PORT_MDNS;
+  SMEMCPY(&pkt.dest_addr, destination, sizeof(pkt.dest_addr));
+  res = mdns_send_outpacket(&pkt, 0);
+
+cleanup:
+  if (pkt.pbuf) {
+    pbuf_free(pkt.pbuf);
+    pkt.pbuf = NULL;
+  }
+  return res;
+}
+
+/**
+ * Timer callback for probing network.
+ */
+static void
+mdns_probe(void* arg)
+{
+  struct netif *netif = (struct netif *)arg;
+  struct mdns_host* mdns = NETIF_TO_HOST(netif);
+
+  if(mdns->probes_sent >= MDNS_PROBE_COUNT) {
+    /* probing successful, announce the new name */
+    mdns->probing_state = MDNS_PROBING_COMPLETE;
+    mdns_resp_announce(netif);
+    if (mdns_name_result_cb != NULL) {
+      mdns_name_result_cb(netif, MDNS_PROBING_SUCCESSFUL);
+    }
+  } else {
+#if LWIP_IPV4
+    /*if ipv4 wait with probing until address is set*/
+    if (!ip4_addr_isany_val(*netif_ip4_addr(netif)) &&
+        mdns_send_probe(netif, IP4_ADDR_ANY) == ERR_OK)
+#endif
+    {
+#if LWIP_IPV6
+      if (mdns_send_probe(netif, IP6_ADDR_ANY) == ERR_OK)
+#endif
+      {
+        mdns->probes_sent++;
+      }
+    }
+    sys_timeout(MDNS_PROBE_DELAY_MS, mdns_probe, netif);
+  }
+}
+
+/**
+ * @ingroup mdns
+ * Activate MDNS responder for a network interface.
+ * @param netif The network interface to activate.
+ * @param hostname Name to use. Queries for &lt;hostname&gt;.local will be answered
+ *                 with the IP addresses of the netif. The hostname will be copied, the
+ *                 given pointer can be on the stack.
+ * @param dns_ttl Validity time in seconds to send out for IP address data in DNS replies
+ * @return ERR_OK if netif was added, an err_t otherwise
+ */
+err_t
+mdns_resp_add_netif(struct netif *netif, const char *hostname, u32_t dns_ttl)
+{
+  err_t res;
+  struct mdns_host *mdns;
+
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ERROR("mdns_resp_add_netif: netif != NULL", (netif != NULL), return ERR_VAL);
+  LWIP_ERROR("mdns_resp_add_netif: Hostname too long", (strlen(hostname) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
+
+  LWIP_ASSERT("mdns_resp_add_netif: Double add", NETIF_TO_HOST(netif) == NULL);
+  mdns = (struct mdns_host *) mem_calloc(1, sizeof(struct mdns_host));
+  LWIP_ERROR("mdns_resp_add_netif: Alloc failed", (mdns != NULL), return ERR_MEM);
+
+  netif_set_client_data(netif, mdns_netif_client_id, mdns);
+
+  MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(hostname)));
+  mdns->dns_ttl = dns_ttl;
+  mdns->probes_sent = 0;
+  mdns->probing_state = MDNS_PROBING_NOT_STARTED;
+
+  /* Join multicast groups */
+#if LWIP_IPV4
+  res = igmp_joingroup_netif(netif, ip_2_ip4(&v4group));
+  if (res != ERR_OK) {
+    goto cleanup;
+  }
+#endif
+#if LWIP_IPV6
+  res = mld6_joingroup_netif(netif, ip_2_ip6(&v6group));
+  if (res != ERR_OK) {
+    goto cleanup;
+  }
+#endif
+
+  mdns_resp_restart(netif);
+
+  return ERR_OK;
+
+cleanup:
+  mem_free(mdns);
+  netif_set_client_data(netif, mdns_netif_client_id, NULL);
+  return res;
+}
+
+/**
+ * @ingroup mdns
+ * Stop responding to MDNS queries on this interface, leave multicast groups,
+ * and free the helper structure and any of its services.
+ * @param netif The network interface to remove.
+ * @return ERR_OK if netif was removed, an err_t otherwise
+ */
+err_t
+mdns_resp_remove_netif(struct netif *netif)
+{
+  int i;
+  struct mdns_host *mdns;
+
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("mdns_resp_remove_netif: Null pointer", netif);
+  mdns = NETIF_TO_HOST(netif);
+  LWIP_ERROR("mdns_resp_remove_netif: Not an active netif", (mdns != NULL), return ERR_VAL);
+
+  if (mdns->probing_state == MDNS_PROBING_ONGOING) {
+    sys_untimeout(mdns_probe, netif);
+  }
+
+  for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+    struct mdns_service *service = mdns->services[i];
+    if (service) {
+      mem_free(service);
+    }
+  }
+
+  /* Leave multicast groups */
+#if LWIP_IPV4
+  igmp_leavegroup_netif(netif, ip_2_ip4(&v4group));
+#endif
+#if LWIP_IPV6
+  mld6_leavegroup_netif(netif, ip_2_ip6(&v6group));
+#endif
+
+  mem_free(mdns);
+  netif_set_client_data(netif, mdns_netif_client_id, NULL);
+  return ERR_OK;
+}
+
+/**
+ * @ingroup mdns
+ * Update MDNS hostname for a network interface.
+ * @param netif The network interface to activate.
+ * @param hostname Name to use. Queries for &lt;hostname&gt;.local will be answered
+ *                 with the IP addresses of the netif. The hostname will be copied, the
+ *                 given pointer can be on the stack.
+ * @return ERR_OK if name could be set on netif, an err_t otherwise
+ */
+err_t
+mdns_resp_rename_netif(struct netif *netif, const char *hostname)
+{
+  struct mdns_host *mdns;
+  size_t len;
+
+  LWIP_ASSERT_CORE_LOCKED();
+  len = strlen(hostname);
+  LWIP_ERROR("mdns_resp_rename_netif: netif != NULL", (netif != NULL), return ERR_VAL);
+  LWIP_ERROR("mdns_resp_rename_netif: Hostname too long", (len <= MDNS_LABEL_MAXLEN), return ERR_VAL);
+  mdns = NETIF_TO_HOST(netif);
+  LWIP_ERROR("mdns_resp_rename_netif: Not an mdns netif", (mdns != NULL), return ERR_VAL);
+
+  MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, len));
+  mdns->name[len] = '\0'; /* null termination in case new name is shorter than previous */
+
+  mdns_resp_restart(netif);
+
+  return ERR_OK;
+}
+
+/**
+ * @ingroup mdns
+ * Add a service to the selected network interface.
+ * @param netif The network interface to publish this service on
+ * @param name The name of the service
+ * @param service The service type, like "_http"
+ * @param proto The service protocol, DNSSD_PROTO_TCP for TCP ("_tcp") and DNSSD_PROTO_UDP
+ *              for others ("_udp")
+ * @param port The port the service listens to
+ * @param dns_ttl Validity time in seconds to send out for service data in DNS replies
+ * @param txt_fn Callback to get TXT data. Will be called each time a TXT reply is created to
+ *               allow dynamic replies.
+ * @param txt_data Userdata pointer for txt_fn
+ * @return service_id if the service was added to the netif, an err_t otherwise
+ */
+s8_t
+mdns_resp_add_service(struct netif *netif, const char *name, const char *service, enum mdns_sd_proto proto, u16_t port, u32_t dns_ttl, service_get_txt_fn_t txt_fn, void *txt_data)
+{
+  s8_t i;
+  s8_t slot = -1;
+  struct mdns_service *srv;
+  struct mdns_host *mdns;
+
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("mdns_resp_add_service: netif != NULL", netif);
+  mdns = NETIF_TO_HOST(netif);
+  LWIP_ERROR("mdns_resp_add_service: Not an mdns netif", (mdns != NULL), return ERR_VAL);
+
+  LWIP_ERROR("mdns_resp_add_service: Name too long", (strlen(name) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
+  LWIP_ERROR("mdns_resp_add_service: Service too long", (strlen(service) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
+  LWIP_ERROR("mdns_resp_add_service: Bad proto (need TCP or UDP)", (proto == DNSSD_PROTO_TCP || proto == DNSSD_PROTO_UDP), return ERR_VAL);
+
+  for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+    if (mdns->services[i] == NULL) {
+      slot = i;
+      break;
+    }
+  }
+  LWIP_ERROR("mdns_resp_add_service: Service list full (increase MDNS_MAX_SERVICES)", (slot >= 0), return ERR_MEM);
+
+  srv = (struct mdns_service *)mem_calloc(1, sizeof(struct mdns_service));
+  LWIP_ERROR("mdns_resp_add_service: Alloc failed", (srv != NULL), return ERR_MEM);
+
+  MEMCPY(&srv->name, name, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(name)));
+  MEMCPY(&srv->service, service, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(service)));
+  srv->txt_fn = txt_fn;
+  srv->txt_userdata = txt_data;
+  srv->proto = (u16_t)proto;
+  srv->port = port;
+  srv->dns_ttl = dns_ttl;
+
+  mdns->services[slot] = srv;
+
+  mdns_resp_restart(netif);
+
+  return slot;
+}
+
+/**
+ * @ingroup mdns
+ * Delete a service on the selected network interface.
+ * @param netif The network interface on which service should be removed
+ * @param slot The service slot number returned by mdns_resp_add_service
+ * @return ERR_OK if the service was removed from the netif, an err_t otherwise
+ */
+err_t
+mdns_resp_del_service(struct netif *netif, s8_t slot)
+{
+  struct mdns_host *mdns;
+  struct mdns_service *srv;
+  LWIP_ASSERT("mdns_resp_del_service: netif != NULL", netif);
+  mdns = NETIF_TO_HOST(netif);
+  LWIP_ERROR("mdns_resp_del_service: Not an mdns netif", (mdns != NULL), return ERR_VAL);
+  LWIP_ERROR("mdns_resp_del_service: Invalid Service ID", (slot >= 0) && (slot < MDNS_MAX_SERVICES), return ERR_VAL);
+  LWIP_ERROR("mdns_resp_del_service: Invalid Service ID", (mdns->services[slot] != NULL), return ERR_VAL);
+
+  srv = mdns->services[slot];
+  mdns->services[slot] = NULL;
+  mem_free(srv);
+  return ERR_OK;
+}
+
+/**
+ * @ingroup mdns
+ * Update name for an MDNS service.
+ * @param netif The network interface to activate.
+ * @param slot The service slot number returned by mdns_resp_add_service
+ * @param name The new name for the service
+ * @return ERR_OK if name could be set on service, an err_t otherwise
+ */
+err_t
+mdns_resp_rename_service(struct netif *netif, s8_t slot, const char *name)
+{
+  struct mdns_service *srv;
+  struct mdns_host *mdns;
+  size_t len;
+
+  LWIP_ASSERT_CORE_LOCKED();
+  len = strlen(name);
+  LWIP_ASSERT("mdns_resp_rename_service: netif != NULL", netif);
+  mdns = NETIF_TO_HOST(netif);
+  LWIP_ERROR("mdns_resp_rename_service: Not an mdns netif", (mdns != NULL), return ERR_VAL);
+  LWIP_ERROR("mdns_resp_rename_service: Name too long", (len <= MDNS_LABEL_MAXLEN), return ERR_VAL);
+  LWIP_ERROR("mdns_resp_rename_service: Invalid Service ID", (slot >= 0) && (slot < MDNS_MAX_SERVICES), return ERR_VAL);
+  LWIP_ERROR("mdns_resp_rename_service: Invalid Service ID", (mdns->services[slot] != NULL), return ERR_VAL);
+
+  srv = mdns->services[slot];
+
+  MEMCPY(&srv->name, name, LWIP_MIN(MDNS_LABEL_MAXLEN, len));
+  srv->name[len] = '\0'; /* null termination in case new name is shorter than previous */
+
+  mdns_resp_restart(netif);
+
+  return ERR_OK;
+}
+
+/**
+ * @ingroup mdns
+ * Call this function from inside the service_get_txt_fn_t callback to add text data.
+ * Buffer for TXT data is 256 bytes, and each field is prefixed with a length byte.
+ * @param service The service provided to the get_txt callback
+ * @param txt String to add to the TXT field.
+ * @param txt_len Length of string
+ * @return ERR_OK if the string was added to the reply, an err_t otherwise
+ */
+err_t
+mdns_resp_add_service_txtitem(struct mdns_service *service, const char *txt, u8_t txt_len)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("mdns_resp_add_service_txtitem: service != NULL", service);
+
+  /* Use a mdns_domain struct to store txt chunks since it is the same encoding */
+  return mdns_domain_add_label(&service->txtdata, txt, txt_len);
+}
+
+/**
+ * @ingroup mdns
+ * Send unsolicited answer containing all our known data
+ * @param netif The network interface to send on
+ */
+void
+mdns_resp_announce(struct netif *netif)
+{
+  struct mdns_host* mdns;
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ERROR("mdns_resp_announce: netif != NULL", (netif != NULL), return);
+
+  mdns = NETIF_TO_HOST(netif);
+  if (mdns == NULL) {
+    return;
+  }
+
+  if (mdns->probing_state == MDNS_PROBING_COMPLETE) {
+    /* Announce on IPv6 and IPv4 */
+#if LWIP_IPV6
+    mdns_announce(netif, IP6_ADDR_ANY);
+#endif
+#if LWIP_IPV4
+    if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+      mdns_announce(netif, IP4_ADDR_ANY);
+    }
+#endif
+  } /* else: ip address changed while probing was ongoing? @todo reset counter to restart? */
+}
+
+/** Register a callback function that is called if probing is completed successfully
+ * or with a conflict. */
+void
+mdns_resp_register_name_result_cb(mdns_name_result_cb_t cb)
+{
+  mdns_name_result_cb = cb;
+}
+
+/**
+ * @ingroup mdns
+ * Restart mdns responder. Call this when cable is connected after being disconnected or
+ * administrative interface is set up after being down
+ * @param netif The network interface to send on
+ */
+void
+mdns_resp_restart(struct netif *netif)
+{
+  struct mdns_host* mdns;
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ERROR("mdns_resp_restart: netif != NULL", (netif != NULL), return);
+
+  mdns = NETIF_TO_HOST(netif);
+  if (mdns == NULL) {
+    return;
+  }
+
+  if (mdns->probing_state == MDNS_PROBING_ONGOING) {
+    sys_untimeout(mdns_probe, netif);
+  }
+  /* @todo if we've failed 15 times within a 10 second period we MUST wait 5 seconds (or wait 5 seconds every time except first)*/
+  mdns->probes_sent = 0;
+  mdns->probing_state = MDNS_PROBING_ONGOING;
+  sys_timeout(MDNS_INITIAL_PROBE_DELAY_MS, mdns_probe, netif);
+}
+
+/**
+ * @ingroup mdns
+ * Initiate MDNS responder. Will open UDP sockets on port 5353
+ */
+void
+mdns_resp_init(void)
+{
+  err_t res;
+
+  /* LWIP_ASSERT_CORE_LOCKED(); is checked by udp_new() */
+
+  mdns_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+  LWIP_ASSERT("Failed to allocate pcb", mdns_pcb != NULL);
+#if LWIP_MULTICAST_TX_OPTIONS
+  udp_set_multicast_ttl(mdns_pcb, MDNS_TTL);
+#else
+  mdns_pcb->ttl = MDNS_TTL;
+#endif
+  res = udp_bind(mdns_pcb, IP_ANY_TYPE, LWIP_IANA_PORT_MDNS);
+  LWIP_UNUSED_ARG(res); /* in case of LWIP_NOASSERT */
+  LWIP_ASSERT("Failed to bind pcb", res == ERR_OK);
+  udp_recv(mdns_pcb, mdns_recv, NULL);
+
+  mdns_netif_client_id = netif_alloc_client_data_id();
+
+#if MDNS_RESP_USENETIF_EXTCALLBACK
+  /* register for netif events when started on first netif */
+  netif_add_ext_callback(&netif_callback, mdns_netif_ext_status_callback);
+#endif
+}
+
+#endif /* LWIP_MDNS_RESPONDER */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/mqtt/mqtt.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/mqtt/mqtt.c
new file mode 100644 (file)
index 0000000..595da77
--- /dev/null
@@ -0,0 +1,1462 @@
+/**
+ * @file
+ * MQTT client
+ *
+ * @defgroup mqtt MQTT client
+ * @ingroup apps
+ * @verbinclude mqtt_client.txt
+ */
+
+/*
+ * Copyright (c) 2016 Erik Andersson <erian747@gmail.com>
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack
+ *
+ * Author: Erik Andersson <erian747@gmail.com>
+ *
+ *
+ * @todo:
+ * - Handle large outgoing payloads for PUBLISH messages
+ * - Fix restriction of a single topic in each (UN)SUBSCRIBE message (protocol has support for multiple topics)
+ * - Add support for legacy MQTT protocol version
+ *
+ * Please coordinate changes and requests with Erik Andersson
+ * Erik Andersson <erian747@gmail.com>
+ *
+ */
+#include "lwip/apps/mqtt.h"
+#include "lwip/apps/mqtt_priv.h"
+#include "lwip/timeouts.h"
+#include "lwip/ip_addr.h"
+#include "lwip/mem.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+#include "lwip/altcp.h"
+#include "lwip/altcp_tcp.h"
+#include "lwip/altcp_tls.h"
+#include <string.h>
+
+#if LWIP_TCP && LWIP_CALLBACK_API
+
+/**
+ * MQTT_DEBUG: Default is off.
+ */
+#if !defined MQTT_DEBUG || defined __DOXYGEN__
+#define MQTT_DEBUG                  LWIP_DBG_OFF
+#endif
+
+#define MQTT_DEBUG_TRACE        (MQTT_DEBUG | LWIP_DBG_TRACE)
+#define MQTT_DEBUG_STATE        (MQTT_DEBUG | LWIP_DBG_STATE)
+#define MQTT_DEBUG_WARN         (MQTT_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#define MQTT_DEBUG_WARN_STATE   (MQTT_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
+#define MQTT_DEBUG_SERIOUS      (MQTT_DEBUG | LWIP_DBG_LEVEL_SERIOUS)
+
+
+
+/**
+ * MQTT client connection states
+ */
+enum {
+  TCP_DISCONNECTED,
+  TCP_CONNECTING,
+  MQTT_CONNECTING,
+  MQTT_CONNECTED
+};
+
+/**
+ * MQTT control message types
+ */
+enum mqtt_message_type {
+  MQTT_MSG_TYPE_CONNECT = 1,
+  MQTT_MSG_TYPE_CONNACK = 2,
+  MQTT_MSG_TYPE_PUBLISH = 3,
+  MQTT_MSG_TYPE_PUBACK = 4,
+  MQTT_MSG_TYPE_PUBREC = 5,
+  MQTT_MSG_TYPE_PUBREL = 6,
+  MQTT_MSG_TYPE_PUBCOMP = 7,
+  MQTT_MSG_TYPE_SUBSCRIBE = 8,
+  MQTT_MSG_TYPE_SUBACK = 9,
+  MQTT_MSG_TYPE_UNSUBSCRIBE = 10,
+  MQTT_MSG_TYPE_UNSUBACK = 11,
+  MQTT_MSG_TYPE_PINGREQ = 12,
+  MQTT_MSG_TYPE_PINGRESP = 13,
+  MQTT_MSG_TYPE_DISCONNECT = 14
+};
+
+/** Helpers to extract control packet type and qos from first byte in fixed header */
+#define MQTT_CTL_PACKET_TYPE(fixed_hdr_byte0) ((fixed_hdr_byte0 & 0xf0) >> 4)
+#define MQTT_CTL_PACKET_QOS(fixed_hdr_byte0) ((fixed_hdr_byte0 & 0x6) >> 1)
+
+/**
+ * MQTT connect flags, only used in CONNECT message
+ */
+enum mqtt_connect_flag {
+  MQTT_CONNECT_FLAG_USERNAME = 1 << 7,
+  MQTT_CONNECT_FLAG_PASSWORD = 1 << 6,
+  MQTT_CONNECT_FLAG_WILL_RETAIN = 1 << 5,
+  MQTT_CONNECT_FLAG_WILL = 1 << 2,
+  MQTT_CONNECT_FLAG_CLEAN_SESSION = 1 << 1
+};
+
+
+static void mqtt_cyclic_timer(void *arg);
+
+#if defined(LWIP_DEBUG)
+static const char *const mqtt_message_type_str[15] = {
+  "UNDEFINED",
+  "CONNECT",
+  "CONNACK",
+  "PUBLISH",
+  "PUBACK",
+  "PUBREC",
+  "PUBREL",
+  "PUBCOMP",
+  "SUBSCRIBE",
+  "SUBACK",
+  "UNSUBSCRIBE",
+  "UNSUBACK",
+  "PINGREQ",
+  "PINGRESP",
+  "DISCONNECT"
+};
+
+/**
+ * Message type value to string
+ * @param msg_type see enum mqtt_message_type
+ *
+ * @return Control message type text string
+ */
+static const char *
+mqtt_msg_type_to_str(u8_t msg_type)
+{
+  if (msg_type >= LWIP_ARRAYSIZE(mqtt_message_type_str)) {
+    msg_type = 0;
+  }
+  return mqtt_message_type_str[msg_type];
+}
+
+#endif
+
+
+/**
+ * Generate MQTT packet identifier
+ * @param client MQTT client
+ * @return New packet identifier, range 1 to 65535
+ */
+static u16_t
+msg_generate_packet_id(mqtt_client_t *client)
+{
+  client->pkt_id_seq++;
+  if (client->pkt_id_seq == 0) {
+    client->pkt_id_seq++;
+  }
+  return client->pkt_id_seq;
+}
+
+/*--------------------------------------------------------------------------------------------------------------------- */
+/* Output ring buffer */
+
+/** Add single item to ring buffer */
+static void
+mqtt_ringbuf_put(struct mqtt_ringbuf_t *rb, u8_t item)
+{
+  rb->buf[rb->put] = item;
+  rb->put++;
+  if (rb->put >= MQTT_OUTPUT_RINGBUF_SIZE) {
+    rb->put = 0;
+  }
+}
+
+/** Return pointer to ring buffer get position */
+static u8_t *
+mqtt_ringbuf_get_ptr(struct mqtt_ringbuf_t *rb)
+{
+  return &rb->buf[rb->get];
+}
+
+static void
+mqtt_ringbuf_advance_get_idx(struct mqtt_ringbuf_t *rb, u16_t len)
+{
+  LWIP_ASSERT("mqtt_ringbuf_advance_get_idx: len < MQTT_OUTPUT_RINGBUF_SIZE", len < MQTT_OUTPUT_RINGBUF_SIZE);
+
+  rb->get += len;
+  if (rb->get >= MQTT_OUTPUT_RINGBUF_SIZE) {
+    rb->get = rb->get - MQTT_OUTPUT_RINGBUF_SIZE;
+  }
+}
+
+/** Return number of bytes in ring buffer */
+static u16_t
+mqtt_ringbuf_len(struct mqtt_ringbuf_t *rb)
+{
+  u32_t len = rb->put - rb->get;
+  if (len > 0xFFFF) {
+    len += MQTT_OUTPUT_RINGBUF_SIZE;
+  }
+  return (u16_t)len;
+}
+
+/** Return number of bytes free in ring buffer */
+#define mqtt_ringbuf_free(rb) (MQTT_OUTPUT_RINGBUF_SIZE - mqtt_ringbuf_len(rb))
+
+/** Return number of bytes possible to read without wrapping around */
+#define mqtt_ringbuf_linear_read_length(rb) LWIP_MIN(mqtt_ringbuf_len(rb), (MQTT_OUTPUT_RINGBUF_SIZE - (rb)->get))
+
+/**
+ * Try send as many bytes as possible from output ring buffer
+ * @param rb Output ring buffer
+ * @param tpcb TCP connection handle
+ */
+static void
+mqtt_output_send(struct mqtt_ringbuf_t *rb, struct altcp_pcb *tpcb)
+{
+  err_t err;
+  u8_t wrap = 0;
+  u16_t ringbuf_lin_len = mqtt_ringbuf_linear_read_length(rb);
+  u16_t send_len = altcp_sndbuf(tpcb);
+  LWIP_ASSERT("mqtt_output_send: tpcb != NULL", tpcb != NULL);
+
+  if (send_len == 0 || ringbuf_lin_len == 0) {
+    return;
+  }
+
+  LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_output_send: tcp_sndbuf: %d bytes, ringbuf_linear_available: %d, get %d, put %d\n",
+                                 send_len, ringbuf_lin_len, rb->get, rb->put));
+
+  if (send_len > ringbuf_lin_len) {
+    /* Space in TCP output buffer is larger than available in ring buffer linear portion */
+    send_len = ringbuf_lin_len;
+    /* Wrap around if more data in ring buffer after linear portion */
+    wrap = (mqtt_ringbuf_len(rb) > ringbuf_lin_len);
+  }
+  err = altcp_write(tpcb, mqtt_ringbuf_get_ptr(rb), send_len, TCP_WRITE_FLAG_COPY | (wrap ? TCP_WRITE_FLAG_MORE : 0));
+  if ((err == ERR_OK) && wrap) {
+    mqtt_ringbuf_advance_get_idx(rb, send_len);
+    /* Use the lesser one of ring buffer linear length and TCP send buffer size */
+    send_len = LWIP_MIN(altcp_sndbuf(tpcb), mqtt_ringbuf_linear_read_length(rb));
+    err = altcp_write(tpcb, mqtt_ringbuf_get_ptr(rb), send_len, TCP_WRITE_FLAG_COPY);
+  }
+
+  if (err == ERR_OK) {
+    mqtt_ringbuf_advance_get_idx(rb, send_len);
+    /* Flush */
+    altcp_output(tpcb);
+  } else {
+    LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_output_send: Send failed with err %d (\"%s\")\n", err, lwip_strerr(err)));
+  }
+}
+
+
+
+/*--------------------------------------------------------------------------------------------------------------------- */
+/* Request queue */
+
+/**
+ * Create request item
+ * @param r_objs Pointer to request objects
+ * @param r_objs_len Number of array entries
+ * @param pkt_id Packet identifier of request
+ * @param cb Packet callback to call when requests lifetime ends
+ * @param arg Parameter following callback
+ * @return Request or NULL if failed to create
+ */
+static struct mqtt_request_t *
+mqtt_create_request(struct mqtt_request_t *r_objs, size_t r_objs_len, u16_t pkt_id, mqtt_request_cb_t cb, void *arg)
+{
+  struct mqtt_request_t *r = NULL;
+  u8_t n;
+  LWIP_ASSERT("mqtt_create_request: r_objs != NULL", r_objs != NULL);
+  for (n = 0; n < r_objs_len; n++) {
+    /* Item point to itself if not in use */
+    if (r_objs[n].next == &r_objs[n]) {
+      r = &r_objs[n];
+      r->next = NULL;
+      r->cb = cb;
+      r->arg = arg;
+      r->pkt_id = pkt_id;
+      break;
+    }
+  }
+  return r;
+}
+
+
+/**
+ * Append request to pending request queue
+ * @param tail Pointer to request queue tail pointer
+ * @param r Request to append
+ */
+static void
+mqtt_append_request(struct mqtt_request_t **tail, struct mqtt_request_t *r)
+{
+  struct mqtt_request_t *head = NULL;
+  s16_t time_before = 0;
+  struct mqtt_request_t *iter;
+
+  LWIP_ASSERT("mqtt_append_request: tail != NULL", tail != NULL);
+
+  /* Iterate trough queue to find head, and count total timeout time */
+  for (iter = *tail; iter != NULL; iter = iter->next) {
+    time_before += iter->timeout_diff;
+    head = iter;
+  }
+
+  LWIP_ASSERT("mqtt_append_request: time_before <= MQTT_REQ_TIMEOUT", time_before <= MQTT_REQ_TIMEOUT);
+  r->timeout_diff = MQTT_REQ_TIMEOUT - time_before;
+  if (head == NULL) {
+    *tail = r;
+  } else {
+    head->next = r;
+  }
+}
+
+
+/**
+ * Delete request item
+ * @param r Request item to delete
+ */
+static void
+mqtt_delete_request(struct mqtt_request_t *r)
+{
+  if (r != NULL) {
+    r->next = r;
+  }
+}
+
+/**
+ * Remove a request item with a specific packet identifier from request queue
+ * @param tail Pointer to request queue tail pointer
+ * @param pkt_id Packet identifier of request to take
+ * @return Request item if found, NULL if not
+ */
+static struct mqtt_request_t *
+mqtt_take_request(struct mqtt_request_t **tail, u16_t pkt_id)
+{
+  struct mqtt_request_t *iter = NULL, *prev = NULL;
+  LWIP_ASSERT("mqtt_take_request: tail != NULL", tail != NULL);
+  /* Search all request for pkt_id */
+  for (iter = *tail; iter != NULL; iter = iter->next) {
+    if (iter->pkt_id == pkt_id) {
+      break;
+    }
+    prev = iter;
+  }
+
+  /* If request was found */
+  if (iter != NULL) {
+    /* unchain */
+    if (prev == NULL) {
+      *tail = iter->next;
+    } else {
+      prev->next = iter->next;
+    }
+    /* If exists, add remaining timeout time for the request to next */
+    if (iter->next != NULL) {
+      iter->next->timeout_diff += iter->timeout_diff;
+    }
+    iter->next = NULL;
+  }
+  return iter;
+}
+
+/**
+ * Handle requests timeout
+ * @param tail Pointer to request queue tail pointer
+ * @param t Time since last call in seconds
+ */
+static void
+mqtt_request_time_elapsed(struct mqtt_request_t **tail, u8_t t)
+{
+  struct mqtt_request_t *r;
+  LWIP_ASSERT("mqtt_request_time_elapsed: tail != NULL", tail != NULL);
+  r = *tail;
+  while (t > 0 && r != NULL) {
+    if (t >= r->timeout_diff) {
+      t -= (u8_t)r->timeout_diff;
+      /* Unchain */
+      *tail = r->next;
+      /* Notify upper layer about timeout */
+      if (r->cb != NULL) {
+        r->cb(r->arg, ERR_TIMEOUT);
+      }
+      mqtt_delete_request(r);
+      /* Tail might be be modified in callback, so re-read it in every iteration */
+      r = *(struct mqtt_request_t *const volatile *)tail;
+    } else {
+      r->timeout_diff -= t;
+      t = 0;
+    }
+  }
+}
+
+/**
+ * Free all request items
+ * @param tail Pointer to request queue tail pointer
+ */
+static void
+mqtt_clear_requests(struct mqtt_request_t **tail)
+{
+  struct mqtt_request_t *iter, *next;
+  LWIP_ASSERT("mqtt_clear_requests: tail != NULL", tail != NULL);
+  for (iter = *tail; iter != NULL; iter = next) {
+    next = iter->next;
+    mqtt_delete_request(iter);
+  }
+  *tail = NULL;
+}
+/**
+ * Initialize all request items
+ * @param r_objs Pointer to request objects
+ * @param r_objs_len Number of array entries
+ */
+static void
+mqtt_init_requests(struct mqtt_request_t *r_objs, size_t r_objs_len)
+{
+  u8_t n;
+  LWIP_ASSERT("mqtt_init_requests: r_objs != NULL", r_objs != NULL);
+  for (n = 0; n < r_objs_len; n++) {
+    /* Item pointing to itself indicates unused */
+    r_objs[n].next = &r_objs[n];
+  }
+}
+
+/*--------------------------------------------------------------------------------------------------------------------- */
+/* Output message build helpers */
+
+
+static void
+mqtt_output_append_u8(struct mqtt_ringbuf_t *rb, u8_t value)
+{
+  mqtt_ringbuf_put(rb, value);
+}
+
+static
+void mqtt_output_append_u16(struct mqtt_ringbuf_t *rb, u16_t value)
+{
+  mqtt_ringbuf_put(rb, value >> 8);
+  mqtt_ringbuf_put(rb, value & 0xff);
+}
+
+static void
+mqtt_output_append_buf(struct mqtt_ringbuf_t *rb, const void *data, u16_t length)
+{
+  u16_t n;
+  for (n = 0; n < length; n++) {
+    mqtt_ringbuf_put(rb, ((const u8_t *)data)[n]);
+  }
+}
+
+static void
+mqtt_output_append_string(struct mqtt_ringbuf_t *rb, const char *str, u16_t length)
+{
+  u16_t n;
+  mqtt_ringbuf_put(rb, length >> 8);
+  mqtt_ringbuf_put(rb, length & 0xff);
+  for (n = 0; n < length; n++) {
+    mqtt_ringbuf_put(rb, str[n]);
+  }
+}
+
+/**
+ * Append fixed header
+ * @param rb Output ring buffer
+ * @param msg_type see enum mqtt_message_type
+ * @param fdup MQTT DUP flag
+ * @param fqos MQTT QoS field
+ * @param fretain MQTT retain flag
+ * @param r_length Remaining length after fixed header
+ */
+
+static void
+mqtt_output_append_fixed_header(struct mqtt_ringbuf_t *rb, u8_t msg_type, u8_t fdup,
+                                u8_t fqos, u8_t fretain, u16_t r_length)
+{
+  /* Start with control byte */
+  mqtt_output_append_u8(rb, (((msg_type & 0x0f) << 4) | ((fdup & 1) << 3) | ((fqos & 3) << 1) | (fretain & 1)));
+  /* Encode remaining length field */
+  do {
+    mqtt_output_append_u8(rb, (r_length & 0x7f) | (r_length >= 128 ? 0x80 : 0));
+    r_length >>= 7;
+  } while (r_length > 0);
+}
+
+
+/**
+ * Check output buffer space
+ * @param rb Output ring buffer
+ * @param r_length Remaining length after fixed header
+ * @return 1 if message will fit, 0 if not enough buffer space
+ */
+static u8_t
+mqtt_output_check_space(struct mqtt_ringbuf_t *rb, u16_t r_length)
+{
+  /* Start with length of type byte + remaining length */
+  u16_t total_len = 1 + r_length;
+
+  LWIP_ASSERT("mqtt_output_check_space: rb != NULL", rb != NULL);
+
+  /* Calculate number of required bytes to contain the remaining bytes field and add to total*/
+  do {
+    total_len++;
+    r_length >>= 7;
+  } while (r_length > 0);
+
+  return (total_len <= mqtt_ringbuf_free(rb));
+}
+
+
+/**
+ * Close connection to server
+ * @param client MQTT client
+ * @param reason Reason for disconnection
+ */
+static void
+mqtt_close(mqtt_client_t *client, mqtt_connection_status_t reason)
+{
+  LWIP_ASSERT("mqtt_close: client != NULL", client != NULL);
+
+  /* Bring down TCP connection if not already done */
+  if (client->conn != NULL) {
+    err_t res;
+    altcp_recv(client->conn, NULL);
+    altcp_err(client->conn,  NULL);
+    altcp_sent(client->conn, NULL);
+    res = altcp_close(client->conn);
+    if (res != ERR_OK) {
+      altcp_abort(client->conn);
+      LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_close: Close err=%s\n", lwip_strerr(res)));
+    }
+    client->conn = NULL;
+  }
+
+  /* Remove all pending requests */
+  mqtt_clear_requests(&client->pend_req_queue);
+  /* Stop cyclic timer */
+  sys_untimeout(mqtt_cyclic_timer, client);
+
+  /* Notify upper layer of disconnection if changed state */
+  if (client->conn_state != TCP_DISCONNECTED) {
+
+    client->conn_state = TCP_DISCONNECTED;
+    if (client->connect_cb != NULL) {
+      client->connect_cb(client, client->connect_arg, reason);
+    }
+  }
+}
+
+
+/**
+ * Interval timer, called every MQTT_CYCLIC_TIMER_INTERVAL seconds in MQTT_CONNECTING and MQTT_CONNECTED states
+ * @param arg MQTT client
+ */
+static void
+mqtt_cyclic_timer(void *arg)
+{
+  u8_t restart_timer = 1;
+  mqtt_client_t *client = (mqtt_client_t *)arg;
+  LWIP_ASSERT("mqtt_cyclic_timer: client != NULL", client != NULL);
+
+  if (client->conn_state == MQTT_CONNECTING) {
+    client->cyclic_tick++;
+    if ((client->cyclic_tick * MQTT_CYCLIC_TIMER_INTERVAL) >= MQTT_CONNECT_TIMOUT) {
+      LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_cyclic_timer: CONNECT attempt to server timed out\n"));
+      /* Disconnect TCP */
+      mqtt_close(client, MQTT_CONNECT_TIMEOUT);
+      restart_timer = 0;
+    }
+  } else if (client->conn_state == MQTT_CONNECTED) {
+    /* Handle timeout for pending requests */
+    mqtt_request_time_elapsed(&client->pend_req_queue, MQTT_CYCLIC_TIMER_INTERVAL);
+
+    /* keep_alive > 0 means keep alive functionality shall be used */
+    if (client->keep_alive > 0) {
+
+      client->server_watchdog++;
+      /* If reception from server has been idle for 1.5*keep_alive time, server is considered unresponsive */
+      if ((client->server_watchdog * MQTT_CYCLIC_TIMER_INTERVAL) > (client->keep_alive + client->keep_alive / 2)) {
+        LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_cyclic_timer: Server incoming keep-alive timeout\n"));
+        mqtt_close(client, MQTT_CONNECT_TIMEOUT);
+        restart_timer = 0;
+      }
+
+      /* If time for a keep alive message to be sent, transmission has been idle for keep_alive time */
+      if ((client->cyclic_tick * MQTT_CYCLIC_TIMER_INTERVAL) >= client->keep_alive) {
+        LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_cyclic_timer: Sending keep-alive message to server\n"));
+        if (mqtt_output_check_space(&client->output, 0) != 0) {
+          mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0, 0);
+          client->cyclic_tick = 0;
+        }
+      } else {
+        client->cyclic_tick++;
+      }
+    }
+  } else {
+    LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_cyclic_timer: Timer should not be running in state %d\n", client->conn_state));
+    restart_timer = 0;
+  }
+  if (restart_timer) {
+    sys_timeout(MQTT_CYCLIC_TIMER_INTERVAL * 1000, mqtt_cyclic_timer, arg);
+  }
+}
+
+
+/**
+ * Send PUBACK, PUBREC or PUBREL response message
+ * @param client MQTT client
+ * @param msg PUBACK, PUBREC or PUBREL
+ * @param pkt_id Packet identifier
+ * @param qos QoS value
+ * @return ERR_OK if successful, ERR_MEM if out of memory
+ */
+static err_t
+pub_ack_rec_rel_response(mqtt_client_t *client, u8_t msg, u16_t pkt_id, u8_t qos)
+{
+  err_t err = ERR_OK;
+  if (mqtt_output_check_space(&client->output, 2)) {
+    mqtt_output_append_fixed_header(&client->output, msg, 0, qos, 0, 2);
+    mqtt_output_append_u16(&client->output, pkt_id);
+    mqtt_output_send(&client->output, client->conn);
+  } else {
+    LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("pub_ack_rec_rel_response: OOM creating response: %s with pkt_id: %d\n",
+                                   mqtt_msg_type_to_str(msg), pkt_id));
+    err = ERR_MEM;
+  }
+  return err;
+}
+
+/**
+ * Subscribe response from server
+ * @param r Matching request
+ * @param result Result code from server
+ */
+static void
+mqtt_incomming_suback(struct mqtt_request_t *r, u8_t result)
+{
+  if (r->cb != NULL) {
+    r->cb(r->arg, result < 3 ? ERR_OK : ERR_ABRT);
+  }
+}
+
+
+/**
+ * Complete MQTT message received or buffer full
+ * @param client MQTT client
+ * @param fixed_hdr_len length of fixed header
+ * @param length length received part
+ * @param remaining_length Remaining length of complete message
+ */
+static mqtt_connection_status_t
+mqtt_message_received(mqtt_client_t *client, u8_t fixed_hdr_len, u16_t length, u32_t remaining_length)
+{
+  mqtt_connection_status_t res = MQTT_CONNECT_ACCEPTED;
+
+  u8_t *var_hdr_payload = client->rx_buffer + fixed_hdr_len;
+  size_t var_hdr_payload_bufsize = sizeof(client->rx_buffer) - fixed_hdr_len;
+
+  /* Control packet type */
+  u8_t pkt_type = MQTT_CTL_PACKET_TYPE(client->rx_buffer[0]);
+  u16_t pkt_id = 0;
+
+  LWIP_ASSERT("fixed_hdr_len <= client->msg_idx", fixed_hdr_len <= client->msg_idx);
+  LWIP_ERROR("buffer length mismatch", fixed_hdr_len + length <= MQTT_VAR_HEADER_BUFFER_LEN,
+             return MQTT_CONNECT_DISCONNECTED);
+
+  if (pkt_type == MQTT_MSG_TYPE_CONNACK) {
+    if (client->conn_state == MQTT_CONNECTING) {
+      if (length < 2) {
+        LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received short CONNACK message\n"));
+        goto out_disconnect;
+      }
+      /* Get result code from CONNACK */
+      res = (mqtt_connection_status_t)var_hdr_payload[1];
+      LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_message_received: Connect response code %d\n", res));
+      if (res == MQTT_CONNECT_ACCEPTED) {
+        /* Reset cyclic_tick when changing to connected state */
+        client->cyclic_tick = 0;
+        client->conn_state = MQTT_CONNECTED;
+        /* Notify upper layer */
+        if (client->connect_cb != 0) {
+          client->connect_cb(client, client->connect_arg, res);
+        }
+      }
+    } else {
+      LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_message_received: Received CONNACK in connected state\n"));
+    }
+  } else if (pkt_type == MQTT_MSG_TYPE_PINGRESP) {
+    LWIP_DEBUGF(MQTT_DEBUG_TRACE, ( "mqtt_message_received: Received PINGRESP from server\n"));
+
+  } else if (pkt_type == MQTT_MSG_TYPE_PUBLISH) {
+    u16_t payload_offset = 0;
+    u16_t payload_length = length;
+    u8_t qos = MQTT_CTL_PACKET_QOS(client->rx_buffer[0]);
+
+    if (client->msg_idx <= MQTT_VAR_HEADER_BUFFER_LEN) {
+      /* Should have topic and pkt id*/
+      u8_t *topic;
+      u16_t after_topic;
+      u8_t bkp;
+      u16_t topic_len;
+      u16_t qos_len = (qos ? 2U : 0U);
+      if (length < 2 + qos_len) {
+        LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received short PUBLISH packet\n"));
+        goto out_disconnect;
+      }
+      topic_len = var_hdr_payload[0];
+      topic_len = (topic_len << 8) + (u16_t)(var_hdr_payload[1]);
+      if ((topic_len > length - (2 + qos_len)) ||
+          (topic_len > var_hdr_payload_bufsize - (2 + qos_len))) {
+        LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received short PUBLISH packet (topic)\n"));
+        goto out_disconnect;
+      }
+
+      topic = var_hdr_payload + 2;
+      after_topic = 2 + topic_len;
+      /* Check buffer length, add one byte even for QoS 0 so that zero termination will fit */
+      if ((after_topic + (qos ? 2U : 1U)) > var_hdr_payload_bufsize) {
+        LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_message_received: Receive buffer can not fit topic + pkt_id\n"));
+        goto out_disconnect;
+      }
+
+      /* id for QoS 1 and 2 */
+      if (qos > 0) {
+        if (length < after_topic + 2U) {
+          LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received short PUBLISH packet (after_topic)\n"));
+          goto out_disconnect;
+        }
+        client->inpub_pkt_id = ((u16_t)var_hdr_payload[after_topic] << 8) + (u16_t)var_hdr_payload[after_topic + 1];
+        after_topic += 2;
+      } else {
+        client->inpub_pkt_id = 0;
+      }
+      /* Take backup of byte after topic */
+      bkp = topic[topic_len];
+      /* Zero terminate string */
+      topic[topic_len] = 0;
+      /* Payload data remaining in receive buffer */
+      payload_length = length - after_topic;
+      payload_offset = after_topic;
+
+      LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_incomming_publish: Received message with QoS %d at topic: %s, payload length %"U32_F"\n",
+                                     qos, topic, remaining_length + payload_length));
+      if (client->pub_cb != NULL) {
+        client->pub_cb(client->inpub_arg, (const char *)topic, remaining_length + payload_length);
+      }
+      /* Restore byte after topic */
+      topic[topic_len] = bkp;
+    }
+    if (payload_length > 0 || remaining_length == 0) {
+      if (length < (size_t)(payload_offset + payload_length)) {
+        LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received short packet (payload)\n"));
+        goto out_disconnect;
+      }
+      if (client->data_cb != NULL) {
+        client->data_cb(client->inpub_arg, var_hdr_payload + payload_offset, payload_length, remaining_length == 0 ? MQTT_DATA_FLAG_LAST : 0);
+      }
+      /* Reply if QoS > 0 */
+      if (remaining_length == 0 && qos > 0) {
+        /* Send PUBACK for QoS 1 or PUBREC for QoS 2 */
+        u8_t resp_msg = (qos == 1) ? MQTT_MSG_TYPE_PUBACK : MQTT_MSG_TYPE_PUBREC;
+        LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_incomming_publish: Sending publish response: %s with pkt_id: %d\n",
+                                       mqtt_msg_type_to_str(resp_msg), client->inpub_pkt_id));
+        pub_ack_rec_rel_response(client, resp_msg, client->inpub_pkt_id, 0);
+      }
+    }
+  } else {
+    /* Get packet identifier */
+    pkt_id = (u16_t)var_hdr_payload[0] << 8;
+    pkt_id |= (u16_t)var_hdr_payload[1];
+    if (pkt_id == 0) {
+      LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_message_received: Got message with illegal packet identifier: 0\n"));
+      goto out_disconnect;
+    }
+    if (pkt_type == MQTT_MSG_TYPE_PUBREC) {
+      LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_message_received: PUBREC, sending PUBREL with pkt_id: %d\n", pkt_id));
+      pub_ack_rec_rel_response(client, MQTT_MSG_TYPE_PUBREL, pkt_id, 1);
+
+    } else if (pkt_type == MQTT_MSG_TYPE_PUBREL) {
+      LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_message_received: PUBREL, sending PUBCOMP response with pkt_id: %d\n", pkt_id));
+      pub_ack_rec_rel_response(client, MQTT_MSG_TYPE_PUBCOMP, pkt_id, 0);
+
+    } else if (pkt_type == MQTT_MSG_TYPE_SUBACK || pkt_type == MQTT_MSG_TYPE_UNSUBACK ||
+               pkt_type == MQTT_MSG_TYPE_PUBCOMP || pkt_type == MQTT_MSG_TYPE_PUBACK) {
+      struct mqtt_request_t *r = mqtt_take_request(&client->pend_req_queue, pkt_id);
+      if (r != NULL) {
+        LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_message_received: %s response with id %d\n", mqtt_msg_type_to_str(pkt_type), pkt_id));
+        if (pkt_type == MQTT_MSG_TYPE_SUBACK) {
+          if (length < 3) {
+            LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_message_received: To small SUBACK packet\n"));
+            goto out_disconnect;
+          } else {
+            mqtt_incomming_suback(r, var_hdr_payload[2]);
+          }
+        } else if (r->cb != NULL) {
+          r->cb(r->arg, ERR_OK);
+        }
+        mqtt_delete_request(r);
+      } else {
+        LWIP_DEBUGF(MQTT_DEBUG_WARN, ( "mqtt_message_received: Received %s reply, with wrong pkt_id: %d\n", mqtt_msg_type_to_str(pkt_type), pkt_id));
+      }
+    } else {
+      LWIP_DEBUGF(MQTT_DEBUG_WARN, ( "mqtt_message_received: Received unknown message type: %d\n", pkt_type));
+      goto out_disconnect;
+    }
+  }
+  return res;
+out_disconnect:
+  return MQTT_CONNECT_DISCONNECTED;
+}
+
+
+/**
+ * MQTT incoming message parser
+ * @param client MQTT client
+ * @param p PBUF chain of received data
+ * @return Connection status
+ */
+static mqtt_connection_status_t
+mqtt_parse_incoming(mqtt_client_t *client, struct pbuf *p)
+{
+  u16_t in_offset = 0;
+  u32_t msg_rem_len = 0;
+  u8_t fixed_hdr_len = 0;
+  u8_t b = 0;
+
+  while (p->tot_len > in_offset) {
+    /* We ALWAYS parse the header here first. Even if the header was not
+       included in this segment, we re-parse it here by buffering it in
+       client->rx_buffer. client->msg_idx keeps track of this. */
+    if ((fixed_hdr_len < 2) || ((b & 0x80) != 0)) {
+
+      if (fixed_hdr_len < client->msg_idx) {
+        /* parse header from old pbuf (buffered in client->rx_buffer) */
+        b = client->rx_buffer[fixed_hdr_len];
+      } else {
+        /* parse header from this pbuf and save it in client->rx_buffer in case
+           it comes in segmented */
+        b = pbuf_get_at(p, in_offset++);
+        client->rx_buffer[client->msg_idx++] = b;
+      }
+      fixed_hdr_len++;
+
+      if (fixed_hdr_len >= 2) {
+        /* fixed header contains at least 2 bytes but can contain more, depending on
+           'remaining length'. All bytes but the last of this have 0x80 set to
+           indicate more bytes are coming. */
+        msg_rem_len |= (u32_t)(b & 0x7f) << ((fixed_hdr_len - 2) * 7);
+        if ((b & 0x80) == 0) {
+          /* fixed header is done */
+          LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_parse_incoming: Remaining length after fixed header: %"U32_F"\n", msg_rem_len));
+          if (msg_rem_len == 0) {
+            /* Complete message with no extra headers of payload received */
+            mqtt_message_received(client, fixed_hdr_len, 0, 0);
+            client->msg_idx = 0;
+            fixed_hdr_len = 0;
+          } else {
+            /* Bytes remaining in message (changes remaining length if this is
+               not the first segment of this message) */
+            msg_rem_len = (msg_rem_len + fixed_hdr_len) - client->msg_idx;
+          }
+        }
+      }
+    } else {
+      /* Fixed header has been parsed, parse variable header */
+      u16_t cpy_len, buffer_space;
+
+      /* Allow to copy the lesser one of available length in input data or bytes remaining in message */
+      cpy_len = (u16_t)LWIP_MIN((u16_t)(p->tot_len - in_offset), msg_rem_len);
+
+      /* Limit to available space in buffer */
+      buffer_space = MQTT_VAR_HEADER_BUFFER_LEN - fixed_hdr_len;
+      if (cpy_len > buffer_space) {
+        cpy_len = buffer_space;
+      }
+      pbuf_copy_partial(p, client->rx_buffer + fixed_hdr_len, cpy_len, in_offset);
+
+      /* Advance get and put indexes  */
+      client->msg_idx += cpy_len;
+      in_offset += cpy_len;
+      msg_rem_len -= cpy_len;
+
+      LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_parse_incoming: msg_idx: %"U32_F", cpy_len: %"U16_F", remaining %"U32_F"\n", client->msg_idx, cpy_len, msg_rem_len));
+      if ((msg_rem_len == 0) || (cpy_len == buffer_space)) {
+        /* Whole message received or buffer is full */
+        mqtt_connection_status_t res = mqtt_message_received(client, fixed_hdr_len, cpy_len, msg_rem_len);
+        if (res != MQTT_CONNECT_ACCEPTED) {
+          return res;
+        }
+        if (msg_rem_len == 0) {
+          /* Reset parser state */
+          client->msg_idx = 0;
+          /* msg_tot_len = 0; */
+          fixed_hdr_len = 0;
+        }
+      }
+    }
+  }
+  return MQTT_CONNECT_ACCEPTED;
+}
+
+
+/**
+ * TCP received callback function. @see tcp_recv_fn
+ * @param arg MQTT client
+ * @param p PBUF chain of received data
+ * @param err Passed as return value if not ERR_OK
+ * @return ERR_OK or err passed into callback
+ */
+static err_t
+mqtt_tcp_recv_cb(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+  mqtt_client_t *client = (mqtt_client_t *)arg;
+  LWIP_ASSERT("mqtt_tcp_recv_cb: client != NULL", client != NULL);
+  LWIP_ASSERT("mqtt_tcp_recv_cb: client->conn == pcb", client->conn == pcb);
+
+  if (p == NULL) {
+    LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_tcp_recv_cb: Recv pbuf=NULL, remote has closed connection\n"));
+    mqtt_close(client, MQTT_CONNECT_DISCONNECTED);
+  } else {
+    mqtt_connection_status_t res;
+    if (err != ERR_OK) {
+      LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_tcp_recv_cb: Recv err=%d\n", err));
+      pbuf_free(p);
+      return err;
+    }
+
+    /* Tell remote that data has been received */
+    altcp_recved(pcb, p->tot_len);
+    res = mqtt_parse_incoming(client, p);
+    pbuf_free(p);
+
+    if (res != MQTT_CONNECT_ACCEPTED) {
+      mqtt_close(client, res);
+    }
+    /* If keep alive functionality is used */
+    if (client->keep_alive != 0) {
+      /* Reset server alive watchdog */
+      client->server_watchdog = 0;
+    }
+
+  }
+  return ERR_OK;
+}
+
+
+/**
+ * TCP data sent callback function. @see tcp_sent_fn
+ * @param arg MQTT client
+ * @param tpcb TCP connection handle
+ * @param len Number of bytes sent
+ * @return ERR_OK
+ */
+static err_t
+mqtt_tcp_sent_cb(void *arg, struct altcp_pcb *tpcb, u16_t len)
+{
+  mqtt_client_t *client = (mqtt_client_t *)arg;
+
+  LWIP_UNUSED_ARG(tpcb);
+  LWIP_UNUSED_ARG(len);
+
+  if (client->conn_state == MQTT_CONNECTED) {
+    struct mqtt_request_t *r;
+
+    /* Reset keep-alive send timer and server watchdog */
+    client->cyclic_tick = 0;
+    client->server_watchdog = 0;
+    /* QoS 0 publish has no response from server, so call its callbacks here */
+    while ((r = mqtt_take_request(&client->pend_req_queue, 0)) != NULL) {
+      LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_tcp_sent_cb: Calling QoS 0 publish complete callback\n"));
+      if (r->cb != NULL) {
+        r->cb(r->arg, ERR_OK);
+      }
+      mqtt_delete_request(r);
+    }
+    /* Try send any remaining buffers from output queue */
+    mqtt_output_send(&client->output, client->conn);
+  }
+  return ERR_OK;
+}
+
+/**
+ * TCP error callback function. @see tcp_err_fn
+ * @param arg MQTT client
+ * @param err Error encountered
+ */
+static void
+mqtt_tcp_err_cb(void *arg, err_t err)
+{
+  mqtt_client_t *client = (mqtt_client_t *)arg;
+  LWIP_UNUSED_ARG(err); /* only used for debug output */
+  LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_tcp_err_cb: TCP error callback: error %d, arg: %p\n", err, arg));
+  LWIP_ASSERT("mqtt_tcp_err_cb: client != NULL", client != NULL);
+  /* Set conn to null before calling close as pcb is already deallocated*/
+  client->conn = 0;
+  mqtt_close(client, MQTT_CONNECT_DISCONNECTED);
+}
+
+/**
+ * TCP poll callback function. @see tcp_poll_fn
+ * @param arg MQTT client
+ * @param tpcb TCP connection handle
+ * @return err ERR_OK
+ */
+static err_t
+mqtt_tcp_poll_cb(void *arg, struct altcp_pcb *tpcb)
+{
+  mqtt_client_t *client = (mqtt_client_t *)arg;
+  if (client->conn_state == MQTT_CONNECTED) {
+    /* Try send any remaining buffers from output queue */
+    mqtt_output_send(&client->output, tpcb);
+  }
+  return ERR_OK;
+}
+
+/**
+ * TCP connect callback function. @see tcp_connected_fn
+ * @param arg MQTT client
+ * @param err Always ERR_OK, mqtt_tcp_err_cb is called in case of error
+ * @return ERR_OK
+ */
+static err_t
+mqtt_tcp_connect_cb(void *arg, struct altcp_pcb *tpcb, err_t err)
+{
+  mqtt_client_t *client = (mqtt_client_t *)arg;
+
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_tcp_connect_cb: TCP connect error %d\n", err));
+    return err;
+  }
+
+  /* Initiate receiver state */
+  client->msg_idx = 0;
+
+  /* Setup TCP callbacks */
+  altcp_recv(tpcb, mqtt_tcp_recv_cb);
+  altcp_sent(tpcb, mqtt_tcp_sent_cb);
+  altcp_poll(tpcb, mqtt_tcp_poll_cb, 2);
+
+  LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_tcp_connect_cb: TCP connection established to server\n"));
+  /* Enter MQTT connect state */
+  client->conn_state = MQTT_CONNECTING;
+
+  /* Start cyclic timer */
+  sys_timeout(MQTT_CYCLIC_TIMER_INTERVAL * 1000, mqtt_cyclic_timer, client);
+  client->cyclic_tick = 0;
+
+  /* Start transmission from output queue, connect message is the first one out*/
+  mqtt_output_send(&client->output, client->conn);
+
+  return ERR_OK;
+}
+
+
+
+/*---------------------------------------------------------------------------------------------------- */
+/* Public API */
+
+
+/**
+ * @ingroup mqtt
+ * MQTT publish function.
+ * @param client MQTT client
+ * @param topic Publish topic string
+ * @param payload Data to publish (NULL is allowed)
+ * @param payload_length Length of payload (0 is allowed)
+ * @param qos Quality of service, 0 1 or 2
+ * @param retain MQTT retain flag
+ * @param cb Callback to call when publish is complete or has timed out
+ * @param arg User supplied argument to publish callback
+ * @return ERR_OK if successful
+ *         ERR_CONN if client is disconnected
+ *         ERR_MEM if short on memory
+ */
+err_t
+mqtt_publish(mqtt_client_t *client, const char *topic, const void *payload, u16_t payload_length, u8_t qos, u8_t retain,
+             mqtt_request_cb_t cb, void *arg)
+{
+  struct mqtt_request_t *r;
+  u16_t pkt_id;
+  size_t topic_strlen;
+  size_t total_len;
+  u16_t topic_len;
+  u16_t remaining_length;
+
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("mqtt_publish: client != NULL", client);
+  LWIP_ASSERT("mqtt_publish: topic != NULL", topic);
+  LWIP_ERROR("mqtt_publish: TCP disconnected", (client->conn_state != TCP_DISCONNECTED), return ERR_CONN);
+
+  topic_strlen = strlen(topic);
+  LWIP_ERROR("mqtt_publish: topic length overflow", (topic_strlen <= (0xFFFF - 2)), return ERR_ARG);
+  topic_len = (u16_t)topic_strlen;
+  total_len = 2 + topic_len + payload_length;
+
+  if (qos > 0) {
+    total_len += 2;
+    /* Generate pkt_id id for QoS1 and 2 */
+    pkt_id = msg_generate_packet_id(client);
+  } else {
+    /* Use reserved value pkt_id 0 for QoS 0 in request handle */
+    pkt_id = 0;
+  }
+  LWIP_ERROR("mqtt_publish: total length overflow", (total_len <= 0xFFFF), return ERR_ARG);
+  remaining_length = (u16_t)total_len;
+
+  LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_publish: Publish with payload length %d to topic \"%s\"\n", payload_length, topic));
+
+  r = mqtt_create_request(client->req_list, LWIP_ARRAYSIZE(client->req_list), pkt_id, cb, arg);
+  if (r == NULL) {
+    return ERR_MEM;
+  }
+
+  if (mqtt_output_check_space(&client->output, remaining_length) == 0) {
+    mqtt_delete_request(r);
+    return ERR_MEM;
+  }
+  /* Append fixed header */
+  mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain, remaining_length);
+
+  /* Append Topic */
+  mqtt_output_append_string(&client->output, topic, topic_len);
+
+  /* Append packet if for QoS 1 and 2*/
+  if (qos > 0) {
+    mqtt_output_append_u16(&client->output, pkt_id);
+  }
+
+  /* Append optional publish payload */
+  if ((payload != NULL) && (payload_length > 0)) {
+    mqtt_output_append_buf(&client->output, payload, payload_length);
+  }
+
+  mqtt_append_request(&client->pend_req_queue, r);
+  mqtt_output_send(&client->output, client->conn);
+  return ERR_OK;
+}
+
+
+/**
+ * @ingroup mqtt
+ * MQTT subscribe/unsubscribe function.
+ * @param client MQTT client
+ * @param topic topic to subscribe to
+ * @param qos Quality of service, 0 1 or 2 (only used for subscribe)
+ * @param cb Callback to call when subscribe/unsubscribe reponse is received
+ * @param arg User supplied argument to publish callback
+ * @param sub 1 for subscribe, 0 for unsubscribe
+ * @return ERR_OK if successful, @see err_t enum for other results
+ */
+err_t
+mqtt_sub_unsub(mqtt_client_t *client, const char *topic, u8_t qos, mqtt_request_cb_t cb, void *arg, u8_t sub)
+{
+  size_t topic_strlen;
+  size_t total_len;
+  u16_t topic_len;
+  u16_t remaining_length;
+  u16_t pkt_id;
+  struct mqtt_request_t *r;
+
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("mqtt_sub_unsub: client != NULL", client);
+  LWIP_ASSERT("mqtt_sub_unsub: topic != NULL", topic);
+
+  topic_strlen = strlen(topic);
+  LWIP_ERROR("mqtt_sub_unsub: topic length overflow", (topic_strlen <= (0xFFFF - 2)), return ERR_ARG);
+  topic_len = (u16_t)topic_strlen;
+  /* Topic string, pkt_id, qos for subscribe */
+  total_len =  topic_len + 2 + 2 + (sub != 0);
+  LWIP_ERROR("mqtt_sub_unsub: total length overflow", (total_len <= 0xFFFF), return ERR_ARG);
+  remaining_length = (u16_t)total_len;
+
+  LWIP_ASSERT("mqtt_sub_unsub: qos < 3", qos < 3);
+  if (client->conn_state == TCP_DISCONNECTED) {
+    LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_sub_unsub: Can not (un)subscribe in disconnected state\n"));
+    return ERR_CONN;
+  }
+
+  pkt_id = msg_generate_packet_id(client);
+  r = mqtt_create_request(client->req_list, LWIP_ARRAYSIZE(client->req_list), pkt_id, cb, arg);
+  if (r == NULL) {
+    return ERR_MEM;
+  }
+
+  if (mqtt_output_check_space(&client->output, remaining_length) == 0) {
+    mqtt_delete_request(r);
+    return ERR_MEM;
+  }
+
+  LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_sub_unsub: Client (un)subscribe to topic \"%s\", id: %d\n", topic, pkt_id));
+
+  mqtt_output_append_fixed_header(&client->output, sub ? MQTT_MSG_TYPE_SUBSCRIBE : MQTT_MSG_TYPE_UNSUBSCRIBE, 0, 1, 0, remaining_length);
+  /* Packet id */
+  mqtt_output_append_u16(&client->output, pkt_id);
+  /* Topic */
+  mqtt_output_append_string(&client->output, topic, topic_len);
+  /* QoS */
+  if (sub != 0) {
+    mqtt_output_append_u8(&client->output, LWIP_MIN(qos, 2));
+  }
+
+  mqtt_append_request(&client->pend_req_queue, r);
+  mqtt_output_send(&client->output, client->conn);
+  return ERR_OK;
+}
+
+
+/**
+ * @ingroup mqtt
+ * Set callback to handle incoming publish requests from server
+ * @param client MQTT client
+ * @param pub_cb Callback invoked when publish starts, contain topic and total length of payload
+ * @param data_cb Callback for each fragment of payload that arrives
+ * @param arg User supplied argument to both callbacks
+ */
+void
+mqtt_set_inpub_callback(mqtt_client_t *client, mqtt_incoming_publish_cb_t pub_cb,
+                        mqtt_incoming_data_cb_t data_cb, void *arg)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("mqtt_set_inpub_callback: client != NULL", client != NULL);
+  client->data_cb = data_cb;
+  client->pub_cb = pub_cb;
+  client->inpub_arg = arg;
+}
+
+/**
+ * @ingroup mqtt
+ * Create a new MQTT client instance
+ * @return Pointer to instance on success, NULL otherwise
+ */
+mqtt_client_t *
+mqtt_client_new(void)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  return (mqtt_client_t *)mem_calloc(1, sizeof(mqtt_client_t));
+}
+
+/**
+ * @ingroup mqtt
+ * Free MQTT client instance
+ * @param client Pointer to instance to be freed
+ */
+void
+mqtt_client_free(mqtt_client_t *client)
+{
+  mem_free(client);
+}
+
+/**
+ * @ingroup mqtt
+ * Connect to MQTT server
+ * @param client MQTT client
+ * @param ip_addr Server IP
+ * @param port Server port
+ * @param cb Connection state change callback
+ * @param arg User supplied argument to connection callback
+ * @param client_info Client identification and connection options
+ * @return ERR_OK if successful, @see err_t enum for other results
+ */
+err_t
+mqtt_client_connect(mqtt_client_t *client, const ip_addr_t *ip_addr, u16_t port, mqtt_connection_cb_t cb, void *arg,
+                    const struct mqtt_connect_client_info_t *client_info)
+{
+  err_t err;
+  size_t len;
+  u16_t client_id_length;
+  /* Length is the sum of 2+"MQTT", protocol level, flags and keep alive */
+  u16_t remaining_length = 2 + 4 + 1 + 1 + 2;
+  u8_t flags = 0, will_topic_len = 0, will_msg_len = 0;
+  u16_t client_user_len = 0, client_pass_len = 0;
+
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("mqtt_client_connect: client != NULL", client != NULL);
+  LWIP_ASSERT("mqtt_client_connect: ip_addr != NULL", ip_addr != NULL);
+  LWIP_ASSERT("mqtt_client_connect: client_info != NULL", client_info != NULL);
+  LWIP_ASSERT("mqtt_client_connect: client_info->client_id != NULL", client_info->client_id != NULL);
+
+  if (client->conn_state != TCP_DISCONNECTED) {
+    LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_client_connect: Already connected\n"));
+    return ERR_ISCONN;
+  }
+
+  /* Wipe clean */
+  memset(client, 0, sizeof(mqtt_client_t));
+  client->connect_arg = arg;
+  client->connect_cb = cb;
+  client->keep_alive = client_info->keep_alive;
+  mqtt_init_requests(client->req_list, LWIP_ARRAYSIZE(client->req_list));
+
+  /* Build connect message */
+  if (client_info->will_topic != NULL && client_info->will_msg != NULL) {
+    flags |= MQTT_CONNECT_FLAG_WILL;
+    flags |= (client_info->will_qos & 3) << 3;
+    if (client_info->will_retain) {
+      flags |= MQTT_CONNECT_FLAG_WILL_RETAIN;
+    }
+    len = strlen(client_info->will_topic);
+    LWIP_ERROR("mqtt_client_connect: client_info->will_topic length overflow", len <= 0xFF, return ERR_VAL);
+    LWIP_ERROR("mqtt_client_connect: client_info->will_topic length must be > 0", len > 0, return ERR_VAL);
+    will_topic_len = (u8_t)len;
+    len = strlen(client_info->will_msg);
+    LWIP_ERROR("mqtt_client_connect: client_info->will_msg length overflow", len <= 0xFF, return ERR_VAL);
+    will_msg_len = (u8_t)len;
+    len = remaining_length + 2 + will_topic_len + 2 + will_msg_len;
+    LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
+    remaining_length = (u16_t)len;
+  }
+  if (client_info->client_user != NULL) {
+    flags |= MQTT_CONNECT_FLAG_USERNAME;
+    len = strlen(client_info->client_user);
+    LWIP_ERROR("mqtt_client_connect: client_info->client_user length overflow", len <= 0xFFFF, return ERR_VAL);
+    LWIP_ERROR("mqtt_client_connect: client_info->client_user length must be > 0", len > 0, return ERR_VAL);
+    client_user_len = (u16_t)len;
+    len = remaining_length + 2 + client_user_len;
+    LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
+    remaining_length = (u16_t)len;
+  }
+  if (client_info->client_pass != NULL) {
+    flags |= MQTT_CONNECT_FLAG_PASSWORD;
+    len = strlen(client_info->client_pass);
+    LWIP_ERROR("mqtt_client_connect: client_info->client_pass length overflow", len <= 0xFFFF, return ERR_VAL);
+    LWIP_ERROR("mqtt_client_connect: client_info->client_pass length must be > 0", len > 0, return ERR_VAL);
+    client_pass_len = (u16_t)len;
+    len = remaining_length + 2 + client_pass_len;
+    LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
+    remaining_length = (u16_t)len;
+  }
+
+  /* Don't complicate things, always connect using clean session */
+  flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION;
+
+  len = strlen(client_info->client_id);
+  LWIP_ERROR("mqtt_client_connect: client_info->client_id length overflow", len <= 0xFFFF, return ERR_VAL);
+  client_id_length = (u16_t)len;
+  len = remaining_length + 2 + client_id_length;
+  LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
+  remaining_length = (u16_t)len;
+
+  if (mqtt_output_check_space(&client->output, remaining_length) == 0) {
+    return ERR_MEM;
+  }
+
+#if LWIP_ALTCP && LWIP_ALTCP_TLS
+  if (client_info->tls_config) {
+    client->conn = altcp_tls_new(client_info->tls_config, IP_GET_TYPE(ip_addr));
+  } else
+#endif
+  {
+    client->conn = altcp_tcp_new_ip_type(IP_GET_TYPE(ip_addr));
+  }
+  if (client->conn == NULL) {
+    return ERR_MEM;
+  }
+
+  /* Set arg pointer for callbacks */
+  altcp_arg(client->conn, client);
+  /* Any local address, pick random local port number */
+  err = altcp_bind(client->conn, IP_ADDR_ANY, 0);
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_client_connect: Error binding to local ip/port, %d\n", err));
+    goto tcp_fail;
+  }
+  LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_client_connect: Connecting to host: %s at port:%"U16_F"\n", ipaddr_ntoa(ip_addr), port));
+
+  /* Connect to server */
+  err = altcp_connect(client->conn, ip_addr, port, mqtt_tcp_connect_cb);
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_client_connect: Error connecting to remote ip/port, %d\n", err));
+    goto tcp_fail;
+  }
+  /* Set error callback */
+  altcp_err(client->conn, mqtt_tcp_err_cb);
+  client->conn_state = TCP_CONNECTING;
+
+  /* Append fixed header */
+  mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_CONNECT, 0, 0, 0, remaining_length);
+  /* Append Protocol string */
+  mqtt_output_append_string(&client->output, "MQTT", 4);
+  /* Append Protocol level */
+  mqtt_output_append_u8(&client->output, 4);
+  /* Append connect flags */
+  mqtt_output_append_u8(&client->output, flags);
+  /* Append keep-alive */
+  mqtt_output_append_u16(&client->output, client_info->keep_alive);
+  /* Append client id */
+  mqtt_output_append_string(&client->output, client_info->client_id, client_id_length);
+  /* Append will message if used */
+  if ((flags & MQTT_CONNECT_FLAG_WILL) != 0) {
+    mqtt_output_append_string(&client->output, client_info->will_topic, will_topic_len);
+    mqtt_output_append_string(&client->output, client_info->will_msg, will_msg_len);
+  }
+  /* Append user name if given */
+  if ((flags & MQTT_CONNECT_FLAG_USERNAME) != 0) {
+    mqtt_output_append_string(&client->output, client_info->client_user, client_user_len);
+  }
+  /* Append password if given */
+  if ((flags & MQTT_CONNECT_FLAG_PASSWORD) != 0) {
+    mqtt_output_append_string(&client->output, client_info->client_pass, client_pass_len);
+  }
+  return ERR_OK;
+
+tcp_fail:
+  altcp_abort(client->conn);
+  client->conn = NULL;
+  return err;
+}
+
+
+/**
+ * @ingroup mqtt
+ * Disconnect from MQTT server
+ * @param client MQTT client
+ */
+void
+mqtt_disconnect(mqtt_client_t *client)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("mqtt_disconnect: client != NULL", client);
+  /* If connection in not already closed */
+  if (client->conn_state != TCP_DISCONNECTED) {
+    /* Set conn_state before calling mqtt_close to prevent callback from being called */
+    client->conn_state = TCP_DISCONNECTED;
+    mqtt_close(client, (mqtt_connection_status_t)0);
+  }
+}
+
+/**
+ * @ingroup mqtt
+ * Check connection with server
+ * @param client MQTT client
+ * @return 1 if connected to server, 0 otherwise
+ */
+u8_t
+mqtt_client_is_connected(mqtt_client_t *client)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("mqtt_client_is_connected: client != NULL", client);
+  return client->conn_state == MQTT_CONNECTED;
+}
+
+#endif /* LWIP_TCP && LWIP_CALLBACK_API */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/netbiosns/netbiosns.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/netbiosns/netbiosns.c
new file mode 100644 (file)
index 0000000..479c375
--- /dev/null
@@ -0,0 +1,533 @@
+  /**
+ * @file
+ * NetBIOS name service responder
+ */
+
+/**
+ * @defgroup netbiosns NETBIOS responder
+ * @ingroup apps
+ *
+ * This is an example implementation of a NetBIOS name server.
+ * It responds to name queries for a configurable name.
+ * Name resolving is not supported.
+ *
+ * Note that the device doesn't broadcast it's own name so can't
+ * detect duplicate names!
+ */
+
+/*
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Modifications by Ray Abram to respond to NetBIOS name requests when Incoming name = *
+ * - based on code from "https://github.com/esp8266/Arduino/commit/1f7989b31d26d7df9776a08f36d685eae7ac8f99"
+ * - with permission to relicense to BSD from original author:
+ *   http://www.xpablo.cz/?p=751#more-751
+ */
+
+#include "lwip/apps/netbiosns.h"
+
+#if LWIP_IPV4 && LWIP_UDP  /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/udp.h"
+#include "lwip/ip.h"
+#include "lwip/netif.h"
+#include "lwip/prot/iana.h"
+
+#include <string.h>
+
+/** size of a NetBIOS name */
+#define NETBIOS_NAME_LEN 16
+
+/** The Time-To-Live for NetBIOS name responds (in seconds)
+ * Default is 300000 seconds (3 days, 11 hours, 20 minutes) */
+#define NETBIOS_NAME_TTL 300000u
+
+/** NetBIOS header flags */
+#define NETB_HFLAG_RESPONSE           0x8000U
+#define NETB_HFLAG_OPCODE             0x7800U
+#define NETB_HFLAG_OPCODE_NAME_QUERY  0x0000U
+#define NETB_HFLAG_AUTHORATIVE        0x0400U
+#define NETB_HFLAG_TRUNCATED          0x0200U
+#define NETB_HFLAG_RECURS_DESIRED     0x0100U
+#define NETB_HFLAG_RECURS_AVAILABLE   0x0080U
+#define NETB_HFLAG_BROADCAST          0x0010U
+#define NETB_HFLAG_REPLYCODE          0x0008U
+#define NETB_HFLAG_REPLYCODE_NOERROR  0x0000U
+
+/* NetBIOS question types */
+#define NETB_QTYPE_NB                 0x0020U
+#define NETB_QTYPE_NBSTAT             0x0021U
+
+/** NetBIOS name flags */
+#define NETB_NFLAG_UNIQUE             0x8000U
+#define NETB_NFLAG_NODETYPE           0x6000U
+#define NETB_NFLAG_NODETYPE_HNODE     0x6000U
+#define NETB_NFLAG_NODETYPE_MNODE     0x4000U
+#define NETB_NFLAG_NODETYPE_PNODE     0x2000U
+#define NETB_NFLAG_NODETYPE_BNODE     0x0000U
+
+#define NETB_NFLAG_NAME_IN_CONFLICT   0x0800U /* 1=Yes, 0=No */
+#define NETB_NFLAG_NAME_IS_ACTIVE     0x0400U /* 1=Yes, 0=No */
+#define NETB_NFLAG_NAME_IS_PERMANENT  0x0200U /* 1=Yes (Name is Permanent Node Name), 0=No */
+
+/** NetBIOS message header */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct netbios_hdr {
+  PACK_STRUCT_FIELD(u16_t trans_id);
+  PACK_STRUCT_FIELD(u16_t flags);
+  PACK_STRUCT_FIELD(u16_t questions);
+  PACK_STRUCT_FIELD(u16_t answerRRs);
+  PACK_STRUCT_FIELD(u16_t authorityRRs);
+  PACK_STRUCT_FIELD(u16_t additionalRRs);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/** NetBIOS message question part */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct netbios_question_hdr {
+  PACK_STRUCT_FLD_8(u8_t  nametype);
+  PACK_STRUCT_FLD_8(u8_t  encname[(NETBIOS_NAME_LEN * 2) + 1]);
+  PACK_STRUCT_FIELD(u16_t type);
+  PACK_STRUCT_FIELD(u16_t cls);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/** NetBIOS message name part */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct netbios_name_hdr {
+  PACK_STRUCT_FLD_8(u8_t  nametype);
+  PACK_STRUCT_FLD_8(u8_t  encname[(NETBIOS_NAME_LEN * 2) + 1]);
+  PACK_STRUCT_FIELD(u16_t type);
+  PACK_STRUCT_FIELD(u16_t cls);
+  PACK_STRUCT_FIELD(u32_t ttl);
+  PACK_STRUCT_FIELD(u16_t datalen);
+  PACK_STRUCT_FIELD(u16_t flags);
+  PACK_STRUCT_FLD_S(ip4_addr_p_t addr);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/** NetBIOS message */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct netbios_resp {
+  struct netbios_hdr      resp_hdr;
+  struct netbios_name_hdr resp_name;
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/** The NBNS Structure Responds to a Name Query */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct netbios_answer {
+  struct netbios_hdr      answer_hdr;
+  /** the length of the next string */
+  PACK_STRUCT_FIELD(u8_t  name_size);
+  /** WARNING!!! this item may be of a different length (we use this struct for transmission) */
+  PACK_STRUCT_FLD_8(u8_t  query_name[(NETBIOS_NAME_LEN * 2) + 1]);
+  PACK_STRUCT_FIELD(u16_t packet_type);
+  PACK_STRUCT_FIELD(u16_t cls);
+  PACK_STRUCT_FIELD(u32_t ttl);
+  PACK_STRUCT_FIELD(u16_t data_length);
+#define OFFSETOF_STRUCT_NETBIOS_ANSWER_NUMBER_OF_NAMES 56
+  /** number of names */
+  PACK_STRUCT_FLD_8(u8_t  number_of_names);
+  /** node name */
+  PACK_STRUCT_FLD_8(u8_t  answer_name[NETBIOS_NAME_LEN]);
+  /** node flags */
+  PACK_STRUCT_FIELD(u16_t answer_name_flags);
+  /** Unit ID */
+  PACK_STRUCT_FLD_8(u8_t  unit_id[6]);
+  /** Jumpers */
+  PACK_STRUCT_FLD_8(u8_t  jumpers);
+  /** Test result */
+  PACK_STRUCT_FLD_8(u8_t  test_result);
+  /** Version number */
+  PACK_STRUCT_FIELD(u16_t version_number);
+  /** Period of statistics */
+  PACK_STRUCT_FIELD(u16_t period_of_statistics);
+  /** Statistics */
+  PACK_STRUCT_FIELD(u16_t number_of_crcs);
+  /** Statistics */
+  PACK_STRUCT_FIELD(u16_t number_of_alignment_errors);
+  /** Statistics */
+  PACK_STRUCT_FIELD(u16_t number_of_collisions);
+  /** Statistics */
+  PACK_STRUCT_FIELD(u16_t number_of_send_aborts);
+  /** Statistics */
+  PACK_STRUCT_FIELD(u32_t number_of_good_sends);
+  /** Statistics */
+  PACK_STRUCT_FIELD(u32_t number_of_good_receives);
+  /** Statistics */
+  PACK_STRUCT_FIELD(u16_t number_of_retransmits);
+  /** Statistics */
+  PACK_STRUCT_FIELD(u16_t number_of_no_resource_condition);
+  /** Statistics */
+  PACK_STRUCT_FIELD(u16_t number_of_free_command_blocks);
+  /** Statistics */
+  PACK_STRUCT_FIELD(u16_t total_number_of_command_blocks);
+  /** Statistics */
+  PACK_STRUCT_FIELD(u16_t max_total_number_of_command_blocks);
+  /** Statistics */
+  PACK_STRUCT_FIELD(u16_t number_of_pending_sessions);
+  /** Statistics */
+  PACK_STRUCT_FIELD(u16_t max_number_of_pending_sessions);
+  /** Statistics */
+  PACK_STRUCT_FIELD(u16_t max_total_sessions_possible);
+  /** Statistics */
+  PACK_STRUCT_FIELD(u16_t session_data_packet_size);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+#ifdef NETBIOS_LWIP_NAME
+#define NETBIOS_LOCAL_NAME NETBIOS_LWIP_NAME
+#else
+static char netbiosns_local_name[NETBIOS_NAME_LEN];
+#define NETBIOS_LOCAL_NAME netbiosns_local_name
+#endif
+
+static struct udp_pcb *netbiosns_pcb;
+
+/** Decode a NetBIOS name (from packet to string) */
+static int
+netbiosns_name_decode(const char *name_enc, char *name_dec, int name_dec_len)
+{
+  const char *pname;
+  char       cname;
+  char       cnbname;
+  int        idx = 0;
+
+  LWIP_UNUSED_ARG(name_dec_len);
+
+  /* Start decoding netbios name. */
+  pname  = name_enc;
+  for (;;) {
+    /* Every two characters of the first level-encoded name
+     * turn into one character in the decoded name. */
+    cname = *pname;
+    if (cname == '\0') {
+      break;  /* no more characters */
+    }
+    if (cname == '.') {
+      break;  /* scope ID follows */
+    }
+    if (!lwip_isupper(cname)) {
+      /* Not legal. */
+      return -1;
+    }
+    cname -= 'A';
+    cnbname = cname << 4;
+    pname++;
+
+    cname = *pname;
+    if (!lwip_isupper(cname)) {
+      /* Not legal. */
+      return -1;
+    }
+    cname -= 'A';
+    cnbname |= cname;
+    pname++;
+
+    /* Do we have room to store the character? */
+    if (idx < NETBIOS_NAME_LEN) {
+      /* Yes - store the character. */
+      name_dec[idx++] = (cnbname != ' ' ? cnbname : '\0');
+    }
+  }
+
+  return 0;
+}
+
+#if 0 /* function currently unused */
+/** Encode a NetBIOS name (from string to packet) - currently unused because
+    we don't ask for names. */
+static int
+netbiosns_name_encode(char *name_enc, char *name_dec, int name_dec_len)
+{
+  char         *pname;
+  char          cname;
+  unsigned char ucname;
+  int           idx = 0;
+
+  /* Start encoding netbios name. */
+  pname = name_enc;
+
+  for (;;) {
+    /* Every two characters of the first level-encoded name
+     * turn into one character in the decoded name. */
+    cname = *pname;
+    if (cname == '\0') {
+      break;  /* no more characters */
+    }
+    if (cname == '.') {
+      break;  /* scope ID follows */
+    }
+    if ((cname < 'A' || cname > 'Z') && (cname < '0' || cname > '9')) {
+      /* Not legal. */
+      return -1;
+    }
+
+    /* Do we have room to store the character? */
+    if (idx >= name_dec_len) {
+      return -1;
+    }
+
+    /* Yes - store the character. */
+    ucname = cname;
+    name_dec[idx++] = ('A' + ((ucname >> 4) & 0x0F));
+    name_dec[idx++] = ('A' + ( ucname     & 0x0F));
+    pname++;
+  }
+
+  /* Fill with "space" coding */
+  for (; idx < name_dec_len - 1;) {
+    name_dec[idx++] = 'C';
+    name_dec[idx++] = 'A';
+  }
+
+  /* Terminate string */
+  name_dec[idx] = '\0';
+
+  return 0;
+}
+#endif /* 0 */
+
+/** NetBIOS Name service recv callback */
+static void
+netbiosns_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+  LWIP_UNUSED_ARG(arg);
+
+  /* if packet is valid */
+  if (p != NULL) {
+    char   netbios_name[NETBIOS_NAME_LEN + 1];
+    struct netbios_hdr          *netbios_hdr          = (struct netbios_hdr *)p->payload;
+    struct netbios_question_hdr *netbios_question_hdr = (struct netbios_question_hdr *)(netbios_hdr + 1);
+
+    /* is the packet long enough (we need the header in one piece) */
+    if (p->len < (sizeof(struct netbios_hdr) + sizeof(struct netbios_question_hdr))) {
+      /* packet too short */
+      pbuf_free(p);
+      return;
+    }
+    /* we only answer if we got a default interface */
+    if (netif_default != NULL) {
+      /* @todo: do we need to check answerRRs/authorityRRs/additionalRRs? */
+      /* if the packet is a NetBIOS name query question */
+      if (((netbios_hdr->flags & PP_NTOHS(NETB_HFLAG_OPCODE)) == PP_NTOHS(NETB_HFLAG_OPCODE_NAME_QUERY)) &&
+          ((netbios_hdr->flags & PP_NTOHS(NETB_HFLAG_RESPONSE)) == 0) &&
+          (netbios_hdr->questions == PP_NTOHS(1))) {
+        /* decode the NetBIOS name */
+        netbiosns_name_decode((char *)(netbios_question_hdr->encname), netbios_name, sizeof(netbios_name));
+        /* check the request type */
+        if (netbios_question_hdr->type == PP_HTONS(NETB_QTYPE_NB)) {
+          /* if the packet is for us */
+          if (lwip_strnicmp(netbios_name, NETBIOS_LOCAL_NAME, sizeof(NETBIOS_LOCAL_NAME)) == 0) {
+            struct pbuf *q;
+            struct netbios_resp *resp;
+
+            q = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct netbios_resp), PBUF_RAM);
+            if (q != NULL) {
+              resp = (struct netbios_resp *)q->payload;
+
+              /* prepare NetBIOS header response */
+              resp->resp_hdr.trans_id      = netbios_hdr->trans_id;
+              resp->resp_hdr.flags         = PP_HTONS(NETB_HFLAG_RESPONSE |
+                                                      NETB_HFLAG_OPCODE_NAME_QUERY |
+                                                      NETB_HFLAG_AUTHORATIVE |
+                                                      NETB_HFLAG_RECURS_DESIRED);
+              resp->resp_hdr.questions     = 0;
+              resp->resp_hdr.answerRRs     = PP_HTONS(1);
+              resp->resp_hdr.authorityRRs  = 0;
+              resp->resp_hdr.additionalRRs = 0;
+
+              /* prepare NetBIOS header datas */
+              MEMCPY( resp->resp_name.encname, netbios_question_hdr->encname, sizeof(netbios_question_hdr->encname));
+              resp->resp_name.nametype     = netbios_question_hdr->nametype;
+              resp->resp_name.type         = netbios_question_hdr->type;
+              resp->resp_name.cls          = netbios_question_hdr->cls;
+              resp->resp_name.ttl          = PP_HTONL(NETBIOS_NAME_TTL);
+              resp->resp_name.datalen      = PP_HTONS(sizeof(resp->resp_name.flags) + sizeof(resp->resp_name.addr));
+              resp->resp_name.flags        = PP_HTONS(NETB_NFLAG_NODETYPE_BNODE);
+              ip4_addr_copy(resp->resp_name.addr, *netif_ip4_addr(netif_default));
+
+              /* send the NetBIOS response */
+              udp_sendto(upcb, q, addr, port);
+
+              /* free the "reference" pbuf */
+              pbuf_free(q);
+            }
+          }
+#if LWIP_NETBIOS_RESPOND_NAME_QUERY
+        } else if (netbios_question_hdr->type == PP_HTONS(NETB_QTYPE_NBSTAT)) {
+          /* if the packet is for us or general query */
+          if (!lwip_strnicmp(netbios_name, NETBIOS_LOCAL_NAME, sizeof(NETBIOS_LOCAL_NAME)) ||
+              !lwip_strnicmp(netbios_name, "*", sizeof(NETBIOS_LOCAL_NAME))) {
+            /* general query - ask for our IP address */
+            struct pbuf *q;
+            struct netbios_answer *resp;
+
+            q = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct netbios_answer), PBUF_RAM);
+            if (q != NULL) {
+              /* buffer to which a response is compiled */
+              resp = (struct netbios_answer *) q->payload;
+
+              /* Init response to zero, especially the statistics fields */
+              memset(resp, 0, sizeof(*resp));
+
+              /* copy the query to the response ID */
+              resp->answer_hdr.trans_id        = netbios_hdr->trans_id;
+              /* acknowledgment of termination */
+              resp->answer_hdr.flags           = PP_HTONS(NETB_HFLAG_RESPONSE | NETB_HFLAG_OPCODE_NAME_QUERY | NETB_HFLAG_AUTHORATIVE);
+              /* resp->answer_hdr.questions       = PP_HTONS(0); done by memset() */
+              /* serial number of the answer */
+              resp->answer_hdr.answerRRs       = PP_HTONS(1);
+              /* resp->answer_hdr.authorityRRs    = PP_HTONS(0); done by memset() */
+              /* resp->answer_hdr.additionalRRs   = PP_HTONS(0); done by memset() */
+              /* we will copy the length of the station name */
+              resp->name_size                  = netbios_question_hdr->nametype;
+              /* we will copy the queried name */
+              MEMCPY(resp->query_name, netbios_question_hdr->encname, (NETBIOS_NAME_LEN * 2) + 1);
+              /* NBSTAT */
+              resp->packet_type                = PP_HTONS(0x21);
+              /* Internet name */
+              resp->cls                        = PP_HTONS(1);
+              /* resp->ttl                        = PP_HTONL(0); done by memset() */
+              resp->data_length                = PP_HTONS(sizeof(struct netbios_answer) - offsetof(struct netbios_answer, number_of_names));
+              resp->number_of_names            = 1;
+
+              /* make windows see us as workstation, not as a server */
+              memset(resp->answer_name, 0x20, NETBIOS_NAME_LEN - 1);
+              /* strlen is checked to be < NETBIOS_NAME_LEN during initialization */
+              MEMCPY(resp->answer_name, NETBIOS_LOCAL_NAME, strlen(NETBIOS_LOCAL_NAME));
+
+              /* b-node, unique, active */
+              resp->answer_name_flags          = PP_HTONS(NETB_NFLAG_NAME_IS_ACTIVE);
+
+              /* Set responder netif MAC address */
+              SMEMCPY(resp->unit_id, ip_current_input_netif()->hwaddr, sizeof(resp->unit_id));
+
+              udp_sendto(upcb, q, addr, port);
+              pbuf_free(q);
+            }
+          }
+#endif /* LWIP_NETBIOS_RESPOND_NAME_QUERY */
+        }
+      }
+    }
+    /* free the pbuf */
+    pbuf_free(p);
+  }
+}
+
+/**
+ * @ingroup netbiosns
+ * Init netbios responder
+ */
+void
+netbiosns_init(void)
+{
+  /* LWIP_ASSERT_CORE_LOCKED(); is checked by udp_new() */
+#ifdef NETBIOS_LWIP_NAME
+  LWIP_ASSERT("NetBIOS name is too long!", strlen(NETBIOS_LWIP_NAME) < NETBIOS_NAME_LEN);
+#endif
+
+  netbiosns_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+  if (netbiosns_pcb != NULL) {
+    /* we have to be allowed to send broadcast packets! */
+    ip_set_option(netbiosns_pcb, SOF_BROADCAST);
+    udp_bind(netbiosns_pcb, IP_ANY_TYPE, LWIP_IANA_PORT_NETBIOS);
+    udp_recv(netbiosns_pcb, netbiosns_recv, netbiosns_pcb);
+  }
+}
+
+#ifndef NETBIOS_LWIP_NAME
+/**
+ * @ingroup netbiosns
+ * Set netbios name. ATTENTION: the hostname must be less than 15 characters!
+ *                              the NetBIOS name spec says the name MUST be upper case, so incoming name is forced into uppercase :-)
+ */
+void
+netbiosns_set_name(const char *hostname)
+{
+  size_t i;
+  size_t copy_len = strlen(hostname);
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("NetBIOS name is too long!", copy_len < NETBIOS_NAME_LEN);
+  if (copy_len >= NETBIOS_NAME_LEN) {
+    copy_len = NETBIOS_NAME_LEN - 1;
+  }
+
+  /* make name into upper case */
+  for (i = 0; i < copy_len; i++ ) {
+    netbiosns_local_name[i] = (char)lwip_toupper(hostname[i]);
+  }
+  netbiosns_local_name[copy_len] = '\0';
+}
+#endif /* NETBIOS_LWIP_NAME */
+
+/**
+ * @ingroup netbiosns
+ * Stop netbios responder
+ */
+void
+netbiosns_stop(void)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  if (netbiosns_pcb != NULL) {
+    udp_remove(netbiosns_pcb);
+    netbiosns_pcb = NULL;
+  }
+}
+
+#endif /* LWIP_IPV4 && LWIP_UDP */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/smtp/smtp.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/smtp/smtp.c
new file mode 100644 (file)
index 0000000..55303c3
--- /dev/null
@@ -0,0 +1,1555 @@
+/**
+ * @file
+ * SMTP client module
+ * 
+ * Author: Simon Goldschmidt
+ *
+ * @defgroup smtp SMTP client
+ * @ingroup apps
+ * 
+ * This is simple SMTP client for raw API.
+ * It is a minimal implementation of SMTP as specified in RFC 5321.
+ *
+ * Example usage:
+@code{.c}
+ void my_smtp_result_fn(void *arg, u8_t smtp_result, u16_t srv_err, err_t err)
+ {
+   printf("mail (%p) sent with results: 0x%02x, 0x%04x, 0x%08x\n", arg,
+          smtp_result, srv_err, err);
+ }
+ static void my_smtp_test(void)
+ {
+   smtp_set_server_addr("mymailserver.org");
+   -> set both username and password as NULL if no auth needed
+   smtp_set_auth("username", "password");
+   smtp_send_mail("sender", "recipient", "subject", "body", my_smtp_result_fn,
+                  some_argument);
+ }
+@endcode
+
+ * When using from any other thread than the tcpip_thread (for NO_SYS==0), use
+ * smtp_send_mail_int()!
+ * 
+ * SMTP_BODYDH usage:
+@code{.c}
+ int my_smtp_bodydh_fn(void *arg, struct smtp_bodydh *bdh)
+ {
+    if(bdh->state >= 10) {
+       return BDH_DONE;
+    }
+    sprintf(bdh->buffer,"Line #%2d\r\n",bdh->state);
+    bdh->length = strlen(bdh->buffer);
+    ++bdh->state;
+    return BDH_WORKING;
+ }
+ 
+ smtp_send_mail_bodycback("sender", "recipient", "subject", 
+                my_smtp_bodydh_fn, my_smtp_result_fn, some_argument);
+@endcode
+ * 
+ * @todo:
+ * - attachments (the main difficulty here is streaming base64-encoding to
+ *   prevent having to allocate a buffer for the whole encoded file at once)
+ * - test with more mail servers...
+ *
+ */
+
+#include "lwip/apps/smtp.h"
+
+#if LWIP_TCP && LWIP_CALLBACK_API
+#include "lwip/sys.h"
+#include "lwip/sockets.h"
+#include "lwip/altcp.h"
+#include "lwip/dns.h"
+#include "lwip/mem.h"
+#include "lwip/altcp_tcp.h"
+#include "lwip/altcp_tls.h"
+
+#include <string.h> /* strlen, memcpy */
+#include <stdlib.h>
+
+/** TCP poll interval. Unit is 0.5 sec. */
+#define SMTP_POLL_INTERVAL      4
+/** TCP poll timeout while sending message body, reset after every
+ * successful write. 3 minutes */
+#define SMTP_TIMEOUT_DATABLOCK  ( 3 * 60 * SMTP_POLL_INTERVAL / 2)
+/** TCP poll timeout while waiting for confirmation after sending the body.
+ * 10 minutes */
+#define SMTP_TIMEOUT_DATATERM   (10 * 60 * SMTP_POLL_INTERVAL / 2)
+/** TCP poll timeout while not sending the body.
+ * This is somewhat lower than the RFC states (5 minutes for initial, MAIL
+ * and RCPT) but still OK for us here.
+ * 2 minutes */
+#define SMTP_TIMEOUT            ( 2 * 60 * SMTP_POLL_INTERVAL / 2)
+
+/* the various debug levels for this file */
+#define SMTP_DEBUG_TRACE        (SMTP_DEBUG | LWIP_DBG_TRACE)
+#define SMTP_DEBUG_STATE        (SMTP_DEBUG | LWIP_DBG_STATE)
+#define SMTP_DEBUG_WARN         (SMTP_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#define SMTP_DEBUG_WARN_STATE   (SMTP_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
+#define SMTP_DEBUG_SERIOUS      (SMTP_DEBUG | LWIP_DBG_LEVEL_SERIOUS)
+
+
+#define SMTP_RX_BUF_LEN         255
+#define SMTP_TX_BUF_LEN         255
+#define SMTP_CRLF               "\r\n"
+#define SMTP_CRLF_LEN           2
+
+#define SMTP_RESP_220           "220"
+#define SMTP_RESP_235           "235"
+#define SMTP_RESP_250           "250"
+#define SMTP_RESP_334           "334"
+#define SMTP_RESP_354           "354"
+#define SMTP_RESP_LOGIN_UNAME   "VXNlcm5hbWU6"
+#define SMTP_RESP_LOGIN_PASS    "UGFzc3dvcmQ6"
+
+#define SMTP_KEYWORD_AUTH_SP    "AUTH "
+#define SMTP_KEYWORD_AUTH_EQ    "AUTH="
+#define SMTP_KEYWORD_AUTH_LEN   5
+#define SMTP_AUTH_PARAM_PLAIN   "PLAIN"
+#define SMTP_AUTH_PARAM_LOGIN   "LOGIN"
+
+#define SMTP_CMD_EHLO_1           "EHLO ["
+#define SMTP_CMD_EHLO_1_LEN       6
+#define SMTP_CMD_EHLO_2           "]\r\n"
+#define SMTP_CMD_EHLO_2_LEN       3
+#define SMTP_CMD_AUTHPLAIN_1      "AUTH PLAIN "
+#define SMTP_CMD_AUTHPLAIN_1_LEN  11
+#define SMTP_CMD_AUTHPLAIN_2      "\r\n"
+#define SMTP_CMD_AUTHPLAIN_2_LEN  2
+#define SMTP_CMD_AUTHLOGIN        "AUTH LOGIN\r\n"
+#define SMTP_CMD_AUTHLOGIN_LEN    12
+#define SMTP_CMD_MAIL_1           "MAIL FROM: <"
+#define SMTP_CMD_MAIL_1_LEN       12
+#define SMTP_CMD_MAIL_2           ">\r\n"
+#define SMTP_CMD_MAIL_2_LEN       3
+#define SMTP_CMD_RCPT_1           "RCPT TO: <"
+#define SMTP_CMD_RCPT_1_LEN       10
+#define SMTP_CMD_RCPT_2           ">\r\n"
+#define SMTP_CMD_RCPT_2_LEN       3
+#define SMTP_CMD_DATA             "DATA\r\n"
+#define SMTP_CMD_DATA_LEN         6
+#define SMTP_CMD_HEADER_1         "From: <"
+#define SMTP_CMD_HEADER_1_LEN     7
+#define SMTP_CMD_HEADER_2         ">\r\nTo: <"
+#define SMTP_CMD_HEADER_2_LEN     8
+#define SMTP_CMD_HEADER_3         ">\r\nSubject: "
+#define SMTP_CMD_HEADER_3_LEN     12
+#define SMTP_CMD_HEADER_4         "\r\n\r\n"
+#define SMTP_CMD_HEADER_4_LEN     4
+#define SMTP_CMD_BODY_FINISHED    "\r\n.\r\n"
+#define SMTP_CMD_BODY_FINISHED_LEN 5
+#define SMTP_CMD_QUIT             "QUIT\r\n"
+#define SMTP_CMD_QUIT_LEN         6
+
+#if defined(SMTP_STAT_TX_BUF_MAX) && SMTP_STAT_TX_BUF_MAX
+#define SMTP_TX_BUF_MAX(len) LWIP_MACRO(if((len) > smtp_tx_buf_len_max) smtp_tx_buf_len_max = (len);)
+#else /* SMTP_STAT_TX_BUF_MAX */
+#define SMTP_TX_BUF_MAX(len)
+#endif /* SMTP_STAT_TX_BUF_MAX */
+
+#if SMTP_COPY_AUTHDATA
+#define SMTP_USERNAME(session)        (session)->username
+#define SMTP_PASS(session)            (session)->pass
+#define SMTP_AUTH_PLAIN_DATA(session) (session)->auth_plain
+#define SMTP_AUTH_PLAIN_LEN(session)  (session)->auth_plain_len
+#else /* SMTP_COPY_AUTHDATA */
+#define SMTP_USERNAME(session)        smtp_username
+#define SMTP_PASS(session)            smtp_pass
+#define SMTP_AUTH_PLAIN_DATA(session) smtp_auth_plain
+#define SMTP_AUTH_PLAIN_LEN(session)  smtp_auth_plain_len
+#endif /* SMTP_COPY_AUTHDATA */
+
+#if SMTP_BODYDH
+#ifndef SMTP_BODYDH_MALLOC
+#define SMTP_BODYDH_MALLOC(size)      mem_malloc(size)
+#define SMTP_BODYDH_FREE(ptr)         mem_free(ptr)
+#endif
+
+/* Some internal state return values */
+#define BDHALLDATASENT                2
+#define BDHSOMEDATASENT               1
+
+enum bdh_handler_state {
+  BDH_SENDING,         /* Serving the user function generating body content */
+  BDH_STOP             /* User function stopped, closing */
+};
+#endif
+
+/** State for SMTP client state machine */
+enum smtp_session_state {
+  SMTP_NULL,
+  SMTP_HELO,
+  SMTP_AUTH_PLAIN,
+  SMTP_AUTH_LOGIN_UNAME,
+  SMTP_AUTH_LOGIN_PASS,
+  SMTP_AUTH_LOGIN,
+  SMTP_MAIL,
+  SMTP_RCPT,
+  SMTP_DATA,
+  SMTP_BODY,
+  SMTP_QUIT,
+  SMTP_CLOSED
+};
+
+#ifdef LWIP_DEBUG
+/** State-to-string table for debugging */
+static const char *smtp_state_str[] = {
+  "SMTP_NULL",
+  "SMTP_HELO",
+  "SMTP_AUTH_PLAIN",
+  "SMTP_AUTH_LOGIN_UNAME",
+  "SMTP_AUTH_LOGIN_PASS",
+  "SMTP_AUTH_LOGIN",
+  "SMTP_MAIL",
+  "SMTP_RCPT",
+  "SMTP_DATA",
+  "SMTP_BODY",
+  "SMTP_QUIT",
+  "SMTP_CLOSED",
+};
+
+static const char *smtp_result_strs[] = {
+  "SMTP_RESULT_OK",
+  "SMTP_RESULT_ERR_UNKNOWN",
+  "SMTP_RESULT_ERR_CONNECT",
+  "SMTP_RESULT_ERR_HOSTNAME",
+  "SMTP_RESULT_ERR_CLOSED",
+  "SMTP_RESULT_ERR_TIMEOUT",
+  "SMTP_RESULT_ERR_SVR_RESP",
+  "SMTP_RESULT_ERR_MEM"
+};
+#endif /* LWIP_DEBUG */
+
+#if SMTP_BODYDH
+struct smtp_bodydh_state {
+  smtp_bodycback_fn callback_fn;  /* The function to call (again) */
+  u16_t state;
+  struct smtp_bodydh exposed;     /* the user function structure */
+};
+#endif /* SMTP_BODYDH */
+
+/** struct keeping the body and state of an smtp session */
+struct smtp_session {
+  /** keeping the state of the smtp session */
+  enum smtp_session_state state;
+  /** timeout handling, if this reaches 0, the connection is closed */
+  u16_t timer;
+  /** helper buffer for transmit, not used for sending body */
+  char tx_buf[SMTP_TX_BUF_LEN + 1];
+  struct pbuf* p;
+  /** source email address */
+  const char* from;
+  /** size of the sourceemail address */
+  u16_t from_len;
+  /** target email address */
+  const char* to;
+  /** size of the target email address */
+  u16_t to_len;
+  /** subject of the email */
+  const char *subject;
+  /** length of the subject string */
+  u16_t subject_len;
+  /** this is the body of the mail to be sent */
+  const char* body;
+  /** this is the length of the body to be sent */
+  u16_t body_len;
+  /** amount of data from body already sent */
+  u16_t body_sent;
+  /** callback function to call when closed */
+  smtp_result_fn callback_fn;
+  /** argument for callback function */
+  void *callback_arg;
+#if SMTP_COPY_AUTHDATA
+  /** Username to use for this request */
+  char *username;
+  /** Password to use for this request */
+  char *pass;
+  /** Username and password combined as necessary for PLAIN authentication */
+  char auth_plain[SMTP_MAX_USERNAME_LEN + SMTP_MAX_PASS_LEN + 3];
+  /** Length of smtp_auth_plain string (cannot use strlen since it includes \0) */
+  size_t auth_plain_len;
+#endif /* SMTP_COPY_AUTHDATA */
+#if SMTP_BODYDH
+  struct smtp_bodydh_state *bodydh;
+#endif /* SMTP_BODYDH */
+};
+
+/** IP address or DNS name of the server to use for next SMTP request */
+static char smtp_server[SMTP_MAX_SERVERNAME_LEN + 1];
+/** TCP port of the server to use for next SMTP request */
+static u16_t smtp_server_port = SMTP_DEFAULT_PORT;
+#if LWIP_ALTCP && LWIP_ALTCP_TLS
+/** If this is set, mail is sent using SMTPS */
+static struct altcp_tls_config *smtp_server_tls_config;
+#endif
+/** Username to use for the next SMTP request */
+static char *smtp_username;
+/** Password to use for the next SMTP request */
+static char *smtp_pass;
+/** Username and password combined as necessary for PLAIN authentication */
+static char smtp_auth_plain[SMTP_MAX_USERNAME_LEN + SMTP_MAX_PASS_LEN + 3];
+/** Length of smtp_auth_plain string (cannot use strlen since it includes \0) */
+static size_t smtp_auth_plain_len;
+
+#if SMTP_CHECK_DATA
+static err_t  smtp_verify(const char *data, size_t data_len, u8_t linebreaks_allowed);
+#endif /* SMTP_CHECK_DATA */
+static err_t  smtp_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err);
+static void   smtp_tcp_err(void *arg, err_t err);
+static err_t  smtp_tcp_poll(void *arg, struct altcp_pcb *pcb);
+static err_t  smtp_tcp_sent(void *arg, struct altcp_pcb *pcb, u16_t len);
+static err_t  smtp_tcp_connected(void *arg, struct altcp_pcb *pcb, err_t err);
+#if LWIP_DNS
+static void   smtp_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg);
+#endif /* LWIP_DNS */
+#if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
+static size_t smtp_base64_encode(char* target, size_t target_len, const char* source, size_t source_len);
+#endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
+static enum   smtp_session_state smtp_prepare_mail(struct smtp_session *s, u16_t *tx_buf_len);
+static void   smtp_send_body(struct smtp_session *s, struct altcp_pcb *pcb);
+static void   smtp_process(void *arg, struct altcp_pcb *pcb, struct pbuf *p);
+#if SMTP_BODYDH
+static void   smtp_send_body_data_handler(struct smtp_session *s, struct altcp_pcb *pcb);
+#endif /* SMTP_BODYDH */
+
+
+#ifdef LWIP_DEBUG
+/** Convert an smtp result to a string */
+const char*
+smtp_result_str(u8_t smtp_result)
+{
+  if (smtp_result >= LWIP_ARRAYSIZE(smtp_result_strs)) {
+    return "UNKNOWN";
+  }
+  return smtp_result_strs[smtp_result];
+}
+
+/** Null-terminates the payload of p for printing out messages.
+ * WARNING: use this only if p is not needed any more as the last byte of
+ *          payload is deleted!
+ */
+static const char*
+smtp_pbuf_str(struct pbuf* p)
+{
+  if ((p == NULL) || (p->len == 0)) {
+    return "";
+  }
+  ((char*)p->payload)[p->len] = 0;
+  return (const char*)p->payload;
+}
+#endif /* LWIP_DEBUG */
+
+/** @ingroup smtp
+ * Set IP address or DNS name for next SMTP connection
+ *
+ * @param server IP address (in ASCII representation) or DNS name of the server
+ */
+err_t
+smtp_set_server_addr(const char* server)
+{
+  size_t len = 0;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  if (server != NULL) {
+    /* strlen: returns length WITHOUT terminating 0 byte */
+    len = strlen(server);
+  }
+  if (len > SMTP_MAX_SERVERNAME_LEN) {
+    return ERR_MEM;
+  }
+  if (len != 0) {
+    MEMCPY(smtp_server, server, len);
+  }
+  smtp_server[len] = 0; /* always OK because of smtp_server[SMTP_MAX_SERVERNAME_LEN + 1] */
+  return ERR_OK;
+}
+
+/** @ingroup smtp
+ * Set TCP port for next SMTP connection
+ *
+ * @param port TCP port
+ */
+void
+smtp_set_server_port(u16_t port)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  smtp_server_port = port;
+}
+
+#if LWIP_ALTCP && LWIP_ALTCP_TLS
+/** @ingroup smtp
+ * Set TLS configuration for next SMTP connection
+ *
+ * @param tls_config TLS configuration
+ */
+void
+smtp_set_tls_config(struct altcp_tls_config *tls_config)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  smtp_server_tls_config = tls_config;
+}
+#endif
+
+/** @ingroup smtp
+ * Set authentication parameters for next SMTP connection
+ *
+ * @param username login name as passed to the server
+ * @param pass password passed to the server together with username
+ */
+err_t
+smtp_set_auth(const char* username, const char* pass)
+{
+  size_t uname_len = 0;
+  size_t pass_len = 0;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  memset(smtp_auth_plain, 0xfa, 64);
+  if (username != NULL) {
+    uname_len = strlen(username);
+    if (uname_len > SMTP_MAX_USERNAME_LEN) {
+      LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Username is too long, %d instead of %d\n",
+        (int)uname_len, SMTP_MAX_USERNAME_LEN));
+      return ERR_ARG;
+    }
+  }
+  if (pass != NULL) {
+#if SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN
+    pass_len = strlen(pass);
+    if (pass_len > SMTP_MAX_PASS_LEN) {
+      LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Password is too long, %d instead of %d\n",
+        (int)uname_len, SMTP_MAX_USERNAME_LEN));
+      return ERR_ARG;
+    }
+#else /* SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN */
+    LWIP_DEBUGF(SMTP_DEBUG_WARN, ("Password not supported as no authentication methods are activated\n"));
+#endif /* SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN */
+  }
+  *smtp_auth_plain = 0;
+  if (username != NULL) {
+    smtp_username = smtp_auth_plain + 1;
+    strcpy(smtp_username, username);
+  }
+  if (pass != NULL) {
+    smtp_pass = smtp_auth_plain + uname_len + 2;
+    strcpy(smtp_pass, pass);
+  }
+  smtp_auth_plain_len = uname_len + pass_len + 2;
+
+  return ERR_OK;
+}
+
+#if SMTP_BODYDH
+static void smtp_free_struct(struct smtp_session *s)
+{
+  if (s->bodydh != NULL) {
+    SMTP_BODYDH_FREE(s->bodydh);
+  }
+  SMTP_STATE_FREE(s);
+}
+#else /* SMTP_BODYDH */
+#define smtp_free_struct(x) SMTP_STATE_FREE(x)
+#endif /* SMTP_BODYDH */
+
+static struct altcp_pcb*
+smtp_setup_pcb(struct smtp_session *s, const ip_addr_t* remote_ip)
+{
+  struct altcp_pcb* pcb;
+  LWIP_UNUSED_ARG(remote_ip);
+
+#if LWIP_ALTCP && LWIP_ALTCP_TLS
+  if (smtp_server_tls_config) {
+    pcb = altcp_tls_new(smtp_server_tls_config, IP_GET_TYPE(remote_ip));
+  } else
+#endif
+  {
+    pcb = altcp_tcp_new_ip_type(IP_GET_TYPE(remote_ip));
+  }
+  if (pcb != NULL) {
+    altcp_arg(pcb, s);
+    altcp_recv(pcb, smtp_tcp_recv);
+    altcp_err(pcb, smtp_tcp_err);
+    altcp_poll(pcb, smtp_tcp_poll, SMTP_POLL_INTERVAL);
+    altcp_sent(pcb, smtp_tcp_sent);
+  }
+  return pcb;
+}
+
+/** The actual mail-sending function, called by smtp_send_mail and
+ * smtp_send_mail_static after setting up the struct smtp_session.
+ */
+static err_t
+smtp_send_mail_alloced(struct smtp_session *s)
+{
+  err_t err;
+  struct altcp_pcb* pcb = NULL;
+  ip_addr_t addr;
+
+  LWIP_ASSERT("no smtp_session supplied", s != NULL);
+
+#if SMTP_CHECK_DATA
+  /* check that body conforms to RFC:
+   * - convert all single-CR or -LF in body to CRLF
+   * - only 7-bit ASCII is allowed
+   */
+  if (smtp_verify(s->to, s->to_len, 0) != ERR_OK) {
+    err = ERR_ARG;
+    goto leave;
+  }
+  if (smtp_verify(s->from, s->from_len, 0) != ERR_OK) {
+    err = ERR_ARG;
+    goto leave;
+  }
+  if (smtp_verify(s->subject, s->subject_len, 0) != ERR_OK) {
+    err = ERR_ARG;
+    goto leave;
+  }
+#if SMTP_BODYDH
+  if (s->bodydh == NULL)
+#endif /* SMTP_BODYDH */
+  {
+    if (smtp_verify(s->body, s->body_len, 0) != ERR_OK) {
+      err = ERR_ARG;
+      goto leave;
+    }
+  }
+#endif /* SMTP_CHECK_DATA */
+
+#if SMTP_COPY_AUTHDATA
+  /* copy auth data, ensuring the first byte is always zero */
+  MEMCPY(s->auth_plain + 1, smtp_auth_plain + 1, smtp_auth_plain_len - 1);
+  s->auth_plain_len = smtp_auth_plain_len;
+  /* default username and pass is empty string */
+  s->username = s->auth_plain;
+  s->pass = s->auth_plain;
+  if (smtp_username != NULL) {
+    s->username += smtp_username - smtp_auth_plain;
+  }
+  if (smtp_pass != NULL) {
+    s->pass += smtp_pass - smtp_auth_plain;
+  }
+#endif /* SMTP_COPY_AUTHDATA */
+
+  s->state = SMTP_NULL;
+  s->timer = SMTP_TIMEOUT;
+
+#if LWIP_DNS
+  err = dns_gethostbyname(smtp_server, &addr, smtp_dns_found, s);
+#else /* LWIP_DNS */
+  err = ipaddr_aton(smtp_server, &addr) ? ERR_OK : ERR_ARG;
+#endif /* LWIP_DNS */
+  if (err == ERR_OK) {
+    pcb = smtp_setup_pcb(s, &addr);
+    if (pcb == NULL) {
+      err = ERR_MEM;
+      goto leave;
+    }
+    err = altcp_connect(pcb, &addr, smtp_server_port, smtp_tcp_connected);
+    if (err != ERR_OK) {
+      LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("tcp_connect failed: %d\n", (int)err));
+      goto deallocate_and_leave;
+    }
+  } else if (err != ERR_INPROGRESS) {
+    LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("dns_gethostbyname failed: %d\n", (int)err));
+    goto deallocate_and_leave;
+  }
+  return ERR_OK;
+
+deallocate_and_leave:
+  if (pcb != NULL) {
+    altcp_arg(pcb, NULL);
+    altcp_close(pcb);
+  }
+leave:
+  smtp_free_struct(s);
+  /* no need to call the callback here since we return != ERR_OK */
+  return err;
+}
+
+/** @ingroup smtp
+ *  Send an email via the currently selected server, username and password.
+ *
+ * @param from source email address (must be NULL-terminated)
+ * @param to target email address (must be NULL-terminated)
+ * @param subject email subject (must be NULL-terminated)
+ * @param body email body (must be NULL-terminated)
+ * @param callback_fn callback function
+ * @param callback_arg user argument to callback_fn
+ * @returns - ERR_OK if structures were allocated and no error occured starting the connection
+ *            (this does not mean the email has been successfully sent!)
+ *          - another err_t on error.
+ */
+err_t
+smtp_send_mail(const char* from, const char* to, const char* subject, const char* body,
+               smtp_result_fn callback_fn, void* callback_arg)
+{
+  struct smtp_session* s;
+  size_t from_len = strlen(from);
+  size_t to_len = strlen(to);
+  size_t subject_len = strlen(subject);
+  size_t body_len = strlen(body);
+  size_t mem_len = sizeof(struct smtp_session);
+  char *sfrom, *sto, *ssubject, *sbody;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  mem_len += from_len + to_len + subject_len + body_len + 4;
+  if (mem_len > 0xffff) {
+    /* too long! */
+    return ERR_MEM;
+  }
+
+  /* Allocate memory to keep this email's session state */
+  s = (struct smtp_session *)SMTP_STATE_MALLOC((mem_size_t)mem_len);
+  if (s == NULL) {
+    return ERR_MEM;
+  }
+  /* initialize the structure */
+  memset(s, 0, mem_len);
+  s->from = sfrom = (char*)s + sizeof(struct smtp_session);
+  s->from_len = (u16_t)from_len;
+  s->to = sto = sfrom + from_len + 1;
+  s->to_len = (u16_t)to_len;
+  s->subject = ssubject = sto + to_len + 1;
+  s->subject_len = (u16_t)subject_len;
+  s->body = sbody = ssubject + subject_len + 1;
+  s->body_len = (u16_t)body_len;
+  /* copy source and target email address */
+  /* cast to size_t is a hack to cast away constness */
+  MEMCPY(sfrom, from, from_len + 1);
+  MEMCPY(sto, to, to_len + 1);
+  MEMCPY(ssubject, subject, subject_len + 1);
+  MEMCPY(sbody, body, body_len + 1);
+
+  s->callback_fn = callback_fn;
+  s->callback_arg = callback_arg;
+
+  /* call the actual implementation of this function */
+  return smtp_send_mail_alloced(s);
+}
+
+/** @ingroup smtp
+ * Same as smtp_send_mail, but doesn't copy from, to, subject and body into
+ * an internal buffer to save memory.
+ * WARNING: the above data must stay untouched until the callback function is
+ *          called (unless the function returns != ERR_OK)
+ */
+err_t
+smtp_send_mail_static(const char *from, const char* to, const char* subject,
+  const char* body, smtp_result_fn callback_fn, void* callback_arg)
+{
+  struct smtp_session* s;
+  size_t len;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  s = (struct smtp_session*)SMTP_STATE_MALLOC(sizeof(struct smtp_session));
+  if (s == NULL) {
+    return ERR_MEM;
+  }
+  memset(s, 0, sizeof(struct smtp_session));
+  /* initialize the structure */
+  s->from = from;
+  len = strlen(from);
+  LWIP_ASSERT("string is too long", len <= 0xffff);
+  s->from_len = (u16_t)len;
+  s->to = to;
+  len = strlen(to);
+  LWIP_ASSERT("string is too long", len <= 0xffff);
+  s->to_len = (u16_t)len;
+  s->subject = subject;
+  len = strlen(subject);
+  LWIP_ASSERT("string is too long", len <= 0xffff);
+  s->subject_len = (u16_t)len;
+  s->body = body;
+  len = strlen(body);
+  LWIP_ASSERT("string is too long", len <= 0xffff);
+  s->body_len = (u16_t)len;
+  s->callback_fn = callback_fn;
+  s->callback_arg = callback_arg;
+  /* call the actual implementation of this function */
+  return smtp_send_mail_alloced(s);
+}
+
+
+/** @ingroup smtp
+ * Same as smtp_send_mail but takes a struct smtp_send_request as single
+ * parameter which contains all the other parameters.
+ * To be used with tcpip_callback to send mail from interrupt context or from
+ * another thread.
+ *
+ * WARNING: server and authentication must stay untouched until this function has run!
+ *
+ * Usage example:
+ * - allocate a struct smtp_send_request (in a way that is allowed in interrupt context)
+ * - fill the members of the struct as if calling smtp_send_mail
+ * - specify a callback_function
+ * - set callback_arg to the structure itself
+ * - call this function
+ * - wait for the callback function to be called
+ * - in the callback function, deallocate the structure (passed as arg)
+ */
+void
+smtp_send_mail_int(void *arg)
+{
+  struct smtp_send_request *req = (struct smtp_send_request*)arg;
+  err_t err;
+
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("smtp_send_mail_int: no argument given", arg != NULL);
+
+  if (req->static_data) {
+    err = smtp_send_mail_static(req->from, req->to, req->subject, req->body,
+      req->callback_fn, req->callback_arg);
+  } else {
+    err = smtp_send_mail(req->from, req->to, req->subject, req->body,
+      req->callback_fn, req->callback_arg);
+  }
+  if ((err != ERR_OK) && (req->callback_fn != NULL)) {
+    req->callback_fn(req->callback_arg, SMTP_RESULT_ERR_UNKNOWN, 0, err);
+  }
+}
+
+#if SMTP_CHECK_DATA
+/** Verify that a given string conforms to the SMTP rules
+ * (7-bit only, no single CR or LF,
+ *  @todo: no line consisting of a single dot only)
+ */
+static err_t
+smtp_verify(const char *data, size_t data_len, u8_t linebreaks_allowed)
+{
+  size_t i;
+  u8_t last_was_cr = 0;
+  for (i = 0; i < data_len; i++) {
+    char current = data[i];
+    if ((current & 0x80) != 0) {
+      LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: no 8-bit data supported: %s\n", data));
+      return ERR_ARG;
+    }
+    if (current == '\r') {
+      if (!linebreaks_allowed) {
+        LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found CR where no linebreaks allowed: %s\n", data));
+        return ERR_ARG;
+      }
+      if (last_was_cr) {
+        LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found double CR: %s\n", data));
+        return ERR_ARG;
+      }
+      last_was_cr = 1;
+    } else {
+      if (current == '\n') {
+        if (!last_was_cr) {
+          LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found LF without CR before: %s\n", data));
+          return ERR_ARG;
+        }
+      }
+      last_was_cr = 0;
+    }
+  }
+  return ERR_OK;
+}
+#endif /* SMTP_CHECK_DATA */
+
+/** Frees the smtp_session and calls the callback function */
+static void
+smtp_free(struct smtp_session *s, u8_t result, u16_t srv_err, err_t err)
+{
+  smtp_result_fn fn = s->callback_fn;
+  void *arg = s->callback_arg;
+  if (s->p != NULL) {
+    pbuf_free(s->p);
+  }
+  smtp_free_struct(s);
+  if (fn != NULL) {
+    fn(arg, result, srv_err, err);
+  }
+}
+
+/** Try to close a pcb and free the arg if successful */
+static void
+smtp_close(struct smtp_session *s, struct altcp_pcb *pcb, u8_t result,
+           u16_t srv_err, err_t err)
+{
+  if (pcb != NULL) {
+     altcp_arg(pcb, NULL);
+     if (altcp_close(pcb) == ERR_OK) {
+       if (s != NULL) {
+         smtp_free(s, result, srv_err, err);
+       }
+     } else {
+       /* close failed, set back arg */
+       altcp_arg(pcb, s);
+     }
+  } else {
+    if (s != NULL) {
+      smtp_free(s, result, srv_err, err);
+    }
+  }
+}
+
+/** Raw API TCP err callback: pcb is already deallocated */
+static void
+smtp_tcp_err(void *arg, err_t err)
+{
+  LWIP_UNUSED_ARG(err);
+  if (arg != NULL) {
+    LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_tcp_err: connection reset by remote host\n"));
+    smtp_free((struct smtp_session*)arg, SMTP_RESULT_ERR_CLOSED, 0, err);
+  }
+}
+
+/** Raw API TCP poll callback */
+static err_t
+smtp_tcp_poll(void *arg, struct altcp_pcb *pcb)
+{
+  if (arg != NULL) {
+    struct smtp_session *s = (struct smtp_session*)arg;
+    if (s->timer != 0) {
+      s->timer--;
+    }
+  }
+  smtp_process(arg, pcb, NULL);
+  return ERR_OK;
+}
+
+/** Raw API TCP sent callback */
+static err_t
+smtp_tcp_sent(void *arg, struct altcp_pcb *pcb, u16_t len)
+{
+  LWIP_UNUSED_ARG(len);
+
+  smtp_process(arg, pcb, NULL);
+
+  return ERR_OK;
+}
+
+/** Raw API TCP recv callback */
+static err_t
+smtp_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+  LWIP_UNUSED_ARG(err);
+  if (p != NULL) {
+    altcp_recved(pcb, p->tot_len);
+    smtp_process(arg, pcb, p);
+  } else {
+    LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_tcp_recv: connection closed by remote host\n"));
+    smtp_close((struct smtp_session*)arg, pcb, SMTP_RESULT_ERR_CLOSED, 0, err);
+  }
+  return ERR_OK;
+}
+
+static err_t
+smtp_tcp_connected(void *arg, struct altcp_pcb *pcb, err_t err)
+{
+  LWIP_UNUSED_ARG(arg);
+
+  if (err == ERR_OK) {
+    LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_connected: Waiting for 220\n"));
+  } else {
+    /* shouldn't happen, but we still check 'err', only to be sure */
+    LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_connected: %d\n", (int)err));
+    smtp_close((struct smtp_session*)arg, pcb, SMTP_RESULT_ERR_CONNECT, 0, err);
+  }
+  return ERR_OK;
+}
+
+#if LWIP_DNS
+/** DNS callback
+ * If ipaddr is non-NULL, resolving succeeded, otherwise it failed.
+ */
+static void
+smtp_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg)
+{
+  struct smtp_session *s = (struct smtp_session*)arg;
+  struct altcp_pcb *pcb;
+  err_t err;
+  u8_t result;
+
+  LWIP_UNUSED_ARG(hostname);
+
+  if (ipaddr != NULL) {
+    pcb = smtp_setup_pcb(s, ipaddr);
+    if (pcb != NULL) {
+      LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_dns_found: hostname resolved, connecting\n"));
+      err = altcp_connect(pcb, ipaddr, smtp_server_port, smtp_tcp_connected);
+      if (err == ERR_OK) {
+        return;
+      }
+      LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("tcp_connect failed: %d\n", (int)err));
+      result = SMTP_RESULT_ERR_CONNECT;
+    } else {
+      LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_dns_found: failed to allocate tcp pcb\n"));
+      result = SMTP_RESULT_ERR_MEM;
+      err = ERR_MEM;
+    }
+  } else {
+    LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_dns_found: failed to resolve hostname: %s\n",
+      hostname));
+    pcb = NULL;
+    result = SMTP_RESULT_ERR_HOSTNAME;
+    err = ERR_ARG;
+  }
+  smtp_close(s, pcb, result, 0, err);
+}
+#endif /* LWIP_DNS */
+
+#if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
+
+/** Table 6-bit-index-to-ASCII used for base64-encoding */
+static const char base64_table[] = {
+  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+  'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+  'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
+  'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
+  'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  '+', '/'
+};
+
+/** Base64 encoding */
+static size_t
+smtp_base64_encode(char* target, size_t target_len, const char* source, size_t source_len)
+{
+  size_t i;
+  s8_t j;
+  size_t target_idx = 0;
+  size_t longer = (source_len % 3) ? (3 - (source_len % 3)) : 0;
+  size_t source_len_b64 = source_len + longer;
+  size_t len = (((source_len_b64) * 4) / 3);
+  u8_t x = 5;
+  u8_t current = 0;
+  LWIP_UNUSED_ARG(target_len);
+
+  LWIP_ASSERT("target_len is too short", target_len >= len);
+
+  for (i = 0; i < source_len_b64; i++) {
+    u8_t b = (i < source_len ? (u8_t)source[i] : 0);
+    for (j = 7; j >= 0; j--, x--) {
+      if ((b & (1 << j)) != 0) {
+        current = (u8_t)(current | (1U << x));
+      }
+      if (x == 0) {
+        target[target_idx++] = base64_table[current];
+        x = 6;
+        current = 0;
+      }
+    }
+  }
+  for (i = len - longer; i < len; i++) {
+    target[i] = '=';
+  }
+  return len;
+}
+#endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
+
+/** Parse pbuf to see if it contains the beginning of an answer.
+ * If so, it returns the contained response code as number between 1 and 999.
+ * If not, zero is returned.
+ *
+ * @param s smtp session struct
+ */
+static u16_t
+smtp_is_response(struct smtp_session *s)
+{
+  char digits[4];
+  long num;
+
+  if (s->p == NULL) {
+    return 0;
+  }
+  /* copy three digits and convert them to int */
+  if (pbuf_copy_partial(s->p, digits, 3, 0) != 3) {
+    /* pbuf was too short */
+    return 0;
+  }
+  digits[3] = 0;
+  num = strtol(digits, NULL, 10);
+  if ((num <= 0) || (num >= 1000)) {
+    /* failed to find response code at start of line */
+    return 0;
+  }
+  return (u16_t)num;
+}
+
+/** Parse pbuf to see if it contains a fully received answer.
+ * If one is found, ERR_OK is returned.
+ * If none is found, ERR_VAL is returned.
+ *
+ * A fully received answer is a 3-digit number followed by a space,
+ * some string and a CRLF as line ending.
+ *
+ * @param s smtp session struct
+ */
+static err_t
+smtp_is_response_finished(struct smtp_session *s)
+{
+  u8_t sp;
+  u16_t crlf;
+  u16_t offset;
+
+  if (s->p == NULL) {
+    return ERR_VAL;
+  }
+  offset = 0;
+again:
+  /* We could check the response number here, but we trust the
+   * protocol definition which says the client can rely on it being
+   * the same on every line. */
+
+  /* find CRLF */
+  if (offset > 0xFFFF - 4) {
+    /* would overflow */
+    return ERR_VAL;
+  }
+  crlf = pbuf_memfind(s->p, SMTP_CRLF, SMTP_CRLF_LEN, (u16_t)(offset + 4));
+  if (crlf == 0xFFFF) {
+    /* no CRLF found */
+    return ERR_VAL;
+  }
+  sp = pbuf_get_at(s->p, (u16_t)(offset + 3));
+  if (sp == '-') {
+    /* no space after response code -> try next line */
+    offset = (u16_t)(crlf + 2);
+    if (offset < crlf) {
+      /* overflow */
+      return ERR_VAL;
+    }
+    goto again;
+  } else if (sp == ' ') {
+    /* CRLF found after response code + space -> valid response */
+    return ERR_OK;
+  }
+  /* sp contains invalid character */
+  return ERR_VAL;
+}
+
+/** Prepare HELO/EHLO message */
+static enum smtp_session_state
+smtp_prepare_helo(struct smtp_session *s, u16_t *tx_buf_len, struct altcp_pcb *pcb)
+{
+  size_t ipa_len;
+  const char *ipa = ipaddr_ntoa(altcp_get_ip(pcb, 1));
+  LWIP_ASSERT("ipaddr_ntoa returned NULL", ipa != NULL);
+  ipa_len = strlen(ipa);
+  LWIP_ASSERT("string too long", ipa_len <= (SMTP_TX_BUF_LEN-SMTP_CMD_EHLO_1_LEN-SMTP_CMD_EHLO_2_LEN));
+
+  *tx_buf_len = (u16_t)(SMTP_CMD_EHLO_1_LEN + (u16_t)ipa_len + SMTP_CMD_EHLO_2_LEN);
+  LWIP_ASSERT("tx_buf overflow detected", *tx_buf_len <= SMTP_TX_BUF_LEN);
+
+  SMEMCPY(s->tx_buf, SMTP_CMD_EHLO_1, SMTP_CMD_EHLO_1_LEN);
+  MEMCPY(&s->tx_buf[SMTP_CMD_EHLO_1_LEN], ipa, ipa_len);
+  SMEMCPY(&s->tx_buf[SMTP_CMD_EHLO_1_LEN + ipa_len], SMTP_CMD_EHLO_2, SMTP_CMD_EHLO_2_LEN);
+  return SMTP_HELO;
+}
+
+#if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
+/** Parse last server response (in rx_buf) for supported authentication method,
+ * create data to send out (to tx_buf), set tx_data_len correctly
+ * and return the next state.
+ */
+static enum smtp_session_state
+smtp_prepare_auth_or_mail(struct smtp_session *s, u16_t *tx_buf_len)
+{
+  /* check response for supported authentication method */
+  u16_t auth = pbuf_strstr(s->p, SMTP_KEYWORD_AUTH_SP);
+  if (auth == 0xFFFF) {
+    auth = pbuf_strstr(s->p, SMTP_KEYWORD_AUTH_EQ);
+  }
+  if (auth != 0xFFFF) {
+    u16_t crlf = pbuf_memfind(s->p, SMTP_CRLF, SMTP_CRLF_LEN, auth);
+    if ((crlf != 0xFFFF) && (crlf > auth)) {
+      /* use tx_buf temporarily */
+      u16_t copied = pbuf_copy_partial(s->p, s->tx_buf, (u16_t)(crlf - auth), auth);
+      if (copied != 0) {
+        char *sep = s->tx_buf + SMTP_KEYWORD_AUTH_LEN;
+        s->tx_buf[copied] = 0;
+#if SMTP_SUPPORT_AUTH_PLAIN
+        /* favour PLAIN over LOGIN since it involves less requests */
+        if (strstr(sep, SMTP_AUTH_PARAM_PLAIN) != NULL) {
+          size_t auth_len;
+          /* server supports AUTH PLAIN */
+          SMEMCPY(s->tx_buf, SMTP_CMD_AUTHPLAIN_1, SMTP_CMD_AUTHPLAIN_1_LEN);
+
+          /* add base64-encoded string "\0username\0password" */
+          auth_len = smtp_base64_encode(&s->tx_buf[SMTP_CMD_AUTHPLAIN_1_LEN],
+            SMTP_TX_BUF_LEN - SMTP_CMD_AUTHPLAIN_1_LEN, SMTP_AUTH_PLAIN_DATA(s),
+            SMTP_AUTH_PLAIN_LEN(s));
+          LWIP_ASSERT("string too long", auth_len <= (SMTP_TX_BUF_LEN-SMTP_CMD_AUTHPLAIN_1_LEN-SMTP_CMD_AUTHPLAIN_2_LEN));
+          *tx_buf_len = (u16_t)(SMTP_CMD_AUTHPLAIN_1_LEN + SMTP_CMD_AUTHPLAIN_2_LEN + (u16_t)auth_len);
+          SMEMCPY(&s->tx_buf[SMTP_CMD_AUTHPLAIN_1_LEN + auth_len], SMTP_CMD_AUTHPLAIN_2,
+            SMTP_CMD_AUTHPLAIN_2_LEN);
+          return SMTP_AUTH_PLAIN;
+        } else
+#endif /* SMTP_SUPPORT_AUTH_PLAIN */
+        {
+#if SMTP_SUPPORT_AUTH_LOGIN
+          if (strstr(sep, SMTP_AUTH_PARAM_LOGIN) != NULL) {
+            /* server supports AUTH LOGIN */
+            *tx_buf_len = SMTP_CMD_AUTHLOGIN_LEN;
+            SMEMCPY(s->tx_buf, SMTP_CMD_AUTHLOGIN, SMTP_CMD_AUTHLOGIN_LEN);
+            return SMTP_AUTH_LOGIN_UNAME;
+          }
+#endif /* SMTP_SUPPORT_AUTH_LOGIN */
+        }
+      }
+    }
+  }
+  /* server didnt's send correct keywords for AUTH, try sending directly */
+  return smtp_prepare_mail(s, tx_buf_len);
+}
+#endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
+
+#if SMTP_SUPPORT_AUTH_LOGIN
+/** Send base64-encoded username */
+static enum smtp_session_state
+smtp_prepare_auth_login_uname(struct smtp_session *s, u16_t *tx_buf_len)
+{
+  size_t base64_len = smtp_base64_encode(s->tx_buf, SMTP_TX_BUF_LEN,
+    SMTP_USERNAME(s), strlen(SMTP_USERNAME(s)));
+  /* @todo: support base64-encoded longer than 64k */
+  LWIP_ASSERT("string too long", base64_len <= 0xffff);
+  LWIP_ASSERT("tx_buf overflow detected", base64_len <= SMTP_TX_BUF_LEN - SMTP_CRLF_LEN);
+  *tx_buf_len = (u16_t)(base64_len + SMTP_CRLF_LEN);
+
+  SMEMCPY(&s->tx_buf[base64_len], SMTP_CRLF, SMTP_CRLF_LEN);
+  s->tx_buf[*tx_buf_len] = 0;
+  return SMTP_AUTH_LOGIN_PASS;
+}
+
+/** Send base64-encoded password */
+static enum smtp_session_state
+smtp_prepare_auth_login_pass(struct smtp_session *s, u16_t *tx_buf_len)
+{
+  size_t base64_len = smtp_base64_encode(s->tx_buf, SMTP_TX_BUF_LEN,
+    SMTP_PASS(s), strlen(SMTP_PASS(s)));
+  /* @todo: support base64-encoded longer than 64k */
+  LWIP_ASSERT("string too long", base64_len <= 0xffff);
+  LWIP_ASSERT("tx_buf overflow detected", base64_len <= SMTP_TX_BUF_LEN - SMTP_CRLF_LEN);
+  *tx_buf_len = (u16_t)(base64_len + SMTP_CRLF_LEN);
+
+  SMEMCPY(&s->tx_buf[base64_len], SMTP_CRLF, SMTP_CRLF_LEN);
+  s->tx_buf[*tx_buf_len] = 0;
+  return SMTP_AUTH_LOGIN;
+}
+#endif /* SMTP_SUPPORT_AUTH_LOGIN */
+
+/** Prepare MAIL message */
+static enum smtp_session_state
+smtp_prepare_mail(struct smtp_session *s, u16_t *tx_buf_len)
+{
+  char *target = s->tx_buf;
+  LWIP_ASSERT("tx_buf overflow detected", s->from_len <= (SMTP_TX_BUF_LEN - SMTP_CMD_MAIL_1_LEN - SMTP_CMD_MAIL_2_LEN));
+  *tx_buf_len = (u16_t)(SMTP_CMD_MAIL_1_LEN + SMTP_CMD_MAIL_2_LEN + s->from_len);
+  target[*tx_buf_len] = 0;
+
+  SMEMCPY(target, SMTP_CMD_MAIL_1, SMTP_CMD_MAIL_1_LEN);
+  target += SMTP_CMD_MAIL_1_LEN;
+  MEMCPY(target, s->from, s->from_len);
+  target += s->from_len;
+  SMEMCPY(target, SMTP_CMD_MAIL_2, SMTP_CMD_MAIL_2_LEN);
+  return SMTP_MAIL;
+}
+
+/** Prepare RCPT message */
+static enum smtp_session_state
+smtp_prepare_rcpt(struct smtp_session *s, u16_t *tx_buf_len)
+{
+  char *target = s->tx_buf;
+  LWIP_ASSERT("tx_buf overflow detected", s->to_len <= (SMTP_TX_BUF_LEN - SMTP_CMD_RCPT_1_LEN - SMTP_CMD_RCPT_2_LEN));
+  *tx_buf_len = (u16_t)(SMTP_CMD_RCPT_1_LEN + SMTP_CMD_RCPT_2_LEN + s->to_len);
+  target[*tx_buf_len] = 0;
+
+  SMEMCPY(target, SMTP_CMD_RCPT_1, SMTP_CMD_RCPT_1_LEN);
+  target += SMTP_CMD_RCPT_1_LEN;
+  MEMCPY(target, s->to, s->to_len);
+  target += s->to_len;
+  SMEMCPY(target, SMTP_CMD_RCPT_2, SMTP_CMD_RCPT_2_LEN);
+  return SMTP_RCPT;
+}
+
+/** Prepare header of body */
+static enum smtp_session_state
+smtp_prepare_header(struct smtp_session *s, u16_t *tx_buf_len)
+{
+  char *target = s->tx_buf;
+  int len = SMTP_CMD_HEADER_1_LEN + SMTP_CMD_HEADER_2_LEN +
+    SMTP_CMD_HEADER_3_LEN + SMTP_CMD_HEADER_4_LEN + s->from_len + s->to_len +
+    s->subject_len;
+  LWIP_ASSERT("tx_buf overflow detected", len > 0 && len <= SMTP_TX_BUF_LEN);
+  *tx_buf_len = (u16_t)len;
+  target[*tx_buf_len] = 0;
+
+  SMEMCPY(target, SMTP_CMD_HEADER_1, SMTP_CMD_HEADER_1_LEN);
+  target += SMTP_CMD_HEADER_1_LEN;
+  MEMCPY(target, s->from, s->from_len);
+  target += s->from_len;
+  SMEMCPY(target, SMTP_CMD_HEADER_2, SMTP_CMD_HEADER_2_LEN);
+  target += SMTP_CMD_HEADER_2_LEN;
+  MEMCPY(target, s->to, s->to_len);
+  target += s->to_len;
+  SMEMCPY(target, SMTP_CMD_HEADER_3, SMTP_CMD_HEADER_3_LEN);
+  target += SMTP_CMD_HEADER_3_LEN;
+  MEMCPY(target, s->subject, s->subject_len);
+  target += s->subject_len;
+  SMEMCPY(target, SMTP_CMD_HEADER_4, SMTP_CMD_HEADER_4_LEN);
+
+  return SMTP_BODY;
+}
+
+/** Prepare QUIT message */
+static enum smtp_session_state
+smtp_prepare_quit(struct smtp_session *s, u16_t *tx_buf_len)
+{
+  *tx_buf_len = SMTP_CMD_QUIT_LEN;
+  s->tx_buf[*tx_buf_len] = 0;
+  SMEMCPY(s->tx_buf, SMTP_CMD_QUIT, SMTP_CMD_QUIT_LEN);
+  LWIP_ASSERT("tx_buf overflow detected", *tx_buf_len <= SMTP_TX_BUF_LEN);
+  return SMTP_CLOSED;
+}
+
+/** If in state SMTP_BODY, try to send more body data */
+static void
+smtp_send_body(struct smtp_session *s, struct altcp_pcb *pcb)
+{
+  err_t err;
+
+  if (s->state == SMTP_BODY) {
+#if SMTP_BODYDH
+    if (s->bodydh) {
+      smtp_send_body_data_handler(s, pcb);
+    } else
+#endif /* SMTP_BODYDH */
+    {
+      u16_t send_len = (u16_t)(s->body_len - s->body_sent);
+      if (send_len > 0) {
+        u16_t snd_buf = altcp_sndbuf(pcb);
+        if (send_len > snd_buf) {
+          send_len = snd_buf;
+        }
+        if (send_len > 0) {
+          /* try to send something out */
+          err = altcp_write(pcb, &s->body[s->body_sent], (u16_t)send_len, TCP_WRITE_FLAG_COPY);
+          if (err == ERR_OK) {
+            s->timer = SMTP_TIMEOUT_DATABLOCK;
+            s->body_sent = (u16_t)(s->body_sent + send_len);
+            if (s->body_sent < s->body_len) {
+              LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: %d of %d bytes written\n",
+                s->body_sent, s->body_len));
+            }
+          }
+        }
+      }
+    }
+    if (s->body_sent == s->body_len) {
+      /* the whole body has been written, write last line */
+      LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: body completely written (%d bytes), appending end-of-body\n",
+        s->body_len));
+      err = altcp_write(pcb, SMTP_CMD_BODY_FINISHED, SMTP_CMD_BODY_FINISHED_LEN, 0);
+      if (err == ERR_OK) {
+        s->timer = SMTP_TIMEOUT_DATATERM;
+        LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: end-of-body written, changing state to %s\n",
+          smtp_state_str[SMTP_QUIT]));
+        /* last line written, change state, wait for confirmation */
+        s->state = SMTP_QUIT;
+      }
+    }
+  }
+}
+
+/** State machine-like implementation of an SMTP client.
+ */
+static void
+smtp_process(void *arg, struct altcp_pcb *pcb, struct pbuf *p)
+{
+  struct smtp_session* s = (struct smtp_session*)arg;
+  u16_t response_code = 0;
+  u16_t tx_buf_len = 0;
+  enum smtp_session_state next_state;
+
+  if (arg == NULL) {
+    /* already closed SMTP connection */
+    if (p != NULL) {
+      LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("Received %d bytes after closing: %s\n",
+        p->tot_len, smtp_pbuf_str(p)));
+      pbuf_free(p);
+    }
+    return;
+  }
+
+  next_state = s->state;
+
+  if (p != NULL) {
+    /* received data */
+    if (s->p == NULL) {
+      s->p = p;
+    } else {
+      pbuf_cat(s->p, p);
+    }
+  } else {
+    /* idle timer, close connection if timed out */
+    if (s->timer == 0) {
+      LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_process: connection timed out, closing\n"));
+      smtp_close(s, pcb, SMTP_RESULT_ERR_TIMEOUT, 0, ERR_TIMEOUT);
+      return;
+    }
+    if (s->state == SMTP_BODY) {
+      smtp_send_body(s, pcb);
+      return;
+    }
+  }
+  response_code = smtp_is_response(s);
+  if (response_code) {
+    LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process: received response code: %d\n", response_code));
+    if (smtp_is_response_finished(s) != ERR_OK) {
+      LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process: partly received response code: %d\n", response_code));
+      /* wait for next packet to complete the respone */
+      return;
+    }
+  } else {
+    if (s->p != NULL) {
+      LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_process: unknown data received (%s)\n",
+        smtp_pbuf_str(s->p)));
+      pbuf_free(s->p);
+      s->p = NULL;
+    }
+    return;
+  }
+
+  switch(s->state)
+  {
+  case(SMTP_NULL):
+    /* wait for 220 */
+    if (response_code == 220) {
+      /* then send EHLO */
+      next_state = smtp_prepare_helo(s, &tx_buf_len, pcb);
+    }
+    break;
+  case(SMTP_HELO):
+    /* wait for 250 */
+    if (response_code == 250) {
+#if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
+      /* then send AUTH or MAIL */
+      next_state = smtp_prepare_auth_or_mail(s, &tx_buf_len);
+    }
+    break;
+  case(SMTP_AUTH_LOGIN):
+  case(SMTP_AUTH_PLAIN):
+    /* wait for 235 */
+    if (response_code == 235) {
+#endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
+      /* send MAIL */
+      next_state = smtp_prepare_mail(s, &tx_buf_len);
+    }
+    break;
+#if SMTP_SUPPORT_AUTH_LOGIN
+  case(SMTP_AUTH_LOGIN_UNAME):
+    /* wait for 334 Username */
+    if (response_code == 334) {
+      if (pbuf_strstr(s->p, SMTP_RESP_LOGIN_UNAME) != 0xFFFF) {
+        /* send username */
+        next_state = smtp_prepare_auth_login_uname(s, &tx_buf_len);
+      }
+    }
+    break;
+  case(SMTP_AUTH_LOGIN_PASS):
+    /* wait for 334 Password */
+    if (response_code == 334) {
+      if (pbuf_strstr(s->p, SMTP_RESP_LOGIN_PASS) != 0xFFFF) {
+        /* send username */
+        next_state = smtp_prepare_auth_login_pass(s, &tx_buf_len);
+      }
+    }
+    break;
+#endif /* SMTP_SUPPORT_AUTH_LOGIN */
+  case(SMTP_MAIL):
+    /* wait for 250 */
+    if (response_code == 250) {
+      /* send RCPT */
+      next_state = smtp_prepare_rcpt(s, &tx_buf_len);
+    }
+    break;
+  case(SMTP_RCPT):
+    /* wait for 250 */
+    if (response_code == 250) {
+      /* send DATA */
+      SMEMCPY(s->tx_buf, SMTP_CMD_DATA, SMTP_CMD_DATA_LEN);
+      tx_buf_len = SMTP_CMD_DATA_LEN;
+      next_state = SMTP_DATA;
+    }
+    break;
+  case(SMTP_DATA):
+    /* wait for 354 */
+    if (response_code == 354) {
+      /* send email header */
+      next_state = smtp_prepare_header(s, &tx_buf_len);
+    }
+    break;
+  case(SMTP_BODY):
+    /* nothing to be done here, handled somewhere else */
+    break;
+  case(SMTP_QUIT):
+    /* wait for 250 */
+    if (response_code == 250) {
+      /* send QUIT */
+      next_state = smtp_prepare_quit(s, &tx_buf_len);
+    }
+    break;
+  case(SMTP_CLOSED):
+    /* nothing to do, wait for connection closed from server */
+    return;
+  default:
+    LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Invalid state: %d/%s\n", (int)s->state,
+      smtp_state_str[s->state]));
+    break;
+  }
+  if (s->state == next_state) {
+    LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_process[%s]: unexpected response_code, closing: %d (%s)\n",
+      smtp_state_str[s->state], response_code, smtp_pbuf_str(s->p)));
+    /* close connection */
+    smtp_close(s, pcb, SMTP_RESULT_ERR_SVR_RESP, response_code, ERR_OK);
+    return;
+  }
+  if (tx_buf_len > 0) {
+    SMTP_TX_BUF_MAX(tx_buf_len);
+    if (altcp_write(pcb, s->tx_buf, tx_buf_len, TCP_WRITE_FLAG_COPY) == ERR_OK) {
+      LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process[%s]: received command %d (%s)\n",
+        smtp_state_str[s->state], response_code, smtp_pbuf_str(s->p)));
+      LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process[%s]: sent %"U16_F" bytes: \"%s\"\n",
+        smtp_state_str[s->state], tx_buf_len, s->tx_buf));
+      s->timer = SMTP_TIMEOUT;
+      pbuf_free(s->p);
+      s->p = NULL;
+      LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_process: changing state from %s to %s\n",
+        smtp_state_str[s->state], smtp_state_str[next_state]));
+      s->state = next_state;
+      if (next_state == SMTP_BODY) {
+        /* try to stream-send body data right now */
+        smtp_send_body(s, pcb);
+      } else if (next_state == SMTP_CLOSED) {
+        /* sent out all data, delete structure */
+        altcp_arg(pcb, NULL);
+        smtp_free(s, SMTP_RESULT_OK, 0, ERR_OK);
+      }
+    }
+  }
+}
+
+#if SMTP_BODYDH
+/** Elementary sub-function to send data
+ *
+ * @returns: BDHALLDATASENT all data has been written
+ *           BDHSOMEDATASENT some data has been written
+ *           0 no data has been written
+ */
+static int
+smtp_send_bodyh_data(struct altcp_pcb *pcb, const char **from, u16_t *howmany)
+{
+  err_t err;
+  u16_t len = *howmany;
+
+  len = (u16_t)LWIP_MIN(len, altcp_sndbuf(pcb));
+  err = altcp_write(pcb, *from, len, TCP_WRITE_FLAG_COPY);
+  if (err == ERR_OK) {
+    *from += len;
+    if ((*howmany -= len) > 0) {
+      return BDHSOMEDATASENT;
+    }
+    return BDHALLDATASENT;
+  }
+  return 0;
+}
+
+/** Same as smtp_send_mail_static, but uses a callback function to send body data
+ */
+err_t
+smtp_send_mail_bodycback(const char *from, const char* to, const char* subject,
+  smtp_bodycback_fn bodycback_fn, smtp_result_fn callback_fn, void* callback_arg)
+{
+  struct smtp_session* s;
+  size_t len;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  s = (struct smtp_session*)SMTP_STATE_MALLOC(sizeof(struct smtp_session));
+  if (s == NULL) {
+    return ERR_MEM;
+  }
+  memset(s, 0, sizeof(struct smtp_session));
+  s->bodydh = (struct smtp_bodydh_state*)SMTP_BODYDH_MALLOC(sizeof(struct smtp_bodydh_state));
+  if (s->bodydh == NULL) {
+    SMTP_STATE_FREE(s);
+    return ERR_MEM;
+  }
+  memset(s->bodydh, 0, sizeof(struct smtp_bodydh_state));
+  /* initialize the structure */
+  s->from = from;
+  len = strlen(from);
+  LWIP_ASSERT("string is too long", len <= 0xffff);
+  s->from_len = (u16_t)len;
+  s->to = to;
+  len = strlen(to);
+  LWIP_ASSERT("string is too long", len <= 0xffff);
+  s->to_len = (u16_t)len;
+  s->subject = subject;
+  len = strlen(subject);
+  LWIP_ASSERT("string is too long", len <= 0xffff);
+  s->subject_len = (u16_t)len;
+  s->body = NULL;
+  LWIP_ASSERT("string is too long", len <= 0xffff);
+  s->callback_fn = callback_fn;
+  s->callback_arg = callback_arg;
+  s->bodydh->callback_fn = bodycback_fn;
+  s->bodydh->state = BDH_SENDING;
+  /* call the actual implementation of this function */
+  return smtp_send_mail_alloced(s);
+}
+
+static void
+smtp_send_body_data_handler(struct smtp_session *s, struct altcp_pcb *pcb)
+{
+  struct smtp_bodydh_state *bdh;
+  int res = 0, ret;
+  LWIP_ASSERT("s != NULL", s != NULL);
+  bdh = s->bodydh;
+  LWIP_ASSERT("bodydh != NULL", bdh != NULL);
+
+  /* resume any leftovers from prior memory constraints */
+  if (s->body_len) {
+    LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: resume\n"));
+    if((res = smtp_send_bodyh_data(pcb, (const char **)&s->body, &s->body_len))
+        != BDHALLDATASENT) {
+      s->body_sent = s->body_len - 1;
+      return;
+    }
+  }
+  ret = res;
+  /* all data on buffer has been queued, resume execution */
+  if (bdh->state == BDH_SENDING) {
+    LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: run\n"));
+    do {
+      ret |= res; /* remember if we once queued something to send */
+      bdh->exposed.length = 0;
+      if (bdh->callback_fn(s->callback_arg, &bdh->exposed) == BDH_DONE) {
+        bdh->state = BDH_STOP;
+      }
+      s->body = bdh->exposed.buffer;
+      s->body_len = bdh->exposed.length;
+      LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: trying to send %u bytes\n", (unsigned int)s->body_len));
+    } while (s->body_len &&
+            ((res = smtp_send_bodyh_data(pcb, (const char **)&s->body, &s->body_len)) == BDHALLDATASENT)
+            && (bdh->state != BDH_STOP));
+  }
+  if ((bdh->state != BDH_SENDING) && (ret != BDHSOMEDATASENT)) {
+    LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: stop\n"));
+    s->body_sent = s->body_len;
+  } else {
+    LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: pause\n"));
+    s->body_sent = s->body_len - 1;
+  }
+}
+#endif /* SMTP_BODYDH */
+
+#endif /* LWIP_TCP && LWIP_CALLBACK_API */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_asn1.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_asn1.c
new file mode 100644 (file)
index 0000000..6df34db
--- /dev/null
@@ -0,0 +1,704 @@
+/**
+ * @file
+ * Abstract Syntax Notation One (ISO 8824, 8825) encoding
+ *
+ * @todo not optimised (yet), favor correctness over speed, favor speed over size
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ *         Martin Hentschel <info@cl-soft.de>
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "snmp_asn1.h"
+
+#define PBUF_OP_EXEC(code) \
+  if ((code) != ERR_OK) { \
+    return ERR_BUF; \
+  }
+
+/**
+ * Encodes a TLV into a pbuf stream.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param tlv TLV to encode
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_ans1_enc_tlv(struct snmp_pbuf_stream *pbuf_stream, struct snmp_asn1_tlv *tlv)
+{
+  u8_t data;
+  u8_t length_bytes_required;
+
+  /* write type */
+  if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
+    /* extended format is not used by SNMP so we do not accept those values */
+    return ERR_ARG;
+  }
+  if (tlv->type_len != 0) {
+    /* any other value as auto is not accepted for type (we always use one byte because extended syntax is prohibited) */
+    return ERR_ARG;
+  }
+
+  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, tlv->type));
+  tlv->type_len = 1;
+
+  /* write length */
+  if (tlv->value_len <= 127) {
+    length_bytes_required = 1;
+  } else if (tlv->value_len <= 255) {
+    length_bytes_required = 2;
+  } else  {
+    length_bytes_required = 3;
+  }
+
+  /* check for forced min length */
+  if (tlv->length_len > 0) {
+    if (tlv->length_len < length_bytes_required) {
+      /* unable to code requested length in requested number of bytes */
+      return ERR_ARG;
+    }
+
+    length_bytes_required = tlv->length_len;
+  } else {
+    tlv->length_len = length_bytes_required;
+  }
+
+  if (length_bytes_required > 1) {
+    /* multi byte representation required */
+    length_bytes_required--;
+    data = 0x80 | length_bytes_required; /* extended length definition, 1 length byte follows */
+
+    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
+
+    while (length_bytes_required > 1) {
+      if (length_bytes_required == 2) {
+        /* append high byte */
+        data = (u8_t)(tlv->value_len >> 8);
+      } else {
+        /* append leading 0x00 */
+        data = 0x00;
+      }
+
+      PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
+      length_bytes_required--;
+    }
+  }
+
+  /* append low byte */
+  data = (u8_t)(tlv->value_len & 0xFF);
+  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
+
+  return ERR_OK;
+}
+
+/**
+ * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param raw_len raw data length
+ * @param raw points raw data
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_raw(struct snmp_pbuf_stream *pbuf_stream, const u8_t *raw, u16_t raw_len)
+{
+  PBUF_OP_EXEC(snmp_pbuf_stream_writebuf(pbuf_stream, raw, raw_len));
+
+  return ERR_OK;
+}
+
+/**
+ * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
+ * @param value is the host order u32_t value to be encoded
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_u32t_cnt()
+ */
+err_t
+snmp_asn1_enc_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, u32_t value)
+{
+  if (octets_needed > 5) {
+    return ERR_ARG;
+  }
+  if (octets_needed == 5) {
+    /* not enough bits in 'value' add leading 0x00 */
+    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
+    octets_needed--;
+  }
+
+  while (octets_needed > 1) {
+    octets_needed--;
+    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
+  }
+
+  /* (only) one least significant octet */
+  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
+
+  return ERR_OK;
+}
+/**
+ * Encodes s32_t integer into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt())
+ * @param value is the host order s32_t value to be encoded
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_s32t_cnt()
+ */
+err_t
+snmp_asn1_enc_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, s32_t value)
+{
+  while (octets_needed > 1) {
+    octets_needed--;
+
+    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
+  }
+
+  /* (only) one least significant octet */
+  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
+
+  return ERR_OK;
+}
+
+/**
+ * Encodes object identifier into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param oid points to object identifier array
+ * @param oid_len object identifier array length
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_oid(struct snmp_pbuf_stream *pbuf_stream, const u32_t *oid, u16_t oid_len)
+{
+  if (oid_len > 1) {
+    /* write compressed first two sub id's */
+    u32_t compressed_byte = ((oid[0] * 40) + oid[1]);
+    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)compressed_byte));
+    oid_len -= 2;
+    oid += 2;
+  } else {
+    /* @bug:  allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */
+    /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */
+    return ERR_ARG;
+  }
+
+  while (oid_len > 0) {
+    u32_t sub_id;
+    u8_t shift, tail;
+
+    oid_len--;
+    sub_id = *oid;
+    tail = 0;
+    shift = 28;
+    while (shift > 0) {
+      u8_t code;
+
+      code = (u8_t)(sub_id >> shift);
+      if ((code != 0) || (tail != 0)) {
+        tail = 1;
+        PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, code | 0x80));
+      }
+      shift -= 7;
+    }
+    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)sub_id & 0x7F));
+
+    /* proceed to next sub-identifier */
+    oid++;
+  }
+  return ERR_OK;
+}
+
+/**
+ * Returns octet count for length.
+ *
+ * @param length parameter length
+ * @param octets_needed points to the return value
+ */
+void
+snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed)
+{
+  if (length < 0x80U) {
+    *octets_needed = 1;
+  } else if (length < 0x100U) {
+    *octets_needed = 2;
+  } else {
+    *octets_needed = 3;
+  }
+}
+
+/**
+ * Returns octet count for an u32_t.
+ *
+ * @param value value to be encoded
+ * @param octets_needed points to the return value
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
+ */
+void
+snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed)
+{
+  if (value < 0x80UL) {
+    *octets_needed = 1;
+  } else if (value < 0x8000UL) {
+    *octets_needed = 2;
+  } else if (value < 0x800000UL) {
+    *octets_needed = 3;
+  } else if (value < 0x80000000UL) {
+    *octets_needed = 4;
+  } else {
+    *octets_needed = 5;
+  }
+}
+
+/**
+ * Returns octet count for an s32_t.
+ *
+ * @param value value to be encoded
+ * @param octets_needed points to the return value
+ *
+ * @note ASN coded integers are _always_ signed.
+ */
+void
+snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed)
+{
+  if (value < 0) {
+    value = ~value;
+  }
+  if (value < 0x80L) {
+    *octets_needed = 1;
+  } else if (value < 0x8000L) {
+    *octets_needed = 2;
+  } else if (value < 0x800000L) {
+    *octets_needed = 3;
+  } else {
+    *octets_needed = 4;
+  }
+}
+
+/**
+ * Returns octet count for an object identifier.
+ *
+ * @param oid points to object identifier array
+ * @param oid_len object identifier array length
+ * @param octets_needed points to the return value
+ */
+void
+snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed)
+{
+  u32_t sub_id;
+
+  *octets_needed = 0;
+  if (oid_len > 1) {
+    /* compressed prefix in one octet */
+    (*octets_needed)++;
+    oid_len -= 2;
+    oid += 2;
+  }
+  while (oid_len > 0) {
+    oid_len--;
+    sub_id = *oid;
+
+    sub_id >>= 7;
+    (*octets_needed)++;
+    while (sub_id > 0) {
+      sub_id >>= 7;
+      (*octets_needed)++;
+    }
+    oid++;
+  }
+}
+
+/**
+ * Decodes a TLV from a pbuf stream.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param tlv returns decoded TLV
+ * @return ERR_OK if successful, ERR_VAL if we can't decode
+ */
+err_t
+snmp_asn1_dec_tlv(struct snmp_pbuf_stream *pbuf_stream, struct snmp_asn1_tlv *tlv)
+{
+  u8_t data;
+
+  /* decode type first */
+  PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+  tlv->type = data;
+
+  if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
+    /* extended format is not used by SNMP so we do not accept those values */
+    return ERR_VAL;
+  }
+  tlv->type_len = 1;
+
+  /* now, decode length */
+  PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+
+  if (data < 0x80) { /* short form */
+    tlv->length_len = 1;
+    tlv->value_len  = data;
+  } else if (data > 0x80) { /* long form */
+    u8_t length_bytes = data - 0x80;
+    if (length_bytes > pbuf_stream->length) {
+      return ERR_VAL;
+    }
+    tlv->length_len = length_bytes + 1; /* this byte + defined number of length bytes following */
+    tlv->value_len = 0;
+
+    while (length_bytes > 0) {
+      /* we only support up to u16.maxvalue-1 (2 bytes) but have to accept leading zero bytes */
+      if (tlv->value_len > 0xFF) {
+        return ERR_VAL;
+      }
+      PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+      tlv->value_len <<= 8;
+      tlv->value_len |= data;
+
+      /* take care for special value used for indefinite length */
+      if (tlv->value_len == 0xFFFF) {
+        return ERR_VAL;
+      }
+
+      length_bytes--;
+    }
+  } else { /* data == 0x80 indefinite length form */
+    /* (not allowed for SNMP; RFC 1157, 3.2.2) */
+    return ERR_VAL;
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Decodes positive integer (counter, gauge, timeticks) into u32_t.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded integer field
+ * @param value return host order integer
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
+ */
+err_t
+snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
+{
+  u8_t data;
+
+  if ((len > 0) && (len <= 5)) {
+    PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+
+    /* expecting sign bit to be zero, only unsigned please! */
+    if (((len == 5) && (data == 0x00)) || ((len < 5) && ((data & 0x80) == 0))) {
+      *value = data;
+      len--;
+
+      while (len > 0) {
+        PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+        len--;
+
+        *value <<= 8;
+        *value |= data;
+      }
+
+      return ERR_OK;
+    }
+  }
+
+  return ERR_VAL;
+}
+
+/**
+ * Decodes integer into s32_t.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded integer field
+ * @param value return host order integer
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
+ *
+ * @note ASN coded integers are _always_ signed!
+ */
+err_t
+snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value)
+{
+  u8_t data;
+
+  if ((len > 0) && (len < 5)) {
+    PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+
+    if (data & 0x80) {
+      /* negative, start from -1 */
+      *value = -1;
+      *value = (*value << 8) | data;
+    } else {
+      /* positive, start from 0 */
+      *value = data;
+    }
+    len--;
+    /* shift in the remaining value */
+    while (len > 0) {
+      PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+      *value = (*value << 8) | data;
+      len--;
+    }
+    return ERR_OK;
+  }
+
+  return ERR_VAL;
+}
+
+/**
+ * Decodes object identifier from incoming message into array of u32_t.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded object identifier
+ * @param oid return decoded object identifier
+ * @param oid_len return decoded object identifier length
+ * @param oid_max_len size of oid buffer
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *oid, u8_t *oid_len, u8_t oid_max_len)
+{
+  u32_t *oid_ptr;
+  u8_t data;
+
+  *oid_len = 0;
+  oid_ptr = oid;
+  if (len > 0) {
+    if (oid_max_len < 2) {
+      return ERR_MEM;
+    }
+
+    PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+    len--;
+
+    /* first compressed octet */
+    if (data == 0x2B) {
+      /* (most) common case 1.3 (iso.org) */
+      *oid_ptr = 1;
+      oid_ptr++;
+      *oid_ptr = 3;
+      oid_ptr++;
+    } else if (data < 40) {
+      *oid_ptr = 0;
+      oid_ptr++;
+      *oid_ptr = data;
+      oid_ptr++;
+    } else if (data < 80) {
+      *oid_ptr = 1;
+      oid_ptr++;
+      *oid_ptr = data - 40;
+      oid_ptr++;
+    } else {
+      *oid_ptr = 2;
+      oid_ptr++;
+      *oid_ptr = data - 80;
+      oid_ptr++;
+    }
+    *oid_len = 2;
+  } else {
+    /* accepting zero length identifiers e.g. for getnext operation. uncommon but valid */
+    return ERR_OK;
+  }
+
+  while ((len > 0) && (*oid_len < oid_max_len)) {
+    PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+    len--;
+
+    if ((data & 0x80) == 0x00) {
+      /* sub-identifier uses single octet */
+      *oid_ptr = data;
+    } else {
+      /* sub-identifier uses multiple octets */
+      u32_t sub_id = (data & ~0x80);
+      while ((len > 0) && ((data & 0x80) != 0)) {
+        PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+        len--;
+
+        sub_id = (sub_id << 7) + (data & ~0x80);
+      }
+
+      if ((data & 0x80) != 0) {
+        /* "more bytes following" bit still set at end of len */
+        return ERR_VAL;
+      }
+      *oid_ptr = sub_id;
+    }
+    oid_ptr++;
+    (*oid_len)++;
+  }
+
+  if (len > 0) {
+    /* OID to long to fit in our buffer */
+    return ERR_MEM;
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding)
+ * from incoming message into array.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded raw data (zero is valid, e.g. empty string!)
+ * @param buf return raw bytes
+ * @param buf_len returns length of the raw return value
+ * @param buf_max_len buffer size
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t *buf_len, u16_t buf_max_len)
+{
+  if (len > buf_max_len) {
+    /* not enough dst space */
+    return ERR_MEM;
+  }
+  *buf_len = len;
+
+  while (len > 0) {
+    PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, buf));
+    buf++;
+    len--;
+  }
+
+  return ERR_OK;
+}
+
+#if LWIP_HAVE_INT64
+/**
+ * Returns octet count for an u64_t.
+ *
+ * @param value value to be encoded
+ * @param octets_needed points to the return value
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFFFFFFFFFF is preceded with 0x00 and the length is 9 octets!!
+ */
+void
+snmp_asn1_enc_u64t_cnt(u64_t value, u16_t *octets_needed)
+{
+  /* check if high u32 is 0 */
+  if ((value >> 32) == 0) {
+    /* only low u32 is important */
+    snmp_asn1_enc_u32t_cnt((u32_t)value, octets_needed);
+  } else {
+    /* low u32 does not matter for length determination */
+    snmp_asn1_enc_u32t_cnt((u32_t)(value >> 32), octets_needed);
+    *octets_needed = *octets_needed + 4; /* add the 4 bytes of low u32 */
+  }
+}
+
+/**
+ * Decodes large positive integer (counter64) into 2x u32_t.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded integer field
+ * @param value return 64 bit integer
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFFFFFFFFFF is preceded with 0x00 and the length is 9 octets!!
+ */
+err_t
+snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u64_t *value)
+{
+  u8_t data;
+
+  if ((len > 0) && (len <= 9)) {
+    PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+
+    /* expecting sign bit to be zero, only unsigned please! */
+    if (((len == 9) && (data == 0x00)) || ((len < 9) && ((data & 0x80) == 0))) {
+      *value = data;
+      len--;
+
+      while (len > 0) {
+        PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+        *value <<= 8;
+        *value |= data;
+        len--;
+      }
+
+      return ERR_OK;
+    }
+  }
+
+  return ERR_VAL;
+}
+
+/**
+ * Encodes u64_t (counter64) into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
+ * @param value is the value to be encoded
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_u64t_cnt()
+ */
+err_t
+snmp_asn1_enc_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, u64_t value)
+{
+  if (octets_needed > 9) {
+    return ERR_ARG;
+  }
+  if (octets_needed == 9) {
+    /* not enough bits in 'value' add leading 0x00 */
+    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
+    octets_needed--;
+  }
+
+  while (octets_needed > 1) {
+    octets_needed--;
+    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
+  }
+
+  /* always write at least one octet (also in case of value == 0) */
+  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value)));
+
+  return ERR_OK;
+}
+#endif
+
+#endif /* LWIP_SNMP */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_asn1.h b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_asn1.h
new file mode 100644 (file)
index 0000000..87c0973
--- /dev/null
@@ -0,0 +1,113 @@
+/**
+ * @file
+ * Abstract Syntax Notation One (ISO 8824, 8825) codec.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * Copyright (c) 2016 Elias Oenal.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ *         Martin Hentschel <info@cl-soft.de>
+ *         Elias Oenal <lwip@eliasoenal.com>
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_ASN1_H
+#define LWIP_HDR_APPS_SNMP_ASN1_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP
+
+#include "lwip/err.h"
+#include "lwip/apps/snmp_core.h"
+#include "snmp_pbuf_stream.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SNMP_ASN1_TLV_INDEFINITE_LENGTH 0x80
+
+#define SNMP_ASN1_CLASS_MASK        0xC0
+#define SNMP_ASN1_CONTENTTYPE_MASK  0x20
+#define SNMP_ASN1_DATATYPE_MASK     0x1F
+#define SNMP_ASN1_DATATYPE_EXTENDED 0x1F /* DataType indicating that datatype is encoded in following bytes */
+
+/* context specific (SNMP) tags (from SNMP spec. RFC1157 and RFC1905) */
+#define SNMP_ASN1_CONTEXT_PDU_GET_REQ      0
+#define SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ 1
+#define SNMP_ASN1_CONTEXT_PDU_GET_RESP     2
+#define SNMP_ASN1_CONTEXT_PDU_SET_REQ      3
+#define SNMP_ASN1_CONTEXT_PDU_TRAP         4
+#define SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ 5
+#define SNMP_ASN1_CONTEXT_PDU_INFORM_REQ   6
+#define SNMP_ASN1_CONTEXT_PDU_V2_TRAP      7
+#define SNMP_ASN1_CONTEXT_PDU_REPORT       8
+
+#define SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_OBJECT      0
+#define SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW     2
+
+struct snmp_asn1_tlv {
+  u8_t  type;       /* only U8 because extended types are not specified by SNMP */
+  u8_t  type_len;   /* encoded length of 'type' field (normally 1) */
+  u8_t  length_len; /* indicates how many bytes are required to encode the 'value_len' field */
+  u16_t value_len;  /* encoded length of the value */
+};
+#define SNMP_ASN1_TLV_HDR_LENGTH(tlv) ((tlv).type_len + (tlv).length_len)
+#define SNMP_ASN1_TLV_LENGTH(tlv) ((tlv).type_len + (tlv).length_len + (tlv).value_len)
+#define SNMP_ASN1_SET_TLV_PARAMS(tlv, type_, length_len_, value_len_) do { (tlv).type = (type_); (tlv).type_len = 0; (tlv).length_len = (length_len_); (tlv).value_len = (value_len_); } while (0);
+
+err_t snmp_asn1_dec_tlv(struct snmp_pbuf_stream *pbuf_stream, struct snmp_asn1_tlv *tlv);
+err_t snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value);
+err_t snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value);
+err_t snmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *oid, u8_t *oid_len, u8_t oid_max_len);
+err_t snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t *buf_len, u16_t buf_max_len);
+
+err_t snmp_ans1_enc_tlv(struct snmp_pbuf_stream *pbuf_stream, struct snmp_asn1_tlv *tlv);
+
+void snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed);
+void snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed);
+void snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed);
+void snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed);
+err_t snmp_asn1_enc_oid(struct snmp_pbuf_stream *pbuf_stream, const u32_t *oid, u16_t oid_len);
+err_t snmp_asn1_enc_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, s32_t value);
+err_t snmp_asn1_enc_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, u32_t value);
+err_t snmp_asn1_enc_raw(struct snmp_pbuf_stream *pbuf_stream, const u8_t *raw, u16_t raw_len);
+
+#if LWIP_HAVE_INT64
+err_t snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u64_t *value);
+void snmp_asn1_enc_u64t_cnt(u64_t value, u16_t *octets_needed);
+err_t snmp_asn1_enc_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, u64_t value);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* LWIP_HDR_APPS_SNMP_ASN1_H */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_core.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_core.c
new file mode 100644 (file)
index 0000000..987f106
--- /dev/null
@@ -0,0 +1,1353 @@
+/**
+ * @file
+ * MIB tree access/construction functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ *         Martin Hentschel <info@cl-soft.de>
+*/
+
+/**
+ * @defgroup snmp SNMPv2c/v3 agent
+ * @ingroup apps
+ * SNMPv2c and SNMPv3 compatible agent\n
+ * There is also a MIB compiler and a MIB viewer in lwIP contrib repository
+ * (lwip-contrib/apps/LwipMibCompiler).\n
+ * The agent implements the most important MIB2 MIBs including IPv6 support
+ * (interfaces, UDP, TCP, SNMP, ICMP, SYSTEM). IP MIB is an older version
+ * without IPv6 statistics (TODO).\n
+ * Rewritten by Martin Hentschel <info@cl-soft.de> and
+ * Dirk Ziegelmeier <dziegel@gmx.de>\n
+ *
+ * 0 Agent Capabilities
+ * ====================
+ *
+ * Features:
+ * ---------
+ * - SNMPv2c support.
+ * - SNMPv3 support (a port to ARM mbedtls is provided, LWIP_SNMP_V3_MBEDTLS option).
+ * - Low RAM usage - no memory pools, stack only.
+ * - MIB2 implementation is separated from SNMP stack.
+ * - Support for multiple MIBs (snmp_set_mibs() call) - e.g. for private MIB.
+ * - Simple and generic API for MIB implementation.
+ * - Comfortable node types and helper functions for scalar arrays and tables.
+ * - Counter64, bit and truthvalue datatype support.
+ * - Callbacks for SNMP writes e.g. to implement persistency.
+ * - Runs on two APIs: RAW and netconn.
+ * - Async API is gone - the stack now supports netconn API instead,
+ *   so blocking operations can be done in MIB calls.
+ *   SNMP runs in a worker thread when netconn API is used.
+ * - Simplified thread sync support for MIBs - useful when MIBs
+ *   need to access variables shared with other threads where no locking is
+ *   possible. Used in MIB2 to access lwIP stats from lwIP thread.
+ *
+ * MIB compiler (code generator):
+ * ------------------------------
+ * - Provided in lwIP contrib repository.
+ * - Written in C#. MIB viewer used Windows Forms.
+ * - Developed on Windows with Visual Studio 2010.
+ * - Can be compiled and used on all platforms with http://www.monodevelop.com/.
+ * - Based on a heavily modified version of of SharpSnmpLib (a4bd05c6afb4)
+ *   (https://sharpsnmplib.codeplex.com/SourceControl/network/forks/Nemo157/MIBParserUpdate).
+ * - MIB parser, C file generation framework and LWIP code generation are cleanly
+ *   separated, which means the code may be useful as a base for code generation
+ *   of other SNMP agents.
+ *
+ * Notes:
+ * ------
+ * - Stack and MIB compiler were used to implement a Profinet device.
+ *   Compiled/implemented MIBs: LLDP-MIB, LLDP-EXT-DOT3-MIB, LLDP-EXT-PNO-MIB.
+ *
+ * SNMPv1 per RFC1157 and SNMPv2c per RFC 3416
+ * -------------------------------------------
+ *   Note the S in SNMP stands for "Simple". Note that "Simple" is
+ *   relative. SNMP is simple compared to the complex ISO network
+ *   management protocols CMIP (Common Management Information Protocol)
+ *   and CMOT (CMip Over Tcp).
+ *
+ * SNMPv3
+ * ------
+ * When SNMPv3 is used, several functions from snmpv3.h must be implemented
+ * by the user. This is mainly user management and persistence handling.
+ * The sample provided in lwip-contrib is insecure, don't use it in production
+ * systems, especially the missing persistence for engine boots variable
+ * simplifies replay attacks.
+ *
+ * MIB II
+ * ------
+ *   The standard lwIP stack management information base.
+ *   This is a required MIB, so this is always enabled.
+ *   The groups EGP, CMOT and transmission are disabled by default.
+ *
+ *   Most mib-2 objects are not writable except:
+ *   sysName, sysLocation, sysContact, snmpEnableAuthenTraps.
+ *   Writing to or changing the ARP and IP address and route
+ *   tables is not possible.
+ *
+ *   Note lwIP has a very limited notion of IP routing. It currently
+ *   doen't have a route table and doesn't have a notion of the U,G,H flags.
+ *   Instead lwIP uses the interface list with only one default interface
+ *   acting as a single gateway interface (G) for the default route.
+ *
+ *   The agent returns a "virtual table" with the default route 0.0.0.0
+ *   for the default interface and network routes (no H) for each
+ *   network interface in the netif_list.
+ *   All routes are considered to be up (U).
+ *
+ * Loading additional MIBs
+ * -----------------------
+ *   MIBs can only be added in compile-time, not in run-time.
+ *
+ *
+ * 1 Building the Agent
+ * ====================
+ * First of all you'll need to add the following define
+ * to your local lwipopts.h:
+ * \#define LWIP_SNMP               1
+ *
+ * and add the source files your makefile.
+ *
+ * Note you'll might need to adapt you network driver to update
+ * the mib2 variables for your interface.
+ *
+ * 2 Running the Agent
+ * ===================
+ * The following function calls must be made in your program to
+ * actually get the SNMP agent running.
+ *
+ * Before starting the agent you should supply pointers
+ * for sysContact, sysLocation, and snmpEnableAuthenTraps.
+ * You can do this by calling
+ *
+ * - snmp_mib2_set_syscontact()
+ * - snmp_mib2_set_syslocation()
+ * - snmp_set_auth_traps_enabled()
+ *
+ * You can register a callback which is called on successful write access:
+ * snmp_set_write_callback().
+ *
+ * Additionally you may want to set
+ *
+ * - snmp_mib2_set_sysdescr()
+ * - snmp_set_device_enterprise_oid()
+ * - snmp_mib2_set_sysname()
+ *
+ * Also before starting the agent you need to setup
+ * one or more trap destinations using these calls:
+ *
+ * - snmp_trap_dst_enable()
+ * - snmp_trap_dst_ip_set()
+ *
+ * If you need more than MIB2, set the MIBs you want to use
+ * by snmp_set_mibs().
+ *
+ * Finally, enable the agent by calling snmp_init()
+ *
+ * @defgroup snmp_core Core
+ * @ingroup snmp
+ *
+ * @defgroup snmp_traps Traps
+ * @ingroup snmp
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "snmp_core_priv.h"
+#include "lwip/netif.h"
+#include <string.h>
+
+
+#if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0))
+#error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_SNMP)
+#error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if SNMP_MAX_OBJ_ID_LEN > 255
+#error "SNMP_MAX_OBJ_ID_LEN must fit into an u8_t"
+#endif
+
+struct snmp_statistics snmp_stats;
+static const struct snmp_obj_id  snmp_device_enterprise_oid_default = {SNMP_DEVICE_ENTERPRISE_OID_LEN, SNMP_DEVICE_ENTERPRISE_OID};
+static const struct snmp_obj_id *snmp_device_enterprise_oid         = &snmp_device_enterprise_oid_default;
+
+const u32_t snmp_zero_dot_zero_values[] = { 0, 0 };
+const struct snmp_obj_id_const_ref snmp_zero_dot_zero = { LWIP_ARRAYSIZE(snmp_zero_dot_zero_values), snmp_zero_dot_zero_values };
+
+#if SNMP_LWIP_MIB2 && LWIP_SNMP_V3
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_snmpv2_framework.h"
+#include "lwip/apps/snmp_snmpv2_usm.h"
+static const struct snmp_mib *const default_mibs[] = { &mib2, &snmpframeworkmib, &snmpusmmib };
+static u8_t snmp_num_mibs                          = LWIP_ARRAYSIZE(default_mibs);
+#elif SNMP_LWIP_MIB2
+#include "lwip/apps/snmp_mib2.h"
+static const struct snmp_mib *const default_mibs[] = { &mib2 };
+static u8_t snmp_num_mibs                          = LWIP_ARRAYSIZE(default_mibs);
+#else
+static const struct snmp_mib *const default_mibs[] = { NULL };
+static u8_t snmp_num_mibs                          = 0;
+#endif
+
+/* List of known mibs */
+static struct snmp_mib const *const *snmp_mibs = default_mibs;
+
+/**
+ * @ingroup snmp_core
+ * Sets the MIBs to use.
+ * Example: call snmp_set_mibs() as follows:
+ * static const struct snmp_mib *my_snmp_mibs[] = {
+ *   &mib2,
+ *   &private_mib
+ * };
+ * snmp_set_mibs(my_snmp_mibs, LWIP_ARRAYSIZE(my_snmp_mibs));
+ */
+void
+snmp_set_mibs(const struct snmp_mib **mibs, u8_t num_mibs)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("mibs pointer must be != NULL", (mibs != NULL));
+  LWIP_ASSERT("num_mibs pointer must be != 0", (num_mibs != 0));
+  snmp_mibs     = mibs;
+  snmp_num_mibs = num_mibs;
+}
+
+/**
+ * @ingroup snmp_core
+ * 'device enterprise oid' is used for 'device OID' field in trap PDU's (for identification of generating device)
+ * as well as for value returned by MIB-2 'sysObjectID' field (if internal MIB2 implementation is used).
+ * The 'device enterprise oid' shall point to an OID located under 'private-enterprises' branch (1.3.6.1.4.1.XXX). If a vendor
+ * wants to provide a custom object there, he has to get its own enterprise oid from IANA (http://www.iana.org). It
+ * is not allowed to use LWIP enterprise ID!
+ * In order to identify a specific device it is recommended to create a dedicated OID for each device type under its own
+ * enterprise oid.
+ * e.g.
+ * device a > 1.3.6.1.4.1.XXX(ent-oid).1(devices).1(device a)
+ * device b > 1.3.6.1.4.1.XXX(ent-oid).1(devices).2(device b)
+ * for more details see description of 'sysObjectID' field in RFC1213-MIB
+ */
+void snmp_set_device_enterprise_oid(const struct snmp_obj_id *device_enterprise_oid)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  if (device_enterprise_oid == NULL) {
+    snmp_device_enterprise_oid = &snmp_device_enterprise_oid_default;
+  } else {
+    snmp_device_enterprise_oid = device_enterprise_oid;
+  }
+}
+
+/**
+ * @ingroup snmp_core
+ * Get 'device enterprise oid'
+ */
+const struct snmp_obj_id *snmp_get_device_enterprise_oid(void)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  return snmp_device_enterprise_oid;
+}
+
+#if LWIP_IPV4
+/**
+ * Conversion from InetAddressIPv4 oid to lwIP ip4_addr
+ * @param oid points to u32_t ident[4] input
+ * @param ip points to output struct
+ */
+u8_t
+snmp_oid_to_ip4(const u32_t *oid, ip4_addr_t *ip)
+{
+  if ((oid[0] > 0xFF) ||
+      (oid[1] > 0xFF) ||
+      (oid[2] > 0xFF) ||
+      (oid[3] > 0xFF)) {
+    ip4_addr_copy(*ip, *IP4_ADDR_ANY4);
+    return 0;
+  }
+
+  IP4_ADDR(ip, oid[0], oid[1], oid[2], oid[3]);
+  return 1;
+}
+
+/**
+ * Convert ip4_addr to InetAddressIPv4 (no InetAddressType)
+ * @param ip points to input struct
+ * @param oid points to u32_t ident[4] output
+ */
+void
+snmp_ip4_to_oid(const ip4_addr_t *ip, u32_t *oid)
+{
+  oid[0] = ip4_addr1(ip);
+  oid[1] = ip4_addr2(ip);
+  oid[2] = ip4_addr3(ip);
+  oid[3] = ip4_addr4(ip);
+}
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+/**
+ * Conversion from InetAddressIPv6 oid to lwIP ip6_addr
+ * @param oid points to u32_t oid[16] input
+ * @param ip points to output struct
+ */
+u8_t
+snmp_oid_to_ip6(const u32_t *oid, ip6_addr_t *ip)
+{
+  if ((oid[0]  > 0xFF) ||
+      (oid[1]  > 0xFF) ||
+      (oid[2]  > 0xFF) ||
+      (oid[3]  > 0xFF) ||
+      (oid[4]  > 0xFF) ||
+      (oid[5]  > 0xFF) ||
+      (oid[6]  > 0xFF) ||
+      (oid[7]  > 0xFF) ||
+      (oid[8]  > 0xFF) ||
+      (oid[9]  > 0xFF) ||
+      (oid[10] > 0xFF) ||
+      (oid[11] > 0xFF) ||
+      (oid[12] > 0xFF) ||
+      (oid[13] > 0xFF) ||
+      (oid[14] > 0xFF) ||
+      (oid[15] > 0xFF)) {
+    ip6_addr_set_any(ip);
+    return 0;
+  }
+
+  ip->addr[0] = (oid[0]  << 24) | (oid[1]  << 16) | (oid[2]  << 8) | (oid[3]  << 0);
+  ip->addr[1] = (oid[4]  << 24) | (oid[5]  << 16) | (oid[6]  << 8) | (oid[7]  << 0);
+  ip->addr[2] = (oid[8]  << 24) | (oid[9]  << 16) | (oid[10] << 8) | (oid[11] << 0);
+  ip->addr[3] = (oid[12] << 24) | (oid[13] << 16) | (oid[14] << 8) | (oid[15] << 0);
+  return 1;
+}
+
+/**
+ * Convert ip6_addr to InetAddressIPv6 (no InetAddressType)
+ * @param ip points to input struct
+ * @param oid points to u32_t ident[16] output
+ */
+void
+snmp_ip6_to_oid(const ip6_addr_t *ip, u32_t *oid)
+{
+  oid[0]  = (ip->addr[0] & 0xFF000000) >> 24;
+  oid[1]  = (ip->addr[0] & 0x00FF0000) >> 16;
+  oid[2]  = (ip->addr[0] & 0x0000FF00) >>  8;
+  oid[3]  = (ip->addr[0] & 0x000000FF) >>  0;
+  oid[4]  = (ip->addr[1] & 0xFF000000) >> 24;
+  oid[5]  = (ip->addr[1] & 0x00FF0000) >> 16;
+  oid[6]  = (ip->addr[1] & 0x0000FF00) >>  8;
+  oid[7]  = (ip->addr[1] & 0x000000FF) >>  0;
+  oid[8]  = (ip->addr[2] & 0xFF000000) >> 24;
+  oid[9]  = (ip->addr[2] & 0x00FF0000) >> 16;
+  oid[10] = (ip->addr[2] & 0x0000FF00) >>  8;
+  oid[11] = (ip->addr[2] & 0x000000FF) >>  0;
+  oid[12] = (ip->addr[3] & 0xFF000000) >> 24;
+  oid[13] = (ip->addr[3] & 0x00FF0000) >> 16;
+  oid[14] = (ip->addr[3] & 0x0000FF00) >>  8;
+  oid[15] = (ip->addr[3] & 0x000000FF) >>  0;
+}
+#endif /* LWIP_IPV6 */
+
+#if LWIP_IPV4 || LWIP_IPV6
+/**
+ * Convert to InetAddressType+InetAddress+InetPortNumber
+ * @param ip IP address
+ * @param port Port
+ * @param oid OID
+ * @return OID length
+ */
+u8_t
+snmp_ip_port_to_oid(const ip_addr_t *ip, u16_t port, u32_t *oid)
+{
+  u8_t idx;
+
+  idx = snmp_ip_to_oid(ip, oid);
+  oid[idx] = port;
+  idx++;
+
+  return idx;
+}
+
+/**
+ * Convert to InetAddressType+InetAddress
+ * @param ip IP address
+ * @param oid OID
+ * @return OID length
+ */
+u8_t
+snmp_ip_to_oid(const ip_addr_t *ip, u32_t *oid)
+{
+  if (IP_IS_ANY_TYPE_VAL(*ip)) {
+    oid[0] = 0; /* any */
+    oid[1] = 0; /* no IP OIDs follow */
+    return 2;
+  } else if (IP_IS_V6(ip)) {
+#if LWIP_IPV6
+    oid[0] = 2; /* ipv6 */
+    oid[1] = 16; /* 16 InetAddressIPv6 OIDs follow */
+    snmp_ip6_to_oid(ip_2_ip6(ip), &oid[2]);
+    return 18;
+#else /* LWIP_IPV6 */
+    return 0;
+#endif /* LWIP_IPV6 */
+  } else {
+#if LWIP_IPV4
+    oid[0] = 1; /* ipv4 */
+    oid[1] = 4; /* 4 InetAddressIPv4 OIDs follow */
+    snmp_ip4_to_oid(ip_2_ip4(ip), &oid[2]);
+    return 6;
+#else /* LWIP_IPV4 */
+    return 0;
+#endif /* LWIP_IPV4 */
+  }
+}
+
+/**
+ * Convert from InetAddressType+InetAddress to ip_addr_t
+ * @param oid OID
+ * @param oid_len OID length
+ * @param ip IP address
+ * @return Parsed OID length
+ */
+u8_t
+snmp_oid_to_ip(const u32_t *oid, u8_t oid_len, ip_addr_t *ip)
+{
+  /* InetAddressType */
+  if (oid_len < 1) {
+    return 0;
+  }
+
+  if (oid[0] == 0) { /* any */
+    /* 1x InetAddressType, 1x OID len */
+    if (oid_len < 2) {
+      return 0;
+    }
+    if (oid[1] != 0) {
+      return 0;
+    }
+
+    memset(ip, 0, sizeof(*ip));
+    IP_SET_TYPE(ip, IPADDR_TYPE_ANY);
+
+    return 2;
+  } else if (oid[0] == 1) { /* ipv4 */
+#if LWIP_IPV4
+    /* 1x InetAddressType, 1x OID len, 4x InetAddressIPv4 */
+    if (oid_len < 6) {
+      return 0;
+    }
+
+    /* 4x ipv4 OID */
+    if (oid[1] != 4) {
+      return 0;
+    }
+
+    IP_SET_TYPE(ip, IPADDR_TYPE_V4);
+    if (!snmp_oid_to_ip4(&oid[2], ip_2_ip4(ip))) {
+      return 0;
+    }
+
+    return 6;
+#else /* LWIP_IPV4 */
+    return 0;
+#endif /* LWIP_IPV4 */
+  } else if (oid[0] == 2) { /* ipv6 */
+#if LWIP_IPV6
+    /* 1x InetAddressType, 1x OID len, 16x InetAddressIPv6 */
+    if (oid_len < 18) {
+      return 0;
+    }
+
+    /* 16x ipv6 OID */
+    if (oid[1] != 16) {
+      return 0;
+    }
+
+    IP_SET_TYPE(ip, IPADDR_TYPE_V6);
+    if (!snmp_oid_to_ip6(&oid[2], ip_2_ip6(ip))) {
+      return 0;
+    }
+
+    return 18;
+#else /* LWIP_IPV6 */
+    return 0;
+#endif /* LWIP_IPV6 */
+  } else { /* unsupported InetAddressType */
+    return 0;
+  }
+}
+
+/**
+ * Convert from InetAddressType+InetAddress+InetPortNumber to ip_addr_t and u16_t
+ * @param oid OID
+ * @param oid_len OID length
+ * @param ip IP address
+ * @param port Port
+ * @return Parsed OID length
+ */
+u8_t
+snmp_oid_to_ip_port(const u32_t *oid, u8_t oid_len, ip_addr_t *ip, u16_t *port)
+{
+  u8_t idx;
+
+  /* InetAddressType + InetAddress */
+  idx = snmp_oid_to_ip(&oid[0], oid_len, ip);
+  if (idx == 0) {
+    return 0;
+  }
+
+  /* InetPortNumber */
+  if (oid_len < (idx + 1)) {
+    return 0;
+  }
+  if (oid[idx] > 0xffff) {
+    return 0;
+  }
+  *port = (u16_t)oid[idx];
+  idx++;
+
+  return idx;
+}
+
+#endif /* LWIP_IPV4 || LWIP_IPV6 */
+
+/**
+ * Assign an OID to struct snmp_obj_id
+ * @param target Assignment target
+ * @param oid OID
+ * @param oid_len OID length
+ */
+void
+snmp_oid_assign(struct snmp_obj_id *target, const u32_t *oid, u8_t oid_len)
+{
+  LWIP_ASSERT("oid_len <= SNMP_MAX_OBJ_ID_LEN", oid_len <= SNMP_MAX_OBJ_ID_LEN);
+
+  target->len = oid_len;
+
+  if (oid_len > 0) {
+    MEMCPY(target->id, oid, oid_len * sizeof(u32_t));
+  }
+}
+
+/**
+ * Prefix an OID to OID in struct snmp_obj_id
+ * @param target Assignment target to prefix
+ * @param oid OID
+ * @param oid_len OID length
+ */
+void
+snmp_oid_prefix(struct snmp_obj_id *target, const u32_t *oid, u8_t oid_len)
+{
+  LWIP_ASSERT("target->len + oid_len <= SNMP_MAX_OBJ_ID_LEN", (target->len + oid_len) <= SNMP_MAX_OBJ_ID_LEN);
+
+  if (oid_len > 0) {
+    /* move existing OID to make room at the beginning for OID to insert */
+    int i;
+    for (i = target->len - 1; i >= 0; i--) {
+      target->id[i + oid_len] = target->id[i];
+    }
+
+    /* paste oid at the beginning */
+    MEMCPY(target->id, oid, oid_len * sizeof(u32_t));
+  }
+}
+
+/**
+ * Combine two OIDs into struct snmp_obj_id
+ * @param target Assignmet target
+ * @param oid1 OID 1
+ * @param oid1_len OID 1 length
+ * @param oid2 OID 2
+ * @param oid2_len OID 2 length
+ */
+void
+snmp_oid_combine(struct snmp_obj_id *target, const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
+{
+  snmp_oid_assign(target, oid1, oid1_len);
+  snmp_oid_append(target, oid2, oid2_len);
+}
+
+/**
+ * Append OIDs to struct snmp_obj_id
+ * @param target Assignment target to append to
+ * @param oid OID
+ * @param oid_len OID length
+ */
+void
+snmp_oid_append(struct snmp_obj_id *target, const u32_t *oid, u8_t oid_len)
+{
+  LWIP_ASSERT("offset + oid_len <= SNMP_MAX_OBJ_ID_LEN", (target->len + oid_len) <= SNMP_MAX_OBJ_ID_LEN);
+
+  if (oid_len > 0) {
+    MEMCPY(&target->id[target->len], oid, oid_len * sizeof(u32_t));
+    target->len = (u8_t)(target->len + oid_len);
+  }
+}
+
+/**
+ * Compare two OIDs
+ * @param oid1 OID 1
+ * @param oid1_len OID 1 length
+ * @param oid2 OID 2
+ * @param oid2_len OID 2 length
+ * @return -1: OID1&lt;OID2  1: OID1 &gt;OID2 0: equal
+ */
+s8_t
+snmp_oid_compare(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
+{
+  u8_t level = 0;
+  LWIP_ASSERT("'oid1' param must not be NULL or 'oid1_len' param be 0!", (oid1 != NULL) || (oid1_len == 0));
+  LWIP_ASSERT("'oid2' param must not be NULL or 'oid2_len' param be 0!", (oid2 != NULL) || (oid2_len == 0));
+
+  while ((level < oid1_len) && (level < oid2_len)) {
+    if (*oid1 < *oid2) {
+      return -1;
+    }
+    if (*oid1 > *oid2) {
+      return 1;
+    }
+
+    level++;
+    oid1++;
+    oid2++;
+  }
+
+  /* common part of both OID's is equal, compare length */
+  if (oid1_len < oid2_len) {
+    return -1;
+  }
+  if (oid1_len > oid2_len) {
+    return 1;
+  }
+
+  /* they are equal */
+  return 0;
+}
+
+
+/**
+ * Check of two OIDs are equal
+ * @param oid1 OID 1
+ * @param oid1_len OID 1 length
+ * @param oid2 OID 2
+ * @param oid2_len OID 2 length
+ * @return 1: equal 0: non-equal
+ */
+u8_t
+snmp_oid_equal(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
+{
+  return (snmp_oid_compare(oid1, oid1_len, oid2, oid2_len) == 0) ? 1 : 0;
+}
+
+/**
+ * Convert netif to interface index
+ * @param netif netif
+ * @return index
+ */
+u8_t
+netif_to_num(const struct netif *netif)
+{
+  return netif_get_index(netif);
+}
+
+static const struct snmp_mib *
+snmp_get_mib_from_oid(const u32_t *oid, u8_t oid_len)
+{
+  const u32_t *list_oid;
+  const u32_t *searched_oid;
+  u8_t i, l;
+
+  u8_t max_match_len = 0;
+  const struct snmp_mib *matched_mib = NULL;
+
+  LWIP_ASSERT("'oid' param must not be NULL!", (oid != NULL));
+
+  if (oid_len == 0) {
+    return NULL;
+  }
+
+  for (i = 0; i < snmp_num_mibs; i++) {
+    LWIP_ASSERT("MIB array not initialized correctly", (snmp_mibs[i] != NULL));
+    LWIP_ASSERT("MIB array not initialized correctly - base OID is NULL", (snmp_mibs[i]->base_oid != NULL));
+
+    if (oid_len >= snmp_mibs[i]->base_oid_len) {
+      l            = snmp_mibs[i]->base_oid_len;
+      list_oid     = snmp_mibs[i]->base_oid;
+      searched_oid = oid;
+
+      while (l > 0) {
+        if (*list_oid != *searched_oid) {
+          break;
+        }
+
+        l--;
+        list_oid++;
+        searched_oid++;
+      }
+
+      if ((l == 0) && (snmp_mibs[i]->base_oid_len > max_match_len)) {
+        max_match_len = snmp_mibs[i]->base_oid_len;
+        matched_mib = snmp_mibs[i];
+      }
+    }
+  }
+
+  return matched_mib;
+}
+
+static const struct snmp_mib *
+snmp_get_next_mib(const u32_t *oid, u8_t oid_len)
+{
+  u8_t i;
+  const struct snmp_mib *next_mib = NULL;
+
+  LWIP_ASSERT("'oid' param must not be NULL!", (oid != NULL));
+
+  if (oid_len == 0) {
+    return NULL;
+  }
+
+  for (i = 0; i < snmp_num_mibs; i++) {
+    if (snmp_mibs[i]->base_oid != NULL) {
+      /* check if mib is located behind starting point */
+      if (snmp_oid_compare(snmp_mibs[i]->base_oid, snmp_mibs[i]->base_oid_len, oid, oid_len) > 0) {
+        if ((next_mib == NULL) ||
+            (snmp_oid_compare(snmp_mibs[i]->base_oid, snmp_mibs[i]->base_oid_len,
+                              next_mib->base_oid, next_mib->base_oid_len) < 0)) {
+          next_mib = snmp_mibs[i];
+        }
+      }
+    }
+  }
+
+  return next_mib;
+}
+
+static const struct snmp_mib *
+snmp_get_mib_between(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
+{
+  const struct snmp_mib *next_mib = snmp_get_next_mib(oid1, oid1_len);
+
+  LWIP_ASSERT("'oid2' param must not be NULL!", (oid2 != NULL));
+  LWIP_ASSERT("'oid2_len' param must be greater than 0!", (oid2_len > 0));
+
+  if (next_mib != NULL) {
+    if (snmp_oid_compare(next_mib->base_oid, next_mib->base_oid_len, oid2, oid2_len) < 0) {
+      return next_mib;
+    }
+  }
+
+  return NULL;
+}
+
+u8_t
+snmp_get_node_instance_from_oid(const u32_t *oid, u8_t oid_len, struct snmp_node_instance *node_instance)
+{
+  u8_t result = SNMP_ERR_NOSUCHOBJECT;
+  const struct snmp_mib *mib;
+  const struct snmp_node *mn = NULL;
+
+  mib = snmp_get_mib_from_oid(oid, oid_len);
+  if (mib != NULL) {
+    u8_t oid_instance_len;
+
+    mn = snmp_mib_tree_resolve_exact(mib, oid, oid_len, &oid_instance_len);
+    if ((mn != NULL) && (mn->node_type != SNMP_NODE_TREE)) {
+      /* get instance */
+      const struct snmp_leaf_node *leaf_node = (const struct snmp_leaf_node *)(const void *)mn;
+
+      node_instance->node = mn;
+      snmp_oid_assign(&node_instance->instance_oid, oid + (oid_len - oid_instance_len), oid_instance_len);
+
+      result = leaf_node->get_instance(
+                 oid,
+                 oid_len - oid_instance_len,
+                 node_instance);
+
+#ifdef LWIP_DEBUG
+      if (result == SNMP_ERR_NOERROR) {
+        if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != 0) && (node_instance->get_value == NULL)) {
+          LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is readable but no get_value function is specified\n"));
+        }
+        if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != 0) && (node_instance->set_value == NULL)) {
+          LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is writable but no set_value and/or set_test function is specified\n"));
+        }
+      }
+#endif
+    }
+  }
+
+  return result;
+}
+
+u8_t
+snmp_get_next_node_instance_from_oid(const u32_t *oid, u8_t oid_len, snmp_validate_node_instance_method validate_node_instance_method, void *validate_node_instance_arg, struct snmp_obj_id *node_oid, struct snmp_node_instance *node_instance)
+{
+  const struct snmp_mib      *mib;
+  const struct snmp_node *mn = NULL;
+  const u32_t *start_oid     = NULL;
+  u8_t         start_oid_len = 0;
+
+  /* resolve target MIB from passed OID */
+  mib = snmp_get_mib_from_oid(oid, oid_len);
+  if (mib == NULL) {
+    /* passed OID does not reference any known MIB, start at the next closest MIB */
+    mib = snmp_get_next_mib(oid, oid_len);
+
+    if (mib != NULL) {
+      start_oid     = mib->base_oid;
+      start_oid_len = mib->base_oid_len;
+    }
+  } else {
+    start_oid     = oid;
+    start_oid_len = oid_len;
+  }
+
+  /* resolve target node from MIB, skip to next MIB if no suitable node is found in current MIB */
+  while ((mib != NULL) && (mn == NULL)) {
+    u8_t oid_instance_len;
+
+    /* check if OID directly references a node inside current MIB, in this case we have to ask this node for the next instance */
+    mn = snmp_mib_tree_resolve_exact(mib, start_oid, start_oid_len, &oid_instance_len);
+    if (mn != NULL) {
+      snmp_oid_assign(node_oid, start_oid, start_oid_len - oid_instance_len); /* set oid to node */
+      snmp_oid_assign(&node_instance->instance_oid, start_oid + (start_oid_len - oid_instance_len), oid_instance_len); /* set (relative) instance oid */
+    } else {
+      /* OID does not reference a node, search for the next closest node inside MIB; set instance_oid.len to zero because we want the first instance of this node */
+      mn = snmp_mib_tree_resolve_next(mib, start_oid, start_oid_len, node_oid);
+      node_instance->instance_oid.len = 0;
+    }
+
+    /* validate the node; if the node has no further instance or the returned instance is invalid, search for the next in MIB and validate again */
+    node_instance->node = mn;
+    while (mn != NULL) {
+      u8_t result;
+
+      /* clear fields which may have values from previous loops */
+      node_instance->asn1_type        = 0;
+      node_instance->access           = SNMP_NODE_INSTANCE_NOT_ACCESSIBLE;
+      node_instance->get_value        = NULL;
+      node_instance->set_test         = NULL;
+      node_instance->set_value        = NULL;
+      node_instance->release_instance = NULL;
+      node_instance->reference.ptr    = NULL;
+      node_instance->reference_len    = 0;
+
+      result = ((const struct snmp_leaf_node *)(const void *)mn)->get_next_instance(
+                 node_oid->id,
+                 node_oid->len,
+                 node_instance);
+
+      if (result == SNMP_ERR_NOERROR) {
+#ifdef LWIP_DEBUG
+        if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != 0) && (node_instance->get_value == NULL)) {
+          LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is readable but no get_value function is specified\n"));
+        }
+        if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != 0) && (node_instance->set_value == NULL)) {
+          LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is writable but no set_value function is specified\n"));
+        }
+#endif
+
+        /* validate node because the node may be not accessible for example (but let the caller decide what is valid */
+        if ((validate_node_instance_method == NULL) ||
+            (validate_node_instance_method(node_instance, validate_node_instance_arg) == SNMP_ERR_NOERROR)) {
+          /* node_oid "returns" the full result OID (including the instance part) */
+          snmp_oid_append(node_oid, node_instance->instance_oid.id, node_instance->instance_oid.len);
+          break;
+        }
+
+        if (node_instance->release_instance != NULL) {
+          node_instance->release_instance(node_instance);
+        }
+        /*
+        the instance itself is not valid, ask for next instance from same node.
+        we don't have to change any variables because node_instance->instance_oid is used as input (starting point)
+        as well as output (resulting next OID), so we have to simply call get_next_instance method again
+        */
+      } else {
+        if (node_instance->release_instance != NULL) {
+          node_instance->release_instance(node_instance);
+        }
+
+        /* the node has no further instance, skip to next node */
+        mn = snmp_mib_tree_resolve_next(mib, node_oid->id, node_oid->len, &node_instance->instance_oid); /* misuse node_instance->instance_oid as tmp buffer */
+        if (mn != NULL) {
+          /* prepare for next loop */
+          snmp_oid_assign(node_oid, node_instance->instance_oid.id, node_instance->instance_oid.len);
+          node_instance->instance_oid.len = 0;
+          node_instance->node = mn;
+        }
+      }
+    }
+
+    if (mn != NULL) {
+      /*
+      we found a suitable next node,
+      now we have to check if a inner MIB is located between the searched OID and the resulting OID.
+      this is possible because MIB's may be located anywhere in the global tree, that means also in
+      the subtree of another MIB (e.g. if searched OID is .2 and resulting OID is .4, then another
+      MIB having .3 as root node may exist)
+      */
+      const struct snmp_mib *intermediate_mib;
+      intermediate_mib = snmp_get_mib_between(start_oid, start_oid_len, node_oid->id, node_oid->len);
+
+      if (intermediate_mib != NULL) {
+        /* search for first node inside intermediate mib in next loop */
+        if (node_instance->release_instance != NULL) {
+          node_instance->release_instance(node_instance);
+        }
+
+        mn            = NULL;
+        mib           = intermediate_mib;
+        start_oid     = mib->base_oid;
+        start_oid_len = mib->base_oid_len;
+      }
+      /* else { we found out target node } */
+    } else {
+      /*
+      there is no further (suitable) node inside this MIB, search for the next MIB with following priority
+      1. search for inner MIB's (whose root is located inside tree of current MIB)
+      2. search for surrouding MIB's (where the current MIB is the inner MIB) and continue there if any
+      3. take the next closest MIB (not being related to the current MIB)
+      */
+      const struct snmp_mib *next_mib;
+      next_mib = snmp_get_next_mib(start_oid, start_oid_len); /* returns MIB's related to point 1 and 3 */
+
+      /* is the found MIB an inner MIB? (point 1) */
+      if ((next_mib != NULL) && (next_mib->base_oid_len > mib->base_oid_len) &&
+          (snmp_oid_compare(next_mib->base_oid, mib->base_oid_len, mib->base_oid, mib->base_oid_len) == 0)) {
+        /* yes it is -> continue at inner MIB */
+        mib = next_mib;
+        start_oid     = mib->base_oid;
+        start_oid_len = mib->base_oid_len;
+      } else {
+        /* check if there is a surrounding mib where to continue (point 2) (only possible if OID length > 1) */
+        if (mib->base_oid_len > 1) {
+          mib = snmp_get_mib_from_oid(mib->base_oid, mib->base_oid_len - 1);
+
+          if (mib == NULL) {
+            /* no surrounding mib, use next mib encountered above (point 3) */
+            mib = next_mib;
+
+            if (mib != NULL) {
+              start_oid     = mib->base_oid;
+              start_oid_len = mib->base_oid_len;
+            }
+          }
+          /* else { start_oid stays the same because we want to continue from current offset in surrounding mib (point 2) } */
+        }
+      }
+    }
+  }
+
+  if (mib == NULL) {
+    /* loop is only left when mib == null (error) or mib_node != NULL (success) */
+    return SNMP_ERR_ENDOFMIBVIEW;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+/**
+ * Searches tree for the supplied object identifier.
+ *
+ */
+const struct snmp_node *
+snmp_mib_tree_resolve_exact(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, u8_t *oid_instance_len)
+{
+  const struct snmp_node *const *node = &mib->root_node;
+  u8_t oid_offset = mib->base_oid_len;
+
+  while ((oid_offset < oid_len) && ((*node)->node_type == SNMP_NODE_TREE)) {
+    /* search for matching sub node */
+    u32_t subnode_oid = *(oid + oid_offset);
+
+    u32_t i = (*(const struct snmp_tree_node * const *)node)->subnode_count;
+    node    = (*(const struct snmp_tree_node * const *)node)->subnodes;
+    while ((i > 0) && ((*node)->oid != subnode_oid)) {
+      node++;
+      i--;
+    }
+
+    if (i == 0) {
+      /* no matching subnode found */
+      return NULL;
+    }
+
+    oid_offset++;
+  }
+
+  if ((*node)->node_type != SNMP_NODE_TREE) {
+    /* we found a leaf node */
+    *oid_instance_len = oid_len - oid_offset;
+    return (*node);
+  }
+
+  return NULL;
+}
+
+const struct snmp_node *
+snmp_mib_tree_resolve_next(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, struct snmp_obj_id *oidret)
+{
+  u8_t  oid_offset = mib->base_oid_len;
+  const struct snmp_node *const *node;
+  const struct snmp_tree_node *node_stack[SNMP_MAX_OBJ_ID_LEN];
+  s32_t nsi = 0; /* NodeStackIndex */
+  u32_t subnode_oid;
+
+  if (mib->root_node->node_type != SNMP_NODE_TREE) {
+    /* a next operation on a mib with only a leaf node will always return NULL because there is no other node */
+    return NULL;
+  }
+
+  /* first build node stack related to passed oid (as far as possible), then go backwards to determine the next node */
+  node_stack[nsi] = (const struct snmp_tree_node *)(const void *)mib->root_node;
+  while (oid_offset < oid_len) {
+    /* search for matching sub node */
+    u32_t i = node_stack[nsi]->subnode_count;
+    node    = node_stack[nsi]->subnodes;
+
+    subnode_oid = *(oid + oid_offset);
+
+    while ((i > 0) && ((*node)->oid != subnode_oid)) {
+      node++;
+      i--;
+    }
+
+    if ((i == 0) || ((*node)->node_type != SNMP_NODE_TREE)) {
+      /* no (matching) tree-subnode found */
+      break;
+    }
+    nsi++;
+    node_stack[nsi] = (const struct snmp_tree_node *)(const void *)(*node);
+
+    oid_offset++;
+  }
+
+
+  if (oid_offset >= oid_len) {
+    /* passed oid references a tree node -> return first useable sub node of it */
+    subnode_oid = 0;
+  } else {
+    subnode_oid = *(oid + oid_offset) + 1;
+  }
+
+  while (nsi >= 0) {
+    const struct snmp_node *subnode = NULL;
+
+    /* find next node on current level */
+    s32_t i        = node_stack[nsi]->subnode_count;
+    node           = node_stack[nsi]->subnodes;
+    while (i > 0) {
+      if ((*node)->oid == subnode_oid) {
+        subnode = *node;
+        break;
+      } else if (((*node)->oid > subnode_oid) && ((subnode == NULL) || ((*node)->oid < subnode->oid))) {
+        subnode = *node;
+      }
+
+      node++;
+      i--;
+    }
+
+    if (subnode == NULL) {
+      /* no further node found on this level, go one level up and start searching with index of current node*/
+      subnode_oid = node_stack[nsi]->node.oid + 1;
+      nsi--;
+    } else {
+      if (subnode->node_type == SNMP_NODE_TREE) {
+        /* next is a tree node, go into it and start searching */
+        nsi++;
+        node_stack[nsi] = (const struct snmp_tree_node *)(const void *)subnode;
+        subnode_oid = 0;
+      } else {
+        /* we found a leaf node -> fill oidret and return it */
+        snmp_oid_assign(oidret, mib->base_oid, mib->base_oid_len);
+        i = 1;
+        while (i <= nsi) {
+          oidret->id[oidret->len] = node_stack[i]->node.oid;
+          oidret->len++;
+          i++;
+        }
+
+        oidret->id[oidret->len] = subnode->oid;
+        oidret->len++;
+
+        return subnode;
+      }
+    }
+  }
+
+  return NULL;
+}
+
+/** initialize struct next_oid_state using this function before passing it to next_oid_check */
+void
+snmp_next_oid_init(struct snmp_next_oid_state *state,
+                   const u32_t *start_oid, u8_t start_oid_len,
+                   u32_t *next_oid_buf, u8_t next_oid_max_len)
+{
+  state->start_oid        = start_oid;
+  state->start_oid_len    = start_oid_len;
+  state->next_oid         = next_oid_buf;
+  state->next_oid_len     = 0;
+  state->next_oid_max_len = next_oid_max_len;
+  state->status           = SNMP_NEXT_OID_STATUS_NO_MATCH;
+}
+
+/** checks if the passed incomplete OID may be a possible candidate for snmp_next_oid_check();
+this methid is intended if the complete OID is not yet known but it is very expensive to build it up,
+so it is possible to test the starting part before building up the complete oid and pass it to snmp_next_oid_check()*/
+u8_t
+snmp_next_oid_precheck(struct snmp_next_oid_state *state, const u32_t *oid, u8_t oid_len)
+{
+  if (state->status != SNMP_NEXT_OID_STATUS_BUF_TO_SMALL) {
+    u8_t start_oid_len = (oid_len < state->start_oid_len) ? oid_len : state->start_oid_len;
+
+    /* check passed OID is located behind start offset */
+    if (snmp_oid_compare(oid, oid_len, state->start_oid, start_oid_len) >= 0) {
+      /* check if new oid is located closer to start oid than current closest oid */
+      if ((state->status == SNMP_NEXT_OID_STATUS_NO_MATCH) ||
+          (snmp_oid_compare(oid, oid_len, state->next_oid, state->next_oid_len) < 0)) {
+        return 1;
+      }
+    }
+  }
+
+  return 0;
+}
+
+/** checks the passed OID if it is a candidate to be the next one (get_next); returns !=0 if passed oid is currently closest, otherwise 0 */
+u8_t
+snmp_next_oid_check(struct snmp_next_oid_state *state, const u32_t *oid, u8_t oid_len, void *reference)
+{
+  /* do not overwrite a fail result */
+  if (state->status != SNMP_NEXT_OID_STATUS_BUF_TO_SMALL) {
+    /* check passed OID is located behind start offset */
+    if (snmp_oid_compare(oid, oid_len, state->start_oid, state->start_oid_len) > 0) {
+      /* check if new oid is located closer to start oid than current closest oid */
+      if ((state->status == SNMP_NEXT_OID_STATUS_NO_MATCH) ||
+          (snmp_oid_compare(oid, oid_len, state->next_oid, state->next_oid_len) < 0)) {
+        if (oid_len <= state->next_oid_max_len) {
+          MEMCPY(state->next_oid, oid, oid_len * sizeof(u32_t));
+          state->next_oid_len = oid_len;
+          state->status       = SNMP_NEXT_OID_STATUS_SUCCESS;
+          state->reference    = reference;
+          return 1;
+        } else {
+          state->status = SNMP_NEXT_OID_STATUS_BUF_TO_SMALL;
+        }
+      }
+    }
+  }
+
+  return 0;
+}
+
+u8_t
+snmp_oid_in_range(const u32_t *oid_in, u8_t oid_len, const struct snmp_oid_range *oid_ranges, u8_t oid_ranges_len)
+{
+  u8_t i;
+
+  if (oid_len != oid_ranges_len) {
+    return 0;
+  }
+
+  for (i = 0; i < oid_ranges_len; i++) {
+    if ((oid_in[i] < oid_ranges[i].min) || (oid_in[i] > oid_ranges[i].max)) {
+      return 0;
+    }
+  }
+
+  return 1;
+}
+
+snmp_err_t
+snmp_set_test_ok(struct snmp_node_instance *instance, u16_t value_len, void *value)
+{
+  LWIP_UNUSED_ARG(instance);
+  LWIP_UNUSED_ARG(value_len);
+  LWIP_UNUSED_ARG(value);
+
+  return SNMP_ERR_NOERROR;
+}
+
+/**
+ * Decodes BITS pseudotype value from ASN.1 OctetString.
+ *
+ * @note Because BITS pseudo type is encoded as OCTET STRING, it cannot directly
+ * be encoded/decoded by the agent. Instead call this function as required from
+ * get/test/set methods.
+ *
+ * @param buf points to a buffer holding the ASN1 octet string
+ * @param buf_len length of octet string
+ * @param bit_value decoded Bit value with Bit0 == LSB
+ * @return ERR_OK if successful, ERR_ARG if bit value contains more than 32 bit
+ */
+err_t
+snmp_decode_bits(const u8_t *buf, u32_t buf_len, u32_t *bit_value)
+{
+  u8_t b;
+  u8_t bits_processed = 0;
+  *bit_value = 0;
+
+  while (buf_len > 0) {
+    /* any bit set in this byte? */
+    if (*buf != 0x00) {
+      if (bits_processed >= 32) {
+        /* accept more than 4 bytes, but only when no bits are set */
+        return ERR_VAL;
+      }
+
+      b = *buf;
+      do {
+        if (b & 0x80) {
+          *bit_value |= (1 << bits_processed);
+        }
+        bits_processed++;
+        b <<= 1;
+      } while ((bits_processed & 0x07) != 0); /* &0x07 -> % 8 */
+    } else {
+      bits_processed += 8;
+    }
+
+    buf_len--;
+    buf++;
+  }
+
+  return ERR_OK;
+}
+
+err_t
+snmp_decode_truthvalue(const s32_t *asn1_value, u8_t *bool_value)
+{
+  /* defined by RFC1443:
+   TruthValue ::= TEXTUAL-CONVENTION
+    STATUS       current
+    DESCRIPTION
+     "Represents a boolean value."
+    SYNTAX       INTEGER { true(1), false(2) }
+  */
+
+  if ((asn1_value == NULL) || (bool_value == NULL)) {
+    return ERR_ARG;
+  }
+
+  if (*asn1_value == 1) {
+    *bool_value = 1;
+  } else if (*asn1_value == 2) {
+    *bool_value = 0;
+  } else {
+    return ERR_VAL;
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Encodes BITS pseudotype value into ASN.1 OctetString.
+ *
+ * @note Because BITS pseudo type is encoded as OCTET STRING, it cannot directly
+ * be encoded/decoded by the agent. Instead call this function as required from
+ * get/test/set methods.
+ *
+ * @param buf points to a buffer where the resulting ASN1 octet string is stored to
+ * @param buf_len max length of the bufffer
+ * @param bit_value Bit value to encode with Bit0 == LSB
+ * @param bit_count Number of possible bits for the bit value (according to rfc we have to send all bits independant from their truth value)
+ * @return number of bytes used from buffer to store the resulting OctetString
+ */
+u8_t
+snmp_encode_bits(u8_t *buf, u32_t buf_len, u32_t bit_value, u8_t bit_count)
+{
+  u8_t len = 0;
+  u8_t min_bytes = (bit_count + 7) >> 3; /* >>3 -> / 8 */
+
+  while ((buf_len > 0) && (bit_value != 0x00)) {
+    s8_t i = 7;
+    *buf = 0x00;
+    while (i >= 0) {
+      if (bit_value & 0x01) {
+        *buf |= 0x01;
+      }
+
+      if (i > 0) {
+        *buf <<= 1;
+      }
+
+      bit_value >>= 1;
+      i--;
+    }
+
+    buf++;
+    buf_len--;
+    len++;
+  }
+
+  if (len < min_bytes) {
+    buf     += len;
+    buf_len -= len;
+
+    while ((len < min_bytes) && (buf_len > 0)) {
+      *buf = 0x00;
+      buf++;
+      buf_len--;
+      len++;
+    }
+  }
+
+  return len;
+}
+
+u8_t
+snmp_encode_truthvalue(s32_t *asn1_value, u32_t bool_value)
+{
+  /* defined by RFC1443:
+   TruthValue ::= TEXTUAL-CONVENTION
+    STATUS       current
+    DESCRIPTION
+     "Represents a boolean value."
+    SYNTAX       INTEGER { true(1), false(2) }
+  */
+
+  if (asn1_value == NULL) {
+    return 0;
+  }
+
+  if (bool_value) {
+    *asn1_value = 1; /* defined by RFC1443 */
+  } else {
+    *asn1_value = 2; /* defined by RFC1443 */
+  }
+
+  return sizeof(s32_t);
+}
+
+#endif /* LWIP_SNMP */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_core_priv.h b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_core_priv.h
new file mode 100644 (file)
index 0000000..9532c05
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel <info@cl-soft.de>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_CORE_PRIV_H
+#define LWIP_HDR_APPS_SNMP_CORE_PRIV_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp_core.h"
+#include "snmp_asn1.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* (outdated) SNMPv1 error codes
+ * shall not be used by MIBS anymore, nevertheless required from core for properly answering a v1 request
+ */
+#define SNMP_ERR_NOSUCHNAME 2
+#define SNMP_ERR_BADVALUE   3
+#define SNMP_ERR_READONLY   4
+/* error codes which are internal and shall not be used by MIBS
+ * shall not be used by MIBS anymore, nevertheless required from core for properly answering a v1 request
+ */
+#define SNMP_ERR_TOOBIG               1
+#define SNMP_ERR_AUTHORIZATIONERROR   16
+
+#define SNMP_ERR_UNKNOWN_ENGINEID     30
+#define SNMP_ERR_UNKNOWN_SECURITYNAME 31
+#define SNMP_ERR_UNSUPPORTED_SECLEVEL 32
+#define SNMP_ERR_NOTINTIMEWINDOW      33
+#define SNMP_ERR_DECRYIPTION_ERROR    34
+
+#define SNMP_ERR_NOSUCHOBJECT         SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_OBJECT
+#define SNMP_ERR_ENDOFMIBVIEW         SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW
+
+
+const struct snmp_node *snmp_mib_tree_resolve_exact(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, u8_t *oid_instance_len);
+const struct snmp_node *snmp_mib_tree_resolve_next(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, struct snmp_obj_id *oidret);
+
+typedef u8_t (*snmp_validate_node_instance_method)(struct snmp_node_instance *, void *);
+
+u8_t snmp_get_node_instance_from_oid(const u32_t *oid, u8_t oid_len, struct snmp_node_instance *node_instance);
+u8_t snmp_get_next_node_instance_from_oid(const u32_t *oid, u8_t oid_len, snmp_validate_node_instance_method validate_node_instance_method, void *validate_node_instance_arg, struct snmp_obj_id *node_oid, struct snmp_node_instance *node_instance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* LWIP_HDR_APPS_SNMP_CORE_PRIV_H */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_mib2.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_mib2.c
new file mode 100644 (file)
index 0000000..3383e44
--- /dev/null
@@ -0,0 +1,116 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) objects and functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ *         Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+/**
+ * @defgroup snmp_mib2 MIB2
+ * @ingroup snmp
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2 /* don't build if not configured for use in lwipopts.h */
+
+#if !LWIP_STATS
+#error LWIP_SNMP MIB2 needs LWIP_STATS (for MIB2)
+#endif
+#if !MIB2_STATS
+#error LWIP_SNMP MIB2 needs MIB2_STATS (for MIB2)
+#endif
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_scalar.h"
+
+#if SNMP_USE_NETCONN
+#include "lwip/tcpip.h"
+#include "lwip/priv/tcpip_priv.h"
+void
+snmp_mib2_lwip_synchronizer(snmp_threadsync_called_fn fn, void *arg)
+{
+#if LWIP_TCPIP_CORE_LOCKING
+  LOCK_TCPIP_CORE();
+  fn(arg);
+  UNLOCK_TCPIP_CORE();
+#else
+  tcpip_callback(fn, arg);
+#endif
+}
+
+struct snmp_threadsync_instance snmp_mib2_lwip_locks;
+#endif
+
+/* dot3 and EtherLike MIB not planned. (transmission .1.3.6.1.2.1.10) */
+/* historical (some say hysterical). (cmot .1.3.6.1.2.1.9) */
+/* lwIP has no EGP, thus may not implement it. (egp .1.3.6.1.2.1.8) */
+
+/* --- mib-2 .1.3.6.1.2.1 ----------------------------------------------------- */
+extern const struct snmp_scalar_array_node snmp_mib2_snmp_root;
+extern const struct snmp_tree_node snmp_mib2_udp_root;
+extern const struct snmp_tree_node snmp_mib2_tcp_root;
+extern const struct snmp_scalar_array_node snmp_mib2_icmp_root;
+extern const struct snmp_tree_node snmp_mib2_interface_root;
+extern const struct snmp_scalar_array_node snmp_mib2_system_node;
+extern const struct snmp_tree_node snmp_mib2_at_root;
+extern const struct snmp_tree_node snmp_mib2_ip_root;
+
+static const struct snmp_node *const mib2_nodes[] = {
+  &snmp_mib2_system_node.node.node,
+  &snmp_mib2_interface_root.node,
+#if LWIP_ARP && LWIP_IPV4
+  &snmp_mib2_at_root.node,
+#endif /* LWIP_ARP && LWIP_IPV4 */
+#if LWIP_IPV4
+  &snmp_mib2_ip_root.node,
+#endif /* LWIP_IPV4 */
+#if LWIP_ICMP
+  &snmp_mib2_icmp_root.node.node,
+#endif /* LWIP_ICMP */
+#if LWIP_TCP
+  &snmp_mib2_tcp_root.node,
+#endif /* LWIP_TCP */
+#if LWIP_UDP
+  &snmp_mib2_udp_root.node,
+#endif /* LWIP_UDP */
+  &snmp_mib2_snmp_root.node.node
+};
+
+static const struct snmp_tree_node mib2_root = SNMP_CREATE_TREE_NODE(1, mib2_nodes);
+
+static const u32_t  mib2_base_oid_arr[] = { 1, 3, 6, 1, 2, 1 };
+const struct snmp_mib mib2 = SNMP_MIB_CREATE(mib2_base_oid_arr, &mib2_root.node);
+
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_mib2_icmp.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_mib2_icmp.c
new file mode 100644 (file)
index 0000000..033d229
--- /dev/null
@@ -0,0 +1,182 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) ICMP objects and functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ *         Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/icmp.h"
+#include "lwip/stats.h"
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_ICMP
+
+#if SNMP_USE_NETCONN
+#define SYNC_NODE_NAME(node_name) node_name ## _synced
+#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
+   static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
+#else
+#define SYNC_NODE_NAME(node_name) node_name
+#define CREATE_LWIP_SYNC_NODE(oid, node_name)
+#endif
+
+/* --- icmp .1.3.6.1.2.1.5 ----------------------------------------------------- */
+
+static s16_t
+icmp_get_value(const struct snmp_scalar_array_node_def *node, void *value)
+{
+  u32_t *uint_ptr = (u32_t *)value;
+
+  switch (node->oid) {
+    case 1: /* icmpInMsgs */
+      *uint_ptr = STATS_GET(mib2.icmpinmsgs);
+      return sizeof(*uint_ptr);
+    case 2: /* icmpInErrors */
+      *uint_ptr = STATS_GET(mib2.icmpinerrors);
+      return sizeof(*uint_ptr);
+    case 3: /* icmpInDestUnreachs */
+      *uint_ptr = STATS_GET(mib2.icmpindestunreachs);
+      return sizeof(*uint_ptr);
+    case 4: /* icmpInTimeExcds */
+      *uint_ptr = STATS_GET(mib2.icmpintimeexcds);
+      return sizeof(*uint_ptr);
+    case 5: /* icmpInParmProbs */
+      *uint_ptr = STATS_GET(mib2.icmpinparmprobs);
+      return sizeof(*uint_ptr);
+    case 6: /* icmpInSrcQuenchs */
+      *uint_ptr = STATS_GET(mib2.icmpinsrcquenchs);
+      return sizeof(*uint_ptr);
+    case 7: /* icmpInRedirects */
+      *uint_ptr = STATS_GET(mib2.icmpinredirects);
+      return sizeof(*uint_ptr);
+    case 8: /* icmpInEchos */
+      *uint_ptr = STATS_GET(mib2.icmpinechos);
+      return sizeof(*uint_ptr);
+    case 9: /* icmpInEchoReps */
+      *uint_ptr = STATS_GET(mib2.icmpinechoreps);
+      return sizeof(*uint_ptr);
+    case 10: /* icmpInTimestamps */
+      *uint_ptr = STATS_GET(mib2.icmpintimestamps);
+      return sizeof(*uint_ptr);
+    case 11: /* icmpInTimestampReps */
+      *uint_ptr = STATS_GET(mib2.icmpintimestampreps);
+      return sizeof(*uint_ptr);
+    case 12: /* icmpInAddrMasks */
+      *uint_ptr = STATS_GET(mib2.icmpinaddrmasks);
+      return sizeof(*uint_ptr);
+    case 13: /* icmpInAddrMaskReps */
+      *uint_ptr = STATS_GET(mib2.icmpinaddrmaskreps);
+      return sizeof(*uint_ptr);
+    case 14: /* icmpOutMsgs */
+      *uint_ptr = STATS_GET(mib2.icmpoutmsgs);
+      return sizeof(*uint_ptr);
+    case 15: /* icmpOutErrors */
+      *uint_ptr = STATS_GET(mib2.icmpouterrors);
+      return sizeof(*uint_ptr);
+    case 16: /* icmpOutDestUnreachs */
+      *uint_ptr = STATS_GET(mib2.icmpoutdestunreachs);
+      return sizeof(*uint_ptr);
+    case 17: /* icmpOutTimeExcds */
+      *uint_ptr = STATS_GET(mib2.icmpouttimeexcds);
+      return sizeof(*uint_ptr);
+    case 18: /* icmpOutParmProbs: not supported -> always 0 */
+      *uint_ptr = 0;
+      return sizeof(*uint_ptr);
+    case 19: /* icmpOutSrcQuenchs: not supported -> always 0 */
+      *uint_ptr = 0;
+      return sizeof(*uint_ptr);
+    case 20: /* icmpOutRedirects: not supported -> always 0 */
+      *uint_ptr = 0;
+      return sizeof(*uint_ptr);
+    case 21: /* icmpOutEchos */
+      *uint_ptr = STATS_GET(mib2.icmpoutechos);
+      return sizeof(*uint_ptr);
+    case 22: /* icmpOutEchoReps */
+      *uint_ptr = STATS_GET(mib2.icmpoutechoreps);
+      return sizeof(*uint_ptr);
+    case 23: /* icmpOutTimestamps: not supported -> always 0 */
+      *uint_ptr = 0;
+      return sizeof(*uint_ptr);
+    case 24: /* icmpOutTimestampReps: not supported -> always 0 */
+      *uint_ptr = 0;
+      return sizeof(*uint_ptr);
+    case 25: /* icmpOutAddrMasks: not supported -> always 0 */
+      *uint_ptr = 0;
+      return sizeof(*uint_ptr);
+    case 26: /* icmpOutAddrMaskReps: not supported -> always 0 */
+      *uint_ptr = 0;
+      return sizeof(*uint_ptr);
+    default:
+      LWIP_DEBUGF(SNMP_MIB_DEBUG, ("icmp_get_value(): unknown id: %"S32_F"\n", node->oid));
+      break;
+  }
+
+  return 0;
+}
+
+
+static const struct snmp_scalar_array_node_def icmp_nodes[] = {
+  { 1, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  { 2, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  { 3, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  { 4, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  { 5, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  { 6, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  { 7, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  { 8, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  { 9, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {10, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {11, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {12, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {13, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {14, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {15, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {16, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {17, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {18, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {19, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {20, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {21, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {22, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {23, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {24, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {25, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {26, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}
+};
+
+const struct snmp_scalar_array_node snmp_mib2_icmp_root = SNMP_SCALAR_CREATE_ARRAY_NODE(5, icmp_nodes, icmp_get_value, NULL, NULL);
+
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_ICMP */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_mib2_interfaces.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_mib2_interfaces.c
new file mode 100644 (file)
index 0000000..5f12dd5
--- /dev/null
@@ -0,0 +1,368 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) INTERFACES objects and functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ *         Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/netif.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2
+
+#if SNMP_USE_NETCONN
+#define SYNC_NODE_NAME(node_name) node_name ## _synced
+#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
+   static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
+#else
+#define SYNC_NODE_NAME(node_name) node_name
+#define CREATE_LWIP_SYNC_NODE(oid, node_name)
+#endif
+
+
+/* --- interfaces .1.3.6.1.2.1.2 ----------------------------------------------------- */
+
+static s16_t
+interfaces_get_value(struct snmp_node_instance *instance, void *value)
+{
+  if (instance->node->oid == 1) {
+    s32_t *sint_ptr = (s32_t *)value;
+    s32_t num_netifs = 0;
+
+    struct netif *netif;
+    NETIF_FOREACH(netif) {
+      num_netifs++;
+    }
+
+    *sint_ptr = num_netifs;
+    return sizeof(*sint_ptr);
+  }
+
+  return 0;
+}
+
+/* list of allowed value ranges for incoming OID */
+static const struct snmp_oid_range interfaces_Table_oid_ranges[] = {
+  { 1, 0xff } /* netif->num is u8_t */
+};
+
+static const u8_t iftable_ifOutQLen         = 0;
+
+static const u8_t iftable_ifOperStatus_up   = 1;
+static const u8_t iftable_ifOperStatus_down = 2;
+
+static const u8_t iftable_ifAdminStatus_up             = 1;
+static const u8_t iftable_ifAdminStatus_lowerLayerDown = 7;
+static const u8_t iftable_ifAdminStatus_down           = 2;
+
+static snmp_err_t
+interfaces_Table_get_cell_instance(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, struct snmp_node_instance *cell_instance)
+{
+  u32_t ifIndex;
+  struct netif *netif;
+
+  LWIP_UNUSED_ARG(column);
+
+  /* check if incoming OID length and if values are in plausible range */
+  if (!snmp_oid_in_range(row_oid, row_oid_len, interfaces_Table_oid_ranges, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges))) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* get netif index from incoming OID */
+  ifIndex = row_oid[0];
+
+  /* find netif with index */
+  NETIF_FOREACH(netif) {
+    if (netif_to_num(netif) == ifIndex) {
+      /* store netif pointer for subsequent operations (get/test/set) */
+      cell_instance->reference.ptr = netif;
+      return SNMP_ERR_NOERROR;
+    }
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+interfaces_Table_get_next_cell_instance(const u32_t *column, struct snmp_obj_id *row_oid, struct snmp_node_instance *cell_instance)
+{
+  struct netif *netif;
+  struct snmp_next_oid_state state;
+  u32_t result_temp[LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)];
+
+  LWIP_UNUSED_ARG(column);
+
+  /* init struct to search next oid */
+  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges));
+
+  /* iterate over all possible OIDs to find the next one */
+  NETIF_FOREACH(netif) {
+    u32_t test_oid[LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)];
+    test_oid[0] = netif_to_num(netif);
+
+    /* check generated OID: is it a candidate for the next one? */
+    snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges), netif);
+  }
+
+  /* did we find a next one? */
+  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+    /* store netif pointer for subsequent operations (get/test/set) */
+    cell_instance->reference.ptr = /* (struct netif*) */state.reference;
+    return SNMP_ERR_NOERROR;
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static s16_t
+interfaces_Table_get_value(struct snmp_node_instance *instance, void *value)
+{
+  struct netif *netif = (struct netif *)instance->reference.ptr;
+  u32_t *value_u32 = (u32_t *)value;
+  s32_t *value_s32 = (s32_t *)value;
+  u16_t value_len;
+
+  switch (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id)) {
+    case 1: /* ifIndex */
+      *value_s32 = netif_to_num(netif);
+      value_len = sizeof(*value_s32);
+      break;
+    case 2: /* ifDescr */
+      value_len = sizeof(netif->name);
+      MEMCPY(value, netif->name, value_len);
+      break;
+    case 3: /* ifType */
+      *value_s32 = netif->link_type;
+      value_len = sizeof(*value_s32);
+      break;
+    case 4: /* ifMtu */
+      *value_s32 = netif->mtu;
+      value_len = sizeof(*value_s32);
+      break;
+    case 5: /* ifSpeed */
+      *value_u32 = netif->link_speed;
+      value_len = sizeof(*value_u32);
+      break;
+    case 6: /* ifPhysAddress */
+      value_len = sizeof(netif->hwaddr);
+      MEMCPY(value, &netif->hwaddr, value_len);
+      break;
+    case 7: /* ifAdminStatus */
+      if (netif_is_up(netif)) {
+        *value_s32 = iftable_ifOperStatus_up;
+      } else {
+        *value_s32 = iftable_ifOperStatus_down;
+      }
+      value_len = sizeof(*value_s32);
+      break;
+    case 8: /* ifOperStatus */
+      if (netif_is_up(netif)) {
+        if (netif_is_link_up(netif)) {
+          *value_s32 = iftable_ifAdminStatus_up;
+        } else {
+          *value_s32 = iftable_ifAdminStatus_lowerLayerDown;
+        }
+      } else {
+        *value_s32 = iftable_ifAdminStatus_down;
+      }
+      value_len = sizeof(*value_s32);
+      break;
+    case 9: /* ifLastChange */
+      *value_u32 = netif->ts;
+      value_len = sizeof(*value_u32);
+      break;
+    case 10: /* ifInOctets */
+      *value_u32 = netif->mib2_counters.ifinoctets;
+      value_len = sizeof(*value_u32);
+      break;
+    case 11: /* ifInUcastPkts */
+      *value_u32 = netif->mib2_counters.ifinucastpkts;
+      value_len = sizeof(*value_u32);
+      break;
+    case 12: /* ifInNUcastPkts */
+      *value_u32 = netif->mib2_counters.ifinnucastpkts;
+      value_len = sizeof(*value_u32);
+      break;
+    case 13: /* ifInDiscards */
+      *value_u32 = netif->mib2_counters.ifindiscards;
+      value_len = sizeof(*value_u32);
+      break;
+    case 14: /* ifInErrors */
+      *value_u32 = netif->mib2_counters.ifinerrors;
+      value_len = sizeof(*value_u32);
+      break;
+    case 15: /* ifInUnkownProtos */
+      *value_u32 = netif->mib2_counters.ifinunknownprotos;
+      value_len = sizeof(*value_u32);
+      break;
+    case 16: /* ifOutOctets */
+      *value_u32 = netif->mib2_counters.ifoutoctets;
+      value_len = sizeof(*value_u32);
+      break;
+    case 17: /* ifOutUcastPkts */
+      *value_u32 = netif->mib2_counters.ifoutucastpkts;
+      value_len = sizeof(*value_u32);
+      break;
+    case 18: /* ifOutNUcastPkts */
+      *value_u32 = netif->mib2_counters.ifoutnucastpkts;
+      value_len = sizeof(*value_u32);
+      break;
+    case 19: /* ifOutDiscarts */
+      *value_u32 = netif->mib2_counters.ifoutdiscards;
+      value_len = sizeof(*value_u32);
+      break;
+    case 20: /* ifOutErrors */
+      *value_u32 = netif->mib2_counters.ifouterrors;
+      value_len = sizeof(*value_u32);
+      break;
+    case 21: /* ifOutQLen */
+      *value_u32 = iftable_ifOutQLen;
+      value_len = sizeof(*value_u32);
+      break;
+    /** @note returning zeroDotZero (0.0) no media specific MIB support */
+    case 22: /* ifSpecific */
+      value_len = snmp_zero_dot_zero.len * sizeof(u32_t);
+      MEMCPY(value, snmp_zero_dot_zero.id, value_len);
+      break;
+    default:
+      return 0;
+  }
+
+  return value_len;
+}
+
+#if !SNMP_SAFE_REQUESTS
+
+static snmp_err_t
+interfaces_Table_set_test(struct snmp_node_instance *instance, u16_t len, void *value)
+{
+  s32_t *sint_ptr = (s32_t *)value;
+
+  /* stack should never call this method for another column,
+  because all other columns are set to readonly */
+  LWIP_ASSERT("Invalid column", (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id) == 7));
+  LWIP_UNUSED_ARG(len);
+
+  if (*sint_ptr == 1 || *sint_ptr == 2) {
+    return SNMP_ERR_NOERROR;
+  }
+
+  return SNMP_ERR_WRONGVALUE;
+}
+
+static snmp_err_t
+interfaces_Table_set_value(struct snmp_node_instance *instance, u16_t len, void *value)
+{
+  struct netif *netif = (struct netif *)instance->reference.ptr;
+  s32_t *sint_ptr = (s32_t *)value;
+
+  /* stack should never call this method for another column,
+  because all other columns are set to readonly */
+  LWIP_ASSERT("Invalid column", (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id) == 7));
+  LWIP_UNUSED_ARG(len);
+
+  if (*sint_ptr == 1) {
+    netif_set_up(netif);
+  } else if (*sint_ptr == 2) {
+    netif_set_down(netif);
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+#endif /* SNMP_SAFE_REQUESTS */
+
+static const struct snmp_scalar_node interfaces_Number = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_INTEGER, interfaces_get_value);
+
+static const struct snmp_table_col_def interfaces_Table_columns[] = {
+  {  1, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifIndex */
+  {  2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifDescr */
+  {  3, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifType */
+  {  4, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifMtu */
+  {  5, SNMP_ASN1_TYPE_GAUGE,        SNMP_NODE_INSTANCE_READ_ONLY }, /* ifSpeed */
+  {  6, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifPhysAddress */
+#if !SNMP_SAFE_REQUESTS
+  {  7, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_WRITE }, /* ifAdminStatus */
+#else
+  {  7, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifAdminStatus */
+#endif
+  {  8, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOperStatus */
+  {  9, SNMP_ASN1_TYPE_TIMETICKS,    SNMP_NODE_INSTANCE_READ_ONLY }, /* ifLastChange */
+  { 10, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInOctets */
+  { 11, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInUcastPkts */
+  { 12, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInNUcastPkts */
+  { 13, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInDiscarts */
+  { 14, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInErrors */
+  { 15, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInUnkownProtos */
+  { 16, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutOctets */
+  { 17, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutUcastPkts */
+  { 18, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutNUcastPkts */
+  { 19, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutDiscarts */
+  { 20, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutErrors */
+  { 21, SNMP_ASN1_TYPE_GAUGE,        SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutQLen */
+  { 22, SNMP_ASN1_TYPE_OBJECT_ID,    SNMP_NODE_INSTANCE_READ_ONLY }  /* ifSpecific */
+};
+
+#if !SNMP_SAFE_REQUESTS
+static const struct snmp_table_node interfaces_Table = SNMP_TABLE_CREATE(
+      2, interfaces_Table_columns,
+      interfaces_Table_get_cell_instance, interfaces_Table_get_next_cell_instance,
+      interfaces_Table_get_value, interfaces_Table_set_test, interfaces_Table_set_value);
+#else
+static const struct snmp_table_node interfaces_Table = SNMP_TABLE_CREATE(
+      2, interfaces_Table_columns,
+      interfaces_Table_get_cell_instance, interfaces_Table_get_next_cell_instance,
+      interfaces_Table_get_value, NULL, NULL);
+#endif
+
+/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
+CREATE_LWIP_SYNC_NODE(1, interfaces_Number)
+CREATE_LWIP_SYNC_NODE(2, interfaces_Table)
+
+static const struct snmp_node *const interface_nodes[] = {
+  &SYNC_NODE_NAME(interfaces_Number).node.node,
+  &SYNC_NODE_NAME(interfaces_Table).node.node
+};
+
+const struct snmp_tree_node snmp_mib2_interface_root = SNMP_CREATE_TREE_NODE(2, interface_nodes);
+
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_mib2_ip.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_mib2_ip.c
new file mode 100644 (file)
index 0000000..6931049
--- /dev/null
@@ -0,0 +1,731 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) IP objects and functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ *         Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/stats.h"
+#include "lwip/netif.h"
+#include "lwip/ip.h"
+#include "lwip/etharp.h"
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2
+
+#if SNMP_USE_NETCONN
+#define SYNC_NODE_NAME(node_name) node_name ## _synced
+#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
+   static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
+#else
+#define SYNC_NODE_NAME(node_name) node_name
+#define CREATE_LWIP_SYNC_NODE(oid, node_name)
+#endif
+
+#if LWIP_IPV4
+/* --- ip .1.3.6.1.2.1.4 ----------------------------------------------------- */
+
+static s16_t
+ip_get_value(struct snmp_node_instance *instance, void *value)
+{
+  s32_t *sint_ptr = (s32_t *)value;
+  u32_t *uint_ptr = (u32_t *)value;
+
+  switch (instance->node->oid) {
+    case 1: /* ipForwarding */
+#if IP_FORWARD
+      /* forwarding */
+      *sint_ptr = 1;
+#else
+      /* not-forwarding */
+      *sint_ptr = 2;
+#endif
+      return sizeof(*sint_ptr);
+    case 2: /* ipDefaultTTL */
+      *sint_ptr = IP_DEFAULT_TTL;
+      return sizeof(*sint_ptr);
+    case 3: /* ipInReceives */
+      *uint_ptr = STATS_GET(mib2.ipinreceives);
+      return sizeof(*uint_ptr);
+    case 4: /* ipInHdrErrors */
+      *uint_ptr = STATS_GET(mib2.ipinhdrerrors);
+      return sizeof(*uint_ptr);
+    case 5: /* ipInAddrErrors */
+      *uint_ptr = STATS_GET(mib2.ipinaddrerrors);
+      return sizeof(*uint_ptr);
+    case 6: /* ipForwDatagrams */
+      *uint_ptr = STATS_GET(mib2.ipforwdatagrams);
+      return sizeof(*uint_ptr);
+    case 7: /* ipInUnknownProtos */
+      *uint_ptr = STATS_GET(mib2.ipinunknownprotos);
+      return sizeof(*uint_ptr);
+    case 8: /* ipInDiscards */
+      *uint_ptr = STATS_GET(mib2.ipindiscards);
+      return sizeof(*uint_ptr);
+    case 9: /* ipInDelivers */
+      *uint_ptr = STATS_GET(mib2.ipindelivers);
+      return sizeof(*uint_ptr);
+    case 10: /* ipOutRequests */
+      *uint_ptr = STATS_GET(mib2.ipoutrequests);
+      return sizeof(*uint_ptr);
+    case 11: /* ipOutDiscards */
+      *uint_ptr = STATS_GET(mib2.ipoutdiscards);
+      return sizeof(*uint_ptr);
+    case 12: /* ipOutNoRoutes */
+      *uint_ptr = STATS_GET(mib2.ipoutnoroutes);
+      return sizeof(*uint_ptr);
+    case 13: /* ipReasmTimeout */
+#if IP_REASSEMBLY
+      *sint_ptr = IP_REASS_MAXAGE;
+#else
+      *sint_ptr = 0;
+#endif
+      return sizeof(*sint_ptr);
+    case 14: /* ipReasmReqds */
+      *uint_ptr = STATS_GET(mib2.ipreasmreqds);
+      return sizeof(*uint_ptr);
+    case 15: /* ipReasmOKs */
+      *uint_ptr = STATS_GET(mib2.ipreasmoks);
+      return sizeof(*uint_ptr);
+    case 16: /* ipReasmFails */
+      *uint_ptr = STATS_GET(mib2.ipreasmfails);
+      return sizeof(*uint_ptr);
+    case 17: /* ipFragOKs */
+      *uint_ptr = STATS_GET(mib2.ipfragoks);
+      return sizeof(*uint_ptr);
+    case 18: /* ipFragFails */
+      *uint_ptr = STATS_GET(mib2.ipfragfails);
+      return sizeof(*uint_ptr);
+    case 19: /* ipFragCreates */
+      *uint_ptr = STATS_GET(mib2.ipfragcreates);
+      return sizeof(*uint_ptr);
+    case 23: /* ipRoutingDiscards: not supported -> always 0 */
+      *uint_ptr = 0;
+      return sizeof(*uint_ptr);
+    default:
+      LWIP_DEBUGF(SNMP_MIB_DEBUG, ("ip_get_value(): unknown id: %"S32_F"\n", instance->node->oid));
+      break;
+  }
+
+  return 0;
+}
+
+/**
+ * Test ip object value before setting.
+ *
+ * @param instance node instance
+ * @param len return value space (in bytes)
+ * @param value points to (varbind) space to copy value from.
+ *
+ * @note we allow set if the value matches the hardwired value,
+ *   otherwise return badvalue.
+ */
+static snmp_err_t
+ip_set_test(struct snmp_node_instance *instance, u16_t len, void *value)
+{
+  snmp_err_t ret = SNMP_ERR_WRONGVALUE;
+  s32_t *sint_ptr = (s32_t *)value;
+
+  LWIP_UNUSED_ARG(len);
+  switch (instance->node->oid) {
+    case 1: /* ipForwarding */
+#if IP_FORWARD
+      /* forwarding */
+      if (*sint_ptr == 1)
+#else
+      /* not-forwarding */
+      if (*sint_ptr == 2)
+#endif
+      {
+        ret = SNMP_ERR_NOERROR;
+      }
+      break;
+    case 2: /* ipDefaultTTL */
+      if (*sint_ptr == IP_DEFAULT_TTL) {
+        ret = SNMP_ERR_NOERROR;
+      }
+      break;
+    default:
+      LWIP_DEBUGF(SNMP_MIB_DEBUG, ("ip_set_test(): unknown id: %"S32_F"\n", instance->node->oid));
+      break;
+  }
+
+  return ret;
+}
+
+static snmp_err_t
+ip_set_value(struct snmp_node_instance *instance, u16_t len, void *value)
+{
+  LWIP_UNUSED_ARG(instance);
+  LWIP_UNUSED_ARG(len);
+  LWIP_UNUSED_ARG(value);
+  /* nothing to do here because in set_test we only accept values being the same as our own stored value -> no need to store anything */
+  return SNMP_ERR_NOERROR;
+}
+
+/* --- ipAddrTable --- */
+
+/* list of allowed value ranges for incoming OID */
+static const struct snmp_oid_range ip_AddrTable_oid_ranges[] = {
+  { 0, 0xff }, /* IP A */
+  { 0, 0xff }, /* IP B */
+  { 0, 0xff }, /* IP C */
+  { 0, 0xff }  /* IP D */
+};
+
+static snmp_err_t
+ip_AddrTable_get_cell_value_core(struct netif *netif, const u32_t *column, union snmp_variant_value *value, u32_t *value_len)
+{
+  LWIP_UNUSED_ARG(value_len);
+
+  switch (*column) {
+    case 1: /* ipAdEntAddr */
+      value->u32 = netif_ip4_addr(netif)->addr;
+      break;
+    case 2: /* ipAdEntIfIndex */
+      value->u32 = netif_to_num(netif);
+      break;
+    case 3: /* ipAdEntNetMask */
+      value->u32 = netif_ip4_netmask(netif)->addr;
+      break;
+    case 4: /* ipAdEntBcastAddr */
+      /* lwIP oddity, there's no broadcast
+         address in the netif we can rely on */
+      value->u32 = IPADDR_BROADCAST & 1;
+      break;
+    case 5: /* ipAdEntReasmMaxSize */
+#if IP_REASSEMBLY
+      /* @todo The theoretical maximum is IP_REASS_MAX_PBUFS * size of the pbufs,
+       * but only if receiving one fragmented packet at a time.
+       * The current solution is to calculate for 2 simultaneous packets...
+       */
+      value->u32 = (IP_HLEN + ((IP_REASS_MAX_PBUFS / 2) *
+                               (PBUF_POOL_BUFSIZE - PBUF_LINK_ENCAPSULATION_HLEN - PBUF_LINK_HLEN - IP_HLEN)));
+#else
+      /** @todo returning MTU would be a bad thing and
+          returning a wild guess like '576' isn't good either */
+      value->u32 = 0;
+#endif
+      break;
+    default:
+      return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+ip_AddrTable_get_cell_value(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, union snmp_variant_value *value, u32_t *value_len)
+{
+  ip4_addr_t ip;
+  struct netif *netif;
+
+  /* check if incoming OID length and if values are in plausible range */
+  if (!snmp_oid_in_range(row_oid, row_oid_len, ip_AddrTable_oid_ranges, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges))) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* get IP from incoming OID */
+  snmp_oid_to_ip4(&row_oid[0], &ip); /* we know it succeeds because of oid_in_range check above */
+
+  /* find netif with requested ip */
+  NETIF_FOREACH(netif) {
+    if (ip4_addr_cmp(&ip, netif_ip4_addr(netif))) {
+      /* fill in object properties */
+      return ip_AddrTable_get_cell_value_core(netif, column, value, value_len);
+    }
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+ip_AddrTable_get_next_cell_instance_and_value(const u32_t *column, struct snmp_obj_id *row_oid, union snmp_variant_value *value, u32_t *value_len)
+{
+  struct netif *netif;
+  struct snmp_next_oid_state state;
+  u32_t result_temp[LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges)];
+
+  /* init struct to search next oid */
+  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges));
+
+  /* iterate over all possible OIDs to find the next one */
+  NETIF_FOREACH(netif) {
+    u32_t test_oid[LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges)];
+    snmp_ip4_to_oid(netif_ip4_addr(netif), &test_oid[0]);
+
+    /* check generated OID: is it a candidate for the next one? */
+    snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges), netif);
+  }
+
+  /* did we find a next one? */
+  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+    /* fill in object properties */
+    return ip_AddrTable_get_cell_value_core((struct netif *)state.reference, column, value, value_len);
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+/* --- ipRouteTable --- */
+
+/* list of allowed value ranges for incoming OID */
+static const struct snmp_oid_range ip_RouteTable_oid_ranges[] = {
+  { 0, 0xff }, /* IP A */
+  { 0, 0xff }, /* IP B */
+  { 0, 0xff }, /* IP C */
+  { 0, 0xff }, /* IP D */
+};
+
+static snmp_err_t
+ip_RouteTable_get_cell_value_core(struct netif *netif, u8_t default_route, const u32_t *column, union snmp_variant_value *value, u32_t *value_len)
+{
+  switch (*column) {
+    case 1: /* ipRouteDest */
+      if (default_route) {
+        /* default rte has 0.0.0.0 dest */
+        value->u32 = IP4_ADDR_ANY4->addr;
+      } else {
+        /* netifs have netaddress dest */
+        ip4_addr_t tmp;
+        ip4_addr_get_network(&tmp, netif_ip4_addr(netif), netif_ip4_netmask(netif));
+        value->u32 = tmp.addr;
+      }
+      break;
+    case 2: /* ipRouteIfIndex */
+      value->u32 = netif_to_num(netif);
+      break;
+    case 3: /* ipRouteMetric1 */
+      if (default_route) {
+        value->s32 = 1; /* default */
+      } else {
+        value->s32 = 0; /* normal */
+      }
+      break;
+    case 4: /* ipRouteMetric2 */
+    case 5: /* ipRouteMetric3 */
+    case 6: /* ipRouteMetric4 */
+      value->s32 = -1; /* none */
+      break;
+    case 7: /* ipRouteNextHop */
+      if (default_route) {
+        /* default rte: gateway */
+        value->u32 = netif_ip4_gw(netif)->addr;
+      } else {
+        /* other rtes: netif ip_addr  */
+        value->u32 = netif_ip4_addr(netif)->addr;
+      }
+      break;
+    case 8: /* ipRouteType */
+      if (default_route) {
+        /* default rte is indirect */
+        value->u32 = 4; /* indirect */
+      } else {
+        /* other rtes are direct */
+        value->u32 = 3; /* direct */
+      }
+      break;
+    case 9: /* ipRouteProto */
+      /* locally defined routes */
+      value->u32 = 2; /* local */
+      break;
+    case 10: /* ipRouteAge */
+      /* @todo (sysuptime - timestamp last change) / 100 */
+      value->u32 = 0;
+      break;
+    case 11: /* ipRouteMask */
+      if (default_route) {
+        /* default rte use 0.0.0.0 mask */
+        value->u32 = IP4_ADDR_ANY4->addr;
+      } else {
+        /* other rtes use netmask */
+        value->u32 = netif_ip4_netmask(netif)->addr;
+      }
+      break;
+    case 12: /* ipRouteMetric5 */
+      value->s32 = -1; /* none */
+      break;
+    case 13: /* ipRouteInfo */
+      value->const_ptr = snmp_zero_dot_zero.id;
+      *value_len = snmp_zero_dot_zero.len * sizeof(u32_t);
+      break;
+    default:
+      return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+ip_RouteTable_get_cell_value(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, union snmp_variant_value *value, u32_t *value_len)
+{
+  ip4_addr_t test_ip;
+  struct netif *netif;
+
+  /* check if incoming OID length and if values are in plausible range */
+  if (!snmp_oid_in_range(row_oid, row_oid_len, ip_RouteTable_oid_ranges, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges))) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* get IP and port from incoming OID */
+  snmp_oid_to_ip4(&row_oid[0], &test_ip); /* we know it succeeds because of oid_in_range check above */
+
+  /* default route is on default netif */
+  if (ip4_addr_isany_val(test_ip) && (netif_default != NULL)) {
+    /* fill in object properties */
+    return ip_RouteTable_get_cell_value_core(netif_default, 1, column, value, value_len);
+  }
+
+  /* find netif with requested route */
+  NETIF_FOREACH(netif) {
+    ip4_addr_t dst;
+    ip4_addr_get_network(&dst, netif_ip4_addr(netif), netif_ip4_netmask(netif));
+
+    if (ip4_addr_cmp(&dst, &test_ip)) {
+      /* fill in object properties */
+      return ip_RouteTable_get_cell_value_core(netif, 0, column, value, value_len);
+    }
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+ip_RouteTable_get_next_cell_instance_and_value(const u32_t *column, struct snmp_obj_id *row_oid, union snmp_variant_value *value, u32_t *value_len)
+{
+  struct netif *netif;
+  struct snmp_next_oid_state state;
+  u32_t result_temp[LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges)];
+  u32_t test_oid[LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges)];
+
+  /* init struct to search next oid */
+  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges));
+
+  /* check default route */
+  if (netif_default != NULL) {
+    snmp_ip4_to_oid(IP4_ADDR_ANY4, &test_oid[0]);
+    snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges), netif_default);
+  }
+
+  /* iterate over all possible OIDs to find the next one */
+  NETIF_FOREACH(netif) {
+    ip4_addr_t dst;
+    ip4_addr_get_network(&dst, netif_ip4_addr(netif), netif_ip4_netmask(netif));
+
+    /* check generated OID: is it a candidate for the next one? */
+    if (!ip4_addr_isany_val(dst)) {
+      snmp_ip4_to_oid(&dst, &test_oid[0]);
+      snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges), netif);
+    }
+  }
+
+  /* did we find a next one? */
+  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+    ip4_addr_t dst;
+    snmp_oid_to_ip4(&result_temp[0], &dst);
+    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+    /* fill in object properties */
+    return ip_RouteTable_get_cell_value_core((struct netif *)state.reference, ip4_addr_isany_val(dst), column, value, value_len);
+  } else {
+    /* not found */
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+}
+
+#if LWIP_ARP && LWIP_IPV4
+/* --- ipNetToMediaTable --- */
+
+/* list of allowed value ranges for incoming OID */
+static const struct snmp_oid_range ip_NetToMediaTable_oid_ranges[] = {
+  { 1, 0xff }, /* IfIndex */
+  { 0, 0xff }, /* IP A    */
+  { 0, 0xff }, /* IP B    */
+  { 0, 0xff }, /* IP C    */
+  { 0, 0xff }  /* IP D    */
+};
+
+static snmp_err_t
+ip_NetToMediaTable_get_cell_value_core(size_t arp_table_index, const u32_t *column, union snmp_variant_value *value, u32_t *value_len)
+{
+  ip4_addr_t *ip;
+  struct netif *netif;
+  struct eth_addr *ethaddr;
+
+  etharp_get_entry(arp_table_index, &ip, &netif, &ethaddr);
+
+  /* value */
+  switch (*column) {
+    case 1: /* atIfIndex / ipNetToMediaIfIndex */
+      value->u32 = netif_to_num(netif);
+      break;
+    case 2: /* atPhysAddress / ipNetToMediaPhysAddress */
+      value->ptr = ethaddr;
+      *value_len = sizeof(*ethaddr);
+      break;
+    case 3: /* atNetAddress / ipNetToMediaNetAddress */
+      value->u32 = ip->addr;
+      break;
+    case 4: /* ipNetToMediaType */
+      value->u32 = 3; /* dynamic*/
+      break;
+    default:
+      return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+ip_NetToMediaTable_get_cell_value(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, union snmp_variant_value *value, u32_t *value_len)
+{
+  ip4_addr_t ip_in;
+  u8_t netif_index;
+  size_t i;
+
+  /* check if incoming OID length and if values are in plausible range */
+  if (!snmp_oid_in_range(row_oid, row_oid_len, ip_NetToMediaTable_oid_ranges, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges))) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* get IP from incoming OID */
+  netif_index = (u8_t)row_oid[0];
+  snmp_oid_to_ip4(&row_oid[1], &ip_in); /* we know it succeeds because of oid_in_range check above */
+
+  /* find requested entry */
+  for (i = 0; i < ARP_TABLE_SIZE; i++) {
+    ip4_addr_t *ip;
+    struct netif *netif;
+    struct eth_addr *ethaddr;
+
+    if (etharp_get_entry(i, &ip, &netif, &ethaddr)) {
+      if ((netif_index == netif_to_num(netif)) && ip4_addr_cmp(&ip_in, ip)) {
+        /* fill in object properties */
+        return ip_NetToMediaTable_get_cell_value_core(i, column, value, value_len);
+      }
+    }
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+ip_NetToMediaTable_get_next_cell_instance_and_value(const u32_t *column, struct snmp_obj_id *row_oid, union snmp_variant_value *value, u32_t *value_len)
+{
+  size_t i;
+  struct snmp_next_oid_state state;
+  u32_t result_temp[LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges)];
+
+  /* init struct to search next oid */
+  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges));
+
+  /* iterate over all possible OIDs to find the next one */
+  for (i = 0; i < ARP_TABLE_SIZE; i++) {
+    ip4_addr_t *ip;
+    struct netif *netif;
+    struct eth_addr *ethaddr;
+
+    if (etharp_get_entry(i, &ip, &netif, &ethaddr)) {
+      u32_t test_oid[LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges)];
+
+      test_oid[0] = netif_to_num(netif);
+      snmp_ip4_to_oid(ip, &test_oid[1]);
+
+      /* check generated OID: is it a candidate for the next one? */
+      snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges), LWIP_PTR_NUMERIC_CAST(void *, i));
+    }
+  }
+
+  /* did we find a next one? */
+  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+    /* fill in object properties */
+    return ip_NetToMediaTable_get_cell_value_core(LWIP_PTR_NUMERIC_CAST(size_t, state.reference), column, value, value_len);
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+#endif /* LWIP_ARP && LWIP_IPV4 */
+
+static const struct snmp_scalar_node ip_Forwarding      = SNMP_SCALAR_CREATE_NODE(1, SNMP_NODE_INSTANCE_READ_WRITE, SNMP_ASN1_TYPE_INTEGER, ip_get_value, ip_set_test, ip_set_value);
+static const struct snmp_scalar_node ip_DefaultTTL      = SNMP_SCALAR_CREATE_NODE(2, SNMP_NODE_INSTANCE_READ_WRITE, SNMP_ASN1_TYPE_INTEGER, ip_get_value, ip_set_test, ip_set_value);
+static const struct snmp_scalar_node ip_InReceives      = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_InHdrErrors     = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_InAddrErrors    = SNMP_SCALAR_CREATE_NODE_READONLY(5, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_ForwDatagrams   = SNMP_SCALAR_CREATE_NODE_READONLY(6, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_InUnknownProtos = SNMP_SCALAR_CREATE_NODE_READONLY(7, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_InDiscards      = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_InDelivers      = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_OutRequests     = SNMP_SCALAR_CREATE_NODE_READONLY(10, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_OutDiscards     = SNMP_SCALAR_CREATE_NODE_READONLY(11, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_OutNoRoutes     = SNMP_SCALAR_CREATE_NODE_READONLY(12, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_ReasmTimeout    = SNMP_SCALAR_CREATE_NODE_READONLY(13, SNMP_ASN1_TYPE_INTEGER, ip_get_value);
+static const struct snmp_scalar_node ip_ReasmReqds      = SNMP_SCALAR_CREATE_NODE_READONLY(14, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_ReasmOKs        = SNMP_SCALAR_CREATE_NODE_READONLY(15, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_ReasmFails      = SNMP_SCALAR_CREATE_NODE_READONLY(16, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_FragOKs         = SNMP_SCALAR_CREATE_NODE_READONLY(17, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_FragFails       = SNMP_SCALAR_CREATE_NODE_READONLY(18, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_FragCreates     = SNMP_SCALAR_CREATE_NODE_READONLY(19, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_RoutingDiscards = SNMP_SCALAR_CREATE_NODE_READONLY(23, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+
+static const struct snmp_table_simple_col_def ip_AddrTable_columns[] = {
+  { 1, SNMP_ASN1_TYPE_IPADDR,  SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntAddr */
+  { 2, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntIfIndex */
+  { 3, SNMP_ASN1_TYPE_IPADDR,  SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntNetMask */
+  { 4, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntBcastAddr */
+  { 5, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }  /* ipAdEntReasmMaxSize */
+};
+
+static const struct snmp_table_simple_node ip_AddrTable = SNMP_TABLE_CREATE_SIMPLE(20, ip_AddrTable_columns, ip_AddrTable_get_cell_value, ip_AddrTable_get_next_cell_instance_and_value);
+
+static const struct snmp_table_simple_col_def ip_RouteTable_columns[] = {
+  {  1, SNMP_ASN1_TYPE_IPADDR,    SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteDest */
+  {  2, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteIfIndex */
+  {  3, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric1 */
+  {  4, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric2 */
+  {  5, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric3 */
+  {  6, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric4 */
+  {  7, SNMP_ASN1_TYPE_IPADDR,    SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteNextHop */
+  {  8, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteType */
+  {  9, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteProto */
+  { 10, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteAge */
+  { 11, SNMP_ASN1_TYPE_IPADDR,    SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteMask */
+  { 12, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric5 */
+  { 13, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_VARIANT_VALUE_TYPE_PTR }  /* ipRouteInfo */
+};
+
+static const struct snmp_table_simple_node ip_RouteTable = SNMP_TABLE_CREATE_SIMPLE(21, ip_RouteTable_columns, ip_RouteTable_get_cell_value, ip_RouteTable_get_next_cell_instance_and_value);
+#endif /* LWIP_IPV4 */
+
+#if LWIP_ARP && LWIP_IPV4
+static const struct snmp_table_simple_col_def ip_NetToMediaTable_columns[] = {
+  {  1, SNMP_ASN1_TYPE_INTEGER,      SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipNetToMediaIfIndex */
+  {  2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_VARIANT_VALUE_TYPE_PTR }, /* ipNetToMediaPhysAddress */
+  {  3, SNMP_ASN1_TYPE_IPADDR,       SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipNetToMediaNetAddress */
+  {  4, SNMP_ASN1_TYPE_INTEGER,      SNMP_VARIANT_VALUE_TYPE_U32 }  /* ipNetToMediaType */
+};
+
+static const struct snmp_table_simple_node ip_NetToMediaTable = SNMP_TABLE_CREATE_SIMPLE(22, ip_NetToMediaTable_columns, ip_NetToMediaTable_get_cell_value, ip_NetToMediaTable_get_next_cell_instance_and_value);
+#endif /* LWIP_ARP && LWIP_IPV4 */
+
+#if LWIP_IPV4
+/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
+CREATE_LWIP_SYNC_NODE( 1, ip_Forwarding)
+CREATE_LWIP_SYNC_NODE( 2, ip_DefaultTTL)
+CREATE_LWIP_SYNC_NODE( 3, ip_InReceives)
+CREATE_LWIP_SYNC_NODE( 4, ip_InHdrErrors)
+CREATE_LWIP_SYNC_NODE( 5, ip_InAddrErrors)
+CREATE_LWIP_SYNC_NODE( 6, ip_ForwDatagrams)
+CREATE_LWIP_SYNC_NODE( 7, ip_InUnknownProtos)
+CREATE_LWIP_SYNC_NODE( 8, ip_InDiscards)
+CREATE_LWIP_SYNC_NODE( 9, ip_InDelivers)
+CREATE_LWIP_SYNC_NODE(10, ip_OutRequests)
+CREATE_LWIP_SYNC_NODE(11, ip_OutDiscards)
+CREATE_LWIP_SYNC_NODE(12, ip_OutNoRoutes)
+CREATE_LWIP_SYNC_NODE(13, ip_ReasmTimeout)
+CREATE_LWIP_SYNC_NODE(14, ip_ReasmReqds)
+CREATE_LWIP_SYNC_NODE(15, ip_ReasmOKs)
+CREATE_LWIP_SYNC_NODE(15, ip_ReasmFails)
+CREATE_LWIP_SYNC_NODE(17, ip_FragOKs)
+CREATE_LWIP_SYNC_NODE(18, ip_FragFails)
+CREATE_LWIP_SYNC_NODE(19, ip_FragCreates)
+CREATE_LWIP_SYNC_NODE(20, ip_AddrTable)
+CREATE_LWIP_SYNC_NODE(21, ip_RouteTable)
+#if LWIP_ARP
+CREATE_LWIP_SYNC_NODE(22, ip_NetToMediaTable)
+#endif /* LWIP_ARP */
+CREATE_LWIP_SYNC_NODE(23, ip_RoutingDiscards)
+
+static const struct snmp_node *const ip_nodes[] = {
+  &SYNC_NODE_NAME(ip_Forwarding).node.node,
+  &SYNC_NODE_NAME(ip_DefaultTTL).node.node,
+  &SYNC_NODE_NAME(ip_InReceives).node.node,
+  &SYNC_NODE_NAME(ip_InHdrErrors).node.node,
+  &SYNC_NODE_NAME(ip_InAddrErrors).node.node,
+  &SYNC_NODE_NAME(ip_ForwDatagrams).node.node,
+  &SYNC_NODE_NAME(ip_InUnknownProtos).node.node,
+  &SYNC_NODE_NAME(ip_InDiscards).node.node,
+  &SYNC_NODE_NAME(ip_InDelivers).node.node,
+  &SYNC_NODE_NAME(ip_OutRequests).node.node,
+  &SYNC_NODE_NAME(ip_OutDiscards).node.node,
+  &SYNC_NODE_NAME(ip_OutNoRoutes).node.node,
+  &SYNC_NODE_NAME(ip_ReasmTimeout).node.node,
+  &SYNC_NODE_NAME(ip_ReasmReqds).node.node,
+  &SYNC_NODE_NAME(ip_ReasmOKs).node.node,
+  &SYNC_NODE_NAME(ip_ReasmFails).node.node,
+  &SYNC_NODE_NAME(ip_FragOKs).node.node,
+  &SYNC_NODE_NAME(ip_FragFails).node.node,
+  &SYNC_NODE_NAME(ip_FragCreates).node.node,
+  &SYNC_NODE_NAME(ip_AddrTable).node.node,
+  &SYNC_NODE_NAME(ip_RouteTable).node.node,
+#if LWIP_ARP
+  &SYNC_NODE_NAME(ip_NetToMediaTable).node.node,
+#endif /* LWIP_ARP */
+  &SYNC_NODE_NAME(ip_RoutingDiscards).node.node
+};
+
+const struct snmp_tree_node snmp_mib2_ip_root = SNMP_CREATE_TREE_NODE(4, ip_nodes);
+#endif /* LWIP_IPV4 */
+
+/* --- at .1.3.6.1.2.1.3 ----------------------------------------------------- */
+
+#if LWIP_ARP && LWIP_IPV4
+/* at node table is a subset of ip_nettomedia table (same rows but less columns) */
+static const struct snmp_table_simple_col_def at_Table_columns[] = {
+  { 1, SNMP_ASN1_TYPE_INTEGER,      SNMP_VARIANT_VALUE_TYPE_U32 }, /* atIfIndex */
+  { 2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_VARIANT_VALUE_TYPE_PTR }, /* atPhysAddress */
+  { 3, SNMP_ASN1_TYPE_IPADDR,       SNMP_VARIANT_VALUE_TYPE_U32 }  /* atNetAddress */
+};
+
+static const struct snmp_table_simple_node at_Table = SNMP_TABLE_CREATE_SIMPLE(1, at_Table_columns, ip_NetToMediaTable_get_cell_value, ip_NetToMediaTable_get_next_cell_instance_and_value);
+
+/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
+CREATE_LWIP_SYNC_NODE(1, at_Table)
+
+static const struct snmp_node *const at_nodes[] = {
+  &SYNC_NODE_NAME(at_Table).node.node
+};
+
+const struct snmp_tree_node snmp_mib2_at_root = SNMP_CREATE_TREE_NODE(3, at_nodes);
+#endif /* LWIP_ARP && LWIP_IPV4 */
+
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_mib2_snmp.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_mib2_snmp.c
new file mode 100644 (file)
index 0000000..ca22e41
--- /dev/null
@@ -0,0 +1,227 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) SNMP objects and functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ *         Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_scalar.h"
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2
+
+#define MIB2_AUTH_TRAPS_ENABLED  1
+#define MIB2_AUTH_TRAPS_DISABLED 2
+
+/* --- snmp .1.3.6.1.2.1.11 ----------------------------------------------------- */
+static s16_t
+snmp_get_value(const struct snmp_scalar_array_node_def *node, void *value)
+{
+  u32_t *uint_ptr = (u32_t *)value;
+  switch (node->oid) {
+    case 1: /* snmpInPkts */
+      *uint_ptr = snmp_stats.inpkts;
+      break;
+    case 2: /* snmpOutPkts */
+      *uint_ptr = snmp_stats.outpkts;
+      break;
+    case 3: /* snmpInBadVersions */
+      *uint_ptr = snmp_stats.inbadversions;
+      break;
+    case 4: /* snmpInBadCommunityNames */
+      *uint_ptr = snmp_stats.inbadcommunitynames;
+      break;
+    case 5: /* snmpInBadCommunityUses */
+      *uint_ptr = snmp_stats.inbadcommunityuses;
+      break;
+    case 6: /* snmpInASNParseErrs */
+      *uint_ptr = snmp_stats.inasnparseerrs;
+      break;
+    case 8: /* snmpInTooBigs */
+      *uint_ptr = snmp_stats.intoobigs;
+      break;
+    case 9: /* snmpInNoSuchNames */
+      *uint_ptr = snmp_stats.innosuchnames;
+      break;
+    case 10: /* snmpInBadValues */
+      *uint_ptr = snmp_stats.inbadvalues;
+      break;
+    case 11: /* snmpInReadOnlys */
+      *uint_ptr = snmp_stats.inreadonlys;
+      break;
+    case 12: /* snmpInGenErrs */
+      *uint_ptr = snmp_stats.ingenerrs;
+      break;
+    case 13: /* snmpInTotalReqVars */
+      *uint_ptr = snmp_stats.intotalreqvars;
+      break;
+    case 14: /* snmpInTotalSetVars */
+      *uint_ptr = snmp_stats.intotalsetvars;
+      break;
+    case 15: /* snmpInGetRequests */
+      *uint_ptr = snmp_stats.ingetrequests;
+      break;
+    case 16: /* snmpInGetNexts */
+      *uint_ptr = snmp_stats.ingetnexts;
+      break;
+    case 17: /* snmpInSetRequests */
+      *uint_ptr = snmp_stats.insetrequests;
+      break;
+    case 18: /* snmpInGetResponses */
+      *uint_ptr = snmp_stats.ingetresponses;
+      break;
+    case 19: /* snmpInTraps */
+      *uint_ptr = snmp_stats.intraps;
+      break;
+    case 20: /* snmpOutTooBigs */
+      *uint_ptr = snmp_stats.outtoobigs;
+      break;
+    case 21: /* snmpOutNoSuchNames */
+      *uint_ptr = snmp_stats.outnosuchnames;
+      break;
+    case 22: /* snmpOutBadValues */
+      *uint_ptr = snmp_stats.outbadvalues;
+      break;
+    case 24: /* snmpOutGenErrs */
+      *uint_ptr = snmp_stats.outgenerrs;
+      break;
+    case 25: /* snmpOutGetRequests */
+      *uint_ptr = snmp_stats.outgetrequests;
+      break;
+    case 26: /* snmpOutGetNexts */
+      *uint_ptr = snmp_stats.outgetnexts;
+      break;
+    case 27: /* snmpOutSetRequests */
+      *uint_ptr = snmp_stats.outsetrequests;
+      break;
+    case 28: /* snmpOutGetResponses */
+      *uint_ptr = snmp_stats.outgetresponses;
+      break;
+    case 29: /* snmpOutTraps */
+      *uint_ptr = snmp_stats.outtraps;
+      break;
+    case 30: /* snmpEnableAuthenTraps */
+      if (snmp_get_auth_traps_enabled() == SNMP_AUTH_TRAPS_DISABLED) {
+        *uint_ptr = MIB2_AUTH_TRAPS_DISABLED;
+      } else {
+        *uint_ptr = MIB2_AUTH_TRAPS_ENABLED;
+      }
+      break;
+    case 31: /* snmpSilentDrops */
+      *uint_ptr = 0; /* not supported */
+      break;
+    case 32: /* snmpProxyDrops */
+      *uint_ptr = 0; /* not supported */
+      break;
+    default:
+      LWIP_DEBUGF(SNMP_MIB_DEBUG, ("snmp_get_value(): unknown id: %"S32_F"\n", node->oid));
+      return 0;
+  }
+
+  return sizeof(*uint_ptr);
+}
+
+static snmp_err_t
+snmp_set_test(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
+{
+  snmp_err_t ret = SNMP_ERR_WRONGVALUE;
+  LWIP_UNUSED_ARG(len);
+
+  if (node->oid == 30) {
+    /* snmpEnableAuthenTraps */
+    s32_t *sint_ptr = (s32_t *)value;
+
+    /* we should have writable non-volatile mem here */
+    if ((*sint_ptr == MIB2_AUTH_TRAPS_DISABLED) || (*sint_ptr == MIB2_AUTH_TRAPS_ENABLED)) {
+      ret = SNMP_ERR_NOERROR;
+    }
+  }
+  return ret;
+}
+
+static snmp_err_t
+snmp_set_value(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
+{
+  LWIP_UNUSED_ARG(len);
+
+  if (node->oid == 30) {
+    /* snmpEnableAuthenTraps */
+    s32_t *sint_ptr = (s32_t *)value;
+    if (*sint_ptr == MIB2_AUTH_TRAPS_DISABLED) {
+      snmp_set_auth_traps_enabled(SNMP_AUTH_TRAPS_DISABLED);
+    } else {
+      snmp_set_auth_traps_enabled(SNMP_AUTH_TRAPS_ENABLED);
+    }
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+/* the following nodes access variables in SNMP stack (snmp_stats) from SNMP worker thread -> OK, no sync needed */
+static const struct snmp_scalar_array_node_def snmp_nodes[] = {
+  { 1, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInPkts */
+  { 2, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutPkts */
+  { 3, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInBadVersions */
+  { 4, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInBadCommunityNames */
+  { 5, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInBadCommunityUses */
+  { 6, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInASNParseErrs */
+  { 8, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInTooBigs */
+  { 9, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInNoSuchNames */
+  {10, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInBadValues */
+  {11, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInReadOnlys */
+  {12, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInGenErrs */
+  {13, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInTotalReqVars */
+  {14, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInTotalSetVars */
+  {15, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInGetRequests */
+  {16, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInGetNexts */
+  {17, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInSetRequests */
+  {18, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInGetResponses */
+  {19, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInTraps */
+  {20, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutTooBigs */
+  {21, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutNoSuchNames */
+  {22, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutBadValues */
+  {24, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutGenErrs */
+  {25, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutGetRequests */
+  {26, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutGetNexts */
+  {27, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutSetRequests */
+  {28, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutGetResponses */
+  {29, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutTraps */
+  {30, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_WRITE}, /* snmpEnableAuthenTraps */
+  {31, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpSilentDrops */
+  {32, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}   /* snmpProxyDrops */
+};
+
+const struct snmp_scalar_array_node snmp_mib2_snmp_root = SNMP_SCALAR_CREATE_ARRAY_NODE(11, snmp_nodes, snmp_get_value, snmp_set_test, snmp_set_value);
+
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_mib2_system.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_mib2_system.c
new file mode 100644 (file)
index 0000000..71c1c29
--- /dev/null
@@ -0,0 +1,376 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) SYSTEM objects and functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ *         Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/sys.h"
+
+#include <string.h>
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2
+
+#if SNMP_USE_NETCONN
+#define SYNC_NODE_NAME(node_name) node_name ## _synced
+#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
+   static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
+#else
+#define SYNC_NODE_NAME(node_name) node_name
+#define CREATE_LWIP_SYNC_NODE(oid, node_name)
+#endif
+
+/* --- system .1.3.6.1.2.1.1 ----------------------------------------------------- */
+
+/** mib-2.system.sysDescr */
+static const u8_t   sysdescr_default[] = SNMP_LWIP_MIB2_SYSDESC;
+static const u8_t  *sysdescr           = sysdescr_default;
+static const u16_t *sysdescr_len       = NULL; /* use strlen for determining len */
+
+/** mib-2.system.sysContact */
+static const u8_t   syscontact_default[]     = SNMP_LWIP_MIB2_SYSCONTACT;
+static const u8_t  *syscontact               = syscontact_default;
+static const u16_t *syscontact_len           = NULL; /* use strlen for determining len */
+static u8_t        *syscontact_wr            = NULL; /* if writable, points to the same buffer as syscontact (required for correct constness) */
+static u16_t       *syscontact_wr_len        = NULL; /* if writable, points to the same buffer as syscontact_len (required for correct constness) */
+static u16_t        syscontact_bufsize       = 0;    /* 0=not writable */
+
+/** mib-2.system.sysName */
+static const u8_t   sysname_default[]        = SNMP_LWIP_MIB2_SYSNAME;
+static const u8_t  *sysname                  = sysname_default;
+static const u16_t *sysname_len              = NULL; /* use strlen for determining len */
+static u8_t        *sysname_wr               = NULL; /* if writable, points to the same buffer as sysname (required for correct constness) */
+static u16_t       *sysname_wr_len           = NULL; /* if writable, points to the same buffer as sysname_len (required for correct constness) */
+static u16_t        sysname_bufsize          = 0;    /* 0=not writable */
+
+/** mib-2.system.sysLocation */
+static const u8_t   syslocation_default[]    = SNMP_LWIP_MIB2_SYSLOCATION;
+static const u8_t  *syslocation              = syslocation_default;
+static const u16_t *syslocation_len           = NULL; /* use strlen for determining len */
+static u8_t        *syslocation_wr            = NULL; /* if writable, points to the same buffer as syslocation (required for correct constness) */
+static u16_t       *syslocation_wr_len        = NULL; /* if writable, points to the same buffer as syslocation_len (required for correct constness) */
+static u16_t        syslocation_bufsize       = 0;    /* 0=not writable */
+
+/**
+ * @ingroup snmp_mib2
+ * Initializes sysDescr pointers.
+ *
+ * @param str if non-NULL then copy str pointer
+ * @param len points to string length, excluding zero terminator
+ */
+void
+snmp_mib2_set_sysdescr(const u8_t *str, const u16_t *len)
+{
+  if (str != NULL) {
+    sysdescr     = str;
+    sysdescr_len = len;
+  }
+}
+
+/**
+ * @ingroup snmp_mib2
+ * Initializes sysContact pointers
+ *
+ * @param ocstr if non-NULL then copy str pointer
+ * @param ocstrlen points to string length, excluding zero terminator.
+ *        if set to NULL it is assumed that ocstr is NULL-terminated.
+ * @param bufsize size of the buffer in bytes.
+ *        (this is required because the buffer can be overwritten by snmp-set)
+ *        if ocstrlen is NULL buffer needs space for terminating 0 byte.
+ *        otherwise complete buffer is used for string.
+ *        if bufsize is set to 0, the value is regarded as read-only.
+ */
+void
+snmp_mib2_set_syscontact(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize)
+{
+  if (ocstr != NULL) {
+    syscontact         = ocstr;
+    syscontact_wr      = ocstr;
+    syscontact_len     = ocstrlen;
+    syscontact_wr_len  = ocstrlen;
+    syscontact_bufsize = bufsize;
+  }
+}
+
+/**
+ * @ingroup snmp_mib2
+ * see \ref snmp_mib2_set_syscontact but set pointer to readonly memory
+ */
+void
+snmp_mib2_set_syscontact_readonly(const u8_t *ocstr, const u16_t *ocstrlen)
+{
+  if (ocstr != NULL) {
+    syscontact         = ocstr;
+    syscontact_len     = ocstrlen;
+    syscontact_wr      = NULL;
+    syscontact_wr_len  = NULL;
+    syscontact_bufsize = 0;
+  }
+}
+
+
+/**
+ * @ingroup snmp_mib2
+ * Initializes sysName pointers
+ *
+ * @param ocstr if non-NULL then copy str pointer
+ * @param ocstrlen points to string length, excluding zero terminator.
+ *        if set to NULL it is assumed that ocstr is NULL-terminated.
+ * @param bufsize size of the buffer in bytes.
+ *        (this is required because the buffer can be overwritten by snmp-set)
+ *        if ocstrlen is NULL buffer needs space for terminating 0 byte.
+ *        otherwise complete buffer is used for string.
+ *        if bufsize is set to 0, the value is regarded as read-only.
+ */
+void
+snmp_mib2_set_sysname(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize)
+{
+  if (ocstr != NULL) {
+    sysname         = ocstr;
+    sysname_wr      = ocstr;
+    sysname_len     = ocstrlen;
+    sysname_wr_len  = ocstrlen;
+    sysname_bufsize = bufsize;
+  }
+}
+
+/**
+ * @ingroup snmp_mib2
+ * see \ref snmp_mib2_set_sysname but set pointer to readonly memory
+ */
+void
+snmp_mib2_set_sysname_readonly(const u8_t *ocstr, const u16_t *ocstrlen)
+{
+  if (ocstr != NULL) {
+    sysname         = ocstr;
+    sysname_len     = ocstrlen;
+    sysname_wr      = NULL;
+    sysname_wr_len  = NULL;
+    sysname_bufsize = 0;
+  }
+}
+
+/**
+ * @ingroup snmp_mib2
+ * Initializes sysLocation pointers
+ *
+ * @param ocstr if non-NULL then copy str pointer
+ * @param ocstrlen points to string length, excluding zero terminator.
+ *        if set to NULL it is assumed that ocstr is NULL-terminated.
+ * @param bufsize size of the buffer in bytes.
+ *        (this is required because the buffer can be overwritten by snmp-set)
+ *        if ocstrlen is NULL buffer needs space for terminating 0 byte.
+ *        otherwise complete buffer is used for string.
+ *        if bufsize is set to 0, the value is regarded as read-only.
+ */
+void
+snmp_mib2_set_syslocation(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize)
+{
+  if (ocstr != NULL) {
+    syslocation         = ocstr;
+    syslocation_wr      = ocstr;
+    syslocation_len     = ocstrlen;
+    syslocation_wr_len  = ocstrlen;
+    syslocation_bufsize = bufsize;
+  }
+}
+
+/**
+ * @ingroup snmp_mib2
+ * see \ref snmp_mib2_set_syslocation but set pointer to readonly memory
+ */
+void
+snmp_mib2_set_syslocation_readonly(const u8_t *ocstr, const u16_t *ocstrlen)
+{
+  if (ocstr != NULL) {
+    syslocation         = ocstr;
+    syslocation_len     = ocstrlen;
+    syslocation_wr      = NULL;
+    syslocation_wr_len  = NULL;
+    syslocation_bufsize = 0;
+  }
+}
+
+
+static s16_t
+system_get_value(const struct snmp_scalar_array_node_def *node, void *value)
+{
+  const u8_t  *var = NULL;
+  const s16_t *var_len;
+  u16_t result;
+
+  switch (node->oid) {
+    case 1: /* sysDescr */
+      var     = sysdescr;
+      var_len = (const s16_t *)sysdescr_len;
+      break;
+    case 2: { /* sysObjectID */
+      const struct snmp_obj_id *dev_enterprise_oid = snmp_get_device_enterprise_oid();
+      MEMCPY(value, dev_enterprise_oid->id, dev_enterprise_oid->len * sizeof(u32_t));
+      return dev_enterprise_oid->len * sizeof(u32_t);
+    }
+    case 3: /* sysUpTime */
+      MIB2_COPY_SYSUPTIME_TO((u32_t *)value);
+      return sizeof(u32_t);
+    case 4: /* sysContact */
+      var     = syscontact;
+      var_len = (const s16_t *)syscontact_len;
+      break;
+    case 5: /* sysName */
+      var     = sysname;
+      var_len = (const s16_t *)sysname_len;
+      break;
+    case 6: /* sysLocation */
+      var     = syslocation;
+      var_len = (const s16_t *)syslocation_len;
+      break;
+    case 7: /* sysServices */
+      *(s32_t *)value = SNMP_SYSSERVICES;
+      return sizeof(s32_t);
+    default:
+      LWIP_DEBUGF(SNMP_MIB_DEBUG, ("system_get_value(): unknown id: %"S32_F"\n", node->oid));
+      return 0;
+  }
+
+  /* handle string values (OID 1,4,5 and 6) */
+  LWIP_ASSERT("", (value != NULL));
+  if (var_len == NULL) {
+    result = (s16_t)strlen((const char *)var);
+  } else {
+    result = *var_len;
+  }
+  MEMCPY(value, var, result);
+  return result;
+}
+
+static snmp_err_t
+system_set_test(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
+{
+  snmp_err_t ret = SNMP_ERR_WRONGVALUE;
+  const u16_t *var_bufsize  = NULL;
+  const u16_t *var_wr_len;
+
+  LWIP_UNUSED_ARG(value);
+
+  switch (node->oid) {
+    case 4: /* sysContact */
+      var_bufsize  = &syscontact_bufsize;
+      var_wr_len   = syscontact_wr_len;
+      break;
+    case 5: /* sysName */
+      var_bufsize  = &sysname_bufsize;
+      var_wr_len   = sysname_wr_len;
+      break;
+    case 6: /* sysLocation */
+      var_bufsize  = &syslocation_bufsize;
+      var_wr_len   = syslocation_wr_len;
+      break;
+    default:
+      LWIP_DEBUGF(SNMP_MIB_DEBUG, ("system_set_test(): unknown id: %"S32_F"\n", node->oid));
+      return ret;
+  }
+
+  /* check if value is writable at all */
+  if (*var_bufsize > 0) {
+    if (var_wr_len == NULL) {
+      /* we have to take the terminating 0 into account */
+      if (len < *var_bufsize) {
+        ret = SNMP_ERR_NOERROR;
+      }
+    } else {
+      if (len <= *var_bufsize) {
+        ret = SNMP_ERR_NOERROR;
+      }
+    }
+  } else {
+    ret = SNMP_ERR_NOTWRITABLE;
+  }
+
+  return ret;
+}
+
+static snmp_err_t
+system_set_value(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
+{
+  u8_t  *var_wr = NULL;
+  u16_t *var_wr_len;
+
+  switch (node->oid) {
+    case 4: /* sysContact */
+      var_wr     = syscontact_wr;
+      var_wr_len = syscontact_wr_len;
+      break;
+    case 5: /* sysName */
+      var_wr     = sysname_wr;
+      var_wr_len = sysname_wr_len;
+      break;
+    case 6: /* sysLocation */
+      var_wr     = syslocation_wr;
+      var_wr_len = syslocation_wr_len;
+      break;
+    default:
+      LWIP_DEBUGF(SNMP_MIB_DEBUG, ("system_set_value(): unknown id: %"S32_F"\n", node->oid));
+      return SNMP_ERR_GENERROR;
+  }
+
+  /* no need to check size of target buffer, this was already done in set_test method */
+  LWIP_ASSERT("", var_wr != NULL);
+  MEMCPY(var_wr, value, len);
+
+  if (var_wr_len == NULL) {
+    /* add terminating 0 */
+    var_wr[len] = 0;
+  } else {
+    *var_wr_len = len;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+static const struct snmp_scalar_array_node_def system_nodes[] = {
+  {1, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY},  /* sysDescr */
+  {2, SNMP_ASN1_TYPE_OBJECT_ID,    SNMP_NODE_INSTANCE_READ_ONLY},  /* sysObjectID */
+  {3, SNMP_ASN1_TYPE_TIMETICKS,    SNMP_NODE_INSTANCE_READ_ONLY},  /* sysUpTime */
+  {4, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysContact */
+  {5, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysName */
+  {6, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysLocation */
+  {7, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY}   /* sysServices */
+};
+
+const struct snmp_scalar_array_node snmp_mib2_system_node = SNMP_SCALAR_CREATE_ARRAY_NODE(1, system_nodes, system_get_value, system_set_test, system_set_value);
+
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_mib2_tcp.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_mib2_tcp.c
new file mode 100644 (file)
index 0000000..8a321f3
--- /dev/null
@@ -0,0 +1,607 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) TCP objects and functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ *         Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/tcp.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_TCP
+
+#if SNMP_USE_NETCONN
+#define SYNC_NODE_NAME(node_name) node_name ## _synced
+#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
+   static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
+#else
+#define SYNC_NODE_NAME(node_name) node_name
+#define CREATE_LWIP_SYNC_NODE(oid, node_name)
+#endif
+
+/* --- tcp .1.3.6.1.2.1.6 ----------------------------------------------------- */
+
+static s16_t
+tcp_get_value(struct snmp_node_instance *instance, void *value)
+{
+  u32_t *uint_ptr = (u32_t *)value;
+  s32_t *sint_ptr = (s32_t *)value;
+
+  switch (instance->node->oid) {
+    case 1: /* tcpRtoAlgorithm, vanj(4) */
+      *sint_ptr = 4;
+      return sizeof(*sint_ptr);
+    case 2: /* tcpRtoMin */
+      /* @todo not the actual value, a guess,
+          needs to be calculated */
+      *sint_ptr = 1000;
+      return sizeof(*sint_ptr);
+    case 3: /* tcpRtoMax */
+      /* @todo not the actual value, a guess,
+          needs to be calculated */
+      *sint_ptr = 60000;
+      return sizeof(*sint_ptr);
+    case 4: /* tcpMaxConn */
+      *sint_ptr = MEMP_NUM_TCP_PCB;
+      return sizeof(*sint_ptr);
+    case 5: /* tcpActiveOpens */
+      *uint_ptr = STATS_GET(mib2.tcpactiveopens);
+      return sizeof(*uint_ptr);
+    case 6: /* tcpPassiveOpens */
+      *uint_ptr = STATS_GET(mib2.tcppassiveopens);
+      return sizeof(*uint_ptr);
+    case 7: /* tcpAttemptFails */
+      *uint_ptr = STATS_GET(mib2.tcpattemptfails);
+      return sizeof(*uint_ptr);
+    case 8: /* tcpEstabResets */
+      *uint_ptr = STATS_GET(mib2.tcpestabresets);
+      return sizeof(*uint_ptr);
+    case 9: { /* tcpCurrEstab */
+      u16_t tcpcurrestab = 0;
+      struct tcp_pcb *pcb = tcp_active_pcbs;
+      while (pcb != NULL) {
+        if ((pcb->state == ESTABLISHED) ||
+            (pcb->state == CLOSE_WAIT)) {
+          tcpcurrestab++;
+        }
+        pcb = pcb->next;
+      }
+      *uint_ptr = tcpcurrestab;
+    }
+    return sizeof(*uint_ptr);
+    case 10: /* tcpInSegs */
+      *uint_ptr = STATS_GET(mib2.tcpinsegs);
+      return sizeof(*uint_ptr);
+    case 11: /* tcpOutSegs */
+      *uint_ptr = STATS_GET(mib2.tcpoutsegs);
+      return sizeof(*uint_ptr);
+    case 12: /* tcpRetransSegs */
+      *uint_ptr = STATS_GET(mib2.tcpretranssegs);
+      return sizeof(*uint_ptr);
+    case 14: /* tcpInErrs */
+      *uint_ptr = STATS_GET(mib2.tcpinerrs);
+      return sizeof(*uint_ptr);
+    case 15: /* tcpOutRsts */
+      *uint_ptr = STATS_GET(mib2.tcpoutrsts);
+      return sizeof(*uint_ptr);
+#if LWIP_HAVE_INT64
+    case 17: { /* tcpHCInSegs */
+      /* use the 32 bit counter for now... */
+      u64_t val64 = STATS_GET(mib2.tcpinsegs);
+      *((u64_t *)value) = val64;
+    }
+    return sizeof(u64_t);
+    case 18: { /* tcpHCOutSegs */
+      /* use the 32 bit counter for now... */
+      u64_t val64 = STATS_GET(mib2.tcpoutsegs);
+      *((u64_t *)value) = val64;
+    }
+    return sizeof(u64_t);
+#endif
+    default:
+      LWIP_DEBUGF(SNMP_MIB_DEBUG, ("tcp_get_value(): unknown id: %"S32_F"\n", instance->node->oid));
+      break;
+  }
+
+  return 0;
+}
+
+/* --- tcpConnTable --- */
+
+#if LWIP_IPV4
+
+/* list of allowed value ranges for incoming OID */
+static const struct snmp_oid_range tcp_ConnTable_oid_ranges[] = {
+  { 0, 0xff   }, /* IP A */
+  { 0, 0xff   }, /* IP B */
+  { 0, 0xff   }, /* IP C */
+  { 0, 0xff   }, /* IP D */
+  { 0, 0xffff }, /* Port */
+  { 0, 0xff   }, /* IP A */
+  { 0, 0xff   }, /* IP B */
+  { 0, 0xff   }, /* IP C */
+  { 0, 0xff   }, /* IP D */
+  { 0, 0xffff }  /* Port */
+};
+
+static snmp_err_t
+tcp_ConnTable_get_cell_value_core(struct tcp_pcb *pcb, const u32_t *column, union snmp_variant_value *value, u32_t *value_len)
+{
+  LWIP_UNUSED_ARG(value_len);
+
+  /* value */
+  switch (*column) {
+    case 1: /* tcpConnState */
+      value->u32 = pcb->state + 1;
+      break;
+    case 2: /* tcpConnLocalAddress */
+      value->u32 = ip_2_ip4(&pcb->local_ip)->addr;
+      break;
+    case 3: /* tcpConnLocalPort */
+      value->u32 = pcb->local_port;
+      break;
+    case 4: /* tcpConnRemAddress */
+      if (pcb->state == LISTEN) {
+        value->u32 = IP4_ADDR_ANY4->addr;
+      } else {
+        value->u32 = ip_2_ip4(&pcb->remote_ip)->addr;
+      }
+      break;
+    case 5: /* tcpConnRemPort */
+      if (pcb->state == LISTEN) {
+        value->u32 = 0;
+      } else {
+        value->u32 = pcb->remote_port;
+      }
+      break;
+    default:
+      LWIP_ASSERT("invalid id", 0);
+      return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+tcp_ConnTable_get_cell_value(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, union snmp_variant_value *value, u32_t *value_len)
+{
+  u8_t i;
+  ip4_addr_t local_ip;
+  ip4_addr_t remote_ip;
+  u16_t local_port;
+  u16_t remote_port;
+  struct tcp_pcb *pcb;
+
+  /* check if incoming OID length and if values are in plausible range */
+  if (!snmp_oid_in_range(row_oid, row_oid_len, tcp_ConnTable_oid_ranges, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges))) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* get IPs and ports from incoming OID */
+  snmp_oid_to_ip4(&row_oid[0], &local_ip); /* we know it succeeds because of oid_in_range check above */
+  local_port = (u16_t)row_oid[4];
+  snmp_oid_to_ip4(&row_oid[5], &remote_ip); /* we know it succeeds because of oid_in_range check above */
+  remote_port = (u16_t)row_oid[9];
+
+  /* find tcp_pcb with requested ips and ports */
+  for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_lists); i++) {
+    pcb = *tcp_pcb_lists[i];
+
+    while (pcb != NULL) {
+      /* do local IP and local port match? */
+      if (IP_IS_V4_VAL(pcb->local_ip) &&
+          ip4_addr_cmp(&local_ip, ip_2_ip4(&pcb->local_ip)) && (local_port == pcb->local_port)) {
+
+        /* PCBs in state LISTEN are not connected and have no remote_ip or remote_port */
+        if (pcb->state == LISTEN) {
+          if (ip4_addr_cmp(&remote_ip, IP4_ADDR_ANY4) && (remote_port == 0)) {
+            /* fill in object properties */
+            return tcp_ConnTable_get_cell_value_core(pcb, column, value, value_len);
+          }
+        } else {
+          if (IP_IS_V4_VAL(pcb->remote_ip) &&
+              ip4_addr_cmp(&remote_ip, ip_2_ip4(&pcb->remote_ip)) && (remote_port == pcb->remote_port)) {
+            /* fill in object properties */
+            return tcp_ConnTable_get_cell_value_core(pcb, column, value, value_len);
+          }
+        }
+      }
+
+      pcb = pcb->next;
+    }
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+tcp_ConnTable_get_next_cell_instance_and_value(const u32_t *column, struct snmp_obj_id *row_oid, union snmp_variant_value *value, u32_t *value_len)
+{
+  u8_t i;
+  struct tcp_pcb *pcb;
+  struct snmp_next_oid_state state;
+  u32_t result_temp[LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges)];
+
+  /* init struct to search next oid */
+  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges));
+
+  /* iterate over all possible OIDs to find the next one */
+  for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_lists); i++) {
+    pcb = *tcp_pcb_lists[i];
+    while (pcb != NULL) {
+      u32_t test_oid[LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges)];
+
+      if (IP_IS_V4_VAL(pcb->local_ip)) {
+        snmp_ip4_to_oid(ip_2_ip4(&pcb->local_ip), &test_oid[0]);
+        test_oid[4] = pcb->local_port;
+
+        /* PCBs in state LISTEN are not connected and have no remote_ip or remote_port */
+        if (pcb->state == LISTEN) {
+          snmp_ip4_to_oid(IP4_ADDR_ANY4, &test_oid[5]);
+          test_oid[9] = 0;
+        } else {
+          if (IP_IS_V6_VAL(pcb->remote_ip)) { /* should never happen */
+            continue;
+          }
+          snmp_ip4_to_oid(ip_2_ip4(&pcb->remote_ip), &test_oid[5]);
+          test_oid[9] = pcb->remote_port;
+        }
+
+        /* check generated OID: is it a candidate for the next one? */
+        snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges), pcb);
+      }
+
+      pcb = pcb->next;
+    }
+  }
+
+  /* did we find a next one? */
+  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+    /* fill in object properties */
+    return tcp_ConnTable_get_cell_value_core((struct tcp_pcb *)state.reference, column, value, value_len);
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+#endif /* LWIP_IPV4 */
+
+/* --- tcpConnectionTable --- */
+
+static snmp_err_t
+tcp_ConnectionTable_get_cell_value_core(const u32_t *column, struct tcp_pcb *pcb, union snmp_variant_value *value)
+{
+  /* all items except tcpConnectionState and tcpConnectionProcess are declared as not-accessible */
+  switch (*column) {
+    case 7: /* tcpConnectionState */
+      value->u32 = pcb->state + 1;
+      break;
+    case 8: /* tcpConnectionProcess */
+      value->u32 = 0; /* not supported */
+      break;
+    default:
+      return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+tcp_ConnectionTable_get_cell_value(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, union snmp_variant_value *value, u32_t *value_len)
+{
+  ip_addr_t local_ip, remote_ip;
+  u16_t local_port, remote_port;
+  struct tcp_pcb *pcb;
+  u8_t idx = 0;
+  u8_t i;
+  struct tcp_pcb **const tcp_pcb_nonlisten_lists[] = {&tcp_bound_pcbs, &tcp_active_pcbs, &tcp_tw_pcbs};
+
+  LWIP_UNUSED_ARG(value_len);
+
+  /* tcpConnectionLocalAddressType + tcpConnectionLocalAddress + tcpConnectionLocalPort */
+  idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len - idx, &local_ip, &local_port);
+  if (idx == 0) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* tcpConnectionRemAddressType + tcpConnectionRemAddress + tcpConnectionRemPort */
+  idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len - idx, &remote_ip, &remote_port);
+  if (idx == 0) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* find tcp_pcb with requested ip and port*/
+  for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_nonlisten_lists); i++) {
+    pcb = *tcp_pcb_nonlisten_lists[i];
+
+    while (pcb != NULL) {
+      if (ip_addr_cmp(&local_ip, &pcb->local_ip) &&
+          (local_port == pcb->local_port) &&
+          ip_addr_cmp(&remote_ip, &pcb->remote_ip) &&
+          (remote_port == pcb->remote_port)) {
+        /* fill in object properties */
+        return tcp_ConnectionTable_get_cell_value_core(column, pcb, value);
+      }
+      pcb = pcb->next;
+    }
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+tcp_ConnectionTable_get_next_cell_instance_and_value(const u32_t *column, struct snmp_obj_id *row_oid, union snmp_variant_value *value, u32_t *value_len)
+{
+  struct tcp_pcb *pcb;
+  struct snmp_next_oid_state state;
+  /* 1x tcpConnectionLocalAddressType + 1x OID len + 16x tcpConnectionLocalAddress  + 1x tcpConnectionLocalPort
+   * 1x tcpConnectionRemAddressType   + 1x OID len + 16x tcpConnectionRemAddress    + 1x tcpConnectionRemPort */
+  u32_t  result_temp[38];
+  u8_t i;
+  struct tcp_pcb **const tcp_pcb_nonlisten_lists[] = {&tcp_bound_pcbs, &tcp_active_pcbs, &tcp_tw_pcbs};
+
+  LWIP_UNUSED_ARG(value_len);
+
+  /* init struct to search next oid */
+  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp));
+
+  /* iterate over all possible OIDs to find the next one */
+  for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_nonlisten_lists); i++) {
+    pcb = *tcp_pcb_nonlisten_lists[i];
+
+    while (pcb != NULL) {
+      u8_t idx = 0;
+      u32_t test_oid[LWIP_ARRAYSIZE(result_temp)];
+
+      /* tcpConnectionLocalAddressType + tcpConnectionLocalAddress + tcpConnectionLocalPort */
+      idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]);
+
+      /* tcpConnectionRemAddressType + tcpConnectionRemAddress + tcpConnectionRemPort */
+      idx += snmp_ip_port_to_oid(&pcb->remote_ip, pcb->remote_port, &test_oid[idx]);
+
+      /* check generated OID: is it a candidate for the next one? */
+      snmp_next_oid_check(&state, test_oid, idx, pcb);
+
+      pcb = pcb->next;
+    }
+  }
+
+  /* did we find a next one? */
+  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+    /* fill in object properties */
+    return tcp_ConnectionTable_get_cell_value_core(column, (struct tcp_pcb *)state.reference, value);
+  } else {
+    /* not found */
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+}
+
+/* --- tcpListenerTable --- */
+
+static snmp_err_t
+tcp_ListenerTable_get_cell_value_core(const u32_t *column, union snmp_variant_value *value)
+{
+  /* all items except tcpListenerProcess are declared as not-accessible */
+  switch (*column) {
+    case 4: /* tcpListenerProcess */
+      value->u32 = 0; /* not supported */
+      break;
+    default:
+      return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+tcp_ListenerTable_get_cell_value(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, union snmp_variant_value *value, u32_t *value_len)
+{
+  ip_addr_t local_ip;
+  u16_t local_port;
+  struct tcp_pcb_listen *pcb;
+  u8_t idx = 0;
+
+  LWIP_UNUSED_ARG(value_len);
+
+  /* tcpListenerLocalAddressType + tcpListenerLocalAddress + tcpListenerLocalPort */
+  idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len - idx, &local_ip, &local_port);
+  if (idx == 0) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* find tcp_pcb with requested ip and port*/
+  pcb = tcp_listen_pcbs.listen_pcbs;
+  while (pcb != NULL) {
+    if (ip_addr_cmp(&local_ip, &pcb->local_ip) &&
+        (local_port == pcb->local_port)) {
+      /* fill in object properties */
+      return tcp_ListenerTable_get_cell_value_core(column, value);
+    }
+    pcb = pcb->next;
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+tcp_ListenerTable_get_next_cell_instance_and_value(const u32_t *column, struct snmp_obj_id *row_oid, union snmp_variant_value *value, u32_t *value_len)
+{
+  struct tcp_pcb_listen *pcb;
+  struct snmp_next_oid_state state;
+  /* 1x tcpListenerLocalAddressType + 1x OID len + 16x tcpListenerLocalAddress  + 1x tcpListenerLocalPort */
+  u32_t  result_temp[19];
+
+  LWIP_UNUSED_ARG(value_len);
+
+  /* init struct to search next oid */
+  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp));
+
+  /* iterate over all possible OIDs to find the next one */
+  pcb = tcp_listen_pcbs.listen_pcbs;
+  while (pcb != NULL) {
+    u8_t idx = 0;
+    u32_t test_oid[LWIP_ARRAYSIZE(result_temp)];
+
+    /* tcpListenerLocalAddressType + tcpListenerLocalAddress + tcpListenerLocalPort */
+    idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]);
+
+    /* check generated OID: is it a candidate for the next one? */
+    snmp_next_oid_check(&state, test_oid, idx, NULL);
+
+    pcb = pcb->next;
+  }
+
+  /* did we find a next one? */
+  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+    /* fill in object properties */
+    return tcp_ListenerTable_get_cell_value_core(column, value);
+  } else {
+    /* not found */
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+}
+
+static const struct snmp_scalar_node tcp_RtoAlgorithm  = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
+static const struct snmp_scalar_node tcp_RtoMin        = SNMP_SCALAR_CREATE_NODE_READONLY(2, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
+static const struct snmp_scalar_node tcp_RtoMax        = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
+static const struct snmp_scalar_node tcp_MaxConn       = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
+static const struct snmp_scalar_node tcp_ActiveOpens   = SNMP_SCALAR_CREATE_NODE_READONLY(5, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_PassiveOpens  = SNMP_SCALAR_CREATE_NODE_READONLY(6, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_AttemptFails  = SNMP_SCALAR_CREATE_NODE_READONLY(7, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_EstabResets   = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_CurrEstab     = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_GAUGE, tcp_get_value);
+static const struct snmp_scalar_node tcp_InSegs        = SNMP_SCALAR_CREATE_NODE_READONLY(10, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_OutSegs       = SNMP_SCALAR_CREATE_NODE_READONLY(11, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_RetransSegs   = SNMP_SCALAR_CREATE_NODE_READONLY(12, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_InErrs        = SNMP_SCALAR_CREATE_NODE_READONLY(14, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_OutRsts       = SNMP_SCALAR_CREATE_NODE_READONLY(15, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+#if LWIP_HAVE_INT64
+static const struct snmp_scalar_node tcp_HCInSegs      = SNMP_SCALAR_CREATE_NODE_READONLY(17, SNMP_ASN1_TYPE_COUNTER64, tcp_get_value);
+static const struct snmp_scalar_node tcp_HCOutSegs     = SNMP_SCALAR_CREATE_NODE_READONLY(18, SNMP_ASN1_TYPE_COUNTER64, tcp_get_value);
+#endif
+
+#if LWIP_IPV4
+static const struct snmp_table_simple_col_def tcp_ConnTable_columns[] = {
+  {  1, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnState */
+  {  2, SNMP_ASN1_TYPE_IPADDR,  SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnLocalAddress */
+  {  3, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnLocalPort */
+  {  4, SNMP_ASN1_TYPE_IPADDR,  SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnRemAddress */
+  {  5, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }  /* tcpConnRemPort */
+};
+
+static const struct snmp_table_simple_node tcp_ConnTable = SNMP_TABLE_CREATE_SIMPLE(13, tcp_ConnTable_columns, tcp_ConnTable_get_cell_value, tcp_ConnTable_get_next_cell_instance_and_value);
+#endif /* LWIP_IPV4 */
+
+static const struct snmp_table_simple_col_def tcp_ConnectionTable_columns[] = {
+  /* all items except tcpConnectionState and tcpConnectionProcess are declared as not-accessible */
+  { 7, SNMP_ASN1_TYPE_INTEGER,    SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnectionState */
+  { 8, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 }  /* tcpConnectionProcess */
+};
+
+static const struct snmp_table_simple_node tcp_ConnectionTable = SNMP_TABLE_CREATE_SIMPLE(19, tcp_ConnectionTable_columns, tcp_ConnectionTable_get_cell_value, tcp_ConnectionTable_get_next_cell_instance_and_value);
+
+
+static const struct snmp_table_simple_col_def tcp_ListenerTable_columns[] = {
+  /* all items except tcpListenerProcess are declared as not-accessible */
+  { 4, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 }  /* tcpListenerProcess */
+};
+
+static const struct snmp_table_simple_node tcp_ListenerTable = SNMP_TABLE_CREATE_SIMPLE(20, tcp_ListenerTable_columns, tcp_ListenerTable_get_cell_value, tcp_ListenerTable_get_next_cell_instance_and_value);
+
+/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
+CREATE_LWIP_SYNC_NODE( 1, tcp_RtoAlgorithm)
+CREATE_LWIP_SYNC_NODE( 2, tcp_RtoMin)
+CREATE_LWIP_SYNC_NODE( 3, tcp_RtoMax)
+CREATE_LWIP_SYNC_NODE( 4, tcp_MaxConn)
+CREATE_LWIP_SYNC_NODE( 5, tcp_ActiveOpens)
+CREATE_LWIP_SYNC_NODE( 6, tcp_PassiveOpens)
+CREATE_LWIP_SYNC_NODE( 7, tcp_AttemptFails)
+CREATE_LWIP_SYNC_NODE( 8, tcp_EstabResets)
+CREATE_LWIP_SYNC_NODE( 9, tcp_CurrEstab)
+CREATE_LWIP_SYNC_NODE(10, tcp_InSegs)
+CREATE_LWIP_SYNC_NODE(11, tcp_OutSegs)
+CREATE_LWIP_SYNC_NODE(12, tcp_RetransSegs)
+#if LWIP_IPV4
+CREATE_LWIP_SYNC_NODE(13, tcp_ConnTable)
+#endif /* LWIP_IPV4 */
+CREATE_LWIP_SYNC_NODE(14, tcp_InErrs)
+CREATE_LWIP_SYNC_NODE(15, tcp_OutRsts)
+#if LWIP_HAVE_INT64
+CREATE_LWIP_SYNC_NODE(17, tcp_HCInSegs)
+CREATE_LWIP_SYNC_NODE(18, tcp_HCOutSegs)
+#endif
+CREATE_LWIP_SYNC_NODE(19, tcp_ConnectionTable)
+CREATE_LWIP_SYNC_NODE(20, tcp_ListenerTable)
+
+static const struct snmp_node *const tcp_nodes[] = {
+  &SYNC_NODE_NAME(tcp_RtoAlgorithm).node.node,
+  &SYNC_NODE_NAME(tcp_RtoMin).node.node,
+  &SYNC_NODE_NAME(tcp_RtoMax).node.node,
+  &SYNC_NODE_NAME(tcp_MaxConn).node.node,
+  &SYNC_NODE_NAME(tcp_ActiveOpens).node.node,
+  &SYNC_NODE_NAME(tcp_PassiveOpens).node.node,
+  &SYNC_NODE_NAME(tcp_AttemptFails).node.node,
+  &SYNC_NODE_NAME(tcp_EstabResets).node.node,
+  &SYNC_NODE_NAME(tcp_CurrEstab).node.node,
+  &SYNC_NODE_NAME(tcp_InSegs).node.node,
+  &SYNC_NODE_NAME(tcp_OutSegs).node.node,
+  &SYNC_NODE_NAME(tcp_RetransSegs).node.node,
+#if LWIP_IPV4
+  &SYNC_NODE_NAME(tcp_ConnTable).node.node,
+#endif /* LWIP_IPV4 */
+  &SYNC_NODE_NAME(tcp_InErrs).node.node,
+  &SYNC_NODE_NAME(tcp_OutRsts).node.node,
+  &SYNC_NODE_NAME(tcp_HCInSegs).node.node,
+#if LWIP_HAVE_INT64
+  &SYNC_NODE_NAME(tcp_HCOutSegs).node.node,
+  &SYNC_NODE_NAME(tcp_ConnectionTable).node.node,
+#endif
+  &SYNC_NODE_NAME(tcp_ListenerTable).node.node
+};
+
+const struct snmp_tree_node snmp_mib2_tcp_root = SNMP_CREATE_TREE_NODE(6, tcp_nodes);
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_TCP */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_mib2_udp.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_mib2_udp.c
new file mode 100644 (file)
index 0000000..d0bd5de
--- /dev/null
@@ -0,0 +1,372 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) UDP objects and functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ *         Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/udp.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_UDP
+
+#if SNMP_USE_NETCONN
+#define SYNC_NODE_NAME(node_name) node_name ## _synced
+#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
+   static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
+#else
+#define SYNC_NODE_NAME(node_name) node_name
+#define CREATE_LWIP_SYNC_NODE(oid, node_name)
+#endif
+
+/* --- udp .1.3.6.1.2.1.7 ----------------------------------------------------- */
+
+static s16_t
+udp_get_value(struct snmp_node_instance *instance, void *value)
+{
+  u32_t *uint_ptr = (u32_t *)value;
+
+  switch (instance->node->oid) {
+    case 1: /* udpInDatagrams */
+      *uint_ptr = STATS_GET(mib2.udpindatagrams);
+      return sizeof(*uint_ptr);
+    case 2: /* udpNoPorts */
+      *uint_ptr = STATS_GET(mib2.udpnoports);
+      return sizeof(*uint_ptr);
+    case 3: /* udpInErrors */
+      *uint_ptr = STATS_GET(mib2.udpinerrors);
+      return sizeof(*uint_ptr);
+    case 4: /* udpOutDatagrams */
+      *uint_ptr = STATS_GET(mib2.udpoutdatagrams);
+      return sizeof(*uint_ptr);
+#if LWIP_HAVE_INT64
+    case 8: { /* udpHCInDatagrams */
+      /* use the 32 bit counter for now... */
+      u64_t val64 = STATS_GET(mib2.udpindatagrams);
+      *((u64_t *)value) = val64;
+    }
+    return sizeof(u64_t);
+    case 9: { /* udpHCOutDatagrams */
+      /* use the 32 bit counter for now... */
+      u64_t val64 = STATS_GET(mib2.udpoutdatagrams);
+      *((u64_t *)value) = val64;
+    }
+    return sizeof(u64_t);
+#endif
+    default:
+      LWIP_DEBUGF(SNMP_MIB_DEBUG, ("udp_get_value(): unknown id: %"S32_F"\n", instance->node->oid));
+      break;
+  }
+
+  return 0;
+}
+
+/* --- udpEndpointTable --- */
+
+static snmp_err_t
+udp_endpointTable_get_cell_value_core(const u32_t *column, union snmp_variant_value *value)
+{
+  /* all items except udpEndpointProcess are declared as not-accessible */
+  switch (*column) {
+    case 8: /* udpEndpointProcess */
+      value->u32 = 0; /* not supported */
+      break;
+    default:
+      return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+udp_endpointTable_get_cell_value(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, union snmp_variant_value *value, u32_t *value_len)
+{
+  ip_addr_t local_ip, remote_ip;
+  u16_t local_port, remote_port;
+  struct udp_pcb *pcb;
+  u8_t idx = 0;
+
+  LWIP_UNUSED_ARG(value_len);
+
+  /* udpEndpointLocalAddressType + udpEndpointLocalAddress + udpEndpointLocalPort */
+  idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len - idx, &local_ip, &local_port);
+  if (idx == 0) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* udpEndpointRemoteAddressType + udpEndpointRemoteAddress + udpEndpointRemotePort */
+  idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len - idx, &remote_ip, &remote_port);
+  if (idx == 0) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* udpEndpointInstance */
+  if (row_oid_len < (idx + 1)) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+  if (row_oid[idx] != 0) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* find udp_pcb with requested ip and port*/
+  pcb = udp_pcbs;
+  while (pcb != NULL) {
+    if (ip_addr_cmp(&local_ip, &pcb->local_ip) &&
+        (local_port == pcb->local_port) &&
+        ip_addr_cmp(&remote_ip, &pcb->remote_ip) &&
+        (remote_port == pcb->remote_port)) {
+      /* fill in object properties */
+      return udp_endpointTable_get_cell_value_core(column, value);
+    }
+    pcb = pcb->next;
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+udp_endpointTable_get_next_cell_instance_and_value(const u32_t *column, struct snmp_obj_id *row_oid, union snmp_variant_value *value, u32_t *value_len)
+{
+  struct udp_pcb *pcb;
+  struct snmp_next_oid_state state;
+  /* 1x udpEndpointLocalAddressType  + 1x OID len + 16x udpEndpointLocalAddress  + 1x udpEndpointLocalPort  +
+   * 1x udpEndpointRemoteAddressType + 1x OID len + 16x udpEndpointRemoteAddress + 1x udpEndpointRemotePort +
+   * 1x udpEndpointInstance = 39
+   */
+  u32_t  result_temp[39];
+
+  LWIP_UNUSED_ARG(value_len);
+
+  /* init struct to search next oid */
+  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp));
+
+  /* iterate over all possible OIDs to find the next one */
+  pcb = udp_pcbs;
+  while (pcb != NULL) {
+    u32_t test_oid[LWIP_ARRAYSIZE(result_temp)];
+    u8_t idx = 0;
+
+    /* udpEndpointLocalAddressType + udpEndpointLocalAddress + udpEndpointLocalPort */
+    idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]);
+
+    /* udpEndpointRemoteAddressType + udpEndpointRemoteAddress + udpEndpointRemotePort */
+    idx += snmp_ip_port_to_oid(&pcb->remote_ip, pcb->remote_port, &test_oid[idx]);
+
+    test_oid[idx] = 0; /* udpEndpointInstance */
+    idx++;
+
+    /* check generated OID: is it a candidate for the next one? */
+    snmp_next_oid_check(&state, test_oid, idx, NULL);
+
+    pcb = pcb->next;
+  }
+
+  /* did we find a next one? */
+  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+    /* fill in object properties */
+    return udp_endpointTable_get_cell_value_core(column, value);
+  } else {
+    /* not found */
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+}
+
+/* --- udpTable --- */
+
+#if LWIP_IPV4
+
+/* list of allowed value ranges for incoming OID */
+static const struct snmp_oid_range udp_Table_oid_ranges[] = {
+  { 0, 0xff   }, /* IP A        */
+  { 0, 0xff   }, /* IP B        */
+  { 0, 0xff   }, /* IP C        */
+  { 0, 0xff   }, /* IP D        */
+  { 1, 0xffff }  /* Port        */
+};
+
+static snmp_err_t
+udp_Table_get_cell_value_core(struct udp_pcb *pcb, const u32_t *column, union snmp_variant_value *value, u32_t *value_len)
+{
+  LWIP_UNUSED_ARG(value_len);
+
+  switch (*column) {
+    case 1: /* udpLocalAddress */
+      /* set reference to PCB local IP and return a generic node that copies IP4 addresses */
+      value->u32 = ip_2_ip4(&pcb->local_ip)->addr;
+      break;
+    case 2: /* udpLocalPort */
+      /* set reference to PCB local port and return a generic node that copies u16_t values */
+      value->u32 = pcb->local_port;
+      break;
+    default:
+      return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+udp_Table_get_cell_value(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, union snmp_variant_value *value, u32_t *value_len)
+{
+  ip4_addr_t ip;
+  u16_t port;
+  struct udp_pcb *pcb;
+
+  /* check if incoming OID length and if values are in plausible range */
+  if (!snmp_oid_in_range(row_oid, row_oid_len, udp_Table_oid_ranges, LWIP_ARRAYSIZE(udp_Table_oid_ranges))) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* get IP and port from incoming OID */
+  snmp_oid_to_ip4(&row_oid[0], &ip); /* we know it succeeds because of oid_in_range check above */
+  port = (u16_t)row_oid[4];
+
+  /* find udp_pcb with requested ip and port*/
+  pcb = udp_pcbs;
+  while (pcb != NULL) {
+    if (IP_IS_V4_VAL(pcb->local_ip)) {
+      if (ip4_addr_cmp(&ip, ip_2_ip4(&pcb->local_ip)) && (port == pcb->local_port)) {
+        /* fill in object properties */
+        return udp_Table_get_cell_value_core(pcb, column, value, value_len);
+      }
+    }
+    pcb = pcb->next;
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+udp_Table_get_next_cell_instance_and_value(const u32_t *column, struct snmp_obj_id *row_oid, union snmp_variant_value *value, u32_t *value_len)
+{
+  struct udp_pcb *pcb;
+  struct snmp_next_oid_state state;
+  u32_t  result_temp[LWIP_ARRAYSIZE(udp_Table_oid_ranges)];
+
+  /* init struct to search next oid */
+  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(udp_Table_oid_ranges));
+
+  /* iterate over all possible OIDs to find the next one */
+  pcb = udp_pcbs;
+  while (pcb != NULL) {
+    u32_t test_oid[LWIP_ARRAYSIZE(udp_Table_oid_ranges)];
+
+    if (IP_IS_V4_VAL(pcb->local_ip)) {
+      snmp_ip4_to_oid(ip_2_ip4(&pcb->local_ip), &test_oid[0]);
+      test_oid[4] = pcb->local_port;
+
+      /* check generated OID: is it a candidate for the next one? */
+      snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(udp_Table_oid_ranges), pcb);
+    }
+
+    pcb = pcb->next;
+  }
+
+  /* did we find a next one? */
+  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+    /* fill in object properties */
+    return udp_Table_get_cell_value_core((struct udp_pcb *)state.reference, column, value, value_len);
+  } else {
+    /* not found */
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+}
+
+#endif /* LWIP_IPV4 */
+
+static const struct snmp_scalar_node udp_inDatagrams    = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_COUNTER,   udp_get_value);
+static const struct snmp_scalar_node udp_noPorts        = SNMP_SCALAR_CREATE_NODE_READONLY(2, SNMP_ASN1_TYPE_COUNTER,   udp_get_value);
+static const struct snmp_scalar_node udp_inErrors       = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_COUNTER,   udp_get_value);
+static const struct snmp_scalar_node udp_outDatagrams   = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_COUNTER,   udp_get_value);
+#if LWIP_HAVE_INT64
+static const struct snmp_scalar_node udp_HCInDatagrams  = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER64, udp_get_value);
+static const struct snmp_scalar_node udp_HCOutDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_COUNTER64, udp_get_value);
+#endif
+
+#if LWIP_IPV4
+static const struct snmp_table_simple_col_def udp_Table_columns[] = {
+  { 1, SNMP_ASN1_TYPE_IPADDR,  SNMP_VARIANT_VALUE_TYPE_U32 }, /* udpLocalAddress */
+  { 2, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }  /* udpLocalPort */
+};
+static const struct snmp_table_simple_node udp_Table = SNMP_TABLE_CREATE_SIMPLE(5, udp_Table_columns, udp_Table_get_cell_value, udp_Table_get_next_cell_instance_and_value);
+#endif /* LWIP_IPV4 */
+
+static const struct snmp_table_simple_col_def udp_endpointTable_columns[] = {
+  /* all items except udpEndpointProcess are declared as not-accessible */
+  { 8, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 }  /* udpEndpointProcess */
+};
+
+static const struct snmp_table_simple_node udp_endpointTable = SNMP_TABLE_CREATE_SIMPLE(7, udp_endpointTable_columns, udp_endpointTable_get_cell_value, udp_endpointTable_get_next_cell_instance_and_value);
+
+/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
+CREATE_LWIP_SYNC_NODE(1, udp_inDatagrams)
+CREATE_LWIP_SYNC_NODE(2, udp_noPorts)
+CREATE_LWIP_SYNC_NODE(3, udp_inErrors)
+CREATE_LWIP_SYNC_NODE(4, udp_outDatagrams)
+#if LWIP_IPV4
+CREATE_LWIP_SYNC_NODE(5, udp_Table)
+#endif /* LWIP_IPV4 */
+CREATE_LWIP_SYNC_NODE(7, udp_endpointTable)
+#if LWIP_HAVE_INT64
+CREATE_LWIP_SYNC_NODE(8, udp_HCInDatagrams)
+CREATE_LWIP_SYNC_NODE(9, udp_HCOutDatagrams)
+#endif
+
+static const struct snmp_node *const udp_nodes[] = {
+  &SYNC_NODE_NAME(udp_inDatagrams).node.node,
+  &SYNC_NODE_NAME(udp_noPorts).node.node,
+  &SYNC_NODE_NAME(udp_inErrors).node.node,
+  &SYNC_NODE_NAME(udp_outDatagrams).node.node,
+#if LWIP_IPV4
+  &SYNC_NODE_NAME(udp_Table).node.node,
+#endif /* LWIP_IPV4 */
+  &SYNC_NODE_NAME(udp_endpointTable).node.node
+#if LWIP_HAVE_INT64
+  ,
+  &SYNC_NODE_NAME(udp_HCInDatagrams).node.node,
+  &SYNC_NODE_NAME(udp_HCOutDatagrams).node.node
+#endif
+};
+
+const struct snmp_tree_node snmp_mib2_udp_root = SNMP_CREATE_TREE_NODE(7, udp_nodes);
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_UDP */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_msg.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_msg.c
new file mode 100644 (file)
index 0000000..0b6693a
--- /dev/null
@@ -0,0 +1,1951 @@
+/**
+ * @file
+ * SNMP message processing (RFC1157).
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * Copyright (c) 2016 Elias Oenal.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ *         Martin Hentschel <info@cl-soft.de>
+ *         Elias Oenal <lwip@eliasoenal.com>
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "snmp_msg.h"
+#include "snmp_asn1.h"
+#include "snmp_core_priv.h"
+#include "lwip/ip_addr.h"
+#include "lwip/stats.h"
+
+#if LWIP_SNMP_V3
+#include "lwip/apps/snmpv3.h"
+#include "snmpv3_priv.h"
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+#endif
+
+#include <string.h>
+
+#define SNMP_V3_AUTH_FLAG      0x01
+#define SNMP_V3_PRIV_FLAG      0x02
+
+/* Security levels */
+#define SNMP_V3_NOAUTHNOPRIV   0x00
+#define SNMP_V3_AUTHNOPRIV     SNMP_V3_AUTH_FLAG
+#define SNMP_V3_AUTHPRIV       (SNMP_V3_AUTH_FLAG | SNMP_V3_PRIV_FLAG)
+
+/* public (non-static) constants */
+/** SNMP community string */
+const char *snmp_community = SNMP_COMMUNITY;
+/** SNMP community string for write access */
+const char *snmp_community_write = SNMP_COMMUNITY_WRITE;
+/** SNMP community string for sending traps */
+const char *snmp_community_trap = SNMP_COMMUNITY_TRAP;
+
+snmp_write_callback_fct snmp_write_callback     = NULL;
+void                   *snmp_write_callback_arg = NULL;
+
+#if LWIP_SNMP_CONFIGURE_VERSIONS
+
+static u8_t v1_enabled = 1;
+static u8_t v2c_enabled = 1;
+static u8_t v3_enabled = 1;
+
+static u8_t
+snmp_version_enabled(u8_t version)
+{
+  if (version == SNMP_VERSION_1) {
+    return v1_enabled;
+  } else if (version == SNMP_VERSION_2c) {
+    return v2c_enabled;
+  }
+#if LWIP_SNMP_V3
+  else if (version == SNMP_VERSION_3) {
+    return v3_enabled;
+  }
+#endif
+  else {
+    LWIP_ASSERT("Invalid SNMP version", 0);
+    return 0;
+  }
+}
+
+u8_t
+snmp_v1_enabled(void)
+{
+  return snmp_version_enabled(SNMP_VERSION_1);
+}
+
+u8_t
+snmp_v2c_enabled(void)
+{
+  return snmp_version_enabled(SNMP_VERSION_2c);
+}
+
+u8_t
+snmp_v3_enabled(void)
+{
+  return snmp_version_enabled(SNMP_VERSION_3);
+}
+
+static void
+snmp_version_enable(u8_t version, u8_t enable)
+{
+  if (version == SNMP_VERSION_1) {
+    v1_enabled = enable;
+  } else if (version == SNMP_VERSION_2c) {
+    v2c_enabled = enable;
+  }
+#if LWIP_SNMP_V3
+  else if (version == SNMP_VERSION_3) {
+    v3_enabled = enable;
+  }
+#endif
+  else {
+    LWIP_ASSERT("Invalid SNMP version", 0);
+  }
+}
+
+void
+snmp_v1_enable(u8_t enable)
+{
+  snmp_version_enable(SNMP_VERSION_1, enable);
+}
+
+void
+snmp_v2c_enable(u8_t enable)
+{
+  snmp_version_enable(SNMP_VERSION_2c, enable);
+}
+
+void
+snmp_v3_enable(u8_t enable)
+{
+  snmp_version_enable(SNMP_VERSION_3, enable);
+}
+
+#endif
+
+/**
+ * @ingroup snmp_core
+ * Returns current SNMP community string.
+ * @return current SNMP community string
+ */
+const char *
+snmp_get_community(void)
+{
+  return snmp_community;
+}
+
+/**
+ * @ingroup snmp_core
+ * Sets SNMP community string.
+ * The string itself (its storage) must be valid throughout the whole life of
+ * program (or until it is changed to sth else).
+ *
+ * @param community is a pointer to new community string
+ */
+void
+snmp_set_community(const char *const community)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("community string is too long!", strlen(community) <= SNMP_MAX_COMMUNITY_STR_LEN);
+  snmp_community = community;
+}
+
+/**
+ * @ingroup snmp_core
+ * Returns current SNMP write-access community string.
+ * @return current SNMP write-access community string
+ */
+const char *
+snmp_get_community_write(void)
+{
+  return snmp_community_write;
+}
+
+/**
+ * @ingroup snmp_traps
+ * Returns current SNMP community string used for sending traps.
+ * @return current SNMP community string used for sending traps
+ */
+const char *
+snmp_get_community_trap(void)
+{
+  return snmp_community_trap;
+}
+
+/**
+ * @ingroup snmp_core
+ * Sets SNMP community string for write-access.
+ * The string itself (its storage) must be valid throughout the whole life of
+ * program (or until it is changed to sth else).
+ *
+ * @param community is a pointer to new write-access community string
+ */
+void
+snmp_set_community_write(const char *const community)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("community string must not be NULL", community != NULL);
+  LWIP_ASSERT("community string is too long!", strlen(community) <= SNMP_MAX_COMMUNITY_STR_LEN);
+  snmp_community_write = community;
+}
+
+/**
+ * @ingroup snmp_traps
+ * Sets SNMP community string used for sending traps.
+ * The string itself (its storage) must be valid throughout the whole life of
+ * program (or until it is changed to sth else).
+ *
+ * @param community is a pointer to new trap community string
+ */
+void
+snmp_set_community_trap(const char *const community)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("community string is too long!", strlen(community) <= SNMP_MAX_COMMUNITY_STR_LEN);
+  snmp_community_trap = community;
+}
+
+/**
+ * @ingroup snmp_core
+ * Callback fired on every successful write access
+ */
+void
+snmp_set_write_callback(snmp_write_callback_fct write_callback, void *callback_arg)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  snmp_write_callback     = write_callback;
+  snmp_write_callback_arg = callback_arg;
+}
+
+/* ----------------------------------------------------------------------- */
+/* forward declarations */
+/* ----------------------------------------------------------------------- */
+
+static err_t snmp_process_get_request(struct snmp_request *request);
+static err_t snmp_process_getnext_request(struct snmp_request *request);
+static err_t snmp_process_getbulk_request(struct snmp_request *request);
+static err_t snmp_process_set_request(struct snmp_request *request);
+
+static err_t snmp_parse_inbound_frame(struct snmp_request *request);
+static err_t snmp_prepare_outbound_frame(struct snmp_request *request);
+static err_t snmp_complete_outbound_frame(struct snmp_request *request);
+static void snmp_execute_write_callbacks(struct snmp_request *request);
+
+
+/* ----------------------------------------------------------------------- */
+/* implementation */
+/* ----------------------------------------------------------------------- */
+
+void
+snmp_receive(void *handle, struct pbuf *p, const ip_addr_t *source_ip, u16_t port)
+{
+  err_t err;
+  struct snmp_request request;
+
+  memset(&request, 0, sizeof(request));
+  request.handle       = handle;
+  request.source_ip    = source_ip;
+  request.source_port  = port;
+  request.inbound_pbuf = p;
+
+  snmp_stats.inpkts++;
+
+  err = snmp_parse_inbound_frame(&request);
+  if (err == ERR_OK) {
+    err = snmp_prepare_outbound_frame(&request);
+    if (err == ERR_OK) {
+
+      if (request.error_status == SNMP_ERR_NOERROR) {
+        /* only process frame if we do not already have an error to return (e.g. all readonly) */
+        if (request.request_type == SNMP_ASN1_CONTEXT_PDU_GET_REQ) {
+          err = snmp_process_get_request(&request);
+        } else if (request.request_type == SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ) {
+          err = snmp_process_getnext_request(&request);
+        } else if (request.request_type == SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ) {
+          err = snmp_process_getbulk_request(&request);
+        } else if (request.request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) {
+          err = snmp_process_set_request(&request);
+        }
+      }
+#if LWIP_SNMP_V3
+      else {
+        struct snmp_varbind vb;
+
+        vb.next = NULL;
+        vb.prev = NULL;
+        vb.type = SNMP_ASN1_TYPE_COUNTER32;
+        vb.value_len = sizeof(u32_t);
+
+        switch (request.error_status) {
+          case SNMP_ERR_AUTHORIZATIONERROR: {
+            static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 5, 0 };
+            snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
+            vb.value = &snmp_stats.wrongdigests;
+          }
+          break;
+          case SNMP_ERR_UNKNOWN_ENGINEID: {
+            static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 4, 0 };
+            snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
+            vb.value = &snmp_stats.unknownengineids;
+          }
+          break;
+          case SNMP_ERR_UNKNOWN_SECURITYNAME: {
+            static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 3, 0 };
+            snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
+            vb.value = &snmp_stats.unknownusernames;
+          }
+          break;
+          case SNMP_ERR_UNSUPPORTED_SECLEVEL: {
+            static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 1, 0 };
+            snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
+            vb.value = &snmp_stats.unsupportedseclevels;
+          }
+          break;
+          case SNMP_ERR_NOTINTIMEWINDOW: {
+            static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 2, 0 };
+            snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
+            vb.value = &snmp_stats.notintimewindows;
+          }
+          break;
+          case SNMP_ERR_DECRYIPTION_ERROR: {
+            static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 6, 0 };
+            snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
+            vb.value = &snmp_stats.decryptionerrors;
+          }
+          break;
+          default:
+            /* Unknown or unhandled error_status */
+            err = ERR_ARG;
+        }
+
+        if (err == ERR_OK) {
+          snmp_append_outbound_varbind(&(request.outbound_pbuf_stream), &vb);
+          request.error_status = SNMP_ERR_NOERROR;
+        }
+
+        request.request_out_type = (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_REPORT);
+        request.request_id = request.msg_id;
+      }
+#endif
+
+      if (err == ERR_OK) {
+        err = snmp_complete_outbound_frame(&request);
+
+        if (err == ERR_OK) {
+          err = snmp_sendto(request.handle, request.outbound_pbuf, request.source_ip, request.source_port);
+
+          if ((request.request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ)
+              && (request.error_status == SNMP_ERR_NOERROR)
+              && (snmp_write_callback != NULL)) {
+            /* raise write notification for all written objects */
+            snmp_execute_write_callbacks(&request);
+          }
+        }
+      }
+    }
+
+    if (request.outbound_pbuf != NULL) {
+      pbuf_free(request.outbound_pbuf);
+    }
+  }
+}
+
+static u8_t
+snmp_msg_getnext_validate_node_inst(struct snmp_node_instance *node_instance, void *validate_arg)
+{
+  if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != SNMP_NODE_INSTANCE_ACCESS_READ) || (node_instance->get_value == NULL)) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+#if LWIP_HAVE_INT64
+  if ((node_instance->asn1_type == SNMP_ASN1_TYPE_COUNTER64) && (((struct snmp_request *)validate_arg)->version == SNMP_VERSION_1)) {
+    /* according to RFC 2089 skip Counter64 objects in GetNext requests from v1 clients */
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+#endif
+
+  return SNMP_ERR_NOERROR;
+}
+
+static void
+snmp_process_varbind(struct snmp_request *request, struct snmp_varbind *vb, u8_t get_next)
+{
+  err_t err;
+  struct snmp_node_instance node_instance;
+  memset(&node_instance, 0, sizeof(node_instance));
+
+  if (get_next) {
+    struct snmp_obj_id result_oid;
+    request->error_status = snmp_get_next_node_instance_from_oid(vb->oid.id, vb->oid.len, snmp_msg_getnext_validate_node_inst, request,  &result_oid, &node_instance);
+
+    if (request->error_status == SNMP_ERR_NOERROR) {
+      snmp_oid_assign(&vb->oid, result_oid.id, result_oid.len);
+    }
+  } else {
+    request->error_status = snmp_get_node_instance_from_oid(vb->oid.id, vb->oid.len, &node_instance);
+
+    if (request->error_status == SNMP_ERR_NOERROR) {
+      /* use 'getnext_validate' method for validation to avoid code duplication (some checks have to be executed here) */
+      request->error_status = snmp_msg_getnext_validate_node_inst(&node_instance, request);
+
+      if (request->error_status != SNMP_ERR_NOERROR) {
+        if (node_instance.release_instance != NULL) {
+          node_instance.release_instance(&node_instance);
+        }
+      }
+    }
+  }
+
+  if (request->error_status != SNMP_ERR_NOERROR)  {
+    if (request->error_status >= SNMP_VARBIND_EXCEPTION_OFFSET) {
+      if ((request->version == SNMP_VERSION_2c) || request->version == SNMP_VERSION_3) {
+        /* in SNMP v2c a varbind related exception is stored in varbind and not in frame header */
+        vb->type = (SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_CLASS_CONTEXT | (request->error_status & SNMP_VARBIND_EXCEPTION_MASK));
+        vb->value_len = 0;
+
+        err = snmp_append_outbound_varbind(&(request->outbound_pbuf_stream), vb);
+        if (err == ERR_OK) {
+          /* we stored the exception in varbind -> go on */
+          request->error_status = SNMP_ERR_NOERROR;
+        } else if (err == ERR_BUF) {
+          request->error_status = SNMP_ERR_TOOBIG;
+        } else {
+          request->error_status = SNMP_ERR_GENERROR;
+        }
+      }
+    } else {
+      /* according to RFC 1157/1905, all other errors only return genError */
+      request->error_status = SNMP_ERR_GENERROR;
+    }
+  } else {
+    s16_t len = node_instance.get_value(&node_instance, vb->value);
+
+    if (len >= 0) {
+      vb->value_len = (u16_t)len; /* cast is OK because we checked >= 0 above */
+      vb->type = node_instance.asn1_type;
+
+      LWIP_ASSERT("SNMP_MAX_VALUE_SIZE is configured too low", (vb->value_len & ~SNMP_GET_VALUE_RAW_DATA) <= SNMP_MAX_VALUE_SIZE);
+      err = snmp_append_outbound_varbind(&request->outbound_pbuf_stream, vb);
+
+      if (err == ERR_BUF) {
+        request->error_status = SNMP_ERR_TOOBIG;
+      } else if (err != ERR_OK) {
+        request->error_status = SNMP_ERR_GENERROR;
+      }
+    } else {
+      request->error_status = SNMP_ERR_GENERROR;
+    }
+
+    if (node_instance.release_instance != NULL) {
+      node_instance.release_instance(&node_instance);
+    }
+  }
+}
+
+
+/**
+ * Service an internal or external event for SNMP GET.
+ *
+ * @param request points to the associated message process state
+ */
+static err_t
+snmp_process_get_request(struct snmp_request *request)
+{
+  snmp_vb_enumerator_err_t err;
+  struct snmp_varbind vb;
+  vb.value = request->value_buffer;
+
+  LWIP_DEBUGF(SNMP_DEBUG, ("SNMP get request\n"));
+
+  while (request->error_status == SNMP_ERR_NOERROR) {
+    err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
+    if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
+      if ((vb.type == SNMP_ASN1_TYPE_NULL) && (vb.value_len == 0)) {
+        snmp_process_varbind(request, &vb, 0);
+      } else {
+        request->error_status = SNMP_ERR_GENERROR;
+      }
+    } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
+      /* no more varbinds in request */
+      break;
+    } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) {
+      /* malformed ASN.1, don't answer */
+      return ERR_ARG;
+    } else {
+      request->error_status = SNMP_ERR_GENERROR;
+    }
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Service an internal or external event for SNMP GET.
+ *
+ * @param request points to the associated message process state
+ */
+static err_t
+snmp_process_getnext_request(struct snmp_request *request)
+{
+  snmp_vb_enumerator_err_t err;
+  struct snmp_varbind vb;
+  vb.value = request->value_buffer;
+
+  LWIP_DEBUGF(SNMP_DEBUG, ("SNMP get-next request\n"));
+
+  while (request->error_status == SNMP_ERR_NOERROR) {
+    err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
+    if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
+      if ((vb.type == SNMP_ASN1_TYPE_NULL) && (vb.value_len == 0)) {
+        snmp_process_varbind(request, &vb, 1);
+      } else {
+        request->error_status = SNMP_ERR_GENERROR;
+      }
+    } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
+      /* no more varbinds in request */
+      break;
+    } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) {
+      /* malformed ASN.1, don't answer */
+      return ERR_ARG;
+    } else {
+      request->error_status = SNMP_ERR_GENERROR;
+    }
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Service an internal or external event for SNMP GETBULKT.
+ *
+ * @param request points to the associated message process state
+ */
+static err_t
+snmp_process_getbulk_request(struct snmp_request *request)
+{
+  snmp_vb_enumerator_err_t err;
+  s32_t non_repeaters     = request->non_repeaters;
+  s32_t repetitions;
+  u16_t repetition_offset = 0;
+  struct snmp_varbind_enumerator repetition_varbind_enumerator;
+  struct snmp_varbind vb;
+  vb.value = request->value_buffer;
+
+  if (SNMP_LWIP_GETBULK_MAX_REPETITIONS > 0) {
+    repetitions = LWIP_MIN(request->max_repetitions, SNMP_LWIP_GETBULK_MAX_REPETITIONS);
+  } else {
+    repetitions = request->max_repetitions;
+  }
+
+  LWIP_DEBUGF(SNMP_DEBUG, ("SNMP get-bulk request\n"));
+
+  /* process non repeaters and first repetition */
+  while (request->error_status == SNMP_ERR_NOERROR) {
+    if (non_repeaters == 0) {
+      repetition_offset = request->outbound_pbuf_stream.offset;
+
+      if (repetitions == 0) {
+        /* do not resolve repeaters when repetitions is set to 0 */
+        break;
+      }
+      repetitions--;
+    }
+
+    err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
+    if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
+      /* no more varbinds in request */
+      break;
+    } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) {
+      /* malformed ASN.1, don't answer */
+      return ERR_ARG;
+    } else if ((err != SNMP_VB_ENUMERATOR_ERR_OK) || (vb.type != SNMP_ASN1_TYPE_NULL) || (vb.value_len != 0)) {
+      request->error_status = SNMP_ERR_GENERROR;
+    } else {
+      snmp_process_varbind(request, &vb, 1);
+      non_repeaters--;
+    }
+  }
+
+  /* process repetitions > 1 */
+  while ((request->error_status == SNMP_ERR_NOERROR) && (repetitions > 0) && (request->outbound_pbuf_stream.offset != repetition_offset)) {
+
+    u8_t all_endofmibview = 1;
+
+    snmp_vb_enumerator_init(&repetition_varbind_enumerator, request->outbound_pbuf, repetition_offset, request->outbound_pbuf_stream.offset - repetition_offset);
+    repetition_offset = request->outbound_pbuf_stream.offset; /* for next loop */
+
+    while (request->error_status == SNMP_ERR_NOERROR) {
+      vb.value = NULL; /* do NOT decode value (we enumerate outbound buffer here, so all varbinds have values assigned) */
+      err = snmp_vb_enumerator_get_next(&repetition_varbind_enumerator, &vb);
+      if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
+        vb.value = request->value_buffer;
+        snmp_process_varbind(request, &vb, 1);
+
+        if (request->error_status != SNMP_ERR_NOERROR) {
+          /* already set correct error-index (here it cannot be taken from inbound varbind enumerator) */
+          request->error_index = request->non_repeaters + repetition_varbind_enumerator.varbind_count;
+        } else if (vb.type != (SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW)) {
+          all_endofmibview = 0;
+        }
+      } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
+        /* no more varbinds in request */
+        break;
+      } else {
+        LWIP_DEBUGF(SNMP_DEBUG, ("Very strange, we cannot parse the varbind output that we created just before!"));
+        request->error_status = SNMP_ERR_GENERROR;
+        request->error_index  = request->non_repeaters + repetition_varbind_enumerator.varbind_count;
+      }
+    }
+
+    if ((request->error_status == SNMP_ERR_NOERROR) && all_endofmibview) {
+      /* stop when all varbinds in a loop return EndOfMibView */
+      break;
+    }
+
+    repetitions--;
+  }
+
+  if (request->error_status == SNMP_ERR_TOOBIG) {
+    /* for GetBulk it is ok, if not all requested variables fit into the response -> just return the varbinds added so far */
+    request->error_status = SNMP_ERR_NOERROR;
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Service an internal or external event for SNMP SET.
+ *
+ * @param request points to the associated message process state
+ */
+static err_t
+snmp_process_set_request(struct snmp_request *request)
+{
+  snmp_vb_enumerator_err_t err;
+  struct snmp_varbind vb;
+  vb.value = request->value_buffer;
+
+  LWIP_DEBUGF(SNMP_DEBUG, ("SNMP set request\n"));
+
+  /* perform set test on all objects */
+  while (request->error_status == SNMP_ERR_NOERROR) {
+    err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
+    if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
+      struct snmp_node_instance node_instance;
+      memset(&node_instance, 0, sizeof(node_instance));
+
+      request->error_status = snmp_get_node_instance_from_oid(vb.oid.id, vb.oid.len, &node_instance);
+      if (request->error_status == SNMP_ERR_NOERROR) {
+        if (node_instance.asn1_type != vb.type) {
+          request->error_status = SNMP_ERR_WRONGTYPE;
+        } else if (((node_instance.access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != SNMP_NODE_INSTANCE_ACCESS_WRITE) || (node_instance.set_value == NULL)) {
+          request->error_status = SNMP_ERR_NOTWRITABLE;
+        } else {
+          if (node_instance.set_test != NULL) {
+            request->error_status = node_instance.set_test(&node_instance, vb.value_len, vb.value);
+          }
+        }
+
+        if (node_instance.release_instance != NULL) {
+          node_instance.release_instance(&node_instance);
+        }
+      }
+    } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
+      /* no more varbinds in request */
+      break;
+    } else if (err == SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH) {
+      request->error_status = SNMP_ERR_WRONGLENGTH;
+    } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) {
+      /* malformed ASN.1, don't answer */
+      return ERR_ARG;
+    } else {
+      request->error_status = SNMP_ERR_GENERROR;
+    }
+  }
+
+  /* perform real set operation on all objects */
+  if (request->error_status == SNMP_ERR_NOERROR) {
+    snmp_vb_enumerator_init(&request->inbound_varbind_enumerator, request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len);
+    while (request->error_status == SNMP_ERR_NOERROR) {
+      err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
+      if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
+        struct snmp_node_instance node_instance;
+        memset(&node_instance, 0, sizeof(node_instance));
+        request->error_status = snmp_get_node_instance_from_oid(vb.oid.id, vb.oid.len, &node_instance);
+        if (request->error_status == SNMP_ERR_NOERROR) {
+          if (node_instance.set_value(&node_instance, vb.value_len, vb.value) != SNMP_ERR_NOERROR) {
+            if (request->inbound_varbind_enumerator.varbind_count == 1) {
+              request->error_status = SNMP_ERR_COMMITFAILED;
+            } else {
+              /* we cannot undo the set operations done so far */
+              request->error_status = SNMP_ERR_UNDOFAILED;
+            }
+          }
+
+          if (node_instance.release_instance != NULL) {
+            node_instance.release_instance(&node_instance);
+          }
+        }
+      } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
+        /* no more varbinds in request */
+        break;
+      } else {
+        /* first time enumerating varbinds work but second time not, although nothing should have changed in between ??? */
+        request->error_status = SNMP_ERR_GENERROR;
+      }
+    }
+  }
+
+  return ERR_OK;
+}
+
+#define PARSE_EXEC(code, retValue) \
+  if ((code) != ERR_OK) { \
+    LWIP_DEBUGF(SNMP_DEBUG, ("Malformed ASN.1 detected.\n")); \
+    snmp_stats.inasnparseerrs++; \
+    return retValue; \
+  }
+
+#define PARSE_ASSERT(cond, retValue) \
+  if (!(cond)) { \
+    LWIP_DEBUGF(SNMP_DEBUG, ("SNMP parse assertion failed!: " # cond)); \
+    snmp_stats.inasnparseerrs++; \
+    return retValue; \
+  }
+
+#define BUILD_EXEC(code, retValue) \
+  if ((code) != ERR_OK) { \
+    LWIP_DEBUGF(SNMP_DEBUG, ("SNMP error during creation of outbound frame!: " # code)); \
+    return retValue; \
+  }
+
+#define IF_PARSE_EXEC(code)   PARSE_EXEC(code, ERR_ARG)
+#define IF_PARSE_ASSERT(code) PARSE_ASSERT(code, ERR_ARG)
+
+/**
+ * Checks and decodes incoming SNMP message header, logs header errors.
+ *
+ * @param request points to the current message request state return
+ * @return
+ * - ERR_OK SNMP header is sane and accepted
+ * - ERR_VAL SNMP header is either malformed or rejected
+ */
+static err_t
+snmp_parse_inbound_frame(struct snmp_request *request)
+{
+  struct snmp_pbuf_stream pbuf_stream;
+  struct snmp_asn1_tlv tlv;
+  s32_t parent_tlv_value_len;
+  s32_t s32_value;
+  err_t err;
+#if LWIP_SNMP_V3
+  snmpv3_auth_algo_t auth;
+  snmpv3_priv_algo_t priv;
+#endif
+
+  IF_PARSE_EXEC(snmp_pbuf_stream_init(&pbuf_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
+
+  /* decode main container consisting of version, community and PDU */
+  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+  IF_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len == pbuf_stream.length));
+  parent_tlv_value_len = tlv.value_len;
+
+  /* decode version */
+  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+  IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+  parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+  IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+  IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
+
+  if (((s32_value != SNMP_VERSION_1) &&
+       (s32_value != SNMP_VERSION_2c)
+#if LWIP_SNMP_V3
+       && (s32_value != SNMP_VERSION_3)
+#endif
+      )
+#if LWIP_SNMP_CONFIGURE_VERSIONS
+      || (!snmp_version_enabled(s32_value))
+#endif
+     ) {
+    /* unsupported SNMP version */
+    snmp_stats.inbadversions++;
+    return ERR_ARG;
+  }
+  request->version = (u8_t)s32_value;
+
+#if LWIP_SNMP_V3
+  if (request->version == SNMP_VERSION_3) {
+    u16_t u16_value;
+    u16_t inbound_msgAuthenticationParameters_offset;
+
+    /* SNMPv3 doesn't use communities */
+    /* @todo: Differentiate read/write access */
+    strncpy((char *)request->community, snmp_community, SNMP_MAX_COMMUNITY_STR_LEN);
+    request->community[SNMP_MAX_COMMUNITY_STR_LEN] = 0; /* ensure NULL termination (strncpy does NOT guarantee it!) */
+    request->community_strlen = (u16_t)strnlen((char *)request->community, SNMP_MAX_COMMUNITY_STR_LEN);
+
+    /* RFC3414 globalData */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_SEQUENCE);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    /* decode msgID */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
+    request->msg_id = s32_value;
+
+    /* decode msgMaxSize */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
+    request->msg_max_size = s32_value;
+
+    /* decode msgFlags */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
+    request->msg_flags = (u8_t)s32_value;
+
+    /* decode msgSecurityModel */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
+    request->msg_security_model = s32_value;
+
+    /* RFC3414 msgSecurityParameters
+     * The User-based Security Model defines the contents of the OCTET
+     * STRING as a SEQUENCE.
+     *
+     * We skip the protective dummy OCTET STRING header
+     * to access the SEQUENCE header.
+     */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    /* msgSecurityParameters SEQUENCE header */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_SEQUENCE);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    /* decode msgAuthoritativeEngineID */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_authoritative_engine_id,
+                                    &u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH));
+    request->msg_authoritative_engine_id_len = (u8_t)u16_value;
+
+    /* msgAuthoritativeEngineBoots */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->msg_authoritative_engine_boots));
+
+    /* msgAuthoritativeEngineTime */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->msg_authoritative_engine_time));
+
+    /* msgUserName */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_user_name,
+                                    &u16_value, SNMP_V3_MAX_USER_LENGTH));
+    request->msg_user_name_len = (u8_t)u16_value;
+
+    /* msgAuthenticationParameters */
+    memset(request->msg_authentication_parameters, 0, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+    /* Remember position */
+    inbound_msgAuthenticationParameters_offset = pbuf_stream.offset;
+    LWIP_UNUSED_ARG(inbound_msgAuthenticationParameters_offset);
+    /* Read auth parameters */
+    /* IF_PARSE_ASSERT(tlv.value_len <= SNMP_V3_MAX_AUTH_PARAM_LENGTH); */
+    IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_authentication_parameters,
+                                    &u16_value, tlv.value_len));
+    request->msg_authentication_parameters_len = (u8_t)u16_value;
+
+    /* msgPrivacyParameters */
+    memset(request->msg_privacy_parameters, 0, SNMP_V3_MAX_PRIV_PARAM_LENGTH);
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_privacy_parameters,
+                                    &u16_value, SNMP_V3_MAX_PRIV_PARAM_LENGTH));
+    request->msg_privacy_parameters_len = (u8_t)u16_value;
+
+    /* validate securityParameters here (do this after decoding because we don't want to increase other counters for wrong frames)
+     * 1) securityParameters was correctly serialized if we reach here.
+     * 2) securityParameters are already cached.
+     * 3) if msgAuthoritativeEngineID is unknown, zero-length or too long:
+         b) https://tools.ietf.org/html/rfc3414#section-7
+     */
+    {
+      const char *eid;
+      u8_t eid_len;
+
+      snmpv3_get_engine_id(&eid, &eid_len);
+
+      if ((request->msg_authoritative_engine_id_len == 0) ||
+          (request->msg_authoritative_engine_id_len != eid_len) ||
+          (memcmp(eid, request->msg_authoritative_engine_id, eid_len) != 0)) {
+        snmp_stats.unknownengineids++;
+        request->msg_flags = 0; /* noauthnopriv */
+        request->error_status = SNMP_ERR_UNKNOWN_ENGINEID;
+        return ERR_OK;
+      }
+    }
+
+    /* 4) verify username */
+    if (snmpv3_get_user((char *)request->msg_user_name, &auth, NULL, &priv, NULL)) {
+      snmp_stats.unknownusernames++;
+      request->msg_flags = 0; /* noauthnopriv */
+      request->error_status = SNMP_ERR_UNKNOWN_SECURITYNAME;
+      return ERR_OK;
+    }
+
+    /* 5) verify security level */
+    switch (request->msg_flags & (SNMP_V3_AUTH_FLAG | SNMP_V3_PRIV_FLAG)) {
+      case SNMP_V3_NOAUTHNOPRIV:
+        if ((auth != SNMP_V3_AUTH_ALGO_INVAL) || (priv != SNMP_V3_PRIV_ALGO_INVAL)) {
+          /* Invalid security level for user */
+          snmp_stats.unsupportedseclevels++;
+          request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
+          request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
+          return ERR_OK;
+        }
+        break;
+#if LWIP_SNMP_V3_CRYPTO
+      case SNMP_V3_AUTHNOPRIV:
+        if ((auth == SNMP_V3_AUTH_ALGO_INVAL) || (priv != SNMP_V3_PRIV_ALGO_INVAL)) {
+          /* Invalid security level for user */
+          snmp_stats.unsupportedseclevels++;
+          request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
+          request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
+          return ERR_OK;
+        }
+        break;
+      case SNMP_V3_AUTHPRIV:
+        if ((auth == SNMP_V3_AUTH_ALGO_INVAL) || (priv == SNMP_V3_PRIV_ALGO_INVAL)) {
+          /* Invalid security level for user */
+          snmp_stats.unsupportedseclevels++;
+          request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
+          request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
+          return ERR_OK;
+        }
+        break;
+#endif
+      default:
+        snmp_stats.unsupportedseclevels++;
+        request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
+        request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
+        return ERR_OK;
+    }
+
+    /* 6) if securitylevel specifies authentication, authenticate message. */
+#if LWIP_SNMP_V3_CRYPTO
+    if (request->msg_flags & SNMP_V3_AUTH_FLAG) {
+      const u8_t zero_arr[SNMP_V3_MAX_AUTH_PARAM_LENGTH] = { 0 };
+      u8_t key[20];
+      u8_t hmac[LWIP_MAX(SNMP_V3_SHA_LEN, SNMP_V3_MD5_LEN)];
+      struct snmp_pbuf_stream auth_stream;
+
+      if (request->msg_authentication_parameters_len > SNMP_V3_MAX_AUTH_PARAM_LENGTH) {
+        snmp_stats.wrongdigests++;
+        request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
+        request->error_status = SNMP_ERR_AUTHORIZATIONERROR;
+        return ERR_OK;
+      }
+
+      /* Rewind stream */
+      IF_PARSE_EXEC(snmp_pbuf_stream_init(&auth_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
+      IF_PARSE_EXEC(snmp_pbuf_stream_seek_abs(&auth_stream, inbound_msgAuthenticationParameters_offset));
+      /* Set auth parameters to zero for verification */
+      IF_PARSE_EXEC(snmp_asn1_enc_raw(&auth_stream, zero_arr, request->msg_authentication_parameters_len));
+
+      /* Verify authentication */
+      IF_PARSE_EXEC(snmp_pbuf_stream_init(&auth_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
+
+      IF_PARSE_EXEC(snmpv3_get_user((char *)request->msg_user_name, &auth, key, NULL, NULL));
+      IF_PARSE_EXEC(snmpv3_auth(&auth_stream, request->inbound_pbuf->tot_len, key, auth, hmac));
+
+      if (memcmp(request->msg_authentication_parameters, hmac, SNMP_V3_MAX_AUTH_PARAM_LENGTH)) {
+        snmp_stats.wrongdigests++;
+        request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
+        request->error_status = SNMP_ERR_AUTHORIZATIONERROR;
+        return ERR_OK;
+      }
+
+      /* 7) if securitylevel specifies authentication, verify engineboots, enginetime and lastenginetime */
+      {
+        s32_t boots = snmpv3_get_engine_boots_internal();
+        if ((request->msg_authoritative_engine_boots != boots) || (boots == 2147483647UL)) {
+          snmp_stats.notintimewindows++;
+          request->msg_flags = SNMP_V3_AUTHNOPRIV;
+          request->error_status = SNMP_ERR_NOTINTIMEWINDOW;
+          return ERR_OK;
+        }
+      }
+      {
+        s32_t time = snmpv3_get_engine_time_internal();
+        if (request->msg_authoritative_engine_time > (time + 150)) {
+          snmp_stats.notintimewindows++;
+          request->msg_flags = SNMP_V3_AUTHNOPRIV;
+          request->error_status = SNMP_ERR_NOTINTIMEWINDOW;
+          return ERR_OK;
+        } else if (time > 150) {
+          if (request->msg_authoritative_engine_time < (time - 150)) {
+            snmp_stats.notintimewindows++;
+            request->msg_flags = SNMP_V3_AUTHNOPRIV;
+            request->error_status = SNMP_ERR_NOTINTIMEWINDOW;
+            return ERR_OK;
+          }
+        }
+      }
+    }
+#endif
+
+    /* 8) if securitylevel specifies privacy, decrypt message. */
+#if LWIP_SNMP_V3_CRYPTO
+    if (request->msg_flags & SNMP_V3_PRIV_FLAG) {
+      /* Decrypt message */
+
+      u8_t key[20];
+
+      IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+      IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+      parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
+      IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+      IF_PARSE_EXEC(snmpv3_get_user((char *)request->msg_user_name, NULL, NULL, &priv, key));
+      if (snmpv3_crypt(&pbuf_stream, tlv.value_len, key,
+                       request->msg_privacy_parameters, request->msg_authoritative_engine_boots,
+                       request->msg_authoritative_engine_time, priv, SNMP_V3_PRIV_MODE_DECRYPT) != ERR_OK) {
+        snmp_stats.decryptionerrors++;
+        request->msg_flags = SNMP_V3_AUTHNOPRIV;
+        request->error_status = SNMP_ERR_DECRYIPTION_ERROR;
+        return ERR_OK;
+      }
+    }
+#endif
+    /* 9) calculate max size of scoped pdu?
+     * 10) securityname for user is retrieved from usertable?
+     * 11) security data is cached?
+     * 12)
+     */
+
+    /* Scoped PDU
+     * Encryption context
+     */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_SEQUENCE);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    /* contextEngineID */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->context_engine_id,
+                                    &u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH));
+    request->context_engine_id_len = (u8_t)u16_value;
+    /* TODO: do we need to verify this contextengineid too? */
+
+    /* contextName */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->context_name,
+                                    &u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH));
+    request->context_name_len = (u8_t)u16_value;
+    /* TODO: do we need to verify this contextname too? */
+  } else
+#endif
+  {
+    /* decode community */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    err = snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->community, &request->community_strlen, SNMP_MAX_COMMUNITY_STR_LEN);
+    if (err == ERR_MEM) {
+      /* community string does not fit in our buffer -> its too long -> its invalid */
+      request->community_strlen = 0;
+      snmp_pbuf_stream_seek(&pbuf_stream, tlv.value_len);
+    } else {
+      IF_PARSE_ASSERT(err == ERR_OK);
+    }
+    /* add zero terminator */
+    request->community[request->community_strlen] = 0;
+  }
+
+  /* decode PDU type (next container level) */
+  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+  IF_PARSE_ASSERT(tlv.value_len <= pbuf_stream.length);
+  request->inbound_padding_len = pbuf_stream.length - tlv.value_len;
+  parent_tlv_value_len = tlv.value_len;
+
+  /* validate PDU type */
+  switch (tlv.type) {
+    case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_REQ):
+      /* GetRequest PDU */
+      snmp_stats.ingetrequests++;
+      break;
+    case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ):
+      /* GetNextRequest PDU */
+      snmp_stats.ingetnexts++;
+      break;
+    case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ):
+      /* GetBulkRequest PDU */
+      if (request->version < SNMP_VERSION_2c) {
+        /* RFC2089: invalid, drop packet */
+        return ERR_ARG;
+      }
+      break;
+    case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_SET_REQ):
+      /* SetRequest PDU */
+      snmp_stats.insetrequests++;
+      break;
+    default:
+      /* unsupported input PDU for this agent (no parse error) */
+      LWIP_DEBUGF(SNMP_DEBUG, ("Unknown/Invalid SNMP PDU type received: %d", tlv.type)); \
+      return ERR_ARG;
+  }
+  request->request_type = tlv.type & SNMP_ASN1_DATATYPE_MASK;
+  request->request_out_type = (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_RESP);
+
+  /* validate community (do this after decoding PDU type because we don't want to increase 'inbadcommunitynames' for wrong frame types */
+  if (request->community_strlen == 0) {
+    /* community string was too long or really empty*/
+    snmp_stats.inbadcommunitynames++;
+    snmp_authfail_trap();
+    return ERR_ARG;
+  } else if (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) {
+    if (snmp_community_write[0] == 0) {
+      /* our write community is empty, that means all our objects are readonly */
+      request->error_status = SNMP_ERR_NOTWRITABLE;
+      request->error_index  = 1;
+    } else if (strncmp(snmp_community_write, (const char *)request->community, SNMP_MAX_COMMUNITY_STR_LEN) != 0) {
+      /* community name does not match */
+      snmp_stats.inbadcommunitynames++;
+      snmp_authfail_trap();
+      return ERR_ARG;
+    }
+  } else {
+    if (strncmp(snmp_community, (const char *)request->community, SNMP_MAX_COMMUNITY_STR_LEN) != 0) {
+      /* community name does not match */
+      snmp_stats.inbadcommunitynames++;
+      snmp_authfail_trap();
+      return ERR_ARG;
+    }
+  }
+
+  /* decode request ID */
+  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+  IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+  parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+  IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+  IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->request_id));
+
+  /* decode error status / non-repeaters */
+  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+  IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+  parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+  IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+  if (request->request_type == SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ) {
+    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->non_repeaters));
+    if (request->non_repeaters < 0) {
+      /* RFC 1905, 4.2.3 */
+      request->non_repeaters = 0;
+    }
+  } else {
+    /* only check valid value, don't touch 'request->error_status', maybe a response error status was already set to above; */
+    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
+    IF_PARSE_ASSERT(s32_value == SNMP_ERR_NOERROR);
+  }
+
+  /* decode error index / max-repetitions */
+  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+  IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+  parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+  IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+  if (request->request_type == SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ) {
+    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->max_repetitions));
+    if (request->max_repetitions < 0) {
+      /* RFC 1905, 4.2.3 */
+      request->max_repetitions = 0;
+    }
+  } else {
+    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->error_index));
+    IF_PARSE_ASSERT(s32_value == 0);
+  }
+
+  /* decode varbind-list type (next container level) */
+  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+  IF_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len <= pbuf_stream.length));
+
+  request->inbound_varbind_offset = pbuf_stream.offset;
+  request->inbound_varbind_len    = pbuf_stream.length - request->inbound_padding_len;
+  snmp_vb_enumerator_init(&(request->inbound_varbind_enumerator), request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len);
+
+  return ERR_OK;
+}
+
+#define OF_BUILD_EXEC(code) BUILD_EXEC(code, ERR_ARG)
+
+static err_t
+snmp_prepare_outbound_frame(struct snmp_request *request)
+{
+  struct snmp_asn1_tlv tlv;
+  struct snmp_pbuf_stream *pbuf_stream = &(request->outbound_pbuf_stream);
+
+  /* try allocating pbuf(s) for maximum response size */
+  request->outbound_pbuf = pbuf_alloc(PBUF_TRANSPORT, 1472, PBUF_RAM);
+  if (request->outbound_pbuf == NULL) {
+    return ERR_MEM;
+  }
+
+  snmp_pbuf_stream_init(pbuf_stream, request->outbound_pbuf, 0, request->outbound_pbuf->tot_len);
+
+  /* 'Message' sequence */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, 0);
+  OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+
+  /* version */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+  snmp_asn1_enc_s32t_cnt(request->version, &tlv.value_len);
+  OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+  OF_BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->version) );
+
+#if LWIP_SNMP_V3
+  if (request->version < SNMP_VERSION_3) {
+#endif
+    /* community */
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->community_strlen);
+    OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+    OF_BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, request->community, request->community_strlen) );
+#if LWIP_SNMP_V3
+  } else {
+    const char *id;
+
+    /* globalData */
+    request->outbound_msg_global_data_offset = pbuf_stream->offset;
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, 0);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+
+    /* msgID */
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
+    snmp_asn1_enc_s32t_cnt(request->msg_id, &tlv.value_len);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+    OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_id));
+
+    /* msgMaxSize */
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
+    snmp_asn1_enc_s32t_cnt(request->msg_max_size, &tlv.value_len);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+    OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_max_size));
+
+    /* msgFlags */
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, 1);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+    OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, &request->msg_flags, 1));
+
+    /* msgSecurityModel */
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
+    snmp_asn1_enc_s32t_cnt(request->msg_security_model, &tlv.value_len);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+    OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_security_model));
+
+    /* end of msgGlobalData */
+    request->outbound_msg_global_data_end = pbuf_stream->offset;
+
+    /* msgSecurityParameters */
+    request->outbound_msg_security_parameters_str_offset = pbuf_stream->offset;
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, 0);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+
+    request->outbound_msg_security_parameters_seq_offset = pbuf_stream->offset;
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, 0);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+
+    /* msgAuthoritativeEngineID */
+    snmpv3_get_engine_id(&id, &request->msg_authoritative_engine_id_len);
+    MEMCPY(request->msg_authoritative_engine_id, id, request->msg_authoritative_engine_id_len);
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->msg_authoritative_engine_id_len);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+    OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_authoritative_engine_id, request->msg_authoritative_engine_id_len));
+
+    request->msg_authoritative_engine_time = snmpv3_get_engine_time();
+    request->msg_authoritative_engine_boots = snmpv3_get_engine_boots();
+
+    /* msgAuthoritativeEngineBoots */
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+    snmp_asn1_enc_s32t_cnt(request->msg_authoritative_engine_boots, &tlv.value_len);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+    OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_authoritative_engine_boots));
+
+    /* msgAuthoritativeEngineTime */
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+    snmp_asn1_enc_s32t_cnt(request->msg_authoritative_engine_time, &tlv.value_len);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+    OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_authoritative_engine_time));
+
+    /* msgUserName */
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->msg_user_name_len);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+    OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_user_name, request->msg_user_name_len));
+
+#if LWIP_SNMP_V3_CRYPTO
+    /* msgAuthenticationParameters */
+    if (request->msg_flags & SNMP_V3_AUTH_FLAG) {
+      memset(request->msg_authentication_parameters, 0, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
+      request->outbound_msg_authentication_parameters_offset = pbuf_stream->offset;
+      SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
+      OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+      OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_authentication_parameters, SNMP_V3_MAX_AUTH_PARAM_LENGTH));
+    } else
+#endif
+    {
+      SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, 0);
+      OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+    }
+
+#if LWIP_SNMP_V3_CRYPTO
+    /* msgPrivacyParameters */
+    if (request->msg_flags & SNMP_V3_PRIV_FLAG) {
+      snmpv3_build_priv_param(request->msg_privacy_parameters);
+
+      SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, SNMP_V3_MAX_PRIV_PARAM_LENGTH);
+      OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+      OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_privacy_parameters, SNMP_V3_MAX_PRIV_PARAM_LENGTH));
+    } else
+#endif
+    {
+      SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, 0);
+      OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+    }
+
+    /* End of msgSecurityParameters, so we can calculate the length of this sequence later */
+    request->outbound_msg_security_parameters_end = pbuf_stream->offset;
+
+#if LWIP_SNMP_V3_CRYPTO
+    /* For encryption we have to encapsulate the payload in an octet string */
+    if (request->msg_flags & SNMP_V3_PRIV_FLAG) {
+      request->outbound_scoped_pdu_string_offset = pbuf_stream->offset;
+      SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 3, 0);
+      OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+    }
+#endif
+    /* Scoped PDU
+     * Encryption context
+     */
+    request->outbound_scoped_pdu_seq_offset = pbuf_stream->offset;
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, 0);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+
+    /* contextEngineID */
+    snmpv3_get_engine_id(&id, &request->context_engine_id_len);
+    MEMCPY(request->context_engine_id, id, request->context_engine_id_len);
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->context_engine_id_len);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+    OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->context_engine_id, request->context_engine_id_len));
+
+    /* contextName */
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->context_name_len);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+    OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->context_name, request->context_name_len));
+  }
+#endif
+
+  /* 'PDU' sequence */
+  request->outbound_pdu_offset = pbuf_stream->offset;
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, request->request_out_type, 3, 0);
+  OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+
+  /* request ID */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+  snmp_asn1_enc_s32t_cnt(request->request_id, &tlv.value_len);
+  OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+  OF_BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->request_id) );
+
+  /* error status */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
+  OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+  request->outbound_error_status_offset = pbuf_stream->offset;
+  OF_BUILD_EXEC( snmp_pbuf_stream_write(pbuf_stream, 0) );
+
+  /* error index */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
+  OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+  request->outbound_error_index_offset = pbuf_stream->offset;
+  OF_BUILD_EXEC( snmp_pbuf_stream_write(pbuf_stream, 0) );
+
+  /* 'VarBindList' sequence */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, 0);
+  OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+
+  request->outbound_varbind_offset = pbuf_stream->offset;
+
+  return ERR_OK;
+}
+
+/** Calculate the length of a varbind list */
+err_t
+snmp_varbind_length(struct snmp_varbind *varbind, struct snmp_varbind_len *len)
+{
+  /* calculate required lengths */
+  snmp_asn1_enc_oid_cnt(varbind->oid.id, varbind->oid.len, &len->oid_value_len);
+  snmp_asn1_enc_length_cnt(len->oid_value_len, &len->oid_len_len);
+
+  if (varbind->value_len == 0) {
+    len->value_value_len = 0;
+  } else if (varbind->value_len & SNMP_GET_VALUE_RAW_DATA) {
+    len->value_value_len = varbind->value_len & (~SNMP_GET_VALUE_RAW_DATA);
+  } else {
+    switch (varbind->type) {
+      case SNMP_ASN1_TYPE_INTEGER:
+        if (varbind->value_len != sizeof (s32_t)) {
+          return ERR_VAL;
+        }
+        snmp_asn1_enc_s32t_cnt(*((s32_t *) varbind->value), &len->value_value_len);
+        break;
+      case SNMP_ASN1_TYPE_COUNTER:
+      case SNMP_ASN1_TYPE_GAUGE:
+      case SNMP_ASN1_TYPE_TIMETICKS:
+        if (varbind->value_len != sizeof (u32_t)) {
+          return ERR_VAL;
+        }
+        snmp_asn1_enc_u32t_cnt(*((u32_t *) varbind->value), &len->value_value_len);
+        break;
+      case SNMP_ASN1_TYPE_OCTET_STRING:
+      case SNMP_ASN1_TYPE_IPADDR:
+      case SNMP_ASN1_TYPE_OPAQUE:
+        len->value_value_len = varbind->value_len;
+        break;
+      case SNMP_ASN1_TYPE_NULL:
+        if (varbind->value_len != 0) {
+          return ERR_VAL;
+        }
+        len->value_value_len = 0;
+        break;
+      case SNMP_ASN1_TYPE_OBJECT_ID:
+        if ((varbind->value_len & 0x03) != 0) {
+          return ERR_VAL;
+        }
+        snmp_asn1_enc_oid_cnt((u32_t *) varbind->value, varbind->value_len >> 2, &len->value_value_len);
+        break;
+#if LWIP_HAVE_INT64
+      case SNMP_ASN1_TYPE_COUNTER64:
+        if (varbind->value_len != sizeof(u64_t)) {
+          return ERR_VAL;
+        }
+        snmp_asn1_enc_u64t_cnt(*(u64_t *)varbind->value, &len->value_value_len);
+        break;
+#endif
+      default:
+        /* unsupported type */
+        return ERR_VAL;
+    }
+  }
+  snmp_asn1_enc_length_cnt(len->value_value_len, &len->value_len_len);
+
+  len->vb_value_len = 1 + len->oid_len_len + len->oid_value_len + 1 + len->value_len_len + len->value_value_len;
+  snmp_asn1_enc_length_cnt(len->vb_value_len, &len->vb_len_len);
+
+  return ERR_OK;
+}
+
+#define OVB_BUILD_EXEC(code) BUILD_EXEC(code, ERR_ARG)
+
+err_t
+snmp_append_outbound_varbind(struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbind)
+{
+  struct snmp_asn1_tlv tlv;
+  struct snmp_varbind_len len;
+  err_t err;
+
+  err = snmp_varbind_length(varbind, &len);
+
+  if (err != ERR_OK) {
+    return err;
+  }
+
+  /* check length already before adding first data because in case of GetBulk,
+   *  data added so far is returned and therefore no partial data shall be added
+   */
+  if ((1 + len.vb_len_len + len.vb_value_len) > pbuf_stream->length) {
+    return ERR_BUF;
+  }
+
+  /* 'VarBind' sequence */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, len.vb_len_len, len.vb_value_len);
+  OVB_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+
+  /* VarBind OID */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OBJECT_ID, len.oid_len_len, len.oid_value_len);
+  OVB_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+  OVB_BUILD_EXEC(snmp_asn1_enc_oid(pbuf_stream, varbind->oid.id, varbind->oid.len));
+
+  /* VarBind value */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, varbind->type, len.value_len_len, len.value_value_len);
+  OVB_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+
+  if (len.value_value_len > 0) {
+    if (varbind->value_len & SNMP_GET_VALUE_RAW_DATA) {
+      OVB_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, (u8_t *) varbind->value, len.value_value_len));
+    } else {
+      switch (varbind->type) {
+        case SNMP_ASN1_TYPE_INTEGER:
+          OVB_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, len.value_value_len, *((s32_t *) varbind->value)));
+          break;
+        case SNMP_ASN1_TYPE_COUNTER:
+        case SNMP_ASN1_TYPE_GAUGE:
+        case SNMP_ASN1_TYPE_TIMETICKS:
+          OVB_BUILD_EXEC(snmp_asn1_enc_u32t(pbuf_stream, len.value_value_len, *((u32_t *) varbind->value)));
+          break;
+        case SNMP_ASN1_TYPE_OCTET_STRING:
+        case SNMP_ASN1_TYPE_IPADDR:
+        case SNMP_ASN1_TYPE_OPAQUE:
+          OVB_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, (u8_t *) varbind->value, len.value_value_len));
+          len.value_value_len = varbind->value_len;
+          break;
+        case SNMP_ASN1_TYPE_OBJECT_ID:
+          OVB_BUILD_EXEC(snmp_asn1_enc_oid(pbuf_stream, (u32_t *) varbind->value, varbind->value_len / sizeof (u32_t)));
+          break;
+#if LWIP_HAVE_INT64
+        case SNMP_ASN1_TYPE_COUNTER64:
+          OVB_BUILD_EXEC(snmp_asn1_enc_u64t(pbuf_stream, len.value_value_len, *(u64_t *) varbind->value));
+          break;
+#endif
+        default:
+          LWIP_ASSERT("Unknown variable type", 0);
+          break;
+      }
+    }
+  }
+
+  return ERR_OK;
+}
+
+static err_t
+snmp_complete_outbound_frame(struct snmp_request *request)
+{
+  struct snmp_asn1_tlv tlv;
+  u16_t frame_size;
+  u8_t outbound_padding = 0;
+
+  if (request->version == SNMP_VERSION_1) {
+    if (request->error_status != SNMP_ERR_NOERROR) {
+      /* map v2c error codes to v1 compliant error code (according to RFC 2089) */
+      switch (request->error_status) {
+        /* mapping of implementation specific "virtual" error codes
+         * (during processing of frame we already stored them in error_status field,
+         * so no need to check all varbinds here for those exceptions as suggested by RFC) */
+        case SNMP_ERR_NOSUCHINSTANCE:
+        case SNMP_ERR_NOSUCHOBJECT:
+        case SNMP_ERR_ENDOFMIBVIEW:
+          request->error_status = SNMP_ERR_NOSUCHNAME;
+          break;
+        /* mapping according to RFC */
+        case SNMP_ERR_WRONGVALUE:
+        case SNMP_ERR_WRONGENCODING:
+        case SNMP_ERR_WRONGTYPE:
+        case SNMP_ERR_WRONGLENGTH:
+        case SNMP_ERR_INCONSISTENTVALUE:
+          request->error_status = SNMP_ERR_BADVALUE;
+          break;
+        case SNMP_ERR_NOACCESS:
+        case SNMP_ERR_NOTWRITABLE:
+        case SNMP_ERR_NOCREATION:
+        case SNMP_ERR_INCONSISTENTNAME:
+        case SNMP_ERR_AUTHORIZATIONERROR:
+          request->error_status = SNMP_ERR_NOSUCHNAME;
+          break;
+        case SNMP_ERR_RESOURCEUNAVAILABLE:
+        case SNMP_ERR_COMMITFAILED:
+        case SNMP_ERR_UNDOFAILED:
+        default:
+          request->error_status = SNMP_ERR_GENERROR;
+          break;
+      }
+    }
+  } else {
+    if (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) {
+      /* map error codes to according to RFC 1905 (4.2.5.  The SetRequest-PDU) return 'NotWritable' for unknown OIDs) */
+      switch (request->error_status) {
+        case SNMP_ERR_NOSUCHINSTANCE:
+        case SNMP_ERR_NOSUCHOBJECT:
+        case SNMP_ERR_ENDOFMIBVIEW:
+          request->error_status = SNMP_ERR_NOTWRITABLE;
+          break;
+        default:
+          break;
+      }
+    }
+
+    if (request->error_status >= SNMP_VARBIND_EXCEPTION_OFFSET) {
+      /* should never occur because v2 frames store exceptions directly inside varbinds and not as frame error_status */
+      LWIP_DEBUGF(SNMP_DEBUG, ("snmp_complete_outbound_frame() > Found v2 request with varbind exception code stored as error status!\n"));
+      return ERR_ARG;
+    }
+  }
+
+  if ((request->error_status != SNMP_ERR_NOERROR) || (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ)) {
+    /* all inbound vars are returned in response without any modification for error responses and successful set requests*/
+    struct snmp_pbuf_stream inbound_stream;
+    OF_BUILD_EXEC( snmp_pbuf_stream_init(&inbound_stream, request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len) );
+    OF_BUILD_EXEC( snmp_pbuf_stream_init(&(request->outbound_pbuf_stream), request->outbound_pbuf, request->outbound_varbind_offset, request->outbound_pbuf->tot_len - request->outbound_varbind_offset) );
+    OF_BUILD_EXEC( snmp_pbuf_stream_writeto(&inbound_stream, &(request->outbound_pbuf_stream), 0) );
+  }
+
+  frame_size = request->outbound_pbuf_stream.offset;
+
+#if LWIP_SNMP_V3 && LWIP_SNMP_V3_CRYPTO
+  /* Calculate padding for encryption */
+  if (request->version == SNMP_VERSION_3 && (request->msg_flags & SNMP_V3_PRIV_FLAG)) {
+    u8_t i;
+    outbound_padding = (8 - (u8_t)((frame_size - request->outbound_scoped_pdu_seq_offset) & 0x07)) & 0x07;
+    for (i = 0; i < outbound_padding; i++) {
+      OF_BUILD_EXEC( snmp_pbuf_stream_write(&request->outbound_pbuf_stream, 0) );
+    }
+  }
+#endif
+
+  /* complete missing length in 'Message' sequence ; 'Message' tlv is located at the beginning (offset 0) */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, frame_size + outbound_padding - 1 - 3); /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */
+  OF_BUILD_EXEC( snmp_pbuf_stream_init(&(request->outbound_pbuf_stream), request->outbound_pbuf, 0, request->outbound_pbuf->tot_len) );
+  OF_BUILD_EXEC( snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv) );
+
+#if LWIP_SNMP_V3
+  if (request->version == SNMP_VERSION_3) {
+    /* complete missing length in 'globalData' sequence */
+    /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, request->outbound_msg_global_data_end
+                             - request->outbound_msg_global_data_offset - 1 - 1);
+    OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_msg_global_data_offset));
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
+
+    /* complete missing length in 'msgSecurityParameters' sequence */
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, request->outbound_msg_security_parameters_end
+                             - request->outbound_msg_security_parameters_str_offset - 1 - 1);
+    OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_msg_security_parameters_str_offset));
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
+
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, request->outbound_msg_security_parameters_end
+                             - request->outbound_msg_security_parameters_seq_offset - 1 - 1);
+    OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_msg_security_parameters_seq_offset));
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
+
+    /* complete missing length in scoped PDU sequence */
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, frame_size - request->outbound_scoped_pdu_seq_offset - 1 - 3);
+    OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_scoped_pdu_seq_offset));
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
+  }
+#endif
+
+  /* complete missing length in 'PDU' sequence */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, request->request_out_type, 3,
+                           frame_size - request->outbound_pdu_offset - 1 - 3); /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */
+  OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_pdu_offset) );
+  OF_BUILD_EXEC( snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv) );
+
+  /* process and encode final error status */
+  if (request->error_status != 0) {
+    u16_t len;
+    snmp_asn1_enc_s32t_cnt(request->error_status, &len);
+    if (len != 1) {
+      /* error, we only reserved one byte for it */
+      return ERR_ARG;
+    }
+    OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_error_status_offset) );
+    OF_BUILD_EXEC( snmp_asn1_enc_s32t(&(request->outbound_pbuf_stream), len, request->error_status) );
+
+    /* for compatibility to v1, log statistics; in v2 (RFC 1907) these statistics are obsoleted */
+    switch (request->error_status) {
+      case SNMP_ERR_TOOBIG:
+        snmp_stats.outtoobigs++;
+        break;
+      case SNMP_ERR_NOSUCHNAME:
+        snmp_stats.outnosuchnames++;
+        break;
+      case SNMP_ERR_BADVALUE:
+        snmp_stats.outbadvalues++;
+        break;
+      case SNMP_ERR_GENERROR:
+      default:
+        snmp_stats.outgenerrs++;
+        break;
+    }
+
+    if (request->error_status == SNMP_ERR_TOOBIG) {
+      request->error_index = 0; /* defined by RFC 1157 */
+    } else if (request->error_index == 0) {
+      /* set index to varbind where error occured (if not already set before, e.g. during GetBulk processing) */
+      request->error_index = request->inbound_varbind_enumerator.varbind_count;
+    }
+  } else {
+    if (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) {
+      snmp_stats.intotalsetvars += request->inbound_varbind_enumerator.varbind_count;
+    } else {
+      snmp_stats.intotalreqvars += request->inbound_varbind_enumerator.varbind_count;
+    }
+  }
+
+  /* encode final error index*/
+  if (request->error_index != 0) {
+    u16_t len;
+    snmp_asn1_enc_s32t_cnt(request->error_index, &len);
+    if (len != 1) {
+      /* error, we only reserved one byte for it */
+      return ERR_VAL;
+    }
+    OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_error_index_offset) );
+    OF_BUILD_EXEC( snmp_asn1_enc_s32t(&(request->outbound_pbuf_stream), len, request->error_index) );
+  }
+
+  /* complete missing length in 'VarBindList' sequence ; 'VarBindList' tlv is located directly before varbind offset */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, frame_size - request->outbound_varbind_offset);
+  OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_varbind_offset - 1 - 3) ); /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */
+  OF_BUILD_EXEC( snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv) );
+
+  /* Authenticate response */
+#if LWIP_SNMP_V3 && LWIP_SNMP_V3_CRYPTO
+  /* Encrypt response */
+  if (request->version == SNMP_VERSION_3 && (request->msg_flags & SNMP_V3_PRIV_FLAG)) {
+    u8_t key[20];
+    snmpv3_priv_algo_t algo;
+
+    /* complete missing length in PDU sequence */
+    OF_BUILD_EXEC(snmp_pbuf_stream_init(&request->outbound_pbuf_stream, request->outbound_pbuf, 0, request->outbound_pbuf->tot_len));
+    OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_scoped_pdu_string_offset));
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 3, frame_size + outbound_padding
+                             - request->outbound_scoped_pdu_string_offset - 1 - 3);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
+
+    OF_BUILD_EXEC(snmpv3_get_user((char *)request->msg_user_name, NULL, NULL, &algo, key));
+
+    OF_BUILD_EXEC(snmpv3_crypt(&request->outbound_pbuf_stream, tlv.value_len, key,
+                               request->msg_privacy_parameters, request->msg_authoritative_engine_boots,
+                               request->msg_authoritative_engine_time, algo, SNMP_V3_PRIV_MODE_ENCRYPT));
+  }
+
+  if (request->version == SNMP_VERSION_3 && (request->msg_flags & SNMP_V3_AUTH_FLAG)) {
+    u8_t key[20];
+    snmpv3_auth_algo_t algo;
+    u8_t hmac[20];
+
+    OF_BUILD_EXEC(snmpv3_get_user((char *)request->msg_user_name, &algo, key, NULL, NULL));
+    OF_BUILD_EXEC(snmp_pbuf_stream_init(&(request->outbound_pbuf_stream),
+                                        request->outbound_pbuf, 0, request->outbound_pbuf->tot_len));
+    OF_BUILD_EXEC(snmpv3_auth(&request->outbound_pbuf_stream, frame_size + outbound_padding, key, algo, hmac));
+
+    MEMCPY(request->msg_authentication_parameters, hmac, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
+    OF_BUILD_EXEC(snmp_pbuf_stream_init(&request->outbound_pbuf_stream,
+                                        request->outbound_pbuf, 0, request->outbound_pbuf->tot_len));
+    OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&request->outbound_pbuf_stream,
+                                            request->outbound_msg_authentication_parameters_offset));
+
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(&request->outbound_pbuf_stream, &tlv));
+    OF_BUILD_EXEC(snmp_asn1_enc_raw(&request->outbound_pbuf_stream,
+                                    request->msg_authentication_parameters, SNMP_V3_MAX_AUTH_PARAM_LENGTH));
+  }
+#endif
+
+  pbuf_realloc(request->outbound_pbuf, frame_size + outbound_padding);
+
+  snmp_stats.outgetresponses++;
+  snmp_stats.outpkts++;
+
+  return ERR_OK;
+}
+
+static void
+snmp_execute_write_callbacks(struct snmp_request *request)
+{
+  struct snmp_varbind_enumerator inbound_varbind_enumerator;
+  struct snmp_varbind vb;
+
+  snmp_vb_enumerator_init(&inbound_varbind_enumerator, request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len);
+  vb.value = NULL; /* do NOT decode value (we enumerate outbound buffer here, so all varbinds have values assigned, which we don't need here) */
+
+  while (snmp_vb_enumerator_get_next(&inbound_varbind_enumerator, &vb) == SNMP_VB_ENUMERATOR_ERR_OK) {
+    snmp_write_callback(vb.oid.id, vb.oid.len, snmp_write_callback_arg);
+  }
+}
+
+
+/* ----------------------------------------------------------------------- */
+/* VarBind enumerator methods */
+/* ----------------------------------------------------------------------- */
+
+void
+snmp_vb_enumerator_init(struct snmp_varbind_enumerator *enumerator, struct pbuf *p, u16_t offset, u16_t length)
+{
+  snmp_pbuf_stream_init(&(enumerator->pbuf_stream), p, offset, length);
+  enumerator->varbind_count = 0;
+}
+
+#define VB_PARSE_EXEC(code)   PARSE_EXEC(code, SNMP_VB_ENUMERATOR_ERR_ASN1ERROR)
+#define VB_PARSE_ASSERT(code) PARSE_ASSERT(code, SNMP_VB_ENUMERATOR_ERR_ASN1ERROR)
+
+snmp_vb_enumerator_err_t
+snmp_vb_enumerator_get_next(struct snmp_varbind_enumerator *enumerator, struct snmp_varbind *varbind)
+{
+  struct snmp_asn1_tlv tlv;
+  u16_t  varbind_len;
+  err_t  err;
+
+  if (enumerator->pbuf_stream.length == 0) {
+    return SNMP_VB_ENUMERATOR_ERR_EOVB;
+  }
+  enumerator->varbind_count++;
+
+  /* decode varbind itself (parent container of a varbind) */
+  VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv));
+  VB_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len <= enumerator->pbuf_stream.length));
+  varbind_len = tlv.value_len;
+
+  /* decode varbind name (object id) */
+  VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv));
+  VB_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_OBJECT_ID) && (SNMP_ASN1_TLV_LENGTH(tlv) < varbind_len) && (tlv.value_len < enumerator->pbuf_stream.length));
+
+  VB_PARSE_EXEC(snmp_asn1_dec_oid(&(enumerator->pbuf_stream), tlv.value_len, varbind->oid.id, &(varbind->oid.len), SNMP_MAX_OBJ_ID_LEN));
+  varbind_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+
+  /* decode varbind value (object id) */
+  VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv));
+  VB_PARSE_ASSERT((SNMP_ASN1_TLV_LENGTH(tlv) == varbind_len) && (tlv.value_len <= enumerator->pbuf_stream.length));
+  varbind->type = tlv.type;
+
+  /* shall the value be decoded ? */
+  if (varbind->value != NULL) {
+    switch (varbind->type) {
+      case SNMP_ASN1_TYPE_INTEGER:
+        VB_PARSE_EXEC(snmp_asn1_dec_s32t(&(enumerator->pbuf_stream), tlv.value_len, (s32_t *)varbind->value));
+        varbind->value_len = sizeof(s32_t);
+        break;
+      case SNMP_ASN1_TYPE_COUNTER:
+      case SNMP_ASN1_TYPE_GAUGE:
+      case SNMP_ASN1_TYPE_TIMETICKS:
+        VB_PARSE_EXEC(snmp_asn1_dec_u32t(&(enumerator->pbuf_stream), tlv.value_len, (u32_t *)varbind->value));
+        varbind->value_len = sizeof(u32_t);
+        break;
+      case SNMP_ASN1_TYPE_OCTET_STRING:
+      case SNMP_ASN1_TYPE_OPAQUE:
+        err = snmp_asn1_dec_raw(&(enumerator->pbuf_stream), tlv.value_len, (u8_t *)varbind->value, &varbind->value_len, SNMP_MAX_VALUE_SIZE);
+        if (err == ERR_MEM) {
+          return SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH;
+        }
+        VB_PARSE_ASSERT(err == ERR_OK);
+        break;
+      case SNMP_ASN1_TYPE_NULL:
+        varbind->value_len = 0;
+        break;
+      case SNMP_ASN1_TYPE_OBJECT_ID:
+        /* misuse tlv.length_len as OID_length transporter */
+        err = snmp_asn1_dec_oid(&(enumerator->pbuf_stream), tlv.value_len, (u32_t *)varbind->value, &tlv.length_len, SNMP_MAX_OBJ_ID_LEN);
+        if (err == ERR_MEM) {
+          return SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH;
+        }
+        VB_PARSE_ASSERT(err == ERR_OK);
+        varbind->value_len = tlv.length_len * sizeof(u32_t);
+        break;
+      case SNMP_ASN1_TYPE_IPADDR:
+        if (tlv.value_len == 4) {
+          /* must be exactly 4 octets! */
+          VB_PARSE_EXEC(snmp_asn1_dec_raw(&(enumerator->pbuf_stream), tlv.value_len, (u8_t *)varbind->value, &varbind->value_len, SNMP_MAX_VALUE_SIZE));
+        } else {
+          VB_PARSE_ASSERT(0);
+        }
+        break;
+#if LWIP_HAVE_INT64
+      case SNMP_ASN1_TYPE_COUNTER64:
+        VB_PARSE_EXEC(snmp_asn1_dec_u64t(&(enumerator->pbuf_stream), tlv.value_len, (u64_t *)varbind->value));
+        varbind->value_len = sizeof(u64_t);
+        break;
+#endif
+      default:
+        VB_PARSE_ASSERT(0);
+        break;
+    }
+  } else {
+    snmp_pbuf_stream_seek(&(enumerator->pbuf_stream), tlv.value_len);
+    varbind->value_len = tlv.value_len;
+  }
+
+  return SNMP_VB_ENUMERATOR_ERR_OK;
+}
+
+#endif /* LWIP_SNMP */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_msg.h b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_msg.h
new file mode 100644 (file)
index 0000000..903f08a
--- /dev/null
@@ -0,0 +1,185 @@
+/**
+ * @file
+ * SNMP Agent message handling structures (internal API, do not use in client code).
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * Copyright (c) 2016 Elias Oenal.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ *         Martin Hentschel <info@cl-soft.de>
+ *         Elias Oenal <lwip@eliasoenal.com>
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_MSG_H
+#define LWIP_HDR_APPS_SNMP_MSG_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP
+
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "snmp_pbuf_stream.h"
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+
+#if LWIP_SNMP_V3
+#include "snmpv3_priv.h"
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* version defines used in PDU */
+#define SNMP_VERSION_1  0
+#define SNMP_VERSION_2c 1
+#define SNMP_VERSION_3  3
+
+struct snmp_varbind_enumerator {
+  struct snmp_pbuf_stream pbuf_stream;
+  u16_t varbind_count;
+};
+
+typedef enum {
+  SNMP_VB_ENUMERATOR_ERR_OK            = 0,
+  SNMP_VB_ENUMERATOR_ERR_EOVB          = 1,
+  SNMP_VB_ENUMERATOR_ERR_ASN1ERROR     = 2,
+  SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH = 3
+} snmp_vb_enumerator_err_t;
+
+void snmp_vb_enumerator_init(struct snmp_varbind_enumerator *enumerator, struct pbuf *p, u16_t offset, u16_t length);
+snmp_vb_enumerator_err_t snmp_vb_enumerator_get_next(struct snmp_varbind_enumerator *enumerator, struct snmp_varbind *varbind);
+
+struct snmp_request {
+  /* Communication handle */
+  void *handle;
+  /* source IP address */
+  const ip_addr_t *source_ip;
+  /* source UDP port */
+  u16_t source_port;
+  /* incoming snmp version */
+  u8_t version;
+  /* community name (zero terminated) */
+  u8_t community[SNMP_MAX_COMMUNITY_STR_LEN + 1];
+  /* community string length (exclusive zero term) */
+  u16_t community_strlen;
+  /* request type */
+  u8_t request_type;
+  /* request ID */
+  s32_t request_id;
+  /* error status */
+  s32_t error_status;
+  /* error index */
+  s32_t error_index;
+  /* non-repeaters (getBulkRequest (SNMPv2c)) */
+  s32_t non_repeaters;
+  /* max-repetitions (getBulkRequest (SNMPv2c)) */
+  s32_t max_repetitions;
+
+  /* Usually response-pdu (2). When snmpv3 errors are detected report-pdu(8) */
+  u8_t request_out_type;
+
+#if LWIP_SNMP_V3
+  s32_t msg_id;
+  s32_t msg_max_size;
+  u8_t  msg_flags;
+  s32_t msg_security_model;
+  u8_t  msg_authoritative_engine_id[SNMP_V3_MAX_ENGINE_ID_LENGTH];
+  u8_t  msg_authoritative_engine_id_len;
+  s32_t msg_authoritative_engine_boots;
+  s32_t msg_authoritative_engine_time;
+  u8_t  msg_user_name[SNMP_V3_MAX_USER_LENGTH];
+  u8_t  msg_user_name_len;
+  u8_t  msg_authentication_parameters[SNMP_V3_MAX_AUTH_PARAM_LENGTH];
+  u8_t  msg_authentication_parameters_len;
+  u8_t  msg_privacy_parameters[SNMP_V3_MAX_PRIV_PARAM_LENGTH];
+  u8_t  msg_privacy_parameters_len;
+  u8_t  context_engine_id[SNMP_V3_MAX_ENGINE_ID_LENGTH];
+  u8_t  context_engine_id_len;
+  u8_t  context_name[SNMP_V3_MAX_ENGINE_ID_LENGTH];
+  u8_t  context_name_len;
+#endif
+
+  struct pbuf *inbound_pbuf;
+  struct snmp_varbind_enumerator inbound_varbind_enumerator;
+  u16_t inbound_varbind_offset;
+  u16_t inbound_varbind_len;
+  u16_t inbound_padding_len;
+
+  struct pbuf *outbound_pbuf;
+  struct snmp_pbuf_stream outbound_pbuf_stream;
+  u16_t outbound_pdu_offset;
+  u16_t outbound_error_status_offset;
+  u16_t outbound_error_index_offset;
+  u16_t outbound_varbind_offset;
+#if LWIP_SNMP_V3
+  u16_t outbound_msg_global_data_offset;
+  u16_t outbound_msg_global_data_end;
+  u16_t outbound_msg_security_parameters_str_offset;
+  u16_t outbound_msg_security_parameters_seq_offset;
+  u16_t outbound_msg_security_parameters_end;
+  u16_t outbound_msg_authentication_parameters_offset;
+  u16_t outbound_scoped_pdu_seq_offset;
+  u16_t outbound_scoped_pdu_string_offset;
+#endif
+
+  u8_t value_buffer[SNMP_MAX_VALUE_SIZE];
+};
+
+/** A helper struct keeping length information about varbinds */
+struct snmp_varbind_len {
+  u8_t  vb_len_len;
+  u16_t vb_value_len;
+  u8_t  oid_len_len;
+  u16_t oid_value_len;
+  u8_t  value_len_len;
+  u16_t value_value_len;
+};
+
+/** Agent community string */
+extern const char *snmp_community;
+/** Agent community string for write access */
+extern const char *snmp_community_write;
+/** handle for sending traps */
+extern void *snmp_traps_handle;
+
+void snmp_receive(void *handle, struct pbuf *p, const ip_addr_t *source_ip, u16_t port);
+err_t snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port);
+u8_t snmp_get_local_ip_for_dst(void *handle, const ip_addr_t *dst, ip_addr_t *result);
+err_t snmp_varbind_length(struct snmp_varbind *varbind, struct snmp_varbind_len *len);
+err_t snmp_append_outbound_varbind(struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbind);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* LWIP_HDR_APPS_SNMP_MSG_H */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_netconn.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_netconn.c
new file mode 100644 (file)
index 0000000..8850acb
--- /dev/null
@@ -0,0 +1,123 @@
+/**
+ * @file
+ * SNMP netconn frontend.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP && SNMP_USE_NETCONN
+
+#include <string.h>
+#include "lwip/api.h"
+#include "lwip/ip.h"
+#include "lwip/udp.h"
+#include "snmp_msg.h"
+#include "lwip/sys.h"
+#include "lwip/prot/iana.h"
+
+/** SNMP netconn API worker thread */
+static void
+snmp_netconn_thread(void *arg)
+{
+  struct netconn *conn;
+  struct netbuf *buf;
+  err_t err;
+  LWIP_UNUSED_ARG(arg);
+
+  /* Bind to SNMP port with default IP address */
+#if LWIP_IPV6
+  conn = netconn_new(NETCONN_UDP_IPV6);
+  netconn_bind(conn, IP6_ADDR_ANY, LWIP_IANA_PORT_SNMP);
+#else /* LWIP_IPV6 */
+  conn = netconn_new(NETCONN_UDP);
+  netconn_bind(conn, IP4_ADDR_ANY, LWIP_IANA_PORT_SNMP);
+#endif /* LWIP_IPV6 */
+  LWIP_ERROR("snmp_netconn: invalid conn", (conn != NULL), return;);
+
+  snmp_traps_handle = conn;
+
+  do {
+    err = netconn_recv(conn, &buf);
+
+    if (err == ERR_OK) {
+      snmp_receive(conn, buf->p, &buf->addr, buf->port);
+    }
+
+    if (buf != NULL) {
+      netbuf_delete(buf);
+    }
+  } while (1);
+}
+
+err_t
+snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port)
+{
+  err_t result;
+  struct netbuf buf;
+
+  memset(&buf, 0, sizeof(buf));
+  buf.p = p;
+  result = netconn_sendto((struct netconn *)handle, &buf, dst, port);
+
+  return result;
+}
+
+u8_t
+snmp_get_local_ip_for_dst(void *handle, const ip_addr_t *dst, ip_addr_t *result)
+{
+  struct netconn *conn = (struct netconn *)handle;
+  struct netif *dst_if;
+  const ip_addr_t *dst_ip;
+
+  LWIP_UNUSED_ARG(conn); /* unused in case of IPV4 only configuration */
+
+  ip_route_get_local_ip(&conn->pcb.udp->local_ip, dst, dst_if, dst_ip);
+
+  if ((dst_if != NULL) && (dst_ip != NULL)) {
+    ip_addr_copy(*result, *dst_ip);
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+/**
+ * Starts SNMP Agent.
+ */
+void
+snmp_init(void)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  sys_thread_new("snmp_netconn", snmp_netconn_thread, NULL, SNMP_STACK_SIZE, SNMP_THREAD_PRIO);
+}
+
+#endif /* LWIP_SNMP && SNMP_USE_NETCONN */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_pbuf_stream.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_pbuf_stream.c
new file mode 100644 (file)
index 0000000..42867fb
--- /dev/null
@@ -0,0 +1,156 @@
+/**
+ * @file
+ * SNMP pbuf stream wrapper implementation (internal API, do not use in client code).
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel <info@cl-soft.de>
+ *
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "snmp_pbuf_stream.h"
+#include "lwip/def.h"
+#include <string.h>
+
+err_t
+snmp_pbuf_stream_init(struct snmp_pbuf_stream *pbuf_stream, struct pbuf *p, u16_t offset, u16_t length)
+{
+  pbuf_stream->offset = offset;
+  pbuf_stream->length = length;
+  pbuf_stream->pbuf   = p;
+
+  return ERR_OK;
+}
+
+err_t
+snmp_pbuf_stream_read(struct snmp_pbuf_stream *pbuf_stream, u8_t *data)
+{
+  if (pbuf_stream->length == 0) {
+    return ERR_BUF;
+  }
+
+  if (pbuf_copy_partial(pbuf_stream->pbuf, data, 1, pbuf_stream->offset) == 0) {
+    return ERR_BUF;
+  }
+
+  pbuf_stream->offset++;
+  pbuf_stream->length--;
+
+  return ERR_OK;
+}
+
+err_t
+snmp_pbuf_stream_write(struct snmp_pbuf_stream *pbuf_stream, u8_t data)
+{
+  return snmp_pbuf_stream_writebuf(pbuf_stream, &data, 1);
+}
+
+err_t
+snmp_pbuf_stream_writebuf(struct snmp_pbuf_stream *pbuf_stream, const void *buf, u16_t buf_len)
+{
+  if (pbuf_stream->length < buf_len) {
+    return ERR_BUF;
+  }
+
+  if (pbuf_take_at(pbuf_stream->pbuf, buf, buf_len, pbuf_stream->offset) != ERR_OK) {
+    return ERR_BUF;
+  }
+
+  pbuf_stream->offset += buf_len;
+  pbuf_stream->length -= buf_len;
+
+  return ERR_OK;
+}
+
+err_t
+snmp_pbuf_stream_writeto(struct snmp_pbuf_stream *pbuf_stream, struct snmp_pbuf_stream *target_pbuf_stream, u16_t len)
+{
+
+  if ((pbuf_stream == NULL) || (target_pbuf_stream == NULL)) {
+    return ERR_ARG;
+  }
+  if ((len > pbuf_stream->length) || (len > target_pbuf_stream->length)) {
+    return ERR_ARG;
+  }
+
+  if (len == 0) {
+    len = LWIP_MIN(pbuf_stream->length, target_pbuf_stream->length);
+  }
+
+  while (len > 0) {
+    u16_t chunk_len;
+    err_t err;
+    u16_t target_offset;
+    struct pbuf *pbuf = pbuf_skip(pbuf_stream->pbuf, pbuf_stream->offset, &target_offset);
+
+    if ((pbuf == NULL) || (pbuf->len == 0)) {
+      return ERR_BUF;
+    }
+
+    chunk_len = LWIP_MIN(len, pbuf->len);
+    err = snmp_pbuf_stream_writebuf(target_pbuf_stream, &((u8_t *)pbuf->payload)[target_offset], chunk_len);
+    if (err != ERR_OK) {
+      return err;
+    }
+
+    pbuf_stream->offset   += chunk_len;
+    pbuf_stream->length   -= chunk_len;
+    len -= chunk_len;
+  }
+
+  return ERR_OK;
+}
+
+err_t
+snmp_pbuf_stream_seek(struct snmp_pbuf_stream *pbuf_stream, s32_t offset)
+{
+  if ((offset < 0) || (offset > pbuf_stream->length)) {
+    /* we cannot seek backwards or forward behind stream end */
+    return ERR_ARG;
+  }
+
+  pbuf_stream->offset += (u16_t)offset;
+  pbuf_stream->length -= (u16_t)offset;
+
+  return ERR_OK;
+}
+
+err_t
+snmp_pbuf_stream_seek_abs(struct snmp_pbuf_stream *pbuf_stream, u32_t offset)
+{
+  s32_t rel_offset = offset - pbuf_stream->offset;
+  return snmp_pbuf_stream_seek(pbuf_stream, rel_offset);
+}
+
+#endif /* LWIP_SNMP */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_pbuf_stream.h b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_pbuf_stream.h
new file mode 100644 (file)
index 0000000..682713f
--- /dev/null
@@ -0,0 +1,72 @@
+/**
+ * @file
+ * SNMP pbuf stream wrapper (internal API, do not use in client code).
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel <info@cl-soft.de>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_PBUF_STREAM_H
+#define LWIP_HDR_APPS_SNMP_PBUF_STREAM_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP
+
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct snmp_pbuf_stream {
+  struct pbuf *pbuf;
+  u16_t offset;
+  u16_t length;
+};
+
+err_t snmp_pbuf_stream_init(struct snmp_pbuf_stream *pbuf_stream, struct pbuf *p, u16_t offset, u16_t length);
+err_t snmp_pbuf_stream_read(struct snmp_pbuf_stream *pbuf_stream, u8_t *data);
+err_t snmp_pbuf_stream_write(struct snmp_pbuf_stream *pbuf_stream, u8_t data);
+err_t snmp_pbuf_stream_writebuf(struct snmp_pbuf_stream *pbuf_stream, const void *buf, u16_t buf_len);
+err_t snmp_pbuf_stream_writeto(struct snmp_pbuf_stream *pbuf_stream, struct snmp_pbuf_stream *target_pbuf_stream, u16_t len);
+err_t snmp_pbuf_stream_seek(struct snmp_pbuf_stream *pbuf_stream, s32_t offset);
+err_t snmp_pbuf_stream_seek_abs(struct snmp_pbuf_stream *pbuf_stream, u32_t offset);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* LWIP_HDR_APPS_SNMP_PBUF_STREAM_H */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_raw.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_raw.c
new file mode 100644 (file)
index 0000000..e203cb6
--- /dev/null
@@ -0,0 +1,103 @@
+/**
+ * @file
+ * SNMP RAW API frontend.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ */
+
+#include "lwip/apps/snmp_opts.h"
+#include "lwip/ip_addr.h"
+
+#if LWIP_SNMP && SNMP_USE_RAW
+
+#include "lwip/udp.h"
+#include "lwip/ip.h"
+#include "lwip/prot/iana.h"
+#include "snmp_msg.h"
+
+/* lwIP UDP receive callback function */
+static void
+snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+  LWIP_UNUSED_ARG(arg);
+
+  snmp_receive(pcb, p, addr, port);
+
+  pbuf_free(p);
+}
+
+err_t
+snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port)
+{
+  return udp_sendto((struct udp_pcb *)handle, p, dst, port);
+}
+
+u8_t
+snmp_get_local_ip_for_dst(void *handle, const ip_addr_t *dst, ip_addr_t *result)
+{
+  struct udp_pcb *udp_pcb = (struct udp_pcb *)handle;
+  struct netif *dst_if;
+  const ip_addr_t *dst_ip;
+
+  LWIP_UNUSED_ARG(udp_pcb); /* unused in case of IPV4 only configuration */
+
+  ip_route_get_local_ip(&udp_pcb->local_ip, dst, dst_if, dst_ip);
+
+  if ((dst_if != NULL) && (dst_ip != NULL)) {
+    ip_addr_copy(*result, *dst_ip);
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+/**
+ * @ingroup snmp_core
+ * Starts SNMP Agent.
+ * Allocates UDP pcb and binds it to IP_ANY_TYPE port 161.
+ */
+void
+snmp_init(void)
+{
+  err_t err;
+
+  struct udp_pcb *snmp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+  LWIP_ERROR("snmp_raw: no PCB", (snmp_pcb != NULL), return;);
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  snmp_traps_handle = snmp_pcb;
+
+  udp_recv(snmp_pcb, snmp_recv, NULL);
+  err = udp_bind(snmp_pcb, IP_ANY_TYPE, LWIP_IANA_PORT_SNMP);
+  LWIP_ERROR("snmp_raw: Unable to bind PCB", (err == ERR_OK), return;);
+}
+
+#endif /* LWIP_SNMP && SNMP_USE_RAW */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_scalar.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_scalar.c
new file mode 100644 (file)
index 0000000..094f52f
--- /dev/null
@@ -0,0 +1,232 @@
+/**
+ * @file
+ * SNMP scalar node support implementation.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel <info@cl-soft.de>
+ *
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/apps/snmp_core.h"
+
+static s16_t snmp_scalar_array_get_value(struct snmp_node_instance *instance, void *value);
+static snmp_err_t  snmp_scalar_array_set_test(struct snmp_node_instance *instance, u16_t value_len, void *value);
+static snmp_err_t  snmp_scalar_array_set_value(struct snmp_node_instance *instance, u16_t value_len, void *value);
+
+snmp_err_t
+snmp_scalar_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
+{
+  const struct snmp_scalar_node *scalar_node = (const struct snmp_scalar_node *)(const void *)instance->node;
+
+  LWIP_UNUSED_ARG(root_oid);
+  LWIP_UNUSED_ARG(root_oid_len);
+
+  /* scalar only has one dedicated instance: .0 */
+  if ((instance->instance_oid.len != 1) || (instance->instance_oid.id[0] != 0)) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  instance->access    = scalar_node->access;
+  instance->asn1_type = scalar_node->asn1_type;
+  instance->get_value = scalar_node->get_value;
+  instance->set_test  = scalar_node->set_test;
+  instance->set_value = scalar_node->set_value;
+  return SNMP_ERR_NOERROR;
+}
+
+snmp_err_t
+snmp_scalar_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
+{
+  /* because our only instance is .0 we can only return a next instance if no instance oid is passed */
+  if (instance->instance_oid.len == 0) {
+    instance->instance_oid.len   = 1;
+    instance->instance_oid.id[0] = 0;
+
+    return snmp_scalar_get_instance(root_oid, root_oid_len, instance);
+  }
+
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+
+snmp_err_t
+snmp_scalar_array_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
+{
+  LWIP_UNUSED_ARG(root_oid);
+  LWIP_UNUSED_ARG(root_oid_len);
+
+  if ((instance->instance_oid.len == 2) && (instance->instance_oid.id[1] == 0)) {
+    const struct snmp_scalar_array_node *array_node = (const struct snmp_scalar_array_node *)(const void *)instance->node;
+    const struct snmp_scalar_array_node_def *array_node_def = array_node->array_nodes;
+    u32_t i = 0;
+
+    while (i < array_node->array_node_count) {
+      if (array_node_def->oid == instance->instance_oid.id[0]) {
+        break;
+      }
+
+      array_node_def++;
+      i++;
+    }
+
+    if (i < array_node->array_node_count) {
+      instance->access              = array_node_def->access;
+      instance->asn1_type           = array_node_def->asn1_type;
+      instance->get_value           = snmp_scalar_array_get_value;
+      instance->set_test            = snmp_scalar_array_set_test;
+      instance->set_value           = snmp_scalar_array_set_value;
+      instance->reference.const_ptr = array_node_def;
+
+      return SNMP_ERR_NOERROR;
+    }
+  }
+
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+snmp_err_t
+snmp_scalar_array_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
+{
+  const struct snmp_scalar_array_node *array_node = (const struct snmp_scalar_array_node *)(const void *)instance->node;
+  const struct snmp_scalar_array_node_def *array_node_def = array_node->array_nodes;
+  const struct snmp_scalar_array_node_def *result = NULL;
+
+  LWIP_UNUSED_ARG(root_oid);
+  LWIP_UNUSED_ARG(root_oid_len);
+
+  if ((instance->instance_oid.len == 0) && (array_node->array_node_count > 0)) {
+    /* return node with lowest OID */
+    u16_t i = 0;
+
+    result = array_node_def;
+    array_node_def++;
+
+    for (i = 1; i < array_node->array_node_count; i++) {
+      if (array_node_def->oid < result->oid) {
+        result = array_node_def;
+      }
+      array_node_def++;
+    }
+  } else if (instance->instance_oid.len >= 1) {
+    if (instance->instance_oid.len == 1) {
+      /* if we have the requested OID we return its instance, otherwise we search for the next available */
+      u16_t i = 0;
+      while (i < array_node->array_node_count) {
+        if (array_node_def->oid == instance->instance_oid.id[0]) {
+          result = array_node_def;
+          break;
+        }
+
+        array_node_def++;
+        i++;
+      }
+    }
+    if (result == NULL) {
+      u32_t oid_dist = 0xFFFFFFFFUL;
+      u16_t i        = 0;
+      array_node_def = array_node->array_nodes; /* may be already at the end when if case before was executed without result -> reinitialize to start */
+      while (i < array_node->array_node_count) {
+        if ((array_node_def->oid > instance->instance_oid.id[0]) &&
+            ((u32_t)(array_node_def->oid - instance->instance_oid.id[0]) < oid_dist)) {
+          result   = array_node_def;
+          oid_dist = array_node_def->oid - instance->instance_oid.id[0];
+        }
+
+        array_node_def++;
+        i++;
+      }
+    }
+  }
+
+  if (result == NULL) {
+    /* nothing to return */
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  instance->instance_oid.len   = 2;
+  instance->instance_oid.id[0] = result->oid;
+  instance->instance_oid.id[1] = 0;
+
+  instance->access              = result->access;
+  instance->asn1_type           = result->asn1_type;
+  instance->get_value           = snmp_scalar_array_get_value;
+  instance->set_test            = snmp_scalar_array_set_test;
+  instance->set_value           = snmp_scalar_array_set_value;
+  instance->reference.const_ptr = result;
+
+  return SNMP_ERR_NOERROR;
+}
+
+static s16_t
+snmp_scalar_array_get_value(struct snmp_node_instance *instance, void *value)
+{
+  s16_t result = -1;
+  const struct snmp_scalar_array_node *array_node = (const struct snmp_scalar_array_node *)(const void *)instance->node;
+  const struct snmp_scalar_array_node_def *array_node_def = (const struct snmp_scalar_array_node_def *)instance->reference.const_ptr;
+
+  if (array_node->get_value != NULL) {
+    result = array_node->get_value(array_node_def, value);
+  }
+  return result;
+}
+
+static snmp_err_t
+snmp_scalar_array_set_test(struct snmp_node_instance *instance, u16_t value_len, void *value)
+{
+  snmp_err_t result = SNMP_ERR_NOTWRITABLE;
+  const struct snmp_scalar_array_node *array_node = (const struct snmp_scalar_array_node *)(const void *)instance->node;
+  const struct snmp_scalar_array_node_def *array_node_def = (const struct snmp_scalar_array_node_def *)instance->reference.const_ptr;
+
+  if (array_node->set_test != NULL) {
+    result = array_node->set_test(array_node_def, value_len, value);
+  }
+  return result;
+}
+
+static snmp_err_t
+snmp_scalar_array_set_value(struct snmp_node_instance *instance, u16_t value_len, void *value)
+{
+  snmp_err_t result = SNMP_ERR_NOTWRITABLE;
+  const struct snmp_scalar_array_node *array_node = (const struct snmp_scalar_array_node *)(const void *)instance->node;
+  const struct snmp_scalar_array_node_def *array_node_def = (const struct snmp_scalar_array_node_def *)instance->reference.const_ptr;
+
+  if (array_node->set_value != NULL) {
+    result = array_node->set_value(array_node_def, value_len, value);
+  }
+  return result;
+}
+
+#endif /* LWIP_SNMP */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_snmpv2_framework.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_snmpv2_framework.c
new file mode 100644 (file)
index 0000000..a5611db
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+Generated by LwipMibCompiler
+*/
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP && LWIP_SNMP_V3 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp_snmpv2_framework.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmpv3.h"
+#include "snmpv3_priv.h"
+
+#include "lwip/sys.h"
+
+#include <string.h>
+
+const struct snmp_obj_id usmNoAuthProtocol      = { 10, { 1, 3, 6, 1, 6, 3, 10, 1, 1, 1 } };
+const struct snmp_obj_id usmHMACMD5AuthProtocol = { 10, { 1, 3, 6, 1, 6, 3, 10, 1, 1, 2 } };
+const struct snmp_obj_id usmHMACSHAAuthProtocol = { 10, { 1, 3, 6, 1, 6, 3, 10, 1, 1, 3 } };
+/* .4 sha-224
+ * .5 sha-256
+ * .6 sha-384
+ * .7 sha-512
+ */
+
+const struct snmp_obj_id usmNoPrivProtocol  = { 10, { 1, 3, 6, 1, 6, 3, 10, 1, 2, 1 } };
+const struct snmp_obj_id usmDESPrivProtocol = { 10, { 1, 3, 6, 1, 6, 3, 10, 1, 2, 2 } };
+/* .3 3des-ede */
+const struct snmp_obj_id usmAESPrivProtocol = { 10, { 1, 3, 6, 1, 6, 3, 10, 1, 2, 4 } };
+/* .5 unknown
+ * .6 unknown
+ * .7 unknown
+ */
+
+/* TODO: where should this value come from? */
+#define SNMP_FRAMEWORKMIB_SNMPENGINEMAXMESSAGESIZE 1500
+
+/* --- snmpFrameworkMIBObjects 1.3.6.1.6.3.10.2 ----------------------------------------------------- */
+static s16_t snmpengine_scalars_get_value(const struct snmp_scalar_array_node_def *node, void *value)
+{
+  const char *engineid;
+  u8_t engineid_len;
+
+  switch (node->oid) {
+    case 1: /* snmpEngineID */
+      snmpv3_get_engine_id(&engineid, &engineid_len);
+      MEMCPY(value, engineid, engineid_len);
+      return engineid_len;
+    case 2: /* snmpEngineBoots */
+      *(s32_t *)value = snmpv3_get_engine_boots_internal();
+      return sizeof(s32_t);
+    case 3: /* snmpEngineTime */
+      *(s32_t *)value = snmpv3_get_engine_time_internal();
+      return sizeof(s32_t);
+    case 4: /* snmpEngineMaxMessageSize */
+      *(s32_t *)value = SNMP_FRAMEWORKMIB_SNMPENGINEMAXMESSAGESIZE;
+      return sizeof(s32_t);
+    default:
+      LWIP_DEBUGF(SNMP_MIB_DEBUG, ("snmpengine_scalars_get_value(): unknown id: %"S32_F"\n", node->oid));
+      return 0;
+  }
+}
+
+static const struct snmp_scalar_array_node_def snmpengine_scalars_nodes[] = {
+  {1, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpEngineID */
+  {2, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpEngineBoots */
+  {3, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpEngineTime */
+  {4, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpEngineMaxMessageSize */
+};
+static const struct snmp_scalar_array_node snmpengine_scalars = SNMP_SCALAR_CREATE_ARRAY_NODE(1, snmpengine_scalars_nodes, snmpengine_scalars_get_value, NULL, NULL);
+
+static const struct snmp_node *const snmpframeworkmibobjects_subnodes[] = {
+  &snmpengine_scalars.node.node
+};
+static const struct snmp_tree_node snmpframeworkmibobjects_treenode = SNMP_CREATE_TREE_NODE(2, snmpframeworkmibobjects_subnodes);
+
+/* --- snmpFrameworkMIB  ----------------------------------------------------- */
+static const struct snmp_node *const snmpframeworkmib_subnodes[] = {
+  &snmpframeworkmibobjects_treenode.node
+};
+static const struct snmp_tree_node snmpframeworkmib_root = SNMP_CREATE_TREE_NODE(10, snmpframeworkmib_subnodes);
+static const u32_t snmpframeworkmib_base_oid[] = {1, 3, 6, 1, 6, 3, 10};
+const struct snmp_mib snmpframeworkmib = {snmpframeworkmib_base_oid, LWIP_ARRAYSIZE(snmpframeworkmib_base_oid), &snmpframeworkmib_root.node};
+
+/* --- snmpFrameworkMIB  ----------------------------------------------------- */
+#endif /* LWIP_SNMP */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_snmpv2_usm.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_snmpv2_usm.c
new file mode 100644 (file)
index 0000000..7490c9c
--- /dev/null
@@ -0,0 +1,410 @@
+/*
+Generated by LwipMibCompiler
+*/
+
+#include "lwip/apps/snmp_opts.h"
+#if LWIP_SNMP && LWIP_SNMP_V3
+
+#include "lwip/apps/snmp_snmpv2_usm.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmpv3.h"
+#include "snmpv3_priv.h"
+
+#include "lwip/apps/snmp_snmpv2_framework.h"
+
+#include <string.h>
+
+/* --- usmUser 1.3.6.1.6.3.15.1.2 ----------------------------------------------------- */
+
+static const struct snmp_oid_range usmUserTable_oid_ranges[] = {
+  { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+  { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+  { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+  { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+  { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+  { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+  { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+  { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff }
+};
+
+static void snmp_engineid_to_oid(const char *engineid, u32_t *oid, u32_t len)
+{
+  u8_t i;
+
+  for (i = 0; i < len; i++) {
+    oid[i] = engineid[i];
+  }
+}
+
+static void snmp_oid_to_name(char *name, const u32_t *oid, size_t len)
+{
+  u8_t i;
+
+  for (i = 0; i < len; i++) {
+    name[i] = (char)oid[i];
+  }
+}
+
+static void snmp_name_to_oid(const char *name, u32_t *oid, size_t len)
+{
+  u8_t i;
+
+  for (i = 0; i < len; i++) {
+    oid[i] = name[i];
+  }
+}
+
+static const struct snmp_obj_id *snmp_auth_algo_to_oid(snmpv3_auth_algo_t algo)
+{
+  if (algo == SNMP_V3_AUTH_ALGO_MD5) {
+    return &usmHMACMD5AuthProtocol;
+  } else if (algo ==  SNMP_V3_AUTH_ALGO_SHA) {
+    return &usmHMACMD5AuthProtocol;
+  }
+
+  return &usmNoAuthProtocol;
+}
+
+static const struct snmp_obj_id *snmp_priv_algo_to_oid(snmpv3_priv_algo_t algo)
+{
+  if (algo == SNMP_V3_PRIV_ALGO_DES) {
+    return &usmDESPrivProtocol;
+  } else if (algo == SNMP_V3_PRIV_ALGO_AES) {
+    return &usmAESPrivProtocol;
+  }
+
+  return &usmNoPrivProtocol;
+}
+
+char username[32];
+
+static snmp_err_t usmusertable_get_instance(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, struct snmp_node_instance *cell_instance)
+{
+  const char *engineid;
+  u8_t eid_len;
+
+  u32_t engineid_oid[SNMP_V3_MAX_ENGINE_ID_LENGTH];
+
+  u8_t name_len;
+  u8_t engineid_len;
+
+  u8_t name_start;
+  u8_t engineid_start;
+
+  LWIP_UNUSED_ARG(column);
+
+  snmpv3_get_engine_id(&engineid, &eid_len);
+
+  engineid_len = (u8_t)row_oid[0];
+  engineid_start = 1;
+
+  if (engineid_len != eid_len) {
+    /* EngineID length does not match! */
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  if (engineid_len > row_oid_len) {
+    /* row OID doesn't contain enough data according to engineid_len.*/
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* check if incoming OID length and if values are in plausible range */
+  if (!snmp_oid_in_range(&row_oid[engineid_start], engineid_len, usmUserTable_oid_ranges, engineid_len)) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  snmp_engineid_to_oid(engineid, engineid_oid, engineid_len);
+
+  /* Verify EngineID */
+  if (snmp_oid_equal(&row_oid[engineid_start], engineid_len, engineid_oid, engineid_len)) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  name_len = (u8_t)row_oid[engineid_start + engineid_len];
+  name_start = engineid_start + engineid_len + 1;
+
+  if (name_len > SNMP_V3_MAX_USER_LENGTH) {
+    /* specified name is too long */
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  if (1 + engineid_len + 1 + name_len != row_oid_len) {
+    /* Length of EngineID and name does not match row oid length. (+2 for length fields)*/
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* check if incoming OID length and if values are in plausible range */
+  if (!snmp_oid_in_range(&row_oid[name_start], name_len, usmUserTable_oid_ranges, name_len)) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* Verify if user exists */
+  memset(username, 0, sizeof(username));
+  snmp_oid_to_name(username, &row_oid[name_start], name_len);
+  if (snmpv3_get_user(username, NULL, NULL, NULL, NULL) != ERR_OK) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* Save name in reference pointer to make it easier to handle later on */
+  cell_instance->reference.ptr = username;
+  cell_instance->reference_len = name_len;
+
+  /* user was found */
+  return SNMP_ERR_NOERROR;
+}
+
+/*
+ * valid oid options
+ * <oid>
+ * <oid>.<EngineID length>
+ * <oid>.<EngineID length>.<partial EngineID>
+ * <oid>.<EngineID length>.<EngineID>
+ * <oid>.<EngineID length>.<EngineID>.<UserName length>
+ * <oid>.<EngineID length>.<EngineID>.<UserName length>.<partial UserName>
+ * <oid>.<EngineID length>.<EngineID>.<UserName length>.<UserName>
+ *
+ */
+static snmp_err_t usmusertable_get_next_instance(const u32_t *column, struct snmp_obj_id *row_oid, struct snmp_node_instance *cell_instance)
+{
+  const char *engineid;
+  u8_t eid_len;
+
+  u32_t engineid_oid[SNMP_V3_MAX_ENGINE_ID_LENGTH];
+
+  u8_t name_len;
+  u8_t engineid_len;
+
+  u8_t name_start;
+  u8_t engineid_start = 1;
+  u8_t i;
+
+  struct snmp_next_oid_state state;
+
+  u32_t result_temp[LWIP_ARRAYSIZE(usmUserTable_oid_ranges)];
+
+  LWIP_UNUSED_ARG(column);
+
+  snmpv3_get_engine_id(&engineid, &eid_len);
+
+  /* If EngineID might be given */
+  if (row_oid->len > 0) {
+    engineid_len = (u8_t)row_oid->id[0];
+    engineid_start = 1;
+
+    if (engineid_len != eid_len) {
+      /* EngineID length does not match! */
+      return SNMP_ERR_NOSUCHINSTANCE;
+    }
+
+    if (engineid_len > row_oid->len) {
+      /* Verify partial EngineID */
+      snmp_engineid_to_oid(engineid, engineid_oid, row_oid->len - 1);
+      if (!snmp_oid_equal(&row_oid->id[engineid_start], row_oid->len - 1, engineid_oid, row_oid->len - 1)) {
+        return SNMP_ERR_NOSUCHINSTANCE;
+      }
+    } else {
+      /* Verify complete EngineID */
+      snmp_engineid_to_oid(engineid, engineid_oid, engineid_len);
+      if (!snmp_oid_equal(&row_oid->id[engineid_start], engineid_len, engineid_oid, engineid_len)) {
+        return SNMP_ERR_NOSUCHINSTANCE;
+      }
+    }
+
+    /* At this point, the given EngineID (partially) matches the local EngineID.*/
+
+    /* If name might also be given */
+    if (row_oid->len > engineid_start + engineid_len) {
+      name_len = (u8_t)row_oid->id[engineid_start + engineid_len];
+      name_start = engineid_start + engineid_len + 1;
+
+      if (name_len > SNMP_V3_MAX_USER_LENGTH) {
+        /* specified name is too long, max length is 32 according to mib file.*/
+        return SNMP_ERR_NOSUCHINSTANCE;
+      }
+
+      if (row_oid->len < engineid_len + name_len + 2) {
+        /* Partial name given according to oid.*/
+        u8_t tmplen = row_oid->len - engineid_len - 2;
+        if (!snmp_oid_in_range(&row_oid->id[name_start], tmplen, usmUserTable_oid_ranges, tmplen)) {
+          return SNMP_ERR_NOSUCHINSTANCE;
+        }
+      } else {
+        /* Full name given according to oid. Also test for too much data.*/
+        u8_t tmplen = row_oid->len - engineid_len - 2;
+        if (!snmp_oid_in_range(&row_oid->id[name_start], name_len, usmUserTable_oid_ranges, tmplen)) {
+          return SNMP_ERR_NOSUCHINSTANCE;
+        }
+      }
+
+      /* At this point the EngineID and (partial) UserName match the local EngineID and UserName.*/
+    }
+  }
+
+  /* init struct to search next oid */
+  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(usmUserTable_oid_ranges));
+
+  for (i = 0; i < snmpv3_get_amount_of_users(); i++) {
+    u32_t test_oid[LWIP_ARRAYSIZE(usmUserTable_oid_ranges)];
+
+    test_oid[0] = eid_len;
+    snmp_engineid_to_oid(engineid, &test_oid[1], eid_len);
+
+    snmpv3_get_username(username, i);
+
+    test_oid[1 + eid_len] = strlen(username);
+    snmp_name_to_oid(username, &test_oid[2 + eid_len], strlen(username));
+
+    /* check generated OID: is it a candidate for the next one? */
+    snmp_next_oid_check(&state, test_oid, (u8_t)(1 + eid_len + 1 + strlen(username)), LWIP_PTR_NUMERIC_CAST(void *, i));
+  }
+
+  /* did we find a next one? */
+  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+    /* store username for subsequent operations (get/test/set) */
+    memset(username, 0, sizeof(username));
+    snmpv3_get_username(username, LWIP_PTR_NUMERIC_CAST(u8_t, state.reference));
+    cell_instance->reference.ptr = username;
+    cell_instance->reference_len = strlen(username);
+    return SNMP_ERR_NOERROR;
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static s16_t usmusertable_get_value(struct snmp_node_instance *cell_instance, void *value)
+{
+  snmpv3_user_storagetype_t storage_type;
+
+  switch (SNMP_TABLE_GET_COLUMN_FROM_OID(cell_instance->instance_oid.id)) {
+    case 3: /* usmUserSecurityName */
+      MEMCPY(value, cell_instance->reference.ptr, cell_instance->reference_len);
+      return (s16_t)cell_instance->reference_len;
+    case 4: /* usmUserCloneFrom */
+      MEMCPY(value, snmp_zero_dot_zero.id, snmp_zero_dot_zero.len * sizeof(u32_t));
+      return snmp_zero_dot_zero.len * sizeof(u32_t);
+    case 5: { /* usmUserAuthProtocol */
+      const struct snmp_obj_id *auth_algo;
+      snmpv3_auth_algo_t auth_algo_val;
+      snmpv3_get_user((const char *)cell_instance->reference.ptr, &auth_algo_val, NULL, NULL, NULL);
+      auth_algo = snmp_auth_algo_to_oid(auth_algo_val);
+      MEMCPY(value, auth_algo->id, auth_algo->len * sizeof(u32_t));
+      return auth_algo->len * sizeof(u32_t);
+    }
+    case 6: /* usmUserAuthKeyChange */
+      return 0;
+    case 7: /* usmUserOwnAuthKeyChange */
+      return 0;
+    case 8: { /* usmUserPrivProtocol */
+      const struct snmp_obj_id *priv_algo;
+      snmpv3_priv_algo_t priv_algo_val;
+      snmpv3_get_user((const char *)cell_instance->reference.ptr, NULL, NULL, &priv_algo_val, NULL);
+      priv_algo = snmp_priv_algo_to_oid(priv_algo_val);
+      MEMCPY(value, priv_algo->id, priv_algo->len * sizeof(u32_t));
+      return priv_algo->len * sizeof(u32_t);
+    }
+    case 9: /* usmUserPrivKeyChange */
+      return 0;
+    case 10: /* usmUserOwnPrivKeyChange */
+      return 0;
+    case 11: /* usmUserPublic */
+      /* TODO: Implement usmUserPublic */
+      return 0;
+    case 12: /* usmUserStorageType */
+      snmpv3_get_user_storagetype((const char *)cell_instance->reference.ptr, &storage_type);
+      *(s32_t *)value = storage_type;
+      return sizeof(s32_t);
+    case 13: /* usmUserStatus */
+      *(s32_t *)value = 1; /* active */
+      return sizeof(s32_t);
+    default:
+      LWIP_DEBUGF(SNMP_MIB_DEBUG, ("usmusertable_get_value(): unknown id: %"S32_F"\n", SNMP_TABLE_GET_COLUMN_FROM_OID(cell_instance->instance_oid.id)));
+      return 0;
+  }
+}
+
+/* --- usmMIBObjects 1.3.6.1.6.3.15.1 ----------------------------------------------------- */
+static s16_t usmstats_scalars_get_value(const struct snmp_scalar_array_node_def *node, void *value)
+{
+  u32_t *uint_ptr = (u32_t *)value;
+  switch (node->oid) {
+    case 1: /* usmStatsUnsupportedSecLevels */
+      *uint_ptr = snmp_stats.unsupportedseclevels;
+      break;
+    case 2: /* usmStatsNotInTimeWindows */
+      *uint_ptr = snmp_stats.notintimewindows;
+      break;
+    case 3: /* usmStatsUnknownUserNames */
+      *uint_ptr = snmp_stats.unknownusernames;
+      break;
+    case 4: /* usmStatsUnknownEngineIDs */
+      *uint_ptr = snmp_stats.unknownengineids;
+      break;
+    case 5: /* usmStatsWrongDigests */
+      *uint_ptr = snmp_stats.wrongdigests;
+      break;
+    case 6: /* usmStatsDecryptionErrors */
+      *uint_ptr = snmp_stats.decryptionerrors;
+      break;
+    default:
+      LWIP_DEBUGF(SNMP_MIB_DEBUG, ("usmstats_scalars_get_value(): unknown id: %"S32_F"\n", node->oid));
+      return 0;
+  }
+
+  return sizeof(*uint_ptr);
+}
+
+/* --- snmpUsmMIB  ----------------------------------------------------- */
+
+/* --- usmUser 1.3.6.1.6.3.15.1.2 ----------------------------------------------------- */
+
+static const struct snmp_table_col_def usmusertable_columns[] = {
+  {3,  SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserSecurityName */
+  {4,  SNMP_ASN1_TYPE_OBJECT_ID,    SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserCloneFrom */
+  {5,  SNMP_ASN1_TYPE_OBJECT_ID,    SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserAuthProtocol */
+  {6,  SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserAuthKeyChange */
+  {7,  SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserOwnAuthKeyChange */
+  {8,  SNMP_ASN1_TYPE_OBJECT_ID,    SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserPrivProtocol */
+  {9,  SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserPrivKeyChange */
+  {10, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserOwnPrivKeyChange */
+  {11, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserPublic */
+  {12, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserStorageType */
+  {13, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserStatus */
+};
+static const struct snmp_table_node usmusertable = SNMP_TABLE_CREATE(2, usmusertable_columns, usmusertable_get_instance, usmusertable_get_next_instance, usmusertable_get_value, NULL, NULL);
+
+static const struct snmp_node *const usmuser_subnodes[] = {
+  &usmusertable.node.node
+};
+static const struct snmp_tree_node usmuser_treenode = SNMP_CREATE_TREE_NODE(2, usmuser_subnodes);
+
+/* --- usmMIBObjects 1.3.6.1.6.3.15.1 ----------------------------------------------------- */
+static const struct snmp_scalar_array_node_def usmstats_scalars_nodes[] = {
+  {1, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsUnsupportedSecLevels */
+  {2, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsNotInTimeWindows */
+  {3, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsUnknownUserNames */
+  {4, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsUnknownEngineIDs */
+  {5, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsWrongDigests */
+  {6, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsDecryptionErrors */
+};
+static const struct snmp_scalar_array_node usmstats_scalars = SNMP_SCALAR_CREATE_ARRAY_NODE(1, usmstats_scalars_nodes, usmstats_scalars_get_value, NULL, NULL);
+
+static const struct snmp_node *const usmmibobjects_subnodes[] = {
+  &usmstats_scalars.node.node,
+  &usmuser_treenode.node
+};
+static const struct snmp_tree_node usmmibobjects_treenode = SNMP_CREATE_TREE_NODE(1, usmmibobjects_subnodes);
+
+/* --- snmpUsmMIB  ----------------------------------------------------- */
+static const struct snmp_node *const snmpusmmib_subnodes[] = {
+  &usmmibobjects_treenode.node
+};
+static const struct snmp_tree_node snmpusmmib_root = SNMP_CREATE_TREE_NODE(15, snmpusmmib_subnodes);
+static const u32_t snmpusmmib_base_oid[] = {1, 3, 6, 1, 6, 3, 15};
+const struct snmp_mib snmpusmmib = {snmpusmmib_base_oid, LWIP_ARRAYSIZE(snmpusmmib_base_oid), &snmpusmmib_root.node};
+
+#endif /* LWIP_SNMP */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_table.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_table.c
new file mode 100644 (file)
index 0000000..4b77a47
--- /dev/null
@@ -0,0 +1,342 @@
+/**
+ * @file
+ * SNMP table support implementation.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel <info@cl-soft.de>
+ *
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_table.h"
+#include <string.h>
+
+snmp_err_t snmp_table_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
+{
+  snmp_err_t ret = SNMP_ERR_NOSUCHINSTANCE;
+  const struct snmp_table_node *table_node = (const struct snmp_table_node *)(const void *)instance->node;
+
+  LWIP_UNUSED_ARG(root_oid);
+  LWIP_UNUSED_ARG(root_oid_len);
+
+  /* check min. length (fixed row entry definition, column, row instance oid with at least one entry */
+  /* fixed row entry always has oid 1 */
+  if ((instance->instance_oid.len >= 3) && (instance->instance_oid.id[0] == 1)) {
+    /* search column */
+    const struct snmp_table_col_def *col_def = table_node->columns;
+    u16_t i = table_node->column_count;
+    while (i > 0) {
+      if (col_def->index == instance->instance_oid.id[1]) {
+        break;
+      }
+
+      col_def++;
+      i--;
+    }
+
+    if (i > 0) {
+      /* everything may be overwritten by get_cell_instance_method() in order to implement special handling for single columns/cells */
+      instance->asn1_type = col_def->asn1_type;
+      instance->access    = col_def->access;
+      instance->get_value = table_node->get_value;
+      instance->set_test  = table_node->set_test;
+      instance->set_value = table_node->set_value;
+
+      ret = table_node->get_cell_instance(
+              &(instance->instance_oid.id[1]),
+              &(instance->instance_oid.id[2]),
+              instance->instance_oid.len - 2,
+              instance);
+    }
+  }
+
+  return ret;
+}
+
+snmp_err_t snmp_table_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
+{
+  const struct snmp_table_node *table_node = (const struct snmp_table_node *)(const void *)instance->node;
+  const struct snmp_table_col_def *col_def;
+  struct snmp_obj_id row_oid;
+  u32_t column = 0;
+  snmp_err_t result;
+
+  LWIP_UNUSED_ARG(root_oid);
+  LWIP_UNUSED_ARG(root_oid_len);
+
+  /* check that first part of id is 0 or 1, referencing fixed row entry */
+  if ((instance->instance_oid.len > 0) && (instance->instance_oid.id[0] > 1)) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+  if (instance->instance_oid.len > 1) {
+    column = instance->instance_oid.id[1];
+  }
+  if (instance->instance_oid.len > 2) {
+    snmp_oid_assign(&row_oid, &(instance->instance_oid.id[2]), instance->instance_oid.len - 2);
+  } else {
+    row_oid.len = 0;
+  }
+
+  instance->get_value    = table_node->get_value;
+  instance->set_test     = table_node->set_test;
+  instance->set_value    = table_node->set_value;
+
+  /* resolve column and value */
+  do {
+    u16_t i;
+    const struct snmp_table_col_def *next_col_def = NULL;
+    col_def = table_node->columns;
+
+    for (i = 0; i < table_node->column_count; i++) {
+      if (col_def->index == column) {
+        next_col_def = col_def;
+        break;
+      } else if ((col_def->index > column) && ((next_col_def == NULL) || (col_def->index < next_col_def->index))) {
+        next_col_def = col_def;
+      }
+      col_def++;
+    }
+
+    if (next_col_def == NULL) {
+      /* no further column found */
+      return SNMP_ERR_NOSUCHINSTANCE;
+    }
+
+    instance->asn1_type          = next_col_def->asn1_type;
+    instance->access             = next_col_def->access;
+
+    result = table_node->get_next_cell_instance(
+               &next_col_def->index,
+               &row_oid,
+               instance);
+
+    if (result == SNMP_ERR_NOERROR) {
+      col_def = next_col_def;
+      break;
+    }
+
+    row_oid.len = 0; /* reset row_oid because we switch to next column and start with the first entry there */
+    column = next_col_def->index + 1;
+  } while (1);
+
+  /* build resulting oid */
+  instance->instance_oid.len   = 2;
+  instance->instance_oid.id[0] = 1;
+  instance->instance_oid.id[1] = col_def->index;
+  snmp_oid_append(&instance->instance_oid, row_oid.id, row_oid.len);
+
+  return SNMP_ERR_NOERROR;
+}
+
+
+snmp_err_t snmp_table_simple_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
+{
+  snmp_err_t ret = SNMP_ERR_NOSUCHINSTANCE;
+  const struct snmp_table_simple_node *table_node = (const struct snmp_table_simple_node *)(const void *)instance->node;
+
+  LWIP_UNUSED_ARG(root_oid);
+  LWIP_UNUSED_ARG(root_oid_len);
+
+  /* check min. length (fixed row entry definition, column, row instance oid with at least one entry */
+  /* fixed row entry always has oid 1 */
+  if ((instance->instance_oid.len >= 3) && (instance->instance_oid.id[0] == 1)) {
+    ret = table_node->get_cell_value(
+            &(instance->instance_oid.id[1]),
+            &(instance->instance_oid.id[2]),
+            instance->instance_oid.len - 2,
+            &instance->reference,
+            &instance->reference_len);
+
+    if (ret == SNMP_ERR_NOERROR) {
+      /* search column */
+      const struct snmp_table_simple_col_def *col_def = table_node->columns;
+      u32_t i = table_node->column_count;
+      while (i > 0) {
+        if (col_def->index == instance->instance_oid.id[1]) {
+          break;
+        }
+
+        col_def++;
+        i--;
+      }
+
+      if (i > 0) {
+        instance->asn1_type = col_def->asn1_type;
+        instance->access    = SNMP_NODE_INSTANCE_READ_ONLY;
+        instance->set_test  = NULL;
+        instance->set_value = NULL;
+
+        switch (col_def->data_type) {
+          case SNMP_VARIANT_VALUE_TYPE_U32:
+            instance->get_value = snmp_table_extract_value_from_u32ref;
+            break;
+          case SNMP_VARIANT_VALUE_TYPE_S32:
+            instance->get_value = snmp_table_extract_value_from_s32ref;
+            break;
+          case SNMP_VARIANT_VALUE_TYPE_PTR: /* fall through */
+          case SNMP_VARIANT_VALUE_TYPE_CONST_PTR:
+            instance->get_value = snmp_table_extract_value_from_refconstptr;
+            break;
+          default:
+            LWIP_DEBUGF(SNMP_DEBUG, ("snmp_table_simple_get_instance(): unknown column data_type: %d\n", col_def->data_type));
+            return SNMP_ERR_GENERROR;
+        }
+
+        ret = SNMP_ERR_NOERROR;
+      } else {
+        ret = SNMP_ERR_NOSUCHINSTANCE;
+      }
+    }
+  }
+
+  return ret;
+}
+
+snmp_err_t snmp_table_simple_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
+{
+  const struct snmp_table_simple_node *table_node = (const struct snmp_table_simple_node *)(const void *)instance->node;
+  const struct snmp_table_simple_col_def *col_def;
+  struct snmp_obj_id row_oid;
+  u32_t column = 0;
+  snmp_err_t result;
+
+  LWIP_UNUSED_ARG(root_oid);
+  LWIP_UNUSED_ARG(root_oid_len);
+
+  /* check that first part of id is 0 or 1, referencing fixed row entry */
+  if ((instance->instance_oid.len > 0) && (instance->instance_oid.id[0] > 1)) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+  if (instance->instance_oid.len > 1) {
+    column = instance->instance_oid.id[1];
+  }
+  if (instance->instance_oid.len > 2) {
+    snmp_oid_assign(&row_oid, &(instance->instance_oid.id[2]), instance->instance_oid.len - 2);
+  } else {
+    row_oid.len = 0;
+  }
+
+  /* resolve column and value */
+  do {
+    u32_t i;
+    const struct snmp_table_simple_col_def *next_col_def = NULL;
+    col_def = table_node->columns;
+
+    for (i = 0; i < table_node->column_count; i++) {
+      if (col_def->index == column) {
+        next_col_def = col_def;
+        break;
+      } else if ((col_def->index > column) && ((next_col_def == NULL) ||
+                 (col_def->index < next_col_def->index))) {
+        next_col_def = col_def;
+      }
+      col_def++;
+    }
+
+    if (next_col_def == NULL) {
+      /* no further column found */
+      return SNMP_ERR_NOSUCHINSTANCE;
+    }
+
+    result = table_node->get_next_cell_instance_and_value(
+               &next_col_def->index,
+               &row_oid,
+               &instance->reference,
+               &instance->reference_len);
+
+    if (result == SNMP_ERR_NOERROR) {
+      col_def = next_col_def;
+      break;
+    }
+
+    row_oid.len = 0; /* reset row_oid because we switch to next column and start with the first entry there */
+    column = next_col_def->index + 1;
+  } while (1);
+
+  instance->asn1_type = col_def->asn1_type;
+  instance->access    = SNMP_NODE_INSTANCE_READ_ONLY;
+  instance->set_test  = NULL;
+  instance->set_value = NULL;
+
+  switch (col_def->data_type) {
+    case SNMP_VARIANT_VALUE_TYPE_U32:
+      instance->get_value = snmp_table_extract_value_from_u32ref;
+      break;
+    case SNMP_VARIANT_VALUE_TYPE_S32:
+      instance->get_value = snmp_table_extract_value_from_s32ref;
+      break;
+    case SNMP_VARIANT_VALUE_TYPE_PTR: /* fall through */
+    case SNMP_VARIANT_VALUE_TYPE_CONST_PTR:
+      instance->get_value = snmp_table_extract_value_from_refconstptr;
+      break;
+    default:
+      LWIP_DEBUGF(SNMP_DEBUG, ("snmp_table_simple_get_instance(): unknown column data_type: %d\n", col_def->data_type));
+      return SNMP_ERR_GENERROR;
+  }
+
+  /* build resulting oid */
+  instance->instance_oid.len   = 2;
+  instance->instance_oid.id[0] = 1;
+  instance->instance_oid.id[1] = col_def->index;
+  snmp_oid_append(&instance->instance_oid, row_oid.id, row_oid.len);
+
+  return SNMP_ERR_NOERROR;
+}
+
+
+s16_t
+snmp_table_extract_value_from_s32ref(struct snmp_node_instance *instance, void *value)
+{
+  s32_t *dst = (s32_t *)value;
+  *dst = instance->reference.s32;
+  return sizeof(*dst);
+}
+
+s16_t
+snmp_table_extract_value_from_u32ref(struct snmp_node_instance *instance, void *value)
+{
+  u32_t *dst = (u32_t *)value;
+  *dst = instance->reference.u32;
+  return sizeof(*dst);
+}
+
+s16_t
+snmp_table_extract_value_from_refconstptr(struct snmp_node_instance *instance, void *value)
+{
+  MEMCPY(value, instance->reference.const_ptr, instance->reference_len);
+  return (u16_t)instance->reference_len;
+}
+
+#endif /* LWIP_SNMP */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_threadsync.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_threadsync.c
new file mode 100644 (file)
index 0000000..56d9886
--- /dev/null
@@ -0,0 +1,231 @@
+/**
+ * @file
+ * SNMP thread synchronization implementation.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP && (NO_SYS == 0) /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp_threadsync.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/sys.h"
+#include <string.h>
+
+static void
+call_synced_function(struct threadsync_data *call_data, snmp_threadsync_called_fn fn)
+{
+  sys_mutex_lock(&call_data->threadsync_node->instance->sem_usage_mutex);
+  call_data->threadsync_node->instance->sync_fn(fn, call_data);
+  sys_sem_wait(&call_data->threadsync_node->instance->sem);
+  sys_mutex_unlock(&call_data->threadsync_node->instance->sem_usage_mutex);
+}
+
+static void
+threadsync_get_value_synced(void *ctx)
+{
+  struct threadsync_data *call_data = (struct threadsync_data *)ctx;
+
+  if (call_data->proxy_instance.get_value != NULL) {
+    call_data->retval.s16 = call_data->proxy_instance.get_value(&call_data->proxy_instance, call_data->arg1.value);
+  } else {
+    call_data->retval.s16 = -1;
+  }
+
+  sys_sem_signal(&call_data->threadsync_node->instance->sem);
+}
+
+static s16_t
+threadsync_get_value(struct snmp_node_instance *instance, void *value)
+{
+  struct threadsync_data *call_data = (struct threadsync_data *)instance->reference.ptr;
+
+  call_data->arg1.value = value;
+  call_synced_function(call_data, threadsync_get_value_synced);
+
+  return call_data->retval.s16;
+}
+
+static void
+threadsync_set_test_synced(void *ctx)
+{
+  struct threadsync_data *call_data = (struct threadsync_data *)ctx;
+
+  if (call_data->proxy_instance.set_test != NULL) {
+    call_data->retval.err = call_data->proxy_instance.set_test(&call_data->proxy_instance, call_data->arg2.len, call_data->arg1.value);
+  } else {
+    call_data->retval.err = SNMP_ERR_NOTWRITABLE;
+  }
+
+  sys_sem_signal(&call_data->threadsync_node->instance->sem);
+}
+
+static snmp_err_t
+threadsync_set_test(struct snmp_node_instance *instance, u16_t len, void *value)
+{
+  struct threadsync_data *call_data = (struct threadsync_data *)instance->reference.ptr;
+
+  call_data->arg1.value = value;
+  call_data->arg2.len = len;
+  call_synced_function(call_data, threadsync_set_test_synced);
+
+  return call_data->retval.err;
+}
+
+static void
+threadsync_set_value_synced(void *ctx)
+{
+  struct threadsync_data *call_data = (struct threadsync_data *)ctx;
+
+  if (call_data->proxy_instance.set_value != NULL) {
+    call_data->retval.err = call_data->proxy_instance.set_value(&call_data->proxy_instance, call_data->arg2.len, call_data->arg1.value);
+  } else {
+    call_data->retval.err = SNMP_ERR_NOTWRITABLE;
+  }
+
+  sys_sem_signal(&call_data->threadsync_node->instance->sem);
+}
+
+static snmp_err_t
+threadsync_set_value(struct snmp_node_instance *instance, u16_t len, void *value)
+{
+  struct threadsync_data *call_data = (struct threadsync_data *)instance->reference.ptr;
+
+  call_data->arg1.value = value;
+  call_data->arg2.len = len;
+  call_synced_function(call_data, threadsync_set_value_synced);
+
+  return call_data->retval.err;
+}
+
+static void
+threadsync_release_instance_synced(void *ctx)
+{
+  struct threadsync_data *call_data = (struct threadsync_data *)ctx;
+
+  call_data->proxy_instance.release_instance(&call_data->proxy_instance);
+
+  sys_sem_signal(&call_data->threadsync_node->instance->sem);
+}
+
+static void
+threadsync_release_instance(struct snmp_node_instance *instance)
+{
+  struct threadsync_data *call_data = (struct threadsync_data *)instance->reference.ptr;
+
+  if (call_data->proxy_instance.release_instance != NULL) {
+    call_synced_function(call_data, threadsync_release_instance_synced);
+  }
+}
+
+static void
+get_instance_synced(void *ctx)
+{
+  struct threadsync_data *call_data   = (struct threadsync_data *)ctx;
+  const struct snmp_leaf_node *leaf   = (const struct snmp_leaf_node *)(const void *)call_data->proxy_instance.node;
+
+  call_data->retval.err = leaf->get_instance(call_data->arg1.root_oid, call_data->arg2.root_oid_len, &call_data->proxy_instance);
+
+  sys_sem_signal(&call_data->threadsync_node->instance->sem);
+}
+
+static void
+get_next_instance_synced(void *ctx)
+{
+  struct threadsync_data *call_data   = (struct threadsync_data *)ctx;
+  const struct snmp_leaf_node *leaf   = (const struct snmp_leaf_node *)(const void *)call_data->proxy_instance.node;
+
+  call_data->retval.err = leaf->get_next_instance(call_data->arg1.root_oid, call_data->arg2.root_oid_len, &call_data->proxy_instance);
+
+  sys_sem_signal(&call_data->threadsync_node->instance->sem);
+}
+
+static snmp_err_t
+do_sync(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance, snmp_threadsync_called_fn fn)
+{
+  const struct snmp_threadsync_node *threadsync_node = (const struct snmp_threadsync_node *)(const void *)instance->node;
+  struct threadsync_data *call_data = &threadsync_node->instance->data;
+
+  if (threadsync_node->node.node.oid != threadsync_node->target->node.oid) {
+    LWIP_DEBUGF(SNMP_DEBUG, ("Sync node OID does not match target node OID"));
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  memset(&call_data->proxy_instance, 0, sizeof(call_data->proxy_instance));
+
+  instance->reference.ptr = call_data;
+  snmp_oid_assign(&call_data->proxy_instance.instance_oid, instance->instance_oid.id, instance->instance_oid.len);
+
+  call_data->proxy_instance.node = &threadsync_node->target->node;
+  call_data->threadsync_node     = threadsync_node;
+
+  call_data->arg1.root_oid       = root_oid;
+  call_data->arg2.root_oid_len   = root_oid_len;
+  call_synced_function(call_data, fn);
+
+  if (call_data->retval.err == SNMP_ERR_NOERROR) {
+    instance->access           = call_data->proxy_instance.access;
+    instance->asn1_type        = call_data->proxy_instance.asn1_type;
+    instance->release_instance = threadsync_release_instance;
+    instance->get_value        = (call_data->proxy_instance.get_value != NULL) ? threadsync_get_value : NULL;
+    instance->set_value        = (call_data->proxy_instance.set_value != NULL) ? threadsync_set_value : NULL;
+    instance->set_test         = (call_data->proxy_instance.set_test != NULL) ?  threadsync_set_test  : NULL;
+    snmp_oid_assign(&instance->instance_oid, call_data->proxy_instance.instance_oid.id, call_data->proxy_instance.instance_oid.len);
+  }
+
+  return call_data->retval.err;
+}
+
+snmp_err_t
+snmp_threadsync_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
+{
+  return do_sync(root_oid, root_oid_len, instance, get_instance_synced);
+}
+
+snmp_err_t
+snmp_threadsync_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
+{
+  return do_sync(root_oid, root_oid_len, instance, get_next_instance_synced);
+}
+
+/** Initializes thread synchronization instance */
+void snmp_threadsync_init(struct snmp_threadsync_instance *instance, snmp_threadsync_synchronizer_fn sync_fn)
+{
+  err_t err = sys_mutex_new(&instance->sem_usage_mutex);
+  LWIP_ASSERT("Failed to set up mutex", err == ERR_OK);
+  err = sys_sem_new(&instance->sem, 0);
+  LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
+  LWIP_ASSERT("Failed to set up semaphore", err == ERR_OK);
+  instance->sync_fn = sync_fn;
+}
+
+#endif /* LWIP_SNMP */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_traps.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmp_traps.c
new file mode 100644 (file)
index 0000000..3c9c817
--- /dev/null
@@ -0,0 +1,458 @@
+/**
+ * @file
+ * SNMPv1 traps implementation.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel
+ *         Christiaan Simons <christiaan.simons@axon.tv>
+ *
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include <string.h>
+
+#include "lwip/snmp.h"
+#include "lwip/sys.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/prot/iana.h"
+#include "snmp_msg.h"
+#include "snmp_asn1.h"
+#include "snmp_core_priv.h"
+
+struct snmp_msg_trap {
+  /* source enterprise ID (sysObjectID) */
+  const struct snmp_obj_id *enterprise;
+  /* source IP address, raw network order format */
+  ip_addr_t sip;
+  /* generic trap code */
+  u32_t gen_trap;
+  /* specific trap code */
+  u32_t spc_trap;
+  /* timestamp */
+  u32_t ts;
+  /* snmp_version */
+  u32_t snmp_version;
+
+  /* output trap lengths used in ASN encoding */
+  /* encoding pdu length */
+  u16_t pdulen;
+  /* encoding community length */
+  u16_t comlen;
+  /* encoding sequence length */
+  u16_t seqlen;
+  /* encoding varbinds sequence length */
+  u16_t vbseqlen;
+};
+
+static u16_t snmp_trap_varbind_sum(struct snmp_msg_trap *trap, struct snmp_varbind *varbinds);
+static u16_t snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len);
+static err_t snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream);
+static err_t snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds);
+
+#define BUILD_EXEC(code) \
+  if ((code) != ERR_OK) { \
+    LWIP_DEBUGF(SNMP_DEBUG, ("SNMP error during creation of outbound trap frame!")); \
+    return ERR_ARG; \
+  }
+
+/** Agent community string for sending traps */
+extern const char *snmp_community_trap;
+
+void *snmp_traps_handle;
+
+struct snmp_trap_dst {
+  /* destination IP address in network order */
+  ip_addr_t dip;
+  /* set to 0 when disabled, >0 when enabled */
+  u8_t enable;
+};
+static struct snmp_trap_dst trap_dst[SNMP_TRAP_DESTINATIONS];
+
+static u8_t snmp_auth_traps_enabled = 0;
+
+/**
+ * @ingroup snmp_traps
+ * Sets enable switch for this trap destination.
+ * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
+ * @param enable switch if 0 destination is disabled >0 enabled.
+ */
+void
+snmp_trap_dst_enable(u8_t dst_idx, u8_t enable)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  if (dst_idx < SNMP_TRAP_DESTINATIONS) {
+    trap_dst[dst_idx].enable = enable;
+  }
+}
+
+/**
+ * @ingroup snmp_traps
+ * Sets IPv4 address for this trap destination.
+ * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
+ * @param dst IPv4 address in host order.
+ */
+void
+snmp_trap_dst_ip_set(u8_t dst_idx, const ip_addr_t *dst)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  if (dst_idx < SNMP_TRAP_DESTINATIONS) {
+    ip_addr_set(&trap_dst[dst_idx].dip, dst);
+  }
+}
+
+/**
+ * @ingroup snmp_traps
+ * Enable/disable authentication traps
+ */
+void
+snmp_set_auth_traps_enabled(u8_t enable)
+{
+  snmp_auth_traps_enabled = enable;
+}
+
+/**
+ * @ingroup snmp_traps
+ * Get authentication traps enabled state
+ */
+u8_t
+snmp_get_auth_traps_enabled(void)
+{
+  return snmp_auth_traps_enabled;
+}
+
+
+/**
+ * @ingroup snmp_traps
+ * Sends a generic or enterprise specific trap message.
+ *
+ * @param eoid points to enterprise object identifier
+ * @param generic_trap is the trap code
+ * @param specific_trap used for enterprise traps when generic_trap == 6
+ * @param varbinds linked list of varbinds to be sent
+ * @return ERR_OK when success, ERR_MEM if we're out of memory
+ *
+ * @note the use of the enterprise identifier field
+ * is per RFC1215.
+ * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps
+ * and .iso.org.dod.internet.private.enterprises.yourenterprise
+ * (sysObjectID) for specific traps.
+ */
+err_t
+snmp_send_trap(const struct snmp_obj_id *eoid, s32_t generic_trap, s32_t specific_trap, struct snmp_varbind *varbinds)
+{
+  struct snmp_msg_trap trap_msg;
+  struct snmp_trap_dst *td;
+  struct pbuf *p;
+  u16_t i, tot_len;
+  err_t err = ERR_OK;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  trap_msg.snmp_version = 0;
+
+  for (i = 0, td = &trap_dst[0]; i < SNMP_TRAP_DESTINATIONS; i++, td++) {
+    if ((td->enable != 0) && !ip_addr_isany(&td->dip)) {
+      /* lookup current source address for this dst */
+      if (snmp_get_local_ip_for_dst(snmp_traps_handle, &td->dip, &trap_msg.sip)) {
+        if (eoid == NULL) {
+          trap_msg.enterprise = snmp_get_device_enterprise_oid();
+        } else {
+          trap_msg.enterprise = eoid;
+        }
+
+        trap_msg.gen_trap = generic_trap;
+        if (generic_trap == SNMP_GENTRAP_ENTERPRISE_SPECIFIC) {
+          trap_msg.spc_trap = specific_trap;
+        } else {
+          trap_msg.spc_trap = 0;
+        }
+
+        MIB2_COPY_SYSUPTIME_TO(&trap_msg.ts);
+
+        /* pass 0, calculate length fields */
+        tot_len = snmp_trap_varbind_sum(&trap_msg, varbinds);
+        tot_len = snmp_trap_header_sum(&trap_msg, tot_len);
+
+        /* allocate pbuf(s) */
+        p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_RAM);
+        if (p != NULL) {
+          struct snmp_pbuf_stream pbuf_stream;
+          snmp_pbuf_stream_init(&pbuf_stream, p, 0, tot_len);
+
+          /* pass 1, encode packet into the pbuf(s) */
+          snmp_trap_header_enc(&trap_msg, &pbuf_stream);
+          snmp_trap_varbind_enc(&trap_msg, &pbuf_stream, varbinds);
+
+          snmp_stats.outtraps++;
+          snmp_stats.outpkts++;
+
+          /** send to the TRAP destination */
+          snmp_sendto(snmp_traps_handle, p, &td->dip, LWIP_IANA_PORT_SNMP_TRAP);
+          pbuf_free(p);
+        } else {
+          err = ERR_MEM;
+        }
+      } else {
+        /* routing error */
+        err = ERR_RTE;
+      }
+    }
+  }
+  return err;
+}
+
+/**
+ * @ingroup snmp_traps
+ * Send generic SNMP trap
+ */
+err_t
+snmp_send_trap_generic(s32_t generic_trap)
+{
+  static const struct snmp_obj_id oid = { 7, { 1, 3, 6, 1, 2, 1, 11 } };
+  return snmp_send_trap(&oid, generic_trap, 0, NULL);
+}
+
+/**
+ * @ingroup snmp_traps
+ * Send specific SNMP trap with variable bindings
+ */
+err_t
+snmp_send_trap_specific(s32_t specific_trap, struct snmp_varbind *varbinds)
+{
+  return snmp_send_trap(NULL, SNMP_GENTRAP_ENTERPRISE_SPECIFIC, specific_trap, varbinds);
+}
+
+/**
+ * @ingroup snmp_traps
+ * Send coldstart trap
+ */
+void
+snmp_coldstart_trap(void)
+{
+  snmp_send_trap_generic(SNMP_GENTRAP_COLDSTART);
+}
+
+/**
+ * @ingroup snmp_traps
+ * Send authentication failure trap (used internally by agent)
+ */
+void
+snmp_authfail_trap(void)
+{
+  if (snmp_auth_traps_enabled != 0) {
+    snmp_send_trap_generic(SNMP_GENTRAP_AUTH_FAILURE);
+  }
+}
+
+static u16_t
+snmp_trap_varbind_sum(struct snmp_msg_trap *trap, struct snmp_varbind *varbinds)
+{
+  struct snmp_varbind *varbind;
+  u16_t tot_len;
+  u8_t tot_len_len;
+
+  tot_len = 0;
+  varbind = varbinds;
+  while (varbind != NULL) {
+    struct snmp_varbind_len len;
+
+    if (snmp_varbind_length(varbind, &len) == ERR_OK) {
+      tot_len += 1 + len.vb_len_len + len.vb_value_len;
+    }
+
+    varbind = varbind->next;
+  }
+
+  trap->vbseqlen = tot_len;
+  snmp_asn1_enc_length_cnt(trap->vbseqlen, &tot_len_len);
+  tot_len += 1 + tot_len_len;
+
+  return tot_len;
+}
+
+/**
+ * Sums trap header field lengths from tail to head and
+ * returns trap_header_lengths for second encoding pass.
+ *
+ * @param trap Trap message
+ * @param vb_len varbind-list length
+ * @return the required length for encoding the trap header
+ */
+static u16_t
+snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len)
+{
+  u16_t tot_len;
+  u16_t len;
+  u8_t lenlen;
+
+  tot_len = vb_len;
+
+  snmp_asn1_enc_u32t_cnt(trap->ts, &len);
+  snmp_asn1_enc_length_cnt(len, &lenlen);
+  tot_len += 1 + len + lenlen;
+
+  snmp_asn1_enc_s32t_cnt(trap->spc_trap, &len);
+  snmp_asn1_enc_length_cnt(len, &lenlen);
+  tot_len += 1 + len + lenlen;
+
+  snmp_asn1_enc_s32t_cnt(trap->gen_trap, &len);
+  snmp_asn1_enc_length_cnt(len, &lenlen);
+  tot_len += 1 + len + lenlen;
+
+  if (IP_IS_V6_VAL(trap->sip)) {
+#if LWIP_IPV6
+    len = sizeof(ip_2_ip6(&trap->sip)->addr);
+#endif
+  } else {
+#if LWIP_IPV4
+    len = sizeof(ip_2_ip4(&trap->sip)->addr);
+#endif
+  }
+  snmp_asn1_enc_length_cnt(len, &lenlen);
+  tot_len += 1 + len + lenlen;
+
+  snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &len);
+  snmp_asn1_enc_length_cnt(len, &lenlen);
+  tot_len += 1 + len + lenlen;
+
+  trap->pdulen = tot_len;
+  snmp_asn1_enc_length_cnt(trap->pdulen, &lenlen);
+  tot_len += 1 + lenlen;
+
+  trap->comlen = (u16_t)LWIP_MIN(strlen(snmp_community_trap), 0xFFFF);
+  snmp_asn1_enc_length_cnt(trap->comlen, &lenlen);
+  tot_len += 1 + lenlen + trap->comlen;
+
+  snmp_asn1_enc_s32t_cnt(trap->snmp_version, &len);
+  snmp_asn1_enc_length_cnt(len, &lenlen);
+  tot_len += 1 + len + lenlen;
+
+  trap->seqlen = tot_len;
+  snmp_asn1_enc_length_cnt(trap->seqlen, &lenlen);
+  tot_len += 1 + lenlen;
+
+  return tot_len;
+}
+
+static err_t
+snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds)
+{
+  struct snmp_asn1_tlv tlv;
+  struct snmp_varbind *varbind;
+
+  varbind = varbinds;
+
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->vbseqlen);
+  BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+
+  while (varbind != NULL) {
+    BUILD_EXEC( snmp_append_outbound_varbind(pbuf_stream, varbind) );
+
+    varbind = varbind->next;
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Encodes trap header from head to tail.
+ */
+static err_t
+snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream)
+{
+  struct snmp_asn1_tlv tlv;
+
+  /* 'Message' sequence */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->seqlen);
+  BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+
+  /* version */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+  snmp_asn1_enc_s32t_cnt(trap->snmp_version, &tlv.value_len);
+  BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+  BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->snmp_version) );
+
+  /* community */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, trap->comlen);
+  BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+  BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream,  (const u8_t *)snmp_community_trap, trap->comlen) );
+
+  /* 'PDU' sequence */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_TRAP), 0, trap->pdulen);
+  BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+
+  /* object ID */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OBJECT_ID, 0, 0);
+  snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &tlv.value_len);
+  BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+  BUILD_EXEC( snmp_asn1_enc_oid(pbuf_stream, trap->enterprise->id, trap->enterprise->len) );
+
+  /* IP addr */
+  if (IP_IS_V6_VAL(trap->sip)) {
+#if LWIP_IPV6
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip6(&trap->sip)->addr));
+    BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+    BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip6(&trap->sip)->addr, sizeof(ip_2_ip6(&trap->sip)->addr)) );
+#endif
+  } else {
+#if LWIP_IPV4
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip4(&trap->sip)->addr));
+    BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+    BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip4(&trap->sip)->addr, sizeof(ip_2_ip4(&trap->sip)->addr)) );
+#endif
+  }
+
+  /* trap length */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+  snmp_asn1_enc_s32t_cnt(trap->gen_trap, &tlv.value_len);
+  BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+  BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->gen_trap) );
+
+  /* specific trap */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+  snmp_asn1_enc_s32t_cnt(trap->spc_trap, &tlv.value_len);
+  BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+  BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->spc_trap) );
+
+  /* timestamp */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_TIMETICKS, 0, 0);
+  snmp_asn1_enc_s32t_cnt(trap->ts, &tlv.value_len);
+  BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+  BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->ts) );
+
+  return ERR_OK;
+}
+
+#endif /* LWIP_SNMP */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmpv3.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmpv3.c
new file mode 100644 (file)
index 0000000..ed5a076
--- /dev/null
@@ -0,0 +1,136 @@
+/**
+ * @file
+ * Additional SNMPv3 functionality RFC3414 and RFC3826.
+ */
+
+/*
+ * Copyright (c) 2016 Elias Oenal.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * Author: Elias Oenal <lwip@eliasoenal.com>
+ */
+
+#include "snmpv3_priv.h"
+#include "lwip/apps/snmpv3.h"
+#include "lwip/sys.h"
+#include <string.h>
+
+#if LWIP_SNMP && LWIP_SNMP_V3
+
+#ifdef LWIP_SNMPV3_INCLUDE_ENGINE
+#include LWIP_SNMPV3_INCLUDE_ENGINE
+#endif
+
+#define SNMP_MAX_TIME_BOOT 2147483647UL
+
+/** Call this if engine has been changed. Has to reset boots, see below */
+void
+snmpv3_engine_id_changed(void)
+{
+  snmpv3_set_engine_boots(0);
+}
+
+/** According to RFC3414 2.2.2.
+ *
+ * The number of times that the SNMP engine has
+ * (re-)initialized itself since snmpEngineID
+ * was last configured.
+ */
+s32_t
+snmpv3_get_engine_boots_internal(void)
+{
+  if (snmpv3_get_engine_boots() == 0 ||
+      snmpv3_get_engine_boots() < SNMP_MAX_TIME_BOOT) {
+    return snmpv3_get_engine_boots();
+  }
+
+  snmpv3_set_engine_boots(SNMP_MAX_TIME_BOOT);
+  return snmpv3_get_engine_boots();
+}
+
+/** RFC3414 2.2.2.
+ *
+ * Once the timer reaches 2147483647 it gets reset to zero and the
+ * engine boot ups get incremented.
+ */
+s32_t
+snmpv3_get_engine_time_internal(void)
+{
+  if (snmpv3_get_engine_time() >= SNMP_MAX_TIME_BOOT) {
+    snmpv3_reset_engine_time();
+
+    if (snmpv3_get_engine_boots() < SNMP_MAX_TIME_BOOT - 1) {
+      snmpv3_set_engine_boots(snmpv3_get_engine_boots() + 1);
+    } else {
+      snmpv3_set_engine_boots(SNMP_MAX_TIME_BOOT);
+    }
+  }
+
+  return snmpv3_get_engine_time();
+}
+
+#if LWIP_SNMP_V3_CRYPTO
+
+/* This function ignores the byte order suggestion in RFC3414
+ * since it simply doesn't influence the effectiveness of an IV.
+ *
+ * Implementing RFC3826 priv param algorithm if LWIP_RAND is available.
+ *
+ * @todo: This is a potential thread safety issue.
+ */
+err_t
+snmpv3_build_priv_param(u8_t *priv_param)
+{
+#ifdef LWIP_RAND /* Based on RFC3826 */
+  static u8_t init;
+  static u32_t priv1, priv2;
+
+  /* Lazy initialisation */
+  if (init == 0) {
+    init = 1;
+    priv1 = LWIP_RAND();
+    priv2 = LWIP_RAND();
+  }
+
+  SMEMCPY(&priv_param[0], &priv1, sizeof(priv1));
+  SMEMCPY(&priv_param[4], &priv2, sizeof(priv2));
+
+  /* Emulate 64bit increment */
+  priv1++;
+  if (!priv1) { /* Overflow */
+    priv2++;
+  }
+#else /* Based on RFC3414 */
+  static u32_t ctr;
+  u32_t boots = snmpv3_get_engine_boots_internal();
+  SMEMCPY(&priv_param[0], &boots, 4);
+  SMEMCPY(&priv_param[4], &ctr, 4);
+  ctr++;
+#endif
+  return ERR_OK;
+}
+#endif /* LWIP_SNMP_V3_CRYPTO */
+
+#endif
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmpv3_mbedtls.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmpv3_mbedtls.c
new file mode 100644 (file)
index 0000000..48c1a81
--- /dev/null
@@ -0,0 +1,342 @@
+/**
+ * @file
+ * SNMPv3 crypto/auth functions implemented for ARM mbedtls.
+ */
+
+/*
+ * Copyright (c) 2016 Elias Oenal and Dirk Ziegelmeier.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * Author: Elias Oenal <lwip@eliasoenal.com>
+ *         Dirk Ziegelmeier <dirk@ziegelmeier.net>
+ */
+
+#include "lwip/apps/snmpv3.h"
+#include "snmpv3_priv.h"
+#include "lwip/arch.h"
+#include "snmp_msg.h"
+#include "lwip/sys.h"
+#include <string.h>
+
+#if LWIP_SNMP && LWIP_SNMP_V3 && LWIP_SNMP_V3_MBEDTLS
+
+#include "mbedtls/md.h"
+#include "mbedtls/cipher.h"
+
+#include "mbedtls/md5.h"
+#include "mbedtls/sha1.h"
+
+err_t
+snmpv3_auth(struct snmp_pbuf_stream *stream, u16_t length,
+            const u8_t *key, snmpv3_auth_algo_t algo, u8_t *hmac_out)
+{
+  u32_t i;
+  u8_t key_len;
+  const mbedtls_md_info_t *md_info;
+  mbedtls_md_context_t ctx;
+  struct snmp_pbuf_stream read_stream;
+  snmp_pbuf_stream_init(&read_stream, stream->pbuf, stream->offset, stream->length);
+
+  if (algo == SNMP_V3_AUTH_ALGO_MD5) {
+    md_info = mbedtls_md_info_from_type(MBEDTLS_MD_MD5);
+    key_len = SNMP_V3_MD5_LEN;
+  } else if (algo == SNMP_V3_AUTH_ALGO_SHA) {
+    md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
+    key_len = SNMP_V3_SHA_LEN;
+  } else {
+    return ERR_ARG;
+  }
+
+  mbedtls_md_init(&ctx);
+  if (mbedtls_md_setup(&ctx, md_info, 1) != 0) {
+    return ERR_ARG;
+  }
+
+  if (mbedtls_md_hmac_starts(&ctx, key, key_len) != 0) {
+    goto free_md;
+  }
+
+  for (i = 0; i < length; i++) {
+    u8_t byte;
+
+    if (snmp_pbuf_stream_read(&read_stream, &byte)) {
+      goto free_md;
+    }
+
+    if (mbedtls_md_hmac_update(&ctx, &byte, 1) != 0) {
+      goto free_md;
+    }
+  }
+
+  if (mbedtls_md_hmac_finish(&ctx, hmac_out) != 0) {
+    goto free_md;
+  }
+
+  mbedtls_md_free(&ctx);
+  return ERR_OK;
+
+free_md:
+  mbedtls_md_free(&ctx);
+  return ERR_ARG;
+}
+
+#if LWIP_SNMP_V3_CRYPTO
+
+err_t
+snmpv3_crypt(struct snmp_pbuf_stream *stream, u16_t length,
+             const u8_t *key, const u8_t *priv_param, const u32_t engine_boots,
+             const u32_t engine_time, snmpv3_priv_algo_t algo, snmpv3_priv_mode_t mode)
+{
+  size_t i;
+  mbedtls_cipher_context_t ctx;
+  const mbedtls_cipher_info_t *cipher_info;
+
+  struct snmp_pbuf_stream read_stream;
+  struct snmp_pbuf_stream write_stream;
+  snmp_pbuf_stream_init(&read_stream, stream->pbuf, stream->offset, stream->length);
+  snmp_pbuf_stream_init(&write_stream, stream->pbuf, stream->offset, stream->length);
+  mbedtls_cipher_init(&ctx);
+
+  if (algo == SNMP_V3_PRIV_ALGO_DES) {
+    u8_t iv_local[8];
+    u8_t out_bytes[8];
+    size_t out_len;
+
+    /* RFC 3414 mandates padding for DES */
+    if ((length & 0x07) != 0) {
+      return ERR_ARG;
+    }
+
+    cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_DES_CBC);
+    if (mbedtls_cipher_setup(&ctx, cipher_info) != 0) {
+      return ERR_ARG;
+    }
+    if (mbedtls_cipher_set_padding_mode(&ctx, MBEDTLS_PADDING_NONE) != 0) {
+      return ERR_ARG;
+    }
+    if (mbedtls_cipher_setkey(&ctx, key, 8 * 8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT) ? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) {
+      goto error;
+    }
+
+    /* Prepare IV */
+    for (i = 0; i < LWIP_ARRAYSIZE(iv_local); i++) {
+      iv_local[i] = priv_param[i] ^ key[i + 8];
+    }
+    if (mbedtls_cipher_set_iv(&ctx, iv_local, LWIP_ARRAYSIZE(iv_local)) != 0) {
+      goto error;
+    }
+
+    for (i = 0; i < length; i += 8) {
+      size_t j;
+      u8_t in_bytes[8];
+      out_len = LWIP_ARRAYSIZE(out_bytes) ;
+
+      for (j = 0; j < LWIP_ARRAYSIZE(in_bytes); j++) {
+        if (snmp_pbuf_stream_read(&read_stream, &in_bytes[j]) != ERR_OK) {
+          goto error;
+        }
+      }
+
+      if (mbedtls_cipher_update(&ctx, in_bytes, LWIP_ARRAYSIZE(in_bytes), out_bytes, &out_len) != 0) {
+        goto error;
+      }
+
+      if (snmp_pbuf_stream_writebuf(&write_stream, out_bytes, (u16_t)out_len) != ERR_OK) {
+        goto error;
+      }
+    }
+
+    out_len = LWIP_ARRAYSIZE(out_bytes);
+    if (mbedtls_cipher_finish(&ctx, out_bytes, &out_len) != 0) {
+      goto error;
+    }
+
+    if (snmp_pbuf_stream_writebuf(&write_stream, out_bytes, (u16_t)out_len) != ERR_OK) {
+      goto error;
+    }
+  } else if (algo == SNMP_V3_PRIV_ALGO_AES) {
+    u8_t iv_local[16];
+
+    cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_CFB128);
+    if (mbedtls_cipher_setup(&ctx, cipher_info) != 0) {
+      return ERR_ARG;
+    }
+    if (mbedtls_cipher_setkey(&ctx, key, 16 * 8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT) ? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) {
+      goto error;
+    }
+
+    /*
+     * IV is the big endian concatenation of boots,
+     * uptime and priv param - see RFC3826.
+     */
+    iv_local[0 + 0] = (engine_boots >> 24) & 0xFF;
+    iv_local[0 + 1] = (engine_boots >> 16) & 0xFF;
+    iv_local[0 + 2] = (engine_boots >>  8) & 0xFF;
+    iv_local[0 + 3] = (engine_boots >>  0) & 0xFF;
+    iv_local[4 + 0] = (engine_time  >> 24) & 0xFF;
+    iv_local[4 + 1] = (engine_time  >> 16) & 0xFF;
+    iv_local[4 + 2] = (engine_time  >>  8) & 0xFF;
+    iv_local[4 + 3] = (engine_time  >>  0) & 0xFF;
+    SMEMCPY(iv_local + 8, priv_param, 8);
+    if (mbedtls_cipher_set_iv(&ctx, iv_local, LWIP_ARRAYSIZE(iv_local)) != 0) {
+      goto error;
+    }
+
+    for (i = 0; i < length; i++) {
+      u8_t in_byte;
+      u8_t out_byte;
+      size_t out_len = sizeof(out_byte);
+
+      if (snmp_pbuf_stream_read(&read_stream, &in_byte) != ERR_OK) {
+        goto error;
+      }
+      if (mbedtls_cipher_update(&ctx, &in_byte, sizeof(in_byte), &out_byte, &out_len) != 0) {
+        goto error;
+      }
+      if (snmp_pbuf_stream_write(&write_stream, out_byte) != ERR_OK) {
+        goto error;
+      }
+    }
+  } else {
+    return ERR_ARG;
+  }
+
+  mbedtls_cipher_free(&ctx);
+  return ERR_OK;
+
+error:
+  mbedtls_cipher_free(&ctx);
+  return ERR_OK;
+}
+
+#endif /* LWIP_SNMP_V3_CRYPTO */
+
+/* A.2.1. Password to Key Sample Code for MD5 */
+void
+snmpv3_password_to_key_md5(
+  const u8_t *password,    /* IN */
+  size_t      passwordlen, /* IN */
+  const u8_t *engineID,    /* IN  - pointer to snmpEngineID  */
+  u8_t        engineLength,/* IN  - length of snmpEngineID */
+  u8_t       *key)         /* OUT - pointer to caller 16-octet buffer */
+{
+  mbedtls_md5_context MD;
+  u8_t *cp, password_buf[64];
+  u32_t password_index = 0;
+  u8_t i;
+  u32_t count = 0;
+
+  mbedtls_md5_init(&MD); /* initialize MD5 */
+  mbedtls_md5_starts(&MD);
+
+  /**********************************************/
+  /* Use while loop until we've done 1 Megabyte */
+  /**********************************************/
+  while (count < 1048576) {
+    cp = password_buf;
+    for (i = 0; i < 64; i++) {
+      /*************************************************/
+      /* Take the next octet of the password, wrapping */
+      /* to the beginning of the password as necessary.*/
+      /*************************************************/
+      *cp++ = password[password_index++ % passwordlen];
+    }
+    mbedtls_md5_update(&MD, password_buf, 64);
+    count += 64;
+  }
+  mbedtls_md5_finish(&MD, key); /* tell MD5 we're done */
+
+  /*****************************************************/
+  /* Now localize the key with the engineID and pass   */
+  /* through MD5 to produce final key                  */
+  /* May want to ensure that engineLength <= 32,       */
+  /* otherwise need to use a buffer larger than 64     */
+  /*****************************************************/
+  SMEMCPY(password_buf, key, 16);
+  MEMCPY(password_buf + 16, engineID, engineLength);
+  SMEMCPY(password_buf + 16 + engineLength, key, 16);
+
+  mbedtls_md5_starts(&MD);
+  mbedtls_md5_update(&MD, password_buf, 32 + engineLength);
+  mbedtls_md5_finish(&MD, key);
+
+  mbedtls_md5_free(&MD);
+  return;
+}
+
+/* A.2.2. Password to Key Sample Code for SHA */
+void
+snmpv3_password_to_key_sha(
+  const u8_t *password,    /* IN */
+  size_t      passwordlen, /* IN */
+  const u8_t *engineID,    /* IN  - pointer to snmpEngineID  */
+  u8_t        engineLength,/* IN  - length of snmpEngineID */
+  u8_t       *key)         /* OUT - pointer to caller 20-octet buffer */
+{
+  mbedtls_sha1_context SH;
+  u8_t *cp, password_buf[72];
+  u32_t password_index = 0;
+  u8_t i;
+  u32_t count = 0;
+
+  mbedtls_sha1_init(&SH); /* initialize SHA */
+  mbedtls_sha1_starts(&SH);
+
+  /**********************************************/
+  /* Use while loop until we've done 1 Megabyte */
+  /**********************************************/
+  while (count < 1048576) {
+    cp = password_buf;
+    for (i = 0; i < 64; i++) {
+      /*************************************************/
+      /* Take the next octet of the password, wrapping */
+      /* to the beginning of the password as necessary.*/
+      /*************************************************/
+      *cp++ = password[password_index++ % passwordlen];
+    }
+    mbedtls_sha1_update(&SH, password_buf, 64);
+    count += 64;
+  }
+  mbedtls_sha1_finish(&SH, key); /* tell SHA we're done */
+
+  /*****************************************************/
+  /* Now localize the key with the engineID and pass   */
+  /* through SHA to produce final key                  */
+  /* May want to ensure that engineLength <= 32,       */
+  /* otherwise need to use a buffer larger than 72     */
+  /*****************************************************/
+  SMEMCPY(password_buf, key, 20);
+  MEMCPY(password_buf + 20, engineID, engineLength);
+  SMEMCPY(password_buf + 20 + engineLength, key, 20);
+
+  mbedtls_sha1_starts(&SH);
+  mbedtls_sha1_update(&SH, password_buf, 40 + engineLength);
+  mbedtls_sha1_finish(&SH, key);
+
+  mbedtls_sha1_free(&SH);
+  return;
+}
+
+#endif /* LWIP_SNMP && LWIP_SNMP_V3 && LWIP_SNMP_V3_MBEDTLS */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmpv3_priv.h b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/snmp/snmpv3_priv.h
new file mode 100644 (file)
index 0000000..323364c
--- /dev/null
@@ -0,0 +1,69 @@
+/**
+ * @file
+ * Additional SNMPv3 functionality RFC3414 and RFC3826 (internal API, do not use in client code).
+ */
+
+/*
+ * Copyright (c) 2016 Elias Oenal.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * Author: Elias Oenal <lwip@eliasoenal.com>
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_V3_PRIV_H
+#define LWIP_HDR_APPS_SNMP_V3_PRIV_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP && LWIP_SNMP_V3
+
+#include "lwip/apps/snmpv3.h"
+#include "snmp_pbuf_stream.h"
+
+/* According to RFC 3411 */
+#define SNMP_V3_MAX_ENGINE_ID_LENGTH  32
+#define SNMP_V3_MAX_USER_LENGTH       32
+
+#define SNMP_V3_MAX_AUTH_PARAM_LENGTH  12
+#define SNMP_V3_MAX_PRIV_PARAM_LENGTH  8
+
+#define SNMP_V3_MD5_LEN        16
+#define SNMP_V3_SHA_LEN        20
+
+typedef enum {
+  SNMP_V3_PRIV_MODE_DECRYPT = 0,
+  SNMP_V3_PRIV_MODE_ENCRYPT = 1
+} snmpv3_priv_mode_t;
+
+s32_t snmpv3_get_engine_boots_internal(void);
+err_t snmpv3_auth(struct snmp_pbuf_stream *stream, u16_t length, const u8_t *key, snmpv3_auth_algo_t algo, u8_t *hmac_out);
+err_t snmpv3_crypt(struct snmp_pbuf_stream *stream, u16_t length, const u8_t *key,
+                   const u8_t *priv_param, const u32_t engine_boots, const u32_t engine_time, snmpv3_priv_algo_t algo, snmpv3_priv_mode_t mode);
+err_t snmpv3_build_priv_param(u8_t *priv_param);
+void snmpv3_enginetime_timer(void *arg);
+
+#endif
+
+#endif /* LWIP_HDR_APPS_SNMP_V3_PRIV_H */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/sntp/sntp.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/sntp/sntp.c
new file mode 100644 (file)
index 0000000..5b58a15
--- /dev/null
@@ -0,0 +1,937 @@
+/**
+ * @file
+ * SNTP client module
+ */
+
+/*
+ * Copyright (c) 2007-2009 Frédéric Bernon, Simon Goldschmidt
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Frédéric Bernon, Simon Goldschmidt
+ */
+
+
+/**
+ * @defgroup sntp SNTP
+ * @ingroup apps
+ *
+ * This is simple "SNTP" client for the lwIP raw API.
+ * It is a minimal implementation of SNTPv4 as specified in RFC 4330.
+ *
+ * For a list of some public NTP servers, see this link:
+ * http://support.ntp.org/bin/view/Servers/NTPPoolServers
+ *
+ * @todo:
+ * - complete SNTP_CHECK_RESPONSE checks 3 and 4
+ */
+
+#include "lwip/apps/sntp.h"
+
+#include "lwip/opt.h"
+#include "lwip/timeouts.h"
+#include "lwip/udp.h"
+#include "lwip/dns.h"
+#include "lwip/ip_addr.h"
+#include "lwip/pbuf.h"
+#include "lwip/dhcp.h"
+
+#include <string.h>
+#include <time.h>
+
+#if LWIP_UDP
+
+/* Handle support for more than one server via SNTP_MAX_SERVERS */
+#if SNTP_MAX_SERVERS > 1
+#define SNTP_SUPPORT_MULTIPLE_SERVERS 1
+#else /* NTP_MAX_SERVERS > 1 */
+#define SNTP_SUPPORT_MULTIPLE_SERVERS 0
+#endif /* NTP_MAX_SERVERS > 1 */
+
+#ifndef SNTP_SUPPRESS_DELAY_CHECK
+#if SNTP_UPDATE_DELAY < 15000
+#error "SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds (define SNTP_SUPPRESS_DELAY_CHECK to disable this error)!"
+#endif
+#endif
+
+/* the various debug levels for this file */
+#define SNTP_DEBUG_TRACE        (SNTP_DEBUG | LWIP_DBG_TRACE)
+#define SNTP_DEBUG_STATE        (SNTP_DEBUG | LWIP_DBG_STATE)
+#define SNTP_DEBUG_WARN         (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#define SNTP_DEBUG_WARN_STATE   (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
+#define SNTP_DEBUG_SERIOUS      (SNTP_DEBUG | LWIP_DBG_LEVEL_SERIOUS)
+
+#define SNTP_ERR_KOD                1
+
+/* SNTP protocol defines */
+#define SNTP_MSG_LEN                48
+
+#define SNTP_OFFSET_LI_VN_MODE      0
+#define SNTP_LI_MASK                0xC0
+#define SNTP_LI_NO_WARNING          (0x00 << 6)
+#define SNTP_LI_LAST_MINUTE_61_SEC  (0x01 << 6)
+#define SNTP_LI_LAST_MINUTE_59_SEC  (0x02 << 6)
+#define SNTP_LI_ALARM_CONDITION     (0x03 << 6) /* (clock not synchronized) */
+
+#define SNTP_VERSION_MASK           0x38
+#define SNTP_VERSION                (4/* NTP Version 4*/<<3)
+
+#define SNTP_MODE_MASK              0x07
+#define SNTP_MODE_CLIENT            0x03
+#define SNTP_MODE_SERVER            0x04
+#define SNTP_MODE_BROADCAST         0x05
+
+#define SNTP_OFFSET_STRATUM         1
+#define SNTP_STRATUM_KOD            0x00
+
+#define SNTP_OFFSET_ORIGINATE_TIME  24
+#define SNTP_OFFSET_RECEIVE_TIME    32
+#define SNTP_OFFSET_TRANSMIT_TIME   40
+
+/* Number of seconds between 1970 and Feb 7, 2036 06:28:16 UTC (epoch 1) */
+#define DIFF_SEC_1970_2036          ((u32_t)2085978496L)
+
+/** Convert NTP timestamp fraction to microseconds.
+ */
+#ifndef SNTP_FRAC_TO_US
+# if LWIP_HAVE_INT64
+#  define SNTP_FRAC_TO_US(f)        ((u32_t)(((u64_t)(f) * 1000000UL) >> 32))
+# else
+#  define SNTP_FRAC_TO_US(f)        ((u32_t)(f) / 4295)
+# endif
+#endif /* !SNTP_FRAC_TO_US */
+
+/* Configure behaviour depending on native, microsecond or second precision.
+ * Treat NTP timestamps as signed two's-complement integers. This way,
+ * timestamps that have the MSB set simply become negative offsets from
+ * the epoch (Feb 7, 2036 06:28:16 UTC). Representable dates range from
+ * 1968 to 2104.
+ */
+#ifndef SNTP_SET_SYSTEM_TIME_NTP
+# ifdef SNTP_SET_SYSTEM_TIME_US
+#  define SNTP_SET_SYSTEM_TIME_NTP(s, f) \
+    SNTP_SET_SYSTEM_TIME_US((u32_t)((s) + DIFF_SEC_1970_2036), SNTP_FRAC_TO_US(f))
+# else
+#  define SNTP_SET_SYSTEM_TIME_NTP(s, f) \
+    SNTP_SET_SYSTEM_TIME((u32_t)((s) + DIFF_SEC_1970_2036))
+# endif
+#endif /* !SNTP_SET_SYSTEM_TIME_NTP */
+
+/* Get the system time either natively as NTP timestamp or convert from
+ * Unix time in seconds and microseconds. Take care to avoid overflow if the
+ * microsecond value is at the maximum of 999999. Also add 0.5 us fudge to
+ * avoid special values like 0, and to mask round-off errors that would
+ * otherwise break round-trip conversion identity.
+ */
+#ifndef SNTP_GET_SYSTEM_TIME_NTP
+# define SNTP_GET_SYSTEM_TIME_NTP(s, f) do { \
+    u32_t sec_, usec_; \
+    SNTP_GET_SYSTEM_TIME(sec_, usec_); \
+    (s) = (s32_t)(sec_ - DIFF_SEC_1970_2036); \
+    (f) = usec_ * 4295 - ((usec_ * 2143) >> 16) + 2147; \
+  } while (0)
+#endif /* !SNTP_GET_SYSTEM_TIME_NTP */
+
+/* Start offset of the timestamps to extract from the SNTP packet */
+#define SNTP_OFFSET_TIMESTAMPS \
+    (SNTP_OFFSET_TRANSMIT_TIME + 8 - sizeof(struct sntp_timestamps))
+
+/* Round-trip delay arithmetic helpers */
+#if SNTP_COMP_ROUNDTRIP
+# if !LWIP_HAVE_INT64
+#  error "SNTP round-trip delay compensation requires 64-bit arithmetic"
+# endif
+# define SNTP_SEC_FRAC_TO_S64(s, f) \
+    ((s64_t)(((u64_t)(s) << 32) | (u32_t)(f)))
+# define SNTP_TIMESTAMP_TO_S64(t) \
+    SNTP_SEC_FRAC_TO_S64(lwip_ntohl((t).sec), lwip_ntohl((t).frac))
+#endif /* SNTP_COMP_ROUNDTRIP */
+
+/**
+ * 64-bit NTP timestamp, in network byte order.
+ */
+struct sntp_time {
+  u32_t sec;
+  u32_t frac;
+};
+
+/**
+ * Timestamps to be extracted from the NTP header.
+ */
+struct sntp_timestamps {
+#if SNTP_COMP_ROUNDTRIP || SNTP_CHECK_RESPONSE >= 2
+  struct sntp_time orig;
+  struct sntp_time recv;
+#endif
+  struct sntp_time xmit;
+};
+
+/**
+ * SNTP packet format (without optional fields)
+ * Timestamps are coded as 64 bits:
+ * - signed 32 bits seconds since Feb 07, 2036, 06:28:16 UTC (epoch 1)
+ * - unsigned 32 bits seconds fraction (2^32 = 1 second)
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct sntp_msg {
+  PACK_STRUCT_FLD_8(u8_t  li_vn_mode);
+  PACK_STRUCT_FLD_8(u8_t  stratum);
+  PACK_STRUCT_FLD_8(u8_t  poll);
+  PACK_STRUCT_FLD_8(u8_t  precision);
+  PACK_STRUCT_FIELD(u32_t root_delay);
+  PACK_STRUCT_FIELD(u32_t root_dispersion);
+  PACK_STRUCT_FIELD(u32_t reference_identifier);
+  PACK_STRUCT_FIELD(u32_t reference_timestamp[2]);
+  PACK_STRUCT_FIELD(u32_t originate_timestamp[2]);
+  PACK_STRUCT_FIELD(u32_t receive_timestamp[2]);
+  PACK_STRUCT_FIELD(u32_t transmit_timestamp[2]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/* function prototypes */
+static void sntp_request(void *arg);
+
+/** The operating mode */
+static u8_t sntp_opmode;
+
+/** The UDP pcb used by the SNTP client */
+static struct udp_pcb *sntp_pcb;
+/** Names/Addresses of servers */
+struct sntp_server {
+#if SNTP_SERVER_DNS
+  const char *name;
+#endif /* SNTP_SERVER_DNS */
+  ip_addr_t addr;
+#if SNTP_MONITOR_SERVER_REACHABILITY
+  /** Reachability shift register as described in RFC 5905 */
+  u8_t reachability;
+#endif /* SNTP_MONITOR_SERVER_REACHABILITY */
+};
+static struct sntp_server sntp_servers[SNTP_MAX_SERVERS];
+
+#if SNTP_GET_SERVERS_FROM_DHCP || SNTP_GET_SERVERS_FROM_DHCPV6
+static u8_t sntp_set_servers_from_dhcp;
+#endif /* SNTP_GET_SERVERS_FROM_DHCP || SNTP_GET_SERVERS_FROM_DHCPV6 */
+#if SNTP_SUPPORT_MULTIPLE_SERVERS
+/** The currently used server (initialized to 0) */
+static u8_t sntp_current_server;
+#else /* SNTP_SUPPORT_MULTIPLE_SERVERS */
+#define sntp_current_server 0
+#endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */
+
+#if SNTP_RETRY_TIMEOUT_EXP
+#define SNTP_RESET_RETRY_TIMEOUT() sntp_retry_timeout = SNTP_RETRY_TIMEOUT
+/** Retry time, initialized with SNTP_RETRY_TIMEOUT and doubled with each retry. */
+static u32_t sntp_retry_timeout;
+#else /* SNTP_RETRY_TIMEOUT_EXP */
+#define SNTP_RESET_RETRY_TIMEOUT()
+#define sntp_retry_timeout SNTP_RETRY_TIMEOUT
+#endif /* SNTP_RETRY_TIMEOUT_EXP */
+
+#if SNTP_CHECK_RESPONSE >= 1
+/** Saves the last server address to compare with response */
+static ip_addr_t sntp_last_server_address;
+#endif /* SNTP_CHECK_RESPONSE >= 1 */
+
+#if SNTP_CHECK_RESPONSE >= 2
+/** Saves the last timestamp sent (which is sent back by the server)
+ * to compare against in response. Stored in network byte order. */
+static struct sntp_time sntp_last_timestamp_sent;
+#endif /* SNTP_CHECK_RESPONSE >= 2 */
+
+#if defined(LWIP_DEBUG) && !defined(sntp_format_time)
+/* Debug print helper. */
+static const char *
+sntp_format_time(s32_t sec)
+{
+  time_t ut;
+  ut = (u32_t)((u32_t)sec + DIFF_SEC_1970_2036);
+  return ctime(&ut);
+}
+#endif /* LWIP_DEBUG && !sntp_format_time */
+
+/**
+ * SNTP processing of received timestamp
+ */
+static void
+sntp_process(const struct sntp_timestamps *timestamps)
+{
+  s32_t sec;
+  u32_t frac;
+
+  sec  = (s32_t)lwip_ntohl(timestamps->xmit.sec);
+  frac = lwip_ntohl(timestamps->xmit.frac);
+
+#if SNTP_COMP_ROUNDTRIP
+# if SNTP_CHECK_RESPONSE >= 2
+  if (timestamps->recv.sec != 0 || timestamps->recv.frac != 0)
+# endif
+  {
+    s32_t dest_sec;
+    u32_t dest_frac;
+    u32_t step_sec;
+
+    /* Get the destination time stamp, i.e. the current system time */
+    SNTP_GET_SYSTEM_TIME_NTP(dest_sec, dest_frac);
+
+    step_sec = (dest_sec < sec) ? ((u32_t)sec - (u32_t)dest_sec)
+               : ((u32_t)dest_sec - (u32_t)sec);
+    /* In order to avoid overflows, skip the compensation if the clock step
+     * is larger than about 34 years. */
+    if ((step_sec >> 30) == 0) {
+      s64_t t1, t2, t3, t4;
+
+      t4 = SNTP_SEC_FRAC_TO_S64(dest_sec, dest_frac);
+      t3 = SNTP_SEC_FRAC_TO_S64(sec, frac);
+      t1 = SNTP_TIMESTAMP_TO_S64(timestamps->orig);
+      t2 = SNTP_TIMESTAMP_TO_S64(timestamps->recv);
+      /* Clock offset calculation according to RFC 4330 */
+      t4 += ((t2 - t1) + (t3 - t4)) / 2;
+
+      sec  = (s32_t)((u64_t)t4 >> 32);
+      frac = (u32_t)((u64_t)t4);
+    }
+  }
+#endif /* SNTP_COMP_ROUNDTRIP */
+
+  SNTP_SET_SYSTEM_TIME_NTP(sec, frac);
+  LWIP_UNUSED_ARG(frac); /* might be unused if only seconds are set */
+  LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s, %" U32_F " us\n",
+                                 sntp_format_time(sec), SNTP_FRAC_TO_US(frac)));
+}
+
+/**
+ * Initialize request struct to be sent to server.
+ */
+static void
+sntp_initialize_request(struct sntp_msg *req)
+{
+  memset(req, 0, SNTP_MSG_LEN);
+  req->li_vn_mode = SNTP_LI_NO_WARNING | SNTP_VERSION | SNTP_MODE_CLIENT;
+
+#if SNTP_CHECK_RESPONSE >= 2 || SNTP_COMP_ROUNDTRIP
+  {
+    s32_t secs;
+    u32_t sec, frac;
+    /* Get the transmit timestamp */
+    SNTP_GET_SYSTEM_TIME_NTP(secs, frac);
+    sec  = lwip_htonl((u32_t)secs);
+    frac = lwip_htonl(frac);
+
+# if SNTP_CHECK_RESPONSE >= 2
+    sntp_last_timestamp_sent.sec  = sec;
+    sntp_last_timestamp_sent.frac = frac;
+# endif
+    req->transmit_timestamp[0] = sec;
+    req->transmit_timestamp[1] = frac;
+  }
+#endif /* SNTP_CHECK_RESPONSE >= 2 || SNTP_COMP_ROUNDTRIP */
+}
+
+/**
+ * Retry: send a new request (and increase retry timeout).
+ *
+ * @param arg is unused (only necessary to conform to sys_timeout)
+ */
+static void
+sntp_retry(void *arg)
+{
+  LWIP_UNUSED_ARG(arg);
+
+  LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_retry: Next request will be sent in %"U32_F" ms\n",
+                                 sntp_retry_timeout));
+
+  /* set up a timer to send a retry and increase the retry delay */
+  sys_untimeout(sntp_request, NULL);
+  sys_timeout(sntp_retry_timeout, sntp_request, NULL);
+
+#if SNTP_RETRY_TIMEOUT_EXP
+  {
+    u32_t new_retry_timeout;
+    /* increase the timeout for next retry */
+    new_retry_timeout = sntp_retry_timeout << 1;
+    /* limit to maximum timeout and prevent overflow */
+    if ((new_retry_timeout <= SNTP_RETRY_TIMEOUT_MAX) &&
+        (new_retry_timeout > sntp_retry_timeout)) {
+      sntp_retry_timeout = new_retry_timeout;
+    } else {
+      sntp_retry_timeout = SNTP_RETRY_TIMEOUT_MAX;
+    }
+  }
+#endif /* SNTP_RETRY_TIMEOUT_EXP */
+}
+
+#if SNTP_SUPPORT_MULTIPLE_SERVERS
+/**
+ * If Kiss-of-Death is received (or another packet parsing error),
+ * try the next server or retry the current server and increase the retry
+ * timeout if only one server is available.
+ * (implicitly, SNTP_MAX_SERVERS > 1)
+ *
+ * @param arg is unused (only necessary to conform to sys_timeout)
+ */
+static void
+sntp_try_next_server(void *arg)
+{
+  u8_t old_server, i;
+  LWIP_UNUSED_ARG(arg);
+
+  old_server = sntp_current_server;
+  for (i = 0; i < SNTP_MAX_SERVERS - 1; i++) {
+    sntp_current_server++;
+    if (sntp_current_server >= SNTP_MAX_SERVERS) {
+      sntp_current_server = 0;
+    }
+    if (!ip_addr_isany(&sntp_servers[sntp_current_server].addr)
+#if SNTP_SERVER_DNS
+        || (sntp_servers[sntp_current_server].name != NULL)
+#endif
+       ) {
+      LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_try_next_server: Sending request to server %"U16_F"\n",
+                                     (u16_t)sntp_current_server));
+      /* new server: reset retry timeout */
+      SNTP_RESET_RETRY_TIMEOUT();
+      /* instantly send a request to the next server */
+      sntp_request(NULL);
+      return;
+    }
+  }
+  /* no other valid server found */
+  sntp_current_server = old_server;
+  sntp_retry(NULL);
+}
+#else /* SNTP_SUPPORT_MULTIPLE_SERVERS */
+/* Always retry on error if only one server is supported */
+#define sntp_try_next_server    sntp_retry
+#endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */
+
+/** UDP recv callback for the sntp pcb */
+static void
+sntp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+  struct sntp_timestamps timestamps;
+  u8_t mode;
+  u8_t stratum;
+  err_t err;
+
+  LWIP_UNUSED_ARG(arg);
+  LWIP_UNUSED_ARG(pcb);
+
+  err = ERR_ARG;
+#if SNTP_CHECK_RESPONSE >= 1
+  /* check server address and port */
+  if (((sntp_opmode != SNTP_OPMODE_POLL) || ip_addr_cmp(addr, &sntp_last_server_address)) &&
+      (port == SNTP_PORT))
+#else /* SNTP_CHECK_RESPONSE >= 1 */
+  LWIP_UNUSED_ARG(addr);
+  LWIP_UNUSED_ARG(port);
+#endif /* SNTP_CHECK_RESPONSE >= 1 */
+  {
+    /* process the response */
+    if (p->tot_len == SNTP_MSG_LEN) {
+      mode = pbuf_get_at(p, SNTP_OFFSET_LI_VN_MODE) & SNTP_MODE_MASK;
+      /* if this is a SNTP response... */
+      if (((sntp_opmode == SNTP_OPMODE_POLL)       && (mode == SNTP_MODE_SERVER)) ||
+          ((sntp_opmode == SNTP_OPMODE_LISTENONLY) && (mode == SNTP_MODE_BROADCAST))) {
+        stratum = pbuf_get_at(p, SNTP_OFFSET_STRATUM);
+
+        if (stratum == SNTP_STRATUM_KOD) {
+          /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */
+          err = SNTP_ERR_KOD;
+          LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Received Kiss-of-Death\n"));
+        } else {
+          pbuf_copy_partial(p, &timestamps, sizeof(timestamps), SNTP_OFFSET_TIMESTAMPS);
+#if SNTP_CHECK_RESPONSE >= 2
+          /* check originate_timetamp against sntp_last_timestamp_sent */
+          if (timestamps.orig.sec != sntp_last_timestamp_sent.sec ||
+              timestamps.orig.frac != sntp_last_timestamp_sent.frac) {
+            LWIP_DEBUGF(SNTP_DEBUG_WARN,
+                        ("sntp_recv: Invalid originate timestamp in response\n"));
+          } else
+#endif /* SNTP_CHECK_RESPONSE >= 2 */
+            /* @todo: add code for SNTP_CHECK_RESPONSE >= 3 and >= 4 here */
+          {
+            /* correct answer */
+            err = ERR_OK;
+          }
+        }
+      } else {
+        LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid mode in response: %"U16_F"\n", (u16_t)mode));
+        /* wait for correct response */
+        err = ERR_TIMEOUT;
+      }
+    } else {
+      LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid packet length: %"U16_F"\n", p->tot_len));
+    }
+  }
+#if SNTP_CHECK_RESPONSE >= 1
+  else {
+    /* packet from wrong remote address or port, wait for correct response */
+    err = ERR_TIMEOUT;
+  }
+#endif /* SNTP_CHECK_RESPONSE >= 1 */
+
+  pbuf_free(p);
+
+  if (err == ERR_OK) {
+    /* correct packet received: process it it */
+    sntp_process(&timestamps);
+
+#if SNTP_MONITOR_SERVER_REACHABILITY
+    /* indicate that server responded */
+    sntp_servers[sntp_current_server].reachability |= 1;
+#endif /* SNTP_MONITOR_SERVER_REACHABILITY */
+    /* Set up timeout for next request (only if poll response was received)*/
+    if (sntp_opmode == SNTP_OPMODE_POLL) {
+      u32_t sntp_update_delay;
+      sys_untimeout(sntp_try_next_server, NULL);
+      sys_untimeout(sntp_request, NULL);
+
+      /* Correct response, reset retry timeout */
+      SNTP_RESET_RETRY_TIMEOUT();
+
+      sntp_update_delay = (u32_t)SNTP_UPDATE_DELAY;
+      sys_timeout(sntp_update_delay, sntp_request, NULL);
+      LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Scheduled next time request: %"U32_F" ms\n",
+                                     sntp_update_delay));
+    }
+  } else if (err == SNTP_ERR_KOD) {
+    /* KOD errors are only processed in case of an explicit poll response */
+    if (sntp_opmode == SNTP_OPMODE_POLL) {
+      /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */
+      sntp_try_next_server(NULL);
+    }
+  } else {
+    /* ignore any broken packet, poll mode: retry after timeout to avoid flooding */
+  }
+}
+
+/** Actually send an sntp request to a server.
+ *
+ * @param server_addr resolved IP address of the SNTP server
+ */
+static void
+sntp_send_request(const ip_addr_t *server_addr)
+{
+  struct pbuf *p;
+
+  LWIP_ASSERT("server_addr != NULL", server_addr != NULL);
+
+  p = pbuf_alloc(PBUF_TRANSPORT, SNTP_MSG_LEN, PBUF_RAM);
+  if (p != NULL) {
+    struct sntp_msg *sntpmsg = (struct sntp_msg *)p->payload;
+    LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_send_request: Sending request to server\n"));
+    /* initialize request message */
+    sntp_initialize_request(sntpmsg);
+    /* send request */
+    udp_sendto(sntp_pcb, p, server_addr, SNTP_PORT);
+    /* free the pbuf after sending it */
+    pbuf_free(p);
+#if SNTP_MONITOR_SERVER_REACHABILITY
+    /* indicate new packet has been sent */
+    sntp_servers[sntp_current_server].reachability <<= 1;
+#endif /* SNTP_MONITOR_SERVER_REACHABILITY */
+    /* set up receive timeout: try next server or retry on timeout */
+    sys_untimeout(sntp_try_next_server, NULL);
+    sys_timeout((u32_t)SNTP_RECV_TIMEOUT, sntp_try_next_server, NULL);
+#if SNTP_CHECK_RESPONSE >= 1
+    /* save server address to verify it in sntp_recv */
+    ip_addr_copy(sntp_last_server_address, *server_addr);
+#endif /* SNTP_CHECK_RESPONSE >= 1 */
+  } else {
+    LWIP_DEBUGF(SNTP_DEBUG_SERIOUS, ("sntp_send_request: Out of memory, trying again in %"U32_F" ms\n",
+                                     (u32_t)SNTP_RETRY_TIMEOUT));
+    /* out of memory: set up a timer to send a retry */
+    sys_untimeout(sntp_request, NULL);
+    sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_request, NULL);
+  }
+}
+
+#if SNTP_SERVER_DNS
+/**
+ * DNS found callback when using DNS names as server address.
+ */
+static void
+sntp_dns_found(const char *hostname, const ip_addr_t *ipaddr, void *arg)
+{
+  LWIP_UNUSED_ARG(hostname);
+  LWIP_UNUSED_ARG(arg);
+
+  if (ipaddr != NULL) {
+    /* Address resolved, send request */
+    LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_dns_found: Server address resolved, sending request\n"));
+    sntp_servers[sntp_current_server].addr = *ipaddr;
+    sntp_send_request(ipaddr);
+  } else {
+    /* DNS resolving failed -> try another server */
+    LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_dns_found: Failed to resolve server address resolved, trying next server\n"));
+    sntp_try_next_server(NULL);
+  }
+}
+#endif /* SNTP_SERVER_DNS */
+
+/**
+ * Send out an sntp request.
+ *
+ * @param arg is unused (only necessary to conform to sys_timeout)
+ */
+static void
+sntp_request(void *arg)
+{
+  ip_addr_t sntp_server_address;
+  err_t err;  
+  LWIP_UNUSED_ARG(arg);
+  if (uapi_dev_runtime_get_ble_connect_stat())  
+  {
+      LWIP_DEBUGF(SNTP_DEBUG_STATE, ("ble connected, skip sntp_request.\n"));
+      return;
+  }
+  uapi_cat1_wakeup_cat1(1);
+  /* initialize SNTP server address */
+#if SNTP_SERVER_DNS
+  if (sntp_servers[sntp_current_server].name) {
+    /* always resolve the name and rely on dns-internal caching & timeout */
+    ip_addr_set_zero(&sntp_servers[sntp_current_server].addr);
+    err = dns_gethostbyname(sntp_servers[sntp_current_server].name, &sntp_server_address,
+                            sntp_dns_found, NULL);
+    if (err == ERR_INPROGRESS) {
+      /* DNS request sent, wait for sntp_dns_found being called */
+      LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_request: Waiting for server address to be resolved.\n"));
+      uapi_cat1_wakeup_cat1(0);
+      return;
+    } else if (err == ERR_OK) {
+      sntp_servers[sntp_current_server].addr = sntp_server_address;
+    }
+  } else
+#endif /* SNTP_SERVER_DNS */
+  {
+    sntp_server_address = sntp_servers[sntp_current_server].addr;
+    err = (ip_addr_isany_val(sntp_server_address)) ? ERR_ARG : ERR_OK;
+  }
+
+  if (err == ERR_OK) {
+    LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_request: current server address is %s\n",
+                                   ipaddr_ntoa(&sntp_server_address)));
+    sntp_send_request(&sntp_server_address);
+  } else {
+    /* address conversion failed, try another server */
+    LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_request: Invalid server address, trying next server.\n"));
+    sys_untimeout(sntp_try_next_server, NULL);
+    sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_try_next_server, NULL);
+  }
+  uapi_cat1_wakeup_cat1(0);
+}
+
+/**
+ * @ingroup sntp
+ * Initialize this module.
+ * Send out request instantly or after SNTP_STARTUP_DELAY(_FUNC).
+ * 1、cn.ntp.org.cn:服务器地址为202.114.10.51,由中国科学院国家授时中心提供;
+ * 2、ntp.aliyun.com:由阿里云提供的SNTP服务器,位于杭州,提供多个服务器地址;
+ * 3、ntp2.sysu.edu.cn:由中山大学提供的SNTP服务器,位于广州。
+ * 1、ntp.pool.org:由多个机构提供的NTP服务器集群,可提供全球覆盖的时间同步服务;
+ * 2、ntp1.aliyun.com:由阿里云提供的公共时间服务器池之一,提供高可用的时间同步服务;
+ * 3、cn.pool.ntp.org:中国境内的公共时间服务器池,由多个机构维护。  
+ */
+void 
+sntp_init(void)
+{
+  /* LWIP_ASSERT_CORE_LOCKED(); is checked by udp_new() */
+  LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_init: SNTP initialised\n"));
+
+#ifdef SNTP_SERVER_ADDRESS
+#if SNTP_SERVER_DNS
+  sntp_setservername(0, SNTP_SERVER_ADDRESS);
+#else
+#error SNTP_SERVER_ADDRESS string not supported SNTP_SERVER_DNS==0
+#endif
+#endif /* SNTP_SERVER_ADDRESS */
+
+
+#ifdef SNTP_SERVER_ADDRESS2
+#if SNTP_SERVER_DNS
+  sntp_setservername(1, SNTP_SERVER_ADDRESS2);
+#else
+#error SNTP_SERVER_ADDRESS2 string not supported SNTP_SERVER_DNS==0
+#endif
+#endif /* SNTP_SERVER_ADDRESS2 */
+
+
+#ifdef SNTP_SERVER_ADDRESS3
+#if SNTP_SERVER_DNS
+  sntp_setservername(2, SNTP_SERVER_ADDRESS3);
+#else
+#error SNTP_SERVER_ADDRESS3 string not supported SNTP_SERVER_DNS==0
+#endif
+#endif /* SNTP_SERVER_ADDRESS3 */
+
+
+  if (sntp_pcb == NULL) {
+    sntp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+    LWIP_ASSERT("Failed to allocate udp pcb for sntp client", sntp_pcb != NULL);
+    if (sntp_pcb != NULL) {
+      udp_recv(sntp_pcb, sntp_recv, NULL);
+
+      if (sntp_opmode == SNTP_OPMODE_POLL) {
+        SNTP_RESET_RETRY_TIMEOUT();
+#if SNTP_STARTUP_DELAY
+        sys_timeout((u32_t)SNTP_STARTUP_DELAY_FUNC, sntp_request, NULL);
+#else
+        sntp_request(NULL);
+#endif
+      } else if (sntp_opmode == SNTP_OPMODE_LISTENONLY) {
+        ip_set_option(sntp_pcb, SOF_BROADCAST);
+        udp_bind(sntp_pcb, IP_ANY_TYPE, SNTP_PORT);
+      }
+    }
+  }
+}
+
+/**
+ * @ingroup sntp
+ * Stop this module.
+ */
+void
+sntp_stop(void)
+{
+  LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_stop \n"));
+  LWIP_ASSERT_CORE_LOCKED();
+  if (sntp_pcb != NULL) {
+#if SNTP_MONITOR_SERVER_REACHABILITY
+    u8_t i;
+    for (i = 0; i < SNTP_MAX_SERVERS; i++) {
+      sntp_servers[i].reachability = 0;
+    }
+#endif /* SNTP_MONITOR_SERVER_REACHABILITY */
+    sys_untimeout(sntp_request, NULL);
+    sys_untimeout(sntp_try_next_server, NULL);
+    udp_remove(sntp_pcb);
+    sntp_pcb = NULL;
+  }
+}
+
+/**
+ * @ingroup sntp
+ * Get enabled state.
+ */
+u8_t sntp_enabled(void)
+{
+  return (sntp_pcb != NULL) ? 1 : 0;
+}
+
+/**
+ * @ingroup sntp
+ * Sets the operating mode.
+ * @param operating_mode one of the available operating modes
+ * #define SNTP_OPMODE_POLL            0
+ * #define SNTP_OPMODE_LISTENONLY      1
+ */
+void
+sntp_setoperatingmode(u8_t operating_mode)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("Invalid operating mode", operating_mode <= SNTP_OPMODE_LISTENONLY);
+  LWIP_ASSERT("Operating mode must not be set while SNTP client is running", sntp_pcb == NULL);
+  sntp_opmode = operating_mode;
+}
+
+/**
+ * @ingroup sntp
+ * Gets the operating mode.
+ */
+u8_t
+sntp_getoperatingmode(void)
+{
+  return sntp_opmode;
+}
+
+#if SNTP_MONITOR_SERVER_REACHABILITY
+/**
+ * @ingroup sntp
+ * Gets the server reachability shift register as described in RFC 5905.
+ *
+ * @param idx the index of the NTP server
+ */
+u8_t
+sntp_getreachability(u8_t idx)
+{
+  if (idx < SNTP_MAX_SERVERS) {
+    return sntp_servers[idx].reachability;
+  }
+  return 0;
+}
+#endif /* SNTP_MONITOR_SERVER_REACHABILITY */
+
+#if SNTP_GET_SERVERS_FROM_DHCP || SNTP_GET_SERVERS_FROM_DHCPV6
+/**
+ * Config SNTP server handling by IP address, name, or DHCP; clear table
+ * @param set_servers_from_dhcp enable or disable getting server addresses from dhcp
+ */
+void
+sntp_servermode_dhcp(int set_servers_from_dhcp)
+{
+  u8_t new_mode = set_servers_from_dhcp ? 1 : 0;
+  LWIP_ASSERT_CORE_LOCKED();
+  if (sntp_set_servers_from_dhcp != new_mode) {
+    sntp_set_servers_from_dhcp = new_mode;
+  }
+}
+#endif /* SNTP_GET_SERVERS_FROM_DHCP || SNTP_GET_SERVERS_FROM_DHCPV6 */
+
+/**
+ * @ingroup sntp
+ * Initialize one of the NTP servers by IP address
+ *
+ * @param idx the index of the NTP server to set must be < SNTP_MAX_SERVERS
+ * @param server IP address of the NTP server to set
+ */
+void
+sntp_setserver(u8_t idx, const ip_addr_t *server)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  if (idx < SNTP_MAX_SERVERS) {
+    if (server != NULL) {
+      sntp_servers[idx].addr = (*server);
+    } else {
+      ip_addr_set_zero(&sntp_servers[idx].addr);
+    }
+#if SNTP_SERVER_DNS
+    sntp_servers[idx].name = NULL;
+#endif
+  }
+}
+
+#if LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP
+/**
+ * Initialize one of the NTP servers by IP address, required by DHCP
+ *
+ * @param num the index of the NTP server to set must be < SNTP_MAX_SERVERS
+ * @param server IP address of the NTP server to set
+ */
+void
+dhcp_set_ntp_servers(u8_t num, const ip4_addr_t *server)
+{
+  LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp: %s %u.%u.%u.%u as NTP server #%u via DHCP\n",
+                                 (sntp_set_servers_from_dhcp ? "Got" : "Rejected"),
+                                 ip4_addr1(server), ip4_addr2(server), ip4_addr3(server), ip4_addr4(server), num));
+  if (sntp_set_servers_from_dhcp && num) {
+    u8_t i;
+    for (i = 0; (i < num) && (i < SNTP_MAX_SERVERS); i++) {
+      ip_addr_t addr;
+      ip_addr_copy_from_ip4(addr, server[i]);
+      sntp_setserver(i, &addr);
+    }
+    for (i = num; i < SNTP_MAX_SERVERS; i++) {
+      sntp_setserver(i, NULL);
+    }
+  }
+}
+#endif /* LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP */
+
+#if LWIP_IPV6_DHCP6 && SNTP_GET_SERVERS_FROM_DHCPV6
+/**
+ * Initialize one of the NTP servers by IP address, required by DHCPV6
+ *
+ * @param num the number of NTP server addresses to set must be < SNTP_MAX_SERVERS
+ * @param server array of IP address of the NTP servers to set
+ */
+void
+dhcp6_set_ntp_servers(u8_t num_ntp_servers, ip_addr_t* ntp_server_addrs)
+{
+  LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp: %s %u NTP server(s) via DHCPv6\n",
+                                 (sntp_set_servers_from_dhcp ? "Got" : "Rejected"),
+                                 num_ntp_servers));
+  if (sntp_set_servers_from_dhcp && num_ntp_servers) {
+    u8_t i;
+    for (i = 0; (i < num_ntp_servers) && (i < SNTP_MAX_SERVERS); i++) {
+      LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp: NTP server %u: %s\n",
+                                     i, ipaddr_ntoa(&ntp_server_addrs[i])));
+      sntp_setserver(i, &ntp_server_addrs[i]);
+    }
+    for (i = num_ntp_servers; i < SNTP_MAX_SERVERS; i++) {
+      sntp_setserver(i, NULL);
+    }
+  }
+}
+#endif /* LWIP_DHCPv6 && SNTP_GET_SERVERS_FROM_DHCPV6 */
+
+/**
+ * @ingroup sntp
+ * Obtain one of the currently configured by IP address (or DHCP) NTP servers
+ *
+ * @param idx the index of the NTP server
+ * @return IP address of the indexed NTP server or "ip_addr_any" if the NTP
+ *         server has not been configured by address (or at all).
+ */
+const ip_addr_t *
+sntp_getserver(u8_t idx)
+{
+  if (idx < SNTP_MAX_SERVERS) {
+    return &sntp_servers[idx].addr;
+  }
+  return IP_ADDR_ANY;
+}
+
+#if SNTP_SERVER_DNS
+/**
+ * Initialize one of the NTP servers by name
+ *
+ * @param idx the index of the NTP server to set must be < SNTP_MAX_SERVERS
+ * @param server DNS name of the NTP server to set, to be resolved at contact time
+ */
+void
+sntp_setservername(u8_t idx, const char *server)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  if (idx < SNTP_MAX_SERVERS) {
+    sntp_servers[idx].name = server;
+  }
+}
+
+/**
+ * Obtain one of the currently configured by name NTP servers.
+ *
+ * @param idx the index of the NTP server
+ * @return IP address of the indexed NTP server or NULL if the NTP
+ *         server has not been configured by name (or at all)
+ */
+const char *
+sntp_getservername(u8_t idx)
+{
+  if (idx < SNTP_MAX_SERVERS) {
+    return sntp_servers[idx].name;
+  }
+  return NULL;
+}
+#endif /* SNTP_SERVER_DNS */
+
+#endif /* LWIP_UDP */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/tftp/tftp_server.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/apps/tftp/tftp_server.c
new file mode 100644 (file)
index 0000000..e3f1512
--- /dev/null
@@ -0,0 +1,435 @@
+/**
+ *
+ * @file tftp_server.c
+ *
+ * @author   Logan Gunthorpe <logang@deltatee.com>
+ *           Dirk Ziegelmeier <dziegel@gmx.de>
+ *
+ * @brief    Trivial File Transfer Protocol (RFC 1350)
+ *
+ * Copyright (c) Deltatee Enterprises Ltd. 2013
+ * All rights reserved.
+ *
+ */
+
+/*
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * Author: Logan Gunthorpe <logang@deltatee.com>
+ *         Dirk Ziegelmeier <dziegel@gmx.de>
+ *
+ */
+
+/**
+ * @defgroup tftp TFTP server
+ * @ingroup apps
+ *
+ * This is simple TFTP server for the lwIP raw API.
+ */
+
+#include "lwip/apps/tftp_server.h"
+
+#if LWIP_UDP
+
+#include "lwip/udp.h"
+#include "lwip/timeouts.h"
+#include "lwip/debug.h"
+
+#define TFTP_MAX_PAYLOAD_SIZE 512
+#define TFTP_HEADER_LENGTH    4
+
+#define TFTP_RRQ   1
+#define TFTP_WRQ   2
+#define TFTP_DATA  3
+#define TFTP_ACK   4
+#define TFTP_ERROR 5
+
+enum tftp_error {
+  TFTP_ERROR_FILE_NOT_FOUND    = 1,
+  TFTP_ERROR_ACCESS_VIOLATION  = 2,
+  TFTP_ERROR_DISK_FULL         = 3,
+  TFTP_ERROR_ILLEGAL_OPERATION = 4,
+  TFTP_ERROR_UNKNOWN_TRFR_ID   = 5,
+  TFTP_ERROR_FILE_EXISTS       = 6,
+  TFTP_ERROR_NO_SUCH_USER      = 7
+};
+
+#include <string.h>
+
+struct tftp_state {
+  const struct tftp_context *ctx;
+  void *handle;
+  struct pbuf *last_data;
+  struct udp_pcb *upcb;
+  ip_addr_t addr;
+  u16_t port;
+  int timer;
+  int last_pkt;
+  u16_t blknum;
+  u8_t retries;
+  u8_t mode_write;
+};
+
+static struct tftp_state tftp_state;
+
+static void tftp_tmr(void *arg);
+
+static void
+close_handle(void)
+{
+  tftp_state.port = 0;
+  ip_addr_set_any(0, &tftp_state.addr);
+
+  if (tftp_state.last_data != NULL) {
+    pbuf_free(tftp_state.last_data);
+    tftp_state.last_data = NULL;
+  }
+
+  sys_untimeout(tftp_tmr, NULL);
+
+  if (tftp_state.handle) {
+    tftp_state.ctx->close(tftp_state.handle);
+    tftp_state.handle = NULL;
+    LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: closing\n"));
+  }
+}
+
+static void
+send_error(const ip_addr_t *addr, u16_t port, enum tftp_error code, const char *str)
+{
+  int str_length = strlen(str);
+  struct pbuf *p;
+  u16_t *payload;
+
+  p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(TFTP_HEADER_LENGTH + str_length + 1), PBUF_RAM);
+  if (p == NULL) {
+    return;
+  }
+
+  payload = (u16_t *) p->payload;
+  payload[0] = PP_HTONS(TFTP_ERROR);
+  payload[1] = lwip_htons(code);
+  MEMCPY(&payload[2], str, str_length + 1);
+
+  udp_sendto(tftp_state.upcb, p, addr, port);
+  pbuf_free(p);
+}
+
+static void
+send_ack(u16_t blknum)
+{
+  struct pbuf *p;
+  u16_t *payload;
+
+  p = pbuf_alloc(PBUF_TRANSPORT, TFTP_HEADER_LENGTH, PBUF_RAM);
+  if (p == NULL) {
+    return;
+  }
+  payload = (u16_t *) p->payload;
+
+  payload[0] = PP_HTONS(TFTP_ACK);
+  payload[1] = lwip_htons(blknum);
+  udp_sendto(tftp_state.upcb, p, &tftp_state.addr, tftp_state.port);
+  pbuf_free(p);
+}
+
+static void
+resend_data(void)
+{
+  struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, tftp_state.last_data->len, PBUF_RAM);
+  if (p == NULL) {
+    return;
+  }
+
+  if (pbuf_copy(p, tftp_state.last_data) != ERR_OK) {
+    pbuf_free(p);
+    return;
+  }
+
+  udp_sendto(tftp_state.upcb, p, &tftp_state.addr, tftp_state.port);
+  pbuf_free(p);
+}
+
+static void
+send_data(void)
+{
+  u16_t *payload;
+  int ret;
+
+  if (tftp_state.last_data != NULL) {
+    pbuf_free(tftp_state.last_data);
+  }
+
+  tftp_state.last_data = pbuf_alloc(PBUF_TRANSPORT, TFTP_HEADER_LENGTH + TFTP_MAX_PAYLOAD_SIZE, PBUF_RAM);
+  if (tftp_state.last_data == NULL) {
+    return;
+  }
+
+  payload = (u16_t *) tftp_state.last_data->payload;
+  payload[0] = PP_HTONS(TFTP_DATA);
+  payload[1] = lwip_htons(tftp_state.blknum);
+
+  ret = tftp_state.ctx->read(tftp_state.handle, &payload[2], TFTP_MAX_PAYLOAD_SIZE);
+  if (ret < 0) {
+    send_error(&tftp_state.addr, tftp_state.port, TFTP_ERROR_ACCESS_VIOLATION, "Error occured while reading the file.");
+    close_handle();
+    return;
+  }
+
+  pbuf_realloc(tftp_state.last_data, (u16_t)(TFTP_HEADER_LENGTH + ret));
+  resend_data();
+}
+
+static void
+recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+  u16_t *sbuf = (u16_t *) p->payload;
+  int opcode;
+
+  LWIP_UNUSED_ARG(arg);
+  LWIP_UNUSED_ARG(upcb);
+
+  if (((tftp_state.port != 0) && (port != tftp_state.port)) ||
+      (!ip_addr_isany_val(tftp_state.addr) && !ip_addr_cmp(&tftp_state.addr, addr))) {
+    send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
+    pbuf_free(p);
+    return;
+  }
+
+  opcode = sbuf[0];
+
+  tftp_state.last_pkt = tftp_state.timer;
+  tftp_state.retries = 0;
+
+  switch (opcode) {
+    case PP_HTONS(TFTP_RRQ): /* fall through */
+    case PP_HTONS(TFTP_WRQ): {
+      const char tftp_null = 0;
+      char filename[TFTP_MAX_FILENAME_LEN + 1];
+      char mode[TFTP_MAX_MODE_LEN + 1];
+      u16_t filename_end_offset;
+      u16_t mode_end_offset;
+
+      if (tftp_state.handle != NULL) {
+        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
+        break;
+      }
+
+      sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
+
+      /* find \0 in pbuf -> end of filename string */
+      filename_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), 2);
+      if ((u16_t)(filename_end_offset - 1) > sizeof(filename)) {
+        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Filename too long/not NULL terminated");
+        break;
+      }
+      pbuf_copy_partial(p, filename, filename_end_offset - 1, 2);
+
+      /* find \0 in pbuf -> end of mode string */
+      mode_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), filename_end_offset + 1);
+      if ((u16_t)(mode_end_offset - filename_end_offset) > sizeof(mode)) {
+        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Mode too long/not NULL terminated");
+        break;
+      }
+      pbuf_copy_partial(p, mode, mode_end_offset - filename_end_offset, filename_end_offset + 1);
+
+      tftp_state.handle = tftp_state.ctx->open(filename, mode, opcode == PP_HTONS(TFTP_WRQ));
+      tftp_state.blknum = 1;
+
+      if (!tftp_state.handle) {
+        send_error(addr, port, TFTP_ERROR_FILE_NOT_FOUND, "Unable to open requested file.");
+        break;
+      }
+
+      LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: %s request from ", (opcode == PP_HTONS(TFTP_WRQ)) ? "write" : "read"));
+      ip_addr_debug_print(TFTP_DEBUG | LWIP_DBG_STATE, addr);
+      LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, (" for '%s' mode '%s'\n", filename, mode));
+
+      ip_addr_copy(tftp_state.addr, *addr);
+      tftp_state.port = port;
+
+      if (opcode == PP_HTONS(TFTP_WRQ)) {
+        tftp_state.mode_write = 1;
+        send_ack(0);
+      } else {
+        tftp_state.mode_write = 0;
+        send_data();
+      }
+
+      break;
+    }
+
+    case PP_HTONS(TFTP_DATA): {
+      int ret;
+      u16_t blknum;
+
+      if (tftp_state.handle == NULL) {
+        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
+        break;
+      }
+
+      if (tftp_state.mode_write != 1) {
+        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a write connection");
+        break;
+      }
+
+      blknum = lwip_ntohs(sbuf[1]);
+      if (blknum == tftp_state.blknum) {
+        pbuf_remove_header(p, TFTP_HEADER_LENGTH);
+
+        ret = tftp_state.ctx->write(tftp_state.handle, p);
+        if (ret < 0) {
+          send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "error writing file");
+          close_handle();
+        } else {
+          send_ack(blknum);
+        }
+
+        if (p->tot_len < TFTP_MAX_PAYLOAD_SIZE) {
+          close_handle();
+        } else {
+          tftp_state.blknum++;
+        }
+      } else if ((u16_t)(blknum + 1) == tftp_state.blknum) {
+        /* retransmit of previous block, ack again (casting to u16_t to care for overflow) */
+        send_ack(blknum);
+      } else {
+        send_error(addr, port, TFTP_ERROR_UNKNOWN_TRFR_ID, "Wrong block number");
+      }
+      break;
+    }
+
+    case PP_HTONS(TFTP_ACK): {
+      u16_t blknum;
+      int lastpkt;
+
+      if (tftp_state.handle == NULL) {
+        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
+        break;
+      }
+
+      if (tftp_state.mode_write != 0) {
+        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a read connection");
+        break;
+      }
+
+      blknum = lwip_ntohs(sbuf[1]);
+      if (blknum != tftp_state.blknum) {
+        send_error(addr, port, TFTP_ERROR_UNKNOWN_TRFR_ID, "Wrong block number");
+        break;
+      }
+
+      lastpkt = 0;
+
+      if (tftp_state.last_data != NULL) {
+        lastpkt = tftp_state.last_data->tot_len != (TFTP_MAX_PAYLOAD_SIZE + TFTP_HEADER_LENGTH);
+      }
+
+      if (!lastpkt) {
+        tftp_state.blknum++;
+        send_data();
+      } else {
+        close_handle();
+      }
+
+      break;
+    }
+
+    default:
+      send_error(addr, port, TFTP_ERROR_ILLEGAL_OPERATION, "Unknown operation");
+      break;
+  }
+
+  pbuf_free(p);
+}
+
+static void
+tftp_tmr(void *arg)
+{
+  LWIP_UNUSED_ARG(arg);
+
+  tftp_state.timer++;
+
+  if (tftp_state.handle == NULL) {
+    return;
+  }
+
+  sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
+
+  if ((tftp_state.timer - tftp_state.last_pkt) > (TFTP_TIMEOUT_MSECS / TFTP_TIMER_MSECS)) {
+    if ((tftp_state.last_data != NULL) && (tftp_state.retries < TFTP_MAX_RETRIES)) {
+      LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout, retrying\n"));
+      resend_data();
+      tftp_state.retries++;
+    } else {
+      LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout\n"));
+      close_handle();
+    }
+  }
+}
+
+/** @ingroup tftp
+ * Initialize TFTP server.
+ * @param ctx TFTP callback struct
+ */
+err_t
+tftp_init(const struct tftp_context *ctx)
+{
+  err_t ret;
+
+  /* LWIP_ASSERT_CORE_LOCKED(); is checked by udp_new() */
+  struct udp_pcb *pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+  if (pcb == NULL) {
+    return ERR_MEM;
+  }
+
+  ret = udp_bind(pcb, IP_ANY_TYPE, TFTP_PORT);
+  if (ret != ERR_OK) {
+    udp_remove(pcb);
+    return ret;
+  }
+
+  tftp_state.handle    = NULL;
+  tftp_state.port      = 0;
+  tftp_state.ctx       = ctx;
+  tftp_state.timer     = 0;
+  tftp_state.last_data = NULL;
+  tftp_state.upcb      = pcb;
+
+  udp_recv(pcb, recv, NULL);
+
+  return ERR_OK;
+}
+
+/** @ingroup tftp
+ * Deinitialize ("turn off") TFTP server.
+ */
+void tftp_cleanup(void)
+{
+  LWIP_ASSERT("Cleanup called on non-initialized TFTP", tftp_state.upcb != NULL);
+  udp_remove(tftp_state.upcb);
+  close_handle();
+  memset(&tftp_state, 0, sizeof(tftp_state));
+}
+
+#endif /* LWIP_UDP */
index 6327e84..8c7f3a9 100644 (file)
@@ -375,13 +375,13 @@ lwip_init(void)
 #if LWIP_DNS
   dns_init();
 #endif /* LWIP_DNS */
-#if PPP_SUPPORT
-  ppp_init();
-#endif
 
 #if LWIP_SLIP
   slip_test_init();
 #endif /* LWIP_SLIP */
+#if PPP_SUPPORT
+  ppp_init();
+#endif
 
 #if LWIP_TIMERS
   sys_timeouts_init();
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/FILES b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/FILES
new file mode 100644 (file)
index 0000000..1ffd6f2
--- /dev/null
@@ -0,0 +1,23 @@
+This directory contains generic network interface device drivers that
+do not contain any hardware or architecture specific code. The files
+are:
+
+ethernet.c
+          Shared code for Ethernet based interfaces.
+
+lowpan6.c
+          A 6LoWPAN implementation as a netif.
+
+lowpan6_ble.c
+          A 6LoWPAN over Bluetooth Low Energy (BLE) implementation as netif,
+          according to RFC-7668.
+
+slipif.c
+          A generic implementation of the SLIP (Serial Line IP)
+          protocol. It requires a sio (serial I/O) module to work.
+
+ppp/      Point-to-Point Protocol stack
+          The lwIP PPP support is based from pppd (http://ppp.samba.org) with
+          huge changes to match code size and memory requirements for embedded
+          devices. Please read /doc/ppp.txt and ppp/PPPD_FOLLOWUP for a detailed
+          explanation.
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/bridgeif.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/bridgeif.c
new file mode 100644 (file)
index 0000000..8a97bce
--- /dev/null
@@ -0,0 +1,563 @@
+/**
+ * @file
+ * lwIP netif implementing an IEEE 802.1D MAC Bridge
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+
+/**
+ * @defgroup bridgeif IEEE 802.1D bridge
+ * @ingroup netifs
+ * This file implements an IEEE 802.1D bridge by using a multilayer netif approach
+ * (one hardware-independent netif for the bridge that uses hardware netifs for its ports).
+ * On transmit, the bridge selects the outgoing port(s).
+ * On receive, the port netif calls into the bridge (via its netif->input function) and
+ * the bridge selects the port(s) (and/or its netif->input function) to pass the received pbuf to.
+ *
+ * Usage:
+ * - add the port netifs just like you would when using them as dedicated netif without a bridge
+ *   - only NETIF_FLAG_ETHARP/NETIF_FLAG_ETHERNET netifs are supported as bridge ports
+ *   - add the bridge port netifs without IPv4 addresses (i.e. pass 'NULL, NULL, NULL')
+ *   - don't add IPv6 addresses to the port netifs!
+ * - set up the bridge configuration in a global variable of type 'bridgeif_initdata_t' that contains
+ *   - the MAC address of the bridge
+ *   - some configuration options controlling the memory consumption (maximum number of ports
+ *     and FDB entries)
+ *   - e.g. for a bridge MAC address 00-01-02-03-04-05, 2 bridge ports, 1024 FDB entries + 16 static MAC entries:
+ *     bridgeif_initdata_t mybridge_initdata = BRIDGEIF_INITDATA1(2, 1024, 16, ETH_ADDR(0, 1, 2, 3, 4, 5));
+ * - add the bridge netif (with IPv4 config):
+ *   struct netif bridge_netif;
+ *   netif_add(&bridge_netif, &my_ip, &my_netmask, &my_gw, &mybridge_initdata, bridgeif_init, tcpip_input);
+ *   NOTE: the passed 'input' function depends on BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT setting,
+ *         which controls where the forwarding is done (netif low level input context vs. tcpip_thread)
+ * - set up all ports netifs and the bridge netif
+ *
+ * - When adding a port netif, NETIF_FLAG_ETHARP flag will be removed from a port
+ *   to prevent ETHARP working on that port netif (we only want one IP per bridge not per port).
+ * - When adding a port netif, its input function is changed to call into the bridge.
+ *
+ *
+ * @todo:
+ * - compact static FDB entries (instead of walking the whole array)
+ * - add FDB query/read access
+ * - add FDB change callback (when learning or dropping auto-learned entries)
+ * - prefill FDB with MAC classes that should never be forwarded
+ * - multicast snooping? (and only forward group addresses to interested ports)
+ * - support removing ports
+ * - check SNMP integration
+ * - VLAN handling / trunk ports
+ * - priority handling? (although that largely depends on TX queue limitations and lwIP doesn't provide tx-done handling)
+ */
+
+#include "netif/bridgeif.h"
+#include "lwip/netif.h"
+#include "lwip/sys.h"
+#include "lwip/etharp.h"
+#include "lwip/ethip6.h"
+#include "lwip/snmp.h"
+#include "lwip/timeouts.h"
+#include <string.h>
+
+#if LWIP_NUM_NETIF_CLIENT_DATA
+
+/* Define those to better describe your network interface. */
+#define IFNAME0 'b'
+#define IFNAME1 'r'
+
+struct bridgeif_private_s;
+typedef struct bridgeif_port_private_s {
+  struct bridgeif_private_s *bridge;
+  struct netif *port_netif;
+  u8_t port_num;
+} bridgeif_port_t;
+
+typedef struct bridgeif_fdb_static_entry_s {
+  u8_t used;
+  bridgeif_portmask_t dst_ports;
+  struct eth_addr addr;
+} bridgeif_fdb_static_entry_t;
+
+typedef struct bridgeif_private_s {
+  struct netif     *netif;
+  struct eth_addr   ethaddr;
+  u8_t              max_ports;
+  u8_t              num_ports;
+  bridgeif_port_t  *ports;
+  u16_t             max_fdbs_entries;
+  bridgeif_fdb_static_entry_t *fdbs;
+  u16_t             max_fdbd_entries;
+  void             *fdbd;
+} bridgeif_private_t;
+
+/* netif data index to get the bridge on input */
+u8_t bridgeif_netif_client_id = 0xff;
+
+/**
+ * @ingroup bridgeif
+ * Add a static entry to the forwarding database.
+ * A static entry marks where frames to a specific eth address (unicast or group address) are
+ * forwarded.
+ * bits [0..(BRIDGEIF_MAX_PORTS-1)]: hw ports
+ * bit [BRIDGEIF_MAX_PORTS]: cpu port
+ * 0: drop
+ */
+err_t
+bridgeif_fdb_add(struct netif *bridgeif, const struct eth_addr *addr, bridgeif_portmask_t ports)
+{
+  int i;
+  bridgeif_private_t *br;
+  BRIDGEIF_DECL_PROTECT(lev);
+  LWIP_ASSERT("invalid netif", bridgeif != NULL);
+  br = (bridgeif_private_t *)bridgeif->state;
+  LWIP_ASSERT("invalid state", br != NULL);
+
+  BRIDGEIF_READ_PROTECT(lev);
+  for (i = 0; i < br->max_fdbs_entries; i++) {
+    if (!br->fdbs[i].used) {
+      BRIDGEIF_WRITE_PROTECT(lev);
+      if (!br->fdbs[i].used) {
+        br->fdbs[i].used = 1;
+        br->fdbs[i].dst_ports = ports;
+        memcpy(&br->fdbs[i].addr, addr, sizeof(struct eth_addr));
+        BRIDGEIF_WRITE_UNPROTECT(lev);
+        BRIDGEIF_READ_UNPROTECT(lev);
+        return ERR_OK;
+      }
+      BRIDGEIF_WRITE_UNPROTECT(lev);
+    }
+  }
+  BRIDGEIF_READ_UNPROTECT(lev);
+  return ERR_MEM;
+}
+
+/**
+ * @ingroup bridgeif
+ * Remove a static entry from the forwarding database
+ */
+err_t
+bridgeif_fdb_remove(struct netif *bridgeif, const struct eth_addr *addr)
+{
+  int i;
+  bridgeif_private_t *br;
+  BRIDGEIF_DECL_PROTECT(lev);
+  LWIP_ASSERT("invalid netif", bridgeif != NULL);
+  br = (bridgeif_private_t *)bridgeif->state;
+  LWIP_ASSERT("invalid state", br != NULL);
+
+  BRIDGEIF_READ_PROTECT(lev);
+  for (i = 0; i < br->max_fdbs_entries; i++) {
+    if (br->fdbs[i].used && !memcmp(&br->fdbs[i].addr, addr, sizeof(struct eth_addr))) {
+      BRIDGEIF_WRITE_PROTECT(lev);
+      if (br->fdbs[i].used && !memcmp(&br->fdbs[i].addr, addr, sizeof(struct eth_addr))) {
+        memset(&br->fdbs[i], 0, sizeof(bridgeif_fdb_static_entry_t));
+        BRIDGEIF_WRITE_UNPROTECT(lev);
+        BRIDGEIF_READ_UNPROTECT(lev);
+        return ERR_OK;
+      }
+      BRIDGEIF_WRITE_UNPROTECT(lev);
+    }
+  }
+  BRIDGEIF_READ_UNPROTECT(lev);
+  return ERR_VAL;
+}
+
+/** Get the forwarding port(s) (as bit mask) for the specified destination mac address */
+static bridgeif_portmask_t
+bridgeif_find_dst_ports(bridgeif_private_t *br, struct eth_addr *dst_addr)
+{
+  int i;
+  BRIDGEIF_DECL_PROTECT(lev);
+  BRIDGEIF_READ_PROTECT(lev);
+  /* first check for static entries */
+  for (i = 0; i < br->max_fdbs_entries; i++) {
+    if (br->fdbs[i].used) {
+      if (!memcmp(&br->fdbs[i].addr, dst_addr, sizeof(struct eth_addr))) {
+        bridgeif_portmask_t ret = br->fdbs[i].dst_ports;
+        BRIDGEIF_READ_UNPROTECT(lev);
+        return ret;
+      }
+    }
+  }
+  if (dst_addr->addr[0] & 1) {
+    /* no match found: flood remaining group address */
+    BRIDGEIF_READ_UNPROTECT(lev);
+    return BR_FLOOD;
+  }
+  BRIDGEIF_READ_UNPROTECT(lev);
+  /* no match found: check dynamic fdb for port or fall back to flooding */
+  return bridgeif_fdb_get_dst_ports(br->fdbd, dst_addr);
+}
+
+/** Helper function to see if a destination mac belongs to the bridge
+ * (bridge netif or one of the port netifs), in which case the frame
+ * is sent to the cpu only.
+ */
+static int
+bridgeif_is_local_mac(bridgeif_private_t *br, struct eth_addr *addr)
+{
+  int i;
+  BRIDGEIF_DECL_PROTECT(lev);
+  if (!memcmp(br->netif->hwaddr, addr, sizeof(struct eth_addr))) {
+    return 1;
+  }
+  BRIDGEIF_READ_PROTECT(lev);
+  for (i = 0; i < br->num_ports; i++) {
+    struct netif *portif = br->ports[i].port_netif;
+    if (portif != NULL) {
+      if (!memcmp(portif->hwaddr, addr, sizeof(struct eth_addr))) {
+        BRIDGEIF_READ_UNPROTECT(lev);
+        return 1;
+      }
+    }
+  }
+  BRIDGEIF_READ_UNPROTECT(lev);
+  return 0;
+}
+
+/* Output helper function */
+static err_t
+bridgeif_send_to_port(bridgeif_private_t *br, struct pbuf *p, u8_t dstport_idx)
+{
+  if (dstport_idx < BRIDGEIF_MAX_PORTS) {
+    /* possibly an external port */
+    if (dstport_idx < br->max_ports) {
+      struct netif *portif = br->ports[dstport_idx].port_netif;
+      if ((portif != NULL) && (portif->linkoutput != NULL)) {
+        /* prevent sending out to rx port */
+        if (netif_get_index(portif) != p->if_idx) {
+          if (netif_is_link_up(portif)) {
+            LWIP_DEBUGF(BRIDGEIF_FW_DEBUG, ("br -> flood(%p:%d) -> %d\n", (void *)p, p->if_idx, netif_get_index(portif)));
+            return portif->linkoutput(portif, p);
+          }
+        }
+      }
+    }
+  } else {
+    LWIP_ASSERT("invalid port index", dstport_idx == BRIDGEIF_MAX_PORTS);
+  }
+  return ERR_OK;
+}
+
+/** Helper function to pass a pbuf to all ports marked in 'dstports'
+ */
+static err_t
+bridgeif_send_to_ports(bridgeif_private_t *br, struct pbuf *p, bridgeif_portmask_t dstports)
+{
+  err_t err, ret_err = ERR_OK;
+  u8_t i;
+  bridgeif_portmask_t mask = 1;
+  BRIDGEIF_DECL_PROTECT(lev);
+  BRIDGEIF_READ_PROTECT(lev);
+  for (i = 0; i < BRIDGEIF_MAX_PORTS; i++, mask = (bridgeif_portmask_t)(mask << 1)) {
+    if (dstports & mask) {
+      err = bridgeif_send_to_port(br, p, i);
+      if (err != ERR_OK) {
+        ret_err = err;
+      }
+    }
+  }
+  BRIDGEIF_READ_UNPROTECT(lev);
+  return ret_err;
+}
+
+/** Output function of the application port of the bridge (the one with an ip address).
+ * The forwarding port(s) where this pbuf is sent on is/are automatically selected
+ * from the FDB.
+ */
+static err_t
+bridgeif_output(struct netif *netif, struct pbuf *p)
+{
+  err_t err;
+  bridgeif_private_t *br = (bridgeif_private_t *)netif->state;
+  struct eth_addr *dst = (struct eth_addr *)(p->payload);
+
+  bridgeif_portmask_t dstports = bridgeif_find_dst_ports(br, dst);
+  err = bridgeif_send_to_ports(br, p, dstports);
+
+  MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p->tot_len);
+  if (((u8_t *)p->payload)[0] & 1) {
+    /* broadcast or multicast packet*/
+    MIB2_STATS_NETIF_INC(netif, ifoutnucastpkts);
+  } else {
+    /* unicast packet */
+    MIB2_STATS_NETIF_INC(netif, ifoutucastpkts);
+  }
+  /* increase ifoutdiscards or ifouterrors on error */
+
+  LINK_STATS_INC(link.xmit);
+
+  return err;
+}
+
+/** The actual bridge input function. Port netif's input is changed to call
+ * here. This function decides where the frame is forwarded.
+ */
+static err_t
+bridgeif_input(struct pbuf *p, struct netif *netif)
+{
+  u8_t rx_idx;
+  bridgeif_portmask_t dstports;
+  struct eth_addr *src, *dst;
+  bridgeif_private_t *br;
+  bridgeif_port_t *port;
+  if (p == NULL || netif == NULL) {
+    return ERR_VAL;
+  }
+  port = (bridgeif_port_t *)netif_get_client_data(netif, bridgeif_netif_client_id);
+  LWIP_ASSERT("port data not set", port != NULL);
+  if (port == NULL || port->bridge == NULL) {
+    return ERR_VAL;
+  }
+  br = (bridgeif_private_t *)port->bridge;
+  rx_idx = netif_get_index(netif);
+  /* store receive index in pbuf */
+  p->if_idx = rx_idx;
+
+  dst = (struct eth_addr *)p->payload;
+  src = (struct eth_addr *)(((u8_t *)p->payload) + sizeof(struct eth_addr));
+
+  if ((src->addr[0] & 1) == 0) {
+    /* update src for all non-group addresses */
+    bridgeif_fdb_update_src(br->fdbd, src, port->port_num);
+  }
+
+  if (dst->addr[0] & 1) {
+    /* group address -> flood + cpu? */
+    dstports = bridgeif_find_dst_ports(br, dst);
+    bridgeif_send_to_ports(br, p, dstports);
+    if (dstports & (1 << BRIDGEIF_MAX_PORTS)) {
+      /* we pass the reference to ->input or have to free it */
+      LWIP_DEBUGF(BRIDGEIF_FW_DEBUG, ("br -> input(%p)\n", (void *)p));
+      if (br->netif->input(p, br->netif) != ERR_OK) {
+        pbuf_free(p);
+      }
+    } else {
+      /* all references done */
+      pbuf_free(p);
+    }
+    /* always return ERR_OK here to prevent the caller freeing the pbuf */
+    return ERR_OK;
+  } else {
+    /* is this for one of the local ports? */
+    if (bridgeif_is_local_mac(br, dst)) {
+      /* yes, send to cpu port only */
+      LWIP_DEBUGF(BRIDGEIF_FW_DEBUG, ("br -> input(%p)\n", (void *)p));
+      return br->netif->input(p, br->netif);
+    }
+
+    /* get dst port */
+    dstports = bridgeif_find_dst_ports(br, dst);
+    bridgeif_send_to_ports(br, p, dstports);
+    /* no need to send to cpu, flooding is for external ports only */
+    /* by  this, we consumed the pbuf */
+    pbuf_free(p);
+    /* always return ERR_OK here to prevent the caller freeing the pbuf */
+    return ERR_OK;
+  }
+}
+
+#if !BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT
+/** Input function for port netifs used to synchronize into tcpip_thread.
+ */
+static err_t
+bridgeif_tcpip_input(struct pbuf *p, struct netif *netif)
+{
+  return tcpip_inpkt(p, netif, bridgeif_input);
+}
+#endif /* BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT */
+
+/**
+ * @ingroup bridgeif
+ * Initialization function passed to netif_add().
+ *
+ * ATTENTION: A pointer to a @ref bridgeif_initdata_t must be passed as 'state'
+ *            to @ref netif_add when adding the bridge. I supplies MAC address
+ *            and controls memory allocation (number of ports, FDB size).
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ * @return ERR_OK if the loopif is initialized
+ *         ERR_MEM if private data couldn't be allocated
+ *         any other err_t on error
+ */
+err_t
+bridgeif_init(struct netif *netif)
+{
+  bridgeif_initdata_t *init_data;
+  bridgeif_private_t *br;
+  size_t alloc_len_sizet;
+  mem_size_t alloc_len;
+
+  LWIP_ASSERT("netif != NULL", (netif != NULL));
+  LWIP_ASSERT("bridgeif needs an input callback", (netif->input != NULL));
+#if !BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT
+  if (netif->input == tcpip_input) {
+    LWIP_DEBUGF(BRIDGEIF_DEBUG | LWIP_DBG_ON, ("bridgeif does not need tcpip_input, use netif_input/ethernet_input instead"));
+  }
+#endif
+
+  if (bridgeif_netif_client_id == 0xFF) {
+    bridgeif_netif_client_id = netif_alloc_client_data_id();
+  }
+
+  init_data = (bridgeif_initdata_t *)netif->state;
+  LWIP_ASSERT("init_data != NULL", (init_data != NULL));
+  LWIP_ASSERT("init_data->max_ports <= BRIDGEIF_MAX_PORTS",
+              init_data->max_ports <= BRIDGEIF_MAX_PORTS);
+
+  alloc_len_sizet = sizeof(bridgeif_private_t) + (init_data->max_ports * sizeof(bridgeif_port_t) + (init_data->max_fdb_static_entries * sizeof(bridgeif_fdb_static_entry_t)));
+  alloc_len = (mem_size_t)alloc_len_sizet;
+  LWIP_ASSERT("alloc_len == alloc_len_sizet", alloc_len == alloc_len_sizet);
+  LWIP_DEBUGF(BRIDGEIF_DEBUG, ("bridgeif_init: allocating %d bytes for private data\n", (int)alloc_len));
+  br = (bridgeif_private_t *)mem_calloc(1, alloc_len);
+  if (br == NULL) {
+    LWIP_DEBUGF(NETIF_DEBUG, ("bridgeif_init: out of memory\n"));
+    return ERR_MEM;
+  }
+  memcpy(&br->ethaddr, &init_data->ethaddr, sizeof(br->ethaddr));
+  br->netif = netif;
+
+  br->max_ports = init_data->max_ports;
+  br->ports = (bridgeif_port_t *)(br + 1);
+
+  br->max_fdbs_entries = init_data->max_fdb_static_entries;
+  br->fdbs = (bridgeif_fdb_static_entry_t *)(((u8_t *)(br + 1)) + (init_data->max_ports * sizeof(bridgeif_port_t)));
+
+  br->max_fdbd_entries = init_data->max_fdb_dynamic_entries;
+  br->fdbd = bridgeif_fdb_init(init_data->max_fdb_dynamic_entries);
+  if (br->fdbd == NULL) {
+    LWIP_DEBUGF(NETIF_DEBUG, ("bridgeif_init: out of memory in fdb_init\n"));
+    mem_free(br);
+    return ERR_MEM;
+  }
+
+#if LWIP_NETIF_HOSTNAME
+  /* Initialize interface hostname */
+  netif->hostname = "lwip";
+#endif /* LWIP_NETIF_HOSTNAME */
+
+  /*
+   * Initialize the snmp variables and counters inside the struct netif.
+   * The last argument should be replaced with your link speed, in units
+   * of bits per second.
+   */
+  MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, 0);
+
+  netif->state = br;
+  netif->name[0] = IFNAME0;
+  netif->name[1] = IFNAME1;
+  /* We directly use etharp_output() here to save a function call.
+   * You can instead declare your own function an call etharp_output()
+   * from it if you have to do some checks before sending (e.g. if link
+   * is available...) */
+#if LWIP_IPV4
+  netif->output = etharp_output;
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+  netif->output_ip6 = ethip6_output;
+#endif /* LWIP_IPV6 */
+  netif->linkoutput = bridgeif_output;
+
+  /* set MAC hardware address length */
+  netif->hwaddr_len = ETH_HWADDR_LEN;
+
+  /* set MAC hardware address */
+  memcpy(netif->hwaddr, &br->ethaddr, ETH_HWADDR_LEN);
+
+  /* maximum transfer unit */
+  netif->mtu = 1500;
+
+  /* device capabilities */
+  /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
+  netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_IGMP | NETIF_FLAG_MLD6 | NETIF_FLAG_LINK_UP;
+
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+  /*
+   * For hardware/netifs that implement MAC filtering.
+   * All-nodes link-local is handled by default, so we must let the hardware know
+   * to allow multicast packets in.
+   * Should set mld_mac_filter previously. */
+  if (netif->mld_mac_filter != NULL) {
+    ip6_addr_t ip6_allnodes_ll;
+    ip6_addr_set_allnodes_linklocal(&ip6_allnodes_ll);
+    netif->mld_mac_filter(netif, &ip6_allnodes_ll, NETIF_ADD_MAC_FILTER);
+  }
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+
+  return ERR_OK;
+}
+
+/**
+ * @ingroup bridgeif
+ * Add a port to the bridge
+ */
+err_t
+bridgeif_add_port(struct netif *bridgeif, struct netif *portif)
+{
+  bridgeif_private_t *br;
+  bridgeif_port_t *port;
+
+  LWIP_ASSERT("bridgeif != NULL", bridgeif != NULL);
+  LWIP_ASSERT("bridgeif->state != NULL", bridgeif->state != NULL);
+  LWIP_ASSERT("portif != NULL", portif != NULL);
+
+  if (!(portif->flags & NETIF_FLAG_ETHARP) || !(portif->flags & NETIF_FLAG_ETHERNET)) {
+    /* can only add ETHERNET/ETHARP interfaces */
+    return ERR_VAL;
+  }
+
+  br = (bridgeif_private_t *)bridgeif->state;
+
+  if (br->num_ports >= br->max_ports) {
+    return ERR_VAL;
+  }
+  port = &br->ports[br->num_ports];
+  port->port_netif = portif;
+  port->port_num = br->num_ports;
+  port->bridge = br;
+  br->num_ports++;
+
+  /* let the port call us on input */
+#if BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT
+  portif->input = bridgeif_input;
+#else
+  portif->input = bridgeif_tcpip_input;
+#endif
+  /* store pointer to bridge in netif */
+  netif_set_client_data(portif, bridgeif_netif_client_id, port);
+  /* remove ETHARP flag to prevent sending report events on netif-up */
+  netif_clear_flags(portif, NETIF_FLAG_ETHARP);
+
+  return ERR_OK;
+}
+
+#endif /* LWIP_NUM_NETIF_CLIENT_DATA */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/bridgeif_fdb.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/bridgeif_fdb.c
new file mode 100644 (file)
index 0000000..6739fc2
--- /dev/null
@@ -0,0 +1,212 @@
+/**
+ * @file
+ * lwIP netif implementing an FDB for IEEE 802.1D MAC Bridge
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt.
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+
+/**
+ * @defgroup bridgeif_fdb FDB example code
+ * @ingroup bridgeif
+ * This file implements an example for an FDB (Forwarding DataBase)
+ */
+
+#include "netif/bridgeif.h"
+#include "lwip/sys.h"
+#include "lwip/mem.h"
+#include "lwip/timeouts.h"
+#include <string.h>
+
+#define BRIDGEIF_AGE_TIMER_MS 1000
+
+#define BR_FDB_TIMEOUT_SEC  (60*5) /* 5 minutes FDB timeout */
+
+typedef struct bridgeif_dfdb_entry_s {
+  u8_t used;
+  u8_t port;
+  u32_t ts;
+  struct eth_addr addr;
+} bridgeif_dfdb_entry_t;
+
+typedef struct bridgeif_dfdb_s {
+  u16_t max_fdb_entries;
+  bridgeif_dfdb_entry_t *fdb;
+} bridgeif_dfdb_t;
+
+/**
+ * @ingroup bridgeif_fdb
+ * A real simple and slow implementation of an auto-learning forwarding database that
+ * remembers known src mac addresses to know which port to send frames destined for that
+ * mac address.
+ *
+ * ATTENTION: This is meant as an example only, in real-world use, you should 
+ * provide a better implementation :-)
+ */
+void
+bridgeif_fdb_update_src(void *fdb_ptr, struct eth_addr *src_addr, u8_t port_idx)
+{
+  int i;
+  bridgeif_dfdb_t *fdb = (bridgeif_dfdb_t *)fdb_ptr;
+  BRIDGEIF_DECL_PROTECT(lev);
+  BRIDGEIF_READ_PROTECT(lev);
+  for (i = 0; i < fdb->max_fdb_entries; i++) {
+    bridgeif_dfdb_entry_t *e = &fdb->fdb[i];
+    if (e->used && e->ts) {
+      if (!memcmp(&e->addr, src_addr, sizeof(struct eth_addr))) {
+        LWIP_DEBUGF(BRIDGEIF_FDB_DEBUG, ("br: update src %02x:%02x:%02x:%02x:%02x:%02x (from %d) @ idx %d\n",
+                                         src_addr->addr[0], src_addr->addr[1], src_addr->addr[2], src_addr->addr[3], src_addr->addr[4], src_addr->addr[5],
+                                         port_idx, i));
+        BRIDGEIF_WRITE_PROTECT(lev);
+        e->ts = BR_FDB_TIMEOUT_SEC;
+        e->port = port_idx;
+        BRIDGEIF_WRITE_UNPROTECT(lev);
+        BRIDGEIF_READ_UNPROTECT(lev);
+        return;
+      }
+    }
+  }
+  /* not found, allocate new entry from free */
+  for (i = 0; i < fdb->max_fdb_entries; i++) {
+    bridgeif_dfdb_entry_t *e = &fdb->fdb[i];
+    if (!e->used || !e->ts) {
+      BRIDGEIF_WRITE_PROTECT(lev);
+      /* check again when protected */
+      if (!e->used || !e->ts) {
+        LWIP_DEBUGF(BRIDGEIF_FDB_DEBUG, ("br: create src %02x:%02x:%02x:%02x:%02x:%02x (from %d) @ idx %d\n",
+                                         src_addr->addr[0], src_addr->addr[1], src_addr->addr[2], src_addr->addr[3], src_addr->addr[4], src_addr->addr[5],
+                                         port_idx, i));
+        memcpy(&e->addr, src_addr, sizeof(struct eth_addr));
+        e->ts = BR_FDB_TIMEOUT_SEC;
+        e->port = port_idx;
+        e->used = 1;
+        BRIDGEIF_WRITE_UNPROTECT(lev);
+        BRIDGEIF_READ_UNPROTECT(lev);
+        return;
+      }
+      BRIDGEIF_WRITE_UNPROTECT(lev);
+    }
+  }
+  BRIDGEIF_READ_UNPROTECT(lev);
+  /* not found, no free entry -> flood */
+}
+
+/** 
+ * @ingroup bridgeif_fdb
+ * Walk our list of auto-learnt fdb entries and return a port to forward or BR_FLOOD if unknown 
+ */
+bridgeif_portmask_t
+bridgeif_fdb_get_dst_ports(void *fdb_ptr, struct eth_addr *dst_addr)
+{
+  int i;
+  bridgeif_dfdb_t *fdb = (bridgeif_dfdb_t *)fdb_ptr;
+  BRIDGEIF_DECL_PROTECT(lev);
+  BRIDGEIF_READ_PROTECT(lev);
+  for (i = 0; i < fdb->max_fdb_entries; i++) {
+    bridgeif_dfdb_entry_t *e = &fdb->fdb[i];
+    if (e->used && e->ts) {
+      if (!memcmp(&e->addr, dst_addr, sizeof(struct eth_addr))) {
+        bridgeif_portmask_t ret = (bridgeif_portmask_t)(1 << e->port);
+        BRIDGEIF_READ_UNPROTECT(lev);
+        return ret;
+      }
+    }
+  }
+  BRIDGEIF_READ_UNPROTECT(lev);
+  return BR_FLOOD;
+}
+
+/**
+ * @ingroup bridgeif_fdb
+ * Aging implementation of our simple fdb
+ */
+static void
+bridgeif_fdb_age_one_second(void *fdb_ptr)
+{
+  int i;
+  bridgeif_dfdb_t *fdb;
+  BRIDGEIF_DECL_PROTECT(lev);
+
+  fdb = (bridgeif_dfdb_t *)fdb_ptr;
+  BRIDGEIF_READ_PROTECT(lev);
+
+  for (i = 0; i < fdb->max_fdb_entries; i++) {
+    bridgeif_dfdb_entry_t *e = &fdb->fdb[i];
+    if (e->used && e->ts) {
+      BRIDGEIF_WRITE_PROTECT(lev);
+      /* check again when protected */
+      if (e->used && e->ts) {
+        if (--e->ts == 0) {
+          e->used = 0;
+        }
+      }
+      BRIDGEIF_WRITE_UNPROTECT(lev);
+    }
+  }
+  BRIDGEIF_READ_UNPROTECT(lev);
+}
+
+/** Timer callback for fdb aging, called once per second */
+static void
+bridgeif_age_tmr(void *arg)
+{
+  bridgeif_dfdb_t *fdb = (bridgeif_dfdb_t *)arg;
+
+  LWIP_ASSERT("invalid arg", arg != NULL);
+
+  bridgeif_fdb_age_one_second(fdb);
+  sys_timeout(BRIDGEIF_AGE_TIMER_MS, bridgeif_age_tmr, arg);
+}
+
+/**
+ * @ingroup bridgeif_fdb
+ * Init our simple fdb list
+ */
+void *
+bridgeif_fdb_init(u16_t max_fdb_entries)
+{
+  bridgeif_dfdb_t *fdb;
+  size_t alloc_len_sizet = sizeof(bridgeif_dfdb_t) + (max_fdb_entries * sizeof(bridgeif_dfdb_entry_t));
+  mem_size_t alloc_len = (mem_size_t)alloc_len_sizet;
+  LWIP_ASSERT("alloc_len == alloc_len_sizet", alloc_len == alloc_len_sizet);
+  LWIP_DEBUGF(BRIDGEIF_DEBUG, ("bridgeif_fdb_init: allocating %d bytes for private FDB data\n", (int)alloc_len));
+  fdb = (bridgeif_dfdb_t *)mem_calloc(1, alloc_len);
+  if (fdb == NULL) {
+    return NULL;
+  }
+  fdb->max_fdb_entries = max_fdb_entries;
+  fdb->fdb = (bridgeif_dfdb_entry_t *)(fdb + 1);
+
+  sys_timeout(BRIDGEIF_AGE_TIMER_MS, bridgeif_age_tmr, fdb);
+
+  return fdb;
+}
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/lowpan6_ble.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/lowpan6_ble.c
new file mode 100644 (file)
index 0000000..6de0ae3
--- /dev/null
@@ -0,0 +1,447 @@
+/**
+ * @file
+ * 6LowPAN over BLE output for IPv6 (RFC7668).
+*/
+
+/*
+ * Copyright (c) 2017 Benjamin Aigner
+ * Copyright (c) 2015 Inico Technologies Ltd. , Author: Ivan Delamer <delamer@inicotech.com>
+ * 
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * Author: Benjamin Aigner <aignerb@technikum-wien.at>
+ * 
+ * Based on the original 6lowpan implementation of lwIP ( @see 6lowpan.c)
+ */
+
+
+/**
+ * @defgroup rfc7668if 6LoWPAN over BLE (RFC7668)
+ * @ingroup netifs
+ * This file implements a RFC7668 implementation for 6LoWPAN over
+ * Bluetooth Low Energy. The specification is very similar to 6LoWPAN,
+ * so most of the code is re-used.
+ * Compared to 6LoWPAN, much functionality is already implemented in
+ * lower BLE layers (fragmenting, session management,...).
+ *
+ * Usage:
+ * - add this netif
+ *   - don't add IPv4 addresses (no IPv4 support in RFC7668), pass 'NULL','NULL','NULL'
+ *   - use the BLE to EUI64 conversation util to create an IPv6 link-local address from the BLE MAC (@ref ble_addr_to_eui64)
+ *   - input function: @ref rfc7668_input
+ * - set the link output function, which transmits output data to an established L2CAP channel
+ * - If data arrives (HCI event "L2CAP_DATA_PACKET"):
+ *   - allocate a @ref PBUF_RAW buffer
+ *   - let the pbuf struct point to the incoming data or copy it to the buffer
+ *   - call netif->input
+ *
+ * @todo:
+ * - further testing
+ * - support compression contexts
+ * - support multiple addresses
+ * - support multicast
+ * - support neighbor discovery
+ */
+
+
+#include "netif/lowpan6_ble.h"
+
+#if LWIP_IPV6
+
+#include "lwip/ip.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/nd6.h"
+#include "lwip/mem.h"
+#include "lwip/udp.h"
+#include "lwip/tcpip.h"
+#include "lwip/snmp.h"
+
+#include <string.h>
+
+#if LWIP_6LOWPAN_NUM_CONTEXTS > 0
+/** context memory, containing IPv6 addresses */
+static ip6_addr_t rfc7668_context[LWIP_6LOWPAN_NUM_CONTEXTS];
+#else
+#define rfc7668_context NULL
+#endif
+
+static struct lowpan6_link_addr rfc7668_local_addr;
+static struct lowpan6_link_addr rfc7668_peer_addr;
+
+/**
+ * @ingroup rfc7668if
+ *  convert BT address to EUI64 addr
+ * 
+ * This method converts a Bluetooth MAC address to an EUI64 address,
+ * which is used within IPv6 communication
+ * 
+ * @param dst IPv6 destination space
+ * @param src BLE MAC address source
+ * @param public_addr If the LWIP_RFC7668_LINUX_WORKAROUND_PUBLIC_ADDRESS
+ * option is set, bit 0x02 will be set if param=0 (no public addr); cleared otherwise
+ * 
+ * @see LWIP_RFC7668_LINUX_WORKAROUND_PUBLIC_ADDRESS
+ */
+void
+ble_addr_to_eui64(uint8_t *dst, const uint8_t *src, int public_addr)
+{
+  /* according to RFC7668 ch 3.2.2. */
+  memcpy(dst, src, 3);
+  dst[3] = 0xFF;
+  dst[4] = 0xFE;
+  memcpy(&dst[5], &src[3], 3);
+#if LWIP_RFC7668_LINUX_WORKAROUND_PUBLIC_ADDRESS
+  if(public_addr) {
+    dst[0] &= ~0x02;
+  } else {
+    dst[0] |= 0x02;
+  }
+#else
+  LWIP_UNUSED_ARG(public_addr);
+#endif
+}
+
+/**
+ * @ingroup rfc7668if
+ *  convert EUI64 address to Bluetooth MAC addr
+ * 
+ * This method converts an EUI64 address to a Bluetooth MAC address,
+ * 
+ * @param dst BLE MAC address destination
+ * @param src IPv6 source
+ * 
+ */
+void
+eui64_to_ble_addr(uint8_t *dst, const uint8_t *src)
+{
+  /* according to RFC7668 ch 3.2.2. */
+  memcpy(dst,src,3);
+  memcpy(&dst[3],&src[5],3);
+}
+
+/** Set an address used for stateful compression.
+ * This expects an address of 6 or 8 bytes.
+ */
+static err_t
+rfc7668_set_addr(struct lowpan6_link_addr *addr, const u8_t *in_addr, size_t in_addr_len, int is_mac_48, int is_public_addr)
+{
+  if ((in_addr == NULL) || (addr == NULL)) {
+    return ERR_VAL;
+  }
+  if (is_mac_48) {
+    if (in_addr_len != 6) {
+      return ERR_VAL;
+    }
+    addr->addr_len = 8;
+    ble_addr_to_eui64(addr->addr, in_addr, is_public_addr);
+  } else {
+    if (in_addr_len != 8) {
+      return ERR_VAL;
+    }
+    addr->addr_len = 8;
+    memcpy(addr->addr, in_addr, 8);
+  }
+  return ERR_OK;
+}
+
+
+/** Set the local address used for stateful compression.
+ * This expects an address of 8 bytes.
+ */
+err_t
+rfc7668_set_local_addr_eui64(struct netif *netif, const u8_t *local_addr, size_t local_addr_len)
+{
+  /* netif not used for now, the address is stored globally... */
+  LWIP_UNUSED_ARG(netif);
+  return rfc7668_set_addr(&rfc7668_local_addr, local_addr, local_addr_len, 0, 0);
+}
+
+/** Set the local address used for stateful compression.
+ * This expects an address of 6 bytes.
+ */
+err_t
+rfc7668_set_local_addr_mac48(struct netif *netif, const u8_t *local_addr, size_t local_addr_len, int is_public_addr)
+{
+  /* netif not used for now, the address is stored globally... */
+  LWIP_UNUSED_ARG(netif);
+  return rfc7668_set_addr(&rfc7668_local_addr, local_addr, local_addr_len, 1, is_public_addr);
+}
+
+/** Set the peer address used for stateful compression.
+ * This expects an address of 8 bytes.
+ */
+err_t
+rfc7668_set_peer_addr_eui64(struct netif *netif, const u8_t *peer_addr, size_t peer_addr_len)
+{
+  /* netif not used for now, the address is stored globally... */
+  LWIP_UNUSED_ARG(netif);
+  return rfc7668_set_addr(&rfc7668_peer_addr, peer_addr, peer_addr_len, 0, 0);
+}
+
+/** Set the peer address used for stateful compression.
+ * This expects an address of 6 bytes.
+ */
+err_t
+rfc7668_set_peer_addr_mac48(struct netif *netif, const u8_t *peer_addr, size_t peer_addr_len, int is_public_addr)
+{
+  /* netif not used for now, the address is stored globally... */
+  LWIP_UNUSED_ARG(netif);
+  return rfc7668_set_addr(&rfc7668_peer_addr, peer_addr, peer_addr_len, 1, is_public_addr);
+}
+
+/** Encapsulate IPv6 frames for BLE transmission
+ * 
+ * This method implements the IPv6 header compression:
+ *  *) According to RFC6282
+ *  *) See Figure 2, contains base format of bit positions
+ *  *) Fragmentation not necessary (done at L2CAP layer of BLE)
+ * @note Currently the pbuf allocation uses 256 bytes. If longer packets are used (possible due to MTU=1480Bytes), increase it here!
+ * 
+ * @param p Pbuf struct, containing the payload data
+ * @param netif Output network interface. Should be of RFC7668 type
+ * 
+ * @return Same as netif->output.
+ */
+static err_t
+rfc7668_compress(struct netif *netif, struct pbuf *p)
+{
+  struct pbuf *p_frag;
+  u16_t remaining_len;
+  u8_t *buffer;
+  u8_t lowpan6_header_len;
+  u8_t hidden_header_len;
+  err_t err;
+
+  LWIP_ASSERT("lowpan6_frag: netif->linkoutput not set", netif->linkoutput != NULL);
+
+#if LWIP_6LOWPAN_IPHC
+
+  /* We'll use a dedicated pbuf for building BLE fragments.
+   * We'll over-allocate it by the bytes saved for header compression.
+   */
+  p_frag = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+  if (p_frag == NULL) {
+    MIB2_STATS_NETIF_INC(netif, ifoutdiscards);
+    return ERR_MEM;
+  }
+  LWIP_ASSERT("this needs a pbuf in one piece", p_frag->len == p_frag->tot_len);
+
+  /* Write IP6 header (with IPHC). */
+  buffer = (u8_t*)p_frag->payload;
+
+  err = lowpan6_compress_headers(netif, (u8_t *)p->payload, p->len, buffer, p_frag->len,
+    &lowpan6_header_len, &hidden_header_len, rfc7668_context, &rfc7668_local_addr, &rfc7668_peer_addr);
+  if (err != ERR_OK) {
+    MIB2_STATS_NETIF_INC(netif, ifoutdiscards);
+    pbuf_free(p_frag);
+    return err;
+  }
+  pbuf_remove_header(p, hidden_header_len);
+
+  /* Calculate remaining packet length */
+  remaining_len = p->tot_len;
+
+  /* Copy IPv6 packet */
+  pbuf_copy_partial(p, buffer + lowpan6_header_len, remaining_len, 0);
+
+  /* Calculate frame length */
+  p_frag->len = p_frag->tot_len = remaining_len + lowpan6_header_len;
+
+  /* send the packet */
+  MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p_frag->tot_len);
+  LWIP_DEBUGF(LWIP_LOWPAN6_DEBUG|LWIP_DBG_TRACE, ("rfc7668_output: sending packet %p\n", (void *)p));
+  err = netif->linkoutput(netif, p_frag);
+
+  pbuf_free(p_frag);
+
+  return err;
+#else /* LWIP_6LOWPAN_IPHC */
+  /* 6LoWPAN over BLE requires IPHC! */
+  return ERR_IF;
+#endif/* LWIP_6LOWPAN_IPHC */
+}
+
+/**
+ * @ingroup rfc7668if
+ * Set context id IPv6 address
+ *
+ * Store one IPv6 address to a given context id.
+ *
+ * @param idx Context id
+ * @param context IPv6 addr for this context
+ *
+ * @return ERR_OK (if everything is fine), ERR_ARG (if the context id is out of range), ERR_VAL (if contexts disabled)
+ */
+err_t
+rfc7668_set_context(u8_t idx, const ip6_addr_t *context)
+{
+#if LWIP_6LOWPAN_NUM_CONTEXTS > 0
+  /* check if the ID is possible */
+  if (idx >= LWIP_6LOWPAN_NUM_CONTEXTS) {
+    return ERR_ARG;
+  }
+  /* copy IPv6 address to context storage */
+  ip6_addr_set(&rfc7668_context[idx], context);
+  return ERR_OK;
+#else
+  LWIP_UNUSED_ARG(idx);
+  LWIP_UNUSED_ARG(context);
+  return ERR_VAL;
+#endif
+}
+
+/**
+ * @ingroup rfc7668if
+ * Compress outgoing IPv6 packet and pass it on to netif->linkoutput
+ *
+ * @param netif The lwIP network interface which the IP packet will be sent on.
+ * @param q The pbuf(s) containing the IP packet to be sent.
+ * @param ip6addr The IP address of the packet destination.
+ *
+ * @return See rfc7668_compress
+ */
+err_t
+rfc7668_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr)
+{
+  /* dst ip6addr is not used here, we only have one peer */
+  LWIP_UNUSED_ARG(ip6addr);
+
+  return rfc7668_compress(netif, q);
+}
+
+/**
+ * @ingroup rfc7668if
+ * Process a received raw payload from an L2CAP channel
+ *
+ * @param p the received packet, p->payload pointing to the
+ *        IPv6 header (maybe compressed)
+ * @param netif the network interface on which the packet was received
+ * 
+ * @return ERR_OK if everything was fine
+ */
+err_t
+rfc7668_input(struct pbuf * p, struct netif *netif)
+{
+  u8_t * puc;
+
+  MIB2_STATS_NETIF_ADD(netif, ifinoctets, p->tot_len);
+
+  /* Load first header byte */
+  puc = (u8_t*)p->payload;
+  
+  /* no IP header compression */
+  if (*puc == 0x41) {
+    LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("Completed packet, removing dispatch: 0x%2x \n", *puc));
+    /* This is a complete IPv6 packet, just skip header byte. */
+    pbuf_remove_header(p, 1);
+  /* IPHC header compression */
+  } else if ((*puc & 0xe0 )== 0x60) {
+    LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("Completed packet, decompress dispatch: 0x%2x \n", *puc));
+    /* IPv6 headers are compressed using IPHC. */
+    p = lowpan6_decompress(p, 0, rfc7668_context, &rfc7668_peer_addr, &rfc7668_local_addr);
+    /* if no pbuf is returned, handle as discarded packet */
+    if (p == NULL) {
+      MIB2_STATS_NETIF_INC(netif, ifindiscards);
+      return ERR_OK;
+    }
+  /* invalid header byte, discard */
+  } else {
+    LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("Completed packet, discarding: 0x%2x \n", *puc));
+    MIB2_STATS_NETIF_INC(netif, ifindiscards);
+    pbuf_free(p);
+    return ERR_OK;
+  }
+  /* @todo: distinguish unicast/multicast */
+  MIB2_STATS_NETIF_INC(netif, ifinucastpkts);
+
+#if LWIP_RFC7668_IP_UNCOMPRESSED_DEBUG
+  {
+    u16_t i;
+    LWIP_DEBUGF(LWIP_RFC7668_IP_UNCOMPRESSED_DEBUG, ("IPv6 payload:\n"));
+    for (i = 0; i < p->len; i++) {
+      if ((i%4)==0) {
+        LWIP_DEBUGF(LWIP_RFC7668_IP_UNCOMPRESSED_DEBUG, ("\n"));
+      }
+      LWIP_DEBUGF(LWIP_RFC7668_IP_UNCOMPRESSED_DEBUG, ("%2X ", *((uint8_t *)p->payload+i)));
+    }
+    LWIP_DEBUGF(LWIP_RFC7668_IP_UNCOMPRESSED_DEBUG, ("\np->len: %d\n", p->len));
+  }
+#endif
+  /* pass data to ip6_input */
+  return ip6_input(p, netif);
+}
+
+/**
+ * @ingroup rfc7668if
+ * Initialize the netif
+ * 
+ * No flags are used (broadcast not possible, not ethernet, ...)
+ * The shortname for this netif is "BT"
+ *
+ * @param netif the network interface to be initialized as RFC7668 netif
+ * 
+ * @return ERR_OK if everything went fine
+ */
+err_t
+rfc7668_if_init(struct netif *netif)
+{
+  netif->name[0] = 'b';
+  netif->name[1] = 't';
+  /* local function as IPv6 output */
+  netif->output_ip6 = rfc7668_output;
+
+  MIB2_INIT_NETIF(netif, snmp_ifType_other, 0);
+
+  /* maximum transfer unit, set according to RFC7668 ch2.4 */
+  netif->mtu = IP6_MIN_MTU_LENGTH;
+
+  /* no flags set (no broadcast, ethernet,...)*/
+  netif->flags = 0;
+
+  /* everything fine */
+  return ERR_OK;
+}
+
+#if !NO_SYS
+/**
+ * Pass a received packet to tcpip_thread for input processing
+ *
+ * @param p the received packet, p->payload pointing to the
+ *          IEEE 802.15.4 header.
+ * @param inp the network interface on which the packet was received
+ * 
+ * @return see @ref tcpip_inpkt, same return values
+ */
+err_t
+tcpip_rfc7668_input(struct pbuf *p, struct netif *inp)
+{
+  /* send data to upper layer, return the result */
+  return tcpip_inpkt(p, inp, rfc7668_input);
+}
+#endif /* !NO_SYS */
+
+#endif /* LWIP_IPV6 */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/PPPD_FOLLOWUP b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/PPPD_FOLLOWUP
new file mode 100644 (file)
index 0000000..c231982
--- /dev/null
@@ -0,0 +1,473 @@
+The lwIP PPP support is based from pppd 2.4.5 (http://ppp.samba.org) with
+huge changes to match code size and memory requirements for embedded devices.
+
+Anyway, pppd has a mature codebase for years and the average commit count
+is getting low on their Git repository, meaning that we can follow what
+is happening on their side and merge what is relevant for lwIP.
+
+So, here is the pppd follow up, so that we don't get away too far from pppd.
+
+
+== Patch fetched from from pppd Debian packages ==
+
+This has nothing to do with pppd, but we merged some good patch from
+Debian and this is a good place to be.
+
+- LCP adaptive echo, so that we don't send LCP echo request if we
+  are receiving data from peer, can be enabled by setting PPP_LCP_ADAPTIVE
+  to true.
+ 
+- IPCP no/replace default route option, were added in the early stage of
+  the ppp port, but it wasn't really helpful and was disabled when adding
+  the new API ppp_set_default() call, which gives the lwIP user control over
+  which one is the default interface, it was actually a requirement if you
+  are doing PPP over PPP (i.e. PPPoL2TP, VPN link, over PPPoE, ADSL link).
+
+- using rp-pppoe pppd exits with EXIT_OK after receiving a timeout waiting
+  for PADO due to no modem attached, bug reported to pppd bug tracker, fixed
+  in Debian but not in the latest (at the time when the port were started)
+  pppd release.
+
+
+== Commits on pppd ==
+
+2010-03-06 - Document +ipv6 and ipv6cp-accept-local
+  e7537958aee79b3f653c601e903cb31d78fb7dcc
+
+Don't care.
+
+
+2010-03-06 - Install pppol2tp plugins with sane permissions
+  406215672cfadc03017341fe03802d1c7294b903
+
+Don't care.
+
+
+2010-03-07 - pppd: Terminate correctly if lcp_lowerup delayed calling
+             fsm_lowerup
+  3eb9e810cfa515543655659b72dde30c54fea0a5
+
+Merged 2012-05-17.
+
+
+2010-03-07 - rp_pppoe: Copy acName and pppd_pppoe_service after option parsing
+  cab58617fd9d328029fffabc788020264b4fa91f
+
+Don't care, is a patch for pppd/plugins/rp-pppoe/plugin.c which is not part
+of the port.
+
+
+2010-08-23 - set and reset options to control environment variables
+             for scripts.
+  2b6310fd24dba8e0fca8999916a162f0a1842a84
+
+We can't fork processes in embedded, therefore all the pppd process run
+feature is disabled in the port, so we don't care about the new
+"environment variables" pppd feature.
+
+
+2010-08-23 - Nit: use _exit when exec fails and restrict values to 0-255
+             per POSIX.
+  2b4ea140432eeba5a007c0d4e6236bd0e0c12ba4
+
+Again, we are not running as a heavy process, so all exit() or _exit() calls
+were removed.
+
+
+2010-08-23 - Fix quote handling in configuration files to be more like shell
+             quoting.
+  3089132cdf5b58dbdfc2daf08ec5c08eb47f8aca
+
+We are not parsing config file, all the filesystem I/O stuff were disabled
+in our port.
+
+
+2010-08-24 - rp-pppoe: allow MTU to be increased up to 1500
+  fd1dcdf758418f040da3ed801ab001b5e46854e7
+
+Only concern changes on RP-PPPoE plugin, which we don't use.
+
+
+2010-09-11 - chat: Allow TIMEOUT value to come from environment variable
+  ae80bf833e48a6202f44a935a68083ae52ad3824
+
+See 2b6310fd24dba8e0fca8999916a162f0a1842a84.
+
+
+2011-03-05 - pppdump: Fix printfs with insufficient arguments
+  7b8db569642c83ba3283745034f2e2c95e459423
+
+pppdump is a ppp tool outside pppd source tree.
+
+
+2012-05-06 - pppd: Don't unconditionally disable VJ compression under Linux
+  d8a66adf98a0e525cf38031b42098d539da6eeb6
+
+Patch for sys-linux.c, which we don't use.
+
+
+2012-05-20 - Remove old version of Linux if_pppol2tp.h
+  c41092dd4c49267f232f6cba3d31c6c68bfdf68d
+
+Not in the port.
+
+
+2012-05-20 - pppd: Make MSCHAP-v2 cope better with packet loss
+  08ef47ca532294eb428238c831616748940e24a2
+
+This is an interesting patch. However it consumes much more memory for
+MSCHAP and I am not sure if the benefit worth it. The PPP client can
+always start the authentication again if it failed for whatever reason.
+
+
+2012-05-20 - scripts: Make poff ignore extra arguments to pppd
+  18f515f32c9f5723a9c2c912601e04335106534b
+
+Again, we are not running scripts.
+
+
+2012-05-20 - rp-pppoe plugin: Print leading zeros in MAC address
+  f5dda0cfc220c4b52e26144096d729e27b30f0f7
+
+Again, we are not using the RP-PPPoE plugin.
+
+
+2012-05-20 - pppd: Notify IPv6 up/down as we do for IPv4
+  845cda8fa18939cf56e60b073f63a7efa65336fc
+
+This is just a patch that adds plugins hooks for IPv6, the plugin interface
+was disabled because we don't have .so plugins in embedded.
+
+
+2012-05-20 - pppd: Enable IPV6 by default and fix some warnings
+  0b6118239615e98959f7e0b4e746bdd197533248
+
+Change on Makefile for IPv6, warnings were already cleared during port.
+
+
+2012-05-20 - contrib: Fix pppgetpass.gtk compilation
+  80a8e2ce257ca12cce723519a0f20ea1d663b14a
+
+Change on Makefile, don't care.
+
+
+2012-05-20 - pppd: Don't crash if crypt() returns NULL
+  04c4348108d847e034dd91066cc6843f60d71731
+
+We are using the PolarSSL DES implementation that does not return NULL.
+
+
+2012-05-20 - pppd: Eliminate some warnings
+  c44ae5e6a7338c96eb463881fe709b2dfaffe568
+
+Again, we are handling compilation warnings on our own.
+
+
+2012-05-20 - rp-pppoe plugin: Import some fixes from rp-pppoe-3.10
+  1817d83e51a411044e730ba89ebdb0480e1c8cd4
+
+Once more, we are not using the RP-PPPoE plugin.
+
+
+2013-01-23 - pppd: Clarify circumstances where DNS1/DNS2 environment variables are set
+  cf2f5c9538b9400ade23446a194729b0a4113b3a
+
+Documentation only.
+
+
+2013-02-03 - ppp: ignore unrecognised radiusclient configuration directives
+  7f736dde0da3c19855997d9e67370e351e15e923
+
+Radius plugin, not in the port.
+
+
+2013-02-03 - pppd: Take out unused %r conversion completely
+  356d8d558d844412119aa18c8e5a113bc6459c7b
+
+Merged 2014-04-15.
+
+
+2013-02-03 - pppd: Arrange to use logwtmp from libutil on Linux
+  9617a7eb137f4fee62799a677a9ecf8d834db3f5
+
+Patch for sys-linux.c, which we don't use.
+
+
+2013-02-03 - pppdump: Eliminate some compiler warnings
+  3e3acf1ba2b3046c072a42c19164788a9e419bd1
+
+pppdump is a ppp tool outside pppd source tree.
+
+
+2013-02-03 - chat: Correct spelling errors in the man page
+  8dea1b969d266ccbf6f3a8c5474eb6dcd8838e3b
+
+Documentation only.
+
+
+2013-02-03 - pppd: Fix spelling errors in man page
+  9e05a25d76b3f83096c661678010320df673df6b
+
+Documentation only.
+
+
+2013-02-03 - plugins/passprompt: Fix potential out-of-bounds array reference
+  8edb889b753056a691a3e4b217a110a35f9fdedb
+
+Plugin patch, we do not have plugins.
+
+
+2013-02-03 - chat: Fix *roff errors in the man page
+  a7c3489eeaf44e83ce592143c7c8a5b5c29f4c48
+
+Documentation only.
+
+
+2013-03-02 - pppd: Fix man page description of case when remote IP address isn't known
+  224841f4799f4f1e2e71bc490c54448d66740f4f
+
+Documentation only.
+
+
+2013-03-02 - pppd: Add master_detach option
+  398ed2585640d198c53e736ee5bbd67f7ce8168e
+
+Option for multilink support, we do not support multilink and this option
+is about detaching from the terminal, which is out of the embedded scope.
+
+
+2013-03-11 - pppd: Default exit status to EXIT_CONNECT_FAILED during connection phase
+  225361d64ae737afdc8cb57579a2f33525461bc9
+
+Commented out in our port, and already fixed by a previously applied Debian patch.
+
+
+2013-03-11 - pppstats: Fix undefined macro in man page
+  d16a3985eade5280b8e171f5dd0670a91cba0d39
+
+Documentation only.
+
+
+2013-05-11 - plugins/radius: Handle bindaddr keyword in radiusclient.conf
+  d883b2dbafeed3ebd9d7a56ab1469373bd001a3b
+
+Radius plugin, not in the port.
+
+
+2013-06-09 - pppoatm: Remove explicit loading of pppoatm kernel module
+  52cd43a84bea524033b918b603698104f221bbb7
+
+PPPoATM plugin, not in the port.
+
+
+2013-06-09 - pppd: Fix segfault in update_db_entry()
+  37476164f15a45015310b9d4b197c2d7db1f7f8f
+
+We do not use the samba db.
+
+
+2013-06-09 - chat: Fix some text that was intended to be literal
+  cd9683676618adcee8add2c3cfa3382341b5a1f6
+
+Documentation only.
+
+
+2013-06-09 - README.pppoe: Minor semantic fix
+  b5b8898af6fd3d44e873cfc66810ace5f1f47e17
+
+Documentation only.
+
+
+2013-06-10 - radius: Handle additional attributes
+  2f581cd986a56f2ec4a95abad4f8297a1b10d7e2
+
+Radius plugin, not in the port.
+
+
+2013-06-10 - chat, pppd: Use \e instead of \\ in man pages
+  8d6942415d22f6ca4377340ca26e345c3f5fa5db
+
+Documentation only.
+
+
+2014-01-02 - pppd: Don't crash if NULL pointer passed to vslprintf for %q or %v
+  906814431bddeb2061825fa1ebad1a967b6d87a9
+
+Merged 2014-04-15.
+
+
+2014-01-02 - pppd: Accept IPCP ConfAck packets containing MS-WINS options
+  a243f217f1c6ac1aa7793806bc88590d077f490a
+
+Merged 2014-04-15.
+
+
+2014-01-02 - config: Update Solaris compiler options and enable CHAPMS and IPV6
+  99c46caaed01b7edba87962aa52b77fad61bfd7b
+
+Solaris port, don't care.
+
+
+2014-01-02 - Update README and patchlevel for 2.4.6 release
+  4043750fca36e7e0eb90d702e048ad1da4929418
+
+Just release stuff.
+
+
+2014-02-18 - pppd: Add option "stop-bits" to set number of serial port stop bits.
+  ad993a20ee485f0d0e2ac4105221641b200da6e2
+
+Low level serial port, not in the port.
+
+
+2014-03-09 - pppd: Separate IPv6 handling for sifup/sifdown
+  b04d2dc6df5c6b5650fea44250d58757ee3dac4a
+
+Reimplemented.
+
+
+2014-03-09 - pppol2tp: Connect up/down events to notifiers and add IPv6 ones
+  fafbe50251efc7d6b4a8be652d085316e112b34f
+
+Not in the port.
+
+
+2014-03-09 - pppd: Add declarations to eliminate compile warnings
+  50967962addebe15c7a7e63116ff46a0441dc464
+
+We are handling compilation warnings on our own
+
+
+2014-03-09 - pppd: Eliminate some unnecessary ifdefs
+  de8da14d845ee6db9236ccfddabf1d8ebf045ddb
+
+We mostly did that previously. Anyway, merged 2014-12-24.
+
+
+2014-08-01 - radius: Fix realms-config-file option
+  880a81be7c8e0fe8567227bc17a1bff3ea035943
+
+Radius plugin, not in the port.
+
+
+2014-08-01 - pppd: Eliminate potential integer overflow in option parsing
+  7658e8257183f062dc01f87969c140707c7e52cb
+
+pppd config file parser, not in the port.
+
+
+2014-08-01 - pppd: Eliminate memory leak with multiple instances of a string option
+  b94b7fbbaa0589aa6ec5fdc733aeb9ff294d2656
+
+pppd config file parser, not in the port.
+
+
+2014-08-01 - pppd: Fix a stack variable overflow in MSCHAP-v2
+  36733a891fb56594fcee580f667b33a64b990981
+
+This fixes a bug introduced in 08ef47ca ("pppd: Make MSCHAP-v2 cope better with packet loss").
+
+We didn't merge 08ef47ca ;-)
+
+
+2014-08-01 - winbind plugin: Add -DMPPE=1 to eliminate compiler warnings
+  2b05e22c62095e97dd0a97e4b5588402c2185071
+
+Linux plugin, not in the port.
+
+
+2014-08-09 - Update README and patchlevel for 2.4.7 release
+  6e8eaa7a78b31cdab2edf140a9c8afdb02ffaca5
+
+Just release stuff.
+
+
+2014-08-10 - abort on errors in subdir builds
+  5e90783d11a59268e05f4cfb29ce2343b13e8ab2
+
+Linux Makefile, not in the port.
+
+
+2014-06-03 - pppd: add support for defaultroute-metric option
+  35e5a569c988b1ff865b02a24d9a727a00db4da9
+
+Only necessary for Linux, lwIP does not support route metrics.
+
+
+2014-12-13 - scripts: Avoid killing wrong pppd
+  67811a647d399db5d188a242827760615a0f86b5
+
+pppd helper script, not in the port.
+
+
+2014-12-20 - pppd: Fix sign-extension when displaying bytes in octal
+  5e8c3cb256a7e86e3572a82a75d51c6850efdbdc
+
+Merged 2016-07-02.
+
+
+2015-03-01 - Suppress false error message on PPPoE disconnect
+  219aac3b53d0827549377f1bfe22853ee52d4405
+
+PPPoE plugin, not in the port.
+
+
+2015-03-01 - Send PADT on PPPoE disconnect
+  cd2c14f998c57bbe6a01dc5854f2763c0d7f31fb
+
+PPPoE plugin, not in the port. And our PPPoE implementation already does
+that: pppoe_disconnect() calls pppoe_send_padt().
+
+
+2015-08-14 - pppd: ipxcp: Prevent buffer overrun on remote router name
+  fe149de624f96629a7f46732055d8f718c74b856
+
+We never ported IPX support. lwIP does not support IPX.
+
+
+2015-03-25 - pppd: Fix ccp_options.mppe type
+  234edab99a6bb250cc9ecd384cca27b0c8b475ce
+
+We found that while working on MPPE support in lwIP, that's our patch ;-)
+
+
+2015-03-24 - pppd: Fix ccp_cilen calculated size if both deflate_correct and deflate_draft are enabled
+  094cb8ae4c61db225e67fedadb4964f846dd0c27
+
+We found that while working on MPPE support in lwIP, that's our patch ;-)
+
+
+2015-08-14 - Merge branch 'master' of https://github.com/ncopa/ppp
+  3a5c9a8fbc8970375cd881151d44e4b6fe249c6a
+
+Merge commit, we don't care.
+
+
+2015-08-14 - Merge branch 'master' of git://github.com/vapier/ppp
+  912e4fc6665aca188dced7ea7fdc663ce5a2dd24
+
+Merge commit, we don't care.
+
+
+2015-08-14 - Merge branch 'bug_fix' of git://github.com/radaiming/ppp
+  dfd33d7f526ecd7b39dd1bba8101260d02af5ebb
+
+Merge commit, we don't care.
+
+
+2015-08-14 - Merge branch 'master' of git://github.com/pprindeville/ppp
+  aa4a985f6114d08cf4e47634fb6325da71016473
+
+Merge commit, we don't care.
+
+
+2015-08-14 - Merge branch 'no-error-on-already-closed' of git://github.com/farnz/ppp
+  6edf252483b30dbcdcc5059f01831455365d5b6e
+
+Merge commit, we don't care.
+
+
+2015-08-14 - Merge branch 'send-padt-on-disconnect' of git://github.com/farnz/ppp
+  84684243d651f55f6df69d2a6707b52fbbe62bb9
+
+Merge commit, we don't care.
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/auth.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/auth.c
new file mode 100644 (file)
index 0000000..c8673ad
--- /dev/null
@@ -0,0 +1,2510 @@
+/*
+ * auth.c - PPP authentication and phase control.
+ *
+ * Copyright (c) 1993-2002 Paul Mackerras. All rights reserved.
+ *
+ * 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. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Derived from main.c, which is:
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * 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.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#if 0 /* UNUSED */
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <utmp.h>
+#include <fcntl.h>
+#if defined(_PATH_LASTLOG) && defined(__linux__)
+#include <lastlog.h>
+#endif
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#ifdef HAS_SHADOW
+#include <shadow.h>
+#ifndef PW_PPP
+#define PW_PPP PW_LOGIN
+#endif
+#endif
+
+#include <time.h>
+#endif /* UNUSED */
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/fsm.h"
+#include "netif/ppp/lcp.h"
+#if CCP_SUPPORT
+#include "netif/ppp/ccp.h"
+#endif /* CCP_SUPPORT */
+#if ECP_SUPPORT
+#include "netif/ppp/ecp.h"
+#endif /* ECP_SUPPORT */
+#include "netif/ppp/ipcp.h"
+#if PAP_SUPPORT
+#include "netif/ppp/upap.h"
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+#include "netif/ppp/chap-new.h"
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+#include "netif/ppp/eap.h"
+#endif /* EAP_SUPPORT */
+#if CBCP_SUPPORT
+#include "netif/ppp/cbcp.h"
+#endif
+
+#if 0 /* UNUSED */
+#include "session.h"
+#endif /* UNUSED */
+
+#if 0 /* UNUSED */
+/* Bits in scan_authfile return value */
+#define NONWILD_SERVER 1
+#define NONWILD_CLIENT 2
+
+#define ISWILD(word)   (word[0] == '*' && word[1] == 0)
+#endif /* UNUSED */
+
+#if 0 /* UNUSED */
+/* List of addresses which the peer may use. */
+static struct permitted_ip *addresses[NUM_PPP];
+
+/* Wordlist giving addresses which the peer may use
+   without authenticating itself. */
+static struct wordlist *noauth_addrs;
+
+/* Remote telephone number, if available */
+char remote_number[MAXNAMELEN];
+
+/* Wordlist giving remote telephone numbers which may connect. */
+static struct wordlist *permitted_numbers;
+
+/* Extra options to apply, from the secrets file entry for the peer. */
+static struct wordlist *extra_options;
+#endif /* UNUSED */
+
+#if 0 /* UNUSED */
+/* Set if we require authentication only because we have a default route. */
+static bool default_auth;
+
+/* Hook to enable a plugin to control the idle time limit */
+int (*idle_time_hook) (struct ppp_idle *) = NULL;
+
+/* Hook for a plugin to say whether we can possibly authenticate any peer */
+int (*pap_check_hook) (void) = NULL;
+
+/* Hook for a plugin to check the PAP user and password */
+int (*pap_auth_hook) (char *user, char *passwd, char **msgp,
+                         struct wordlist **paddrs,
+                         struct wordlist **popts) = NULL;
+
+/* Hook for a plugin to know about the PAP user logout */
+void (*pap_logout_hook) (void) = NULL;
+
+/* Hook for a plugin to get the PAP password for authenticating us */
+int (*pap_passwd_hook) (char *user, char *passwd) = NULL;
+
+/* Hook for a plugin to say if we can possibly authenticate a peer using CHAP */
+int (*chap_check_hook) (void) = NULL;
+
+/* Hook for a plugin to get the CHAP password for authenticating us */
+int (*chap_passwd_hook) (char *user, char *passwd) = NULL;
+
+/* Hook for a plugin to say whether it is OK if the peer
+   refuses to authenticate. */
+int (*null_auth_hook) (struct wordlist **paddrs,
+                          struct wordlist **popts) = NULL;
+
+int (*allowed_address_hook) (u32_t addr) = NULL;
+#endif /* UNUSED */
+
+#ifdef HAVE_MULTILINK
+/* Hook for plugin to hear when an interface joins a multilink bundle */
+void (*multilink_join_hook) (void) = NULL;
+#endif
+
+#if PPP_NOTIFY
+/* A notifier for when the peer has authenticated itself,
+   and we are proceeding to the network phase. */
+struct notifier *auth_up_notifier = NULL;
+
+/* A notifier for when the link goes down. */
+struct notifier *link_down_notifier = NULL;
+#endif /* PPP_NOTIFY */
+
+/*
+ * Option variables.
+ */
+#if 0 /* MOVED TO ppp_settings */
+bool uselogin = 0;             /* Use /etc/passwd for checking PAP */
+bool session_mgmt = 0;         /* Do session management (login records) */
+bool cryptpap = 0;             /* Passwords in pap-secrets are encrypted */
+bool refuse_pap = 0;           /* Don't wanna auth. ourselves with PAP */
+bool refuse_chap = 0;          /* Don't wanna auth. ourselves with CHAP */
+bool refuse_eap = 0;           /* Don't wanna auth. ourselves with EAP */
+#if MSCHAP_SUPPORT
+bool refuse_mschap = 0;                /* Don't wanna auth. ourselves with MS-CHAP */
+bool refuse_mschap_v2 = 0;     /* Don't wanna auth. ourselves with MS-CHAPv2 */
+#else /* MSCHAP_SUPPORT */
+bool refuse_mschap = 1;                /* Don't wanna auth. ourselves with MS-CHAP */
+bool refuse_mschap_v2 = 1;     /* Don't wanna auth. ourselves with MS-CHAPv2 */
+#endif /* MSCHAP_SUPPORT */
+bool usehostname = 0;          /* Use hostname for our_name */
+bool auth_required = 0;                /* Always require authentication from peer */
+bool allow_any_ip = 0;         /* Allow peer to use any IP address */
+bool explicit_remote = 0;      /* User specified explicit remote name */
+bool explicit_user = 0;                /* Set if "user" option supplied */
+bool explicit_passwd = 0;      /* Set if "password" option supplied */
+char remote_name[MAXNAMELEN];  /* Peer's name for authentication */
+static char *uafname;          /* name of most recent +ua file */
+
+extern char *crypt (const char *, const char *);
+#endif /* UNUSED */
+/* Prototypes for procedures local to this file. */
+
+static void network_phase(ppp_pcb *pcb);
+#if PPP_IDLETIMELIMIT
+static void check_idle(void *arg);
+#endif /* PPP_IDLETIMELIMIT */
+#if PPP_MAXCONNECT
+static void connect_time_expired(void *arg);
+#endif /* PPP_MAXCONNECT */
+#if 0 /* UNUSED */
+static int  null_login (int);
+/* static int  get_pap_passwd (char *); */
+static int  have_pap_secret (int *);
+static int  have_chap_secret (char *, char *, int, int *);
+static int  have_srp_secret (char *client, char *server, int need_ip,
+    int *lacks_ipp);
+static int  ip_addr_check (u32_t, struct permitted_ip *);
+static int  scan_authfile (FILE *, char *, char *, char *,
+                              struct wordlist **, struct wordlist **,
+                              char *, int);
+static void free_wordlist (struct wordlist *);
+static void set_allowed_addrs (int, struct wordlist *, struct wordlist *);
+static int  some_ip_ok (struct wordlist *);
+static int  setupapfile (char **);
+static int  privgroup (char **);
+static int  set_noauth_addr (char **);
+static int  set_permitted_number (char **);
+static void check_access (FILE *, char *);
+static int  wordlist_count (struct wordlist *);
+#endif /* UNUSED */
+
+#ifdef MAXOCTETS
+static void check_maxoctets (void *);
+#endif
+
+#if PPP_OPTIONS
+/*
+ * Authentication-related options.
+ */
+option_t auth_options[] = {
+    { "auth", o_bool, &auth_required,
+      "Require authentication from peer", OPT_PRIO | 1 },
+    { "noauth", o_bool, &auth_required,
+      "Don't require peer to authenticate", OPT_PRIOSUB | OPT_PRIV,
+      &allow_any_ip },
+    { "require-pap", o_bool, &lcp_wantoptions[0].neg_upap,
+      "Require PAP authentication from peer",
+      OPT_PRIOSUB | 1, &auth_required },
+    { "+pap", o_bool, &lcp_wantoptions[0].neg_upap,
+      "Require PAP authentication from peer",
+      OPT_ALIAS | OPT_PRIOSUB | 1, &auth_required },
+    { "require-chap", o_bool, &auth_required,
+      "Require CHAP authentication from peer",
+      OPT_PRIOSUB | OPT_A2OR | MDTYPE_MD5,
+      &lcp_wantoptions[0].chap_mdtype },
+    { "+chap", o_bool, &auth_required,
+      "Require CHAP authentication from peer",
+      OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MD5,
+      &lcp_wantoptions[0].chap_mdtype },
+#if MSCHAP_SUPPORT
+    { "require-mschap", o_bool, &auth_required,
+      "Require MS-CHAP authentication from peer",
+      OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT,
+      &lcp_wantoptions[0].chap_mdtype },
+    { "+mschap", o_bool, &auth_required,
+      "Require MS-CHAP authentication from peer",
+      OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT,
+      &lcp_wantoptions[0].chap_mdtype },
+    { "require-mschap-v2", o_bool, &auth_required,
+      "Require MS-CHAPv2 authentication from peer",
+      OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT_V2,
+      &lcp_wantoptions[0].chap_mdtype },
+    { "+mschap-v2", o_bool, &auth_required,
+      "Require MS-CHAPv2 authentication from peer",
+      OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT_V2,
+      &lcp_wantoptions[0].chap_mdtype },
+#endif /* MSCHAP_SUPPORT */
+#if 0
+    { "refuse-pap", o_bool, &refuse_pap,
+      "Don't agree to auth to peer with PAP", 1 },
+    { "-pap", o_bool, &refuse_pap,
+      "Don't allow PAP authentication with peer", OPT_ALIAS | 1 },
+    { "refuse-chap", o_bool, &refuse_chap,
+      "Don't agree to auth to peer with CHAP",
+      OPT_A2CLRB | MDTYPE_MD5,
+      &lcp_allowoptions[0].chap_mdtype },
+    { "-chap", o_bool, &refuse_chap,
+      "Don't allow CHAP authentication with peer",
+      OPT_ALIAS | OPT_A2CLRB | MDTYPE_MD5,
+      &lcp_allowoptions[0].chap_mdtype },
+#endif
+#if MSCHAP_SUPPORT
+#if 0
+    { "refuse-mschap", o_bool, &refuse_mschap,
+      "Don't agree to auth to peer with MS-CHAP",
+      OPT_A2CLRB | MDTYPE_MICROSOFT,
+      &lcp_allowoptions[0].chap_mdtype },
+    { "-mschap", o_bool, &refuse_mschap,
+      "Don't allow MS-CHAP authentication with peer",
+      OPT_ALIAS | OPT_A2CLRB | MDTYPE_MICROSOFT,
+      &lcp_allowoptions[0].chap_mdtype },
+    { "refuse-mschap-v2", o_bool, &refuse_mschap_v2,
+      "Don't agree to auth to peer with MS-CHAPv2",
+      OPT_A2CLRB | MDTYPE_MICROSOFT_V2,
+      &lcp_allowoptions[0].chap_mdtype },
+    { "-mschap-v2", o_bool, &refuse_mschap_v2,
+      "Don't allow MS-CHAPv2 authentication with peer",
+      OPT_ALIAS | OPT_A2CLRB | MDTYPE_MICROSOFT_V2,
+      &lcp_allowoptions[0].chap_mdtype },
+#endif
+#endif /* MSCHAP_SUPPORT*/
+#if EAP_SUPPORT
+    { "require-eap", o_bool, &lcp_wantoptions[0].neg_eap,
+      "Require EAP authentication from peer", OPT_PRIOSUB | 1,
+      &auth_required },
+#if 0
+    { "refuse-eap", o_bool, &refuse_eap,
+      "Don't agree to authenticate to peer with EAP", 1 },
+#endif
+#endif /* EAP_SUPPORT */
+    { "name", o_string, our_name,
+      "Set local name for authentication",
+      OPT_PRIO | OPT_PRIV | OPT_STATIC, NULL, MAXNAMELEN },
+
+    { "+ua", o_special, (void *)setupapfile,
+      "Get PAP user and password from file",
+      OPT_PRIO | OPT_A2STRVAL, &uafname },
+
+#if 0
+    { "user", o_string, user,
+      "Set name for auth with peer", OPT_PRIO | OPT_STATIC,
+      &explicit_user, MAXNAMELEN },
+
+    { "password", o_string, passwd,
+      "Password for authenticating us to the peer",
+      OPT_PRIO | OPT_STATIC | OPT_HIDE,
+      &explicit_passwd, MAXSECRETLEN },
+#endif
+
+    { "usehostname", o_bool, &usehostname,
+      "Must use hostname for authentication", 1 },
+
+    { "remotename", o_string, remote_name,
+      "Set remote name for authentication", OPT_PRIO | OPT_STATIC,
+      &explicit_remote, MAXNAMELEN },
+
+    { "login", o_bool, &uselogin,
+      "Use system password database for PAP", OPT_A2COPY | 1 ,
+      &session_mgmt },
+    { "enable-session", o_bool, &session_mgmt,
+      "Enable session accounting for remote peers", OPT_PRIV | 1 },
+
+    { "papcrypt", o_bool, &cryptpap,
+      "PAP passwords are encrypted", 1 },
+
+    { "privgroup", o_special, (void *)privgroup,
+      "Allow group members to use privileged options", OPT_PRIV | OPT_A2LIST },
+
+    { "allow-ip", o_special, (void *)set_noauth_addr,
+      "Set IP address(es) which can be used without authentication",
+      OPT_PRIV | OPT_A2LIST },
+
+    { "remotenumber", o_string, remote_number,
+      "Set remote telephone number for authentication", OPT_PRIO | OPT_STATIC,
+      NULL, MAXNAMELEN },
+
+    { "allow-number", o_special, (void *)set_permitted_number,
+      "Set telephone number(s) which are allowed to connect",
+      OPT_PRIV | OPT_A2LIST },
+
+    { NULL }
+};
+#endif /* PPP_OPTIONS */
+
+#if 0 /* UNUSED */
+/*
+ * setupapfile - specifies UPAP info for authenticating with peer.
+ */
+static int
+setupapfile(argv)
+    char **argv;
+{
+    FILE *ufile;
+    int l;
+    uid_t euid;
+    char u[MAXNAMELEN], p[MAXSECRETLEN];
+    char *fname;
+
+    lcp_allowoptions[0].neg_upap = 1;
+
+    /* open user info file */
+    fname = strdup(*argv);
+    if (fname == NULL)
+       novm("+ua file name");
+    euid = geteuid();
+    if (seteuid(getuid()) == -1) {
+       option_error("unable to reset uid before opening %s: %m", fname);
+       return 0;
+    }
+    ufile = fopen(fname, "r");
+    if (seteuid(euid) == -1)
+       fatal("unable to regain privileges: %m");
+    if (ufile == NULL) {
+       option_error("unable to open user login data file %s", fname);
+       return 0;
+    }
+    check_access(ufile, fname);
+    uafname = fname;
+
+    /* get username */
+    if (fgets(u, MAXNAMELEN - 1, ufile) == NULL
+       || fgets(p, MAXSECRETLEN - 1, ufile) == NULL) {
+       fclose(ufile);
+       option_error("unable to read user login data file %s", fname);
+       return 0;
+    }
+    fclose(ufile);
+
+    /* get rid of newlines */
+    l = strlen(u);
+    if (l > 0 && u[l-1] == '\n')
+       u[l-1] = 0;
+    l = strlen(p);
+    if (l > 0 && p[l-1] == '\n')
+       p[l-1] = 0;
+
+    if (override_value("user", option_priority, fname)) {
+       strlcpy(ppp_settings.user, u, sizeof(ppp_settings.user));
+       explicit_user = 1;
+    }
+    if (override_value("passwd", option_priority, fname)) {
+       strlcpy(ppp_settings.passwd, p, sizeof(ppp_settings.passwd));
+       explicit_passwd = 1;
+    }
+
+    return (1);
+}
+
+/*
+ * privgroup - allow members of the group to have privileged access.
+ */
+static int
+privgroup(argv)
+    char **argv;
+{
+    struct group *g;
+    int i;
+
+    g = getgrnam(*argv);
+    if (g == 0) {
+       option_error("group %s is unknown", *argv);
+       return 0;
+    }
+    for (i = 0; i < ngroups; ++i) {
+       if (groups[i] == g->gr_gid) {
+           privileged = 1;
+           break;
+       }
+    }
+    return 1;
+}
+
+
+/*
+ * set_noauth_addr - set address(es) that can be used without authentication.
+ * Equivalent to specifying an entry like `"" * "" addr' in pap-secrets.
+ */
+static int
+set_noauth_addr(argv)
+    char **argv;
+{
+    char *addr = *argv;
+    int l = strlen(addr) + 1;
+    struct wordlist *wp;
+
+    wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l);
+    if (wp == NULL)
+       novm("allow-ip argument");
+    wp->word = (char *) (wp + 1);
+    wp->next = noauth_addrs;
+    MEMCPY(wp->word, addr, l);
+    noauth_addrs = wp;
+    return 1;
+}
+
+
+/*
+ * set_permitted_number - set remote telephone number(s) that may connect.
+ */
+static int
+set_permitted_number(argv)
+    char **argv;
+{
+    char *number = *argv;
+    int l = strlen(number) + 1;
+    struct wordlist *wp;
+
+    wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l);
+    if (wp == NULL)
+       novm("allow-number argument");
+    wp->word = (char *) (wp + 1);
+    wp->next = permitted_numbers;
+    MEMCPY(wp->word, number, l);
+    permitted_numbers = wp;
+    return 1;
+}
+#endif
+
+/*
+ * An Open on LCP has requested a change from Dead to Establish phase.
+ */
+void link_required(ppp_pcb *pcb) {
+    LWIP_UNUSED_ARG(pcb);
+}
+
+#if 0
+/*
+ * Bring the link up to the point of being able to do ppp.
+ */
+void start_link(unit)
+    int unit;
+{
+    ppp_pcb *pcb = &ppp_pcb_list[unit];
+    char *msg;
+
+    status = EXIT_NEGOTIATION_FAILED;
+    new_phase(pcb, PPP_PHASE_SERIALCONN);
+
+    hungup = 0;
+    devfd = the_channel->connect();
+    msg = "Connect script failed";
+    if (devfd < 0)
+       goto fail;
+
+    /* set up the serial device as a ppp interface */
+    /*
+     * N.B. we used to do tdb_writelock/tdb_writeunlock around this
+     * (from establish_ppp to set_ifunit).  However, we won't be
+     * doing the set_ifunit in multilink mode, which is the only time
+     * we need the atomicity that the tdb_writelock/tdb_writeunlock
+     * gives us.  Thus we don't need the tdb_writelock/tdb_writeunlock.
+     */
+    fd_ppp = the_channel->establish_ppp(devfd);
+    msg = "ppp establishment failed";
+    if (fd_ppp < 0) {
+       status = EXIT_FATAL_ERROR;
+       goto disconnect;
+    }
+
+    if (!demand && ifunit >= 0)
+       set_ifunit(1);
+
+    /*
+     * Start opening the connection and wait for
+     * incoming events (reply, timeout, etc.).
+     */
+    if (ifunit >= 0)
+       ppp_notice("Connect: %s <--> %s", ifname, ppp_devnam);
+    else
+       ppp_notice("Starting negotiation on %s", ppp_devnam);
+    add_fd(fd_ppp);
+
+    new_phase(pcb, PPP_PHASE_ESTABLISH);
+
+    lcp_lowerup(pcb);
+    return;
+
+ disconnect:
+    new_phase(pcb, PPP_PHASE_DISCONNECT);
+    if (the_channel->disconnect)
+       the_channel->disconnect();
+
+ fail:
+    new_phase(pcb, PPP_PHASE_DEAD);
+    if (the_channel->cleanup)
+       (*the_channel->cleanup)();
+}
+#endif
+
+/*
+ * LCP has terminated the link; go to the Dead phase and take the
+ * physical layer down.
+ */
+void link_terminated(ppp_pcb *pcb) {
+    if (pcb->phase == PPP_PHASE_DEAD
+#ifdef HAVE_MULTILINK
+    || pcb->phase == PPP_PHASE_MASTER
+#endif /* HAVE_MULTILINK */
+    )
+       return;
+    new_phase(pcb, PPP_PHASE_DISCONNECT);
+
+#if 0 /* UNUSED */
+    if (pap_logout_hook) {
+       pap_logout_hook();
+    }
+    session_end(devnam);
+#endif /* UNUSED */
+
+    if (!doing_multilink) {
+       ppp_notice("Connection terminated.");
+#if PPP_STATS_SUPPORT
+       print_link_stats();
+#endif /* PPP_STATS_SUPPORT */
+    } else
+       ppp_notice("Link terminated.");
+
+    lcp_lowerdown(pcb);
+
+    ppp_link_terminated(pcb);
+#if 0
+    /*
+     * Delete pid files before disestablishing ppp.  Otherwise it
+     * can happen that another pppd gets the same unit and then
+     * we delete its pid file.
+     */
+    if (!doing_multilink && !demand)
+       remove_pidfiles();
+
+    /*
+     * If we may want to bring the link up again, transfer
+     * the ppp unit back to the loopback.  Set the
+     * real serial device back to its normal mode of operation.
+     */
+    if (fd_ppp >= 0) {
+       remove_fd(fd_ppp);
+       clean_check();
+       the_channel->disestablish_ppp(devfd);
+       if (doing_multilink)
+           mp_exit_bundle();
+       fd_ppp = -1;
+    }
+    if (!hungup)
+       lcp_lowerdown(pcb);
+    if (!doing_multilink && !demand)
+       script_unsetenv("IFNAME");
+
+    /*
+     * Run disconnector script, if requested.
+     * XXX we may not be able to do this if the line has hung up!
+     */
+    if (devfd >= 0 && the_channel->disconnect) {
+       the_channel->disconnect();
+       devfd = -1;
+    }
+    if (the_channel->cleanup)
+       (*the_channel->cleanup)();
+
+    if (doing_multilink && multilink_master) {
+       if (!bundle_terminating)
+           new_phase(pcb, PPP_PHASE_MASTER);
+       else
+           mp_bundle_terminated();
+    } else
+       new_phase(pcb, PPP_PHASE_DEAD);
+#endif
+}
+
+/*
+ * LCP has gone down; it will either die or try to re-establish.
+ */
+void link_down(ppp_pcb *pcb) {
+#if PPP_NOTIFY
+    notify(link_down_notifier, 0);
+#endif /* PPP_NOTIFY */
+
+    if (!doing_multilink) {
+       upper_layers_down(pcb);
+       if (pcb->phase != PPP_PHASE_DEAD
+#ifdef HAVE_MULTILINK
+       && pcb->phase != PPP_PHASE_MASTER
+#endif /* HAVE_MULTILINK */
+       )
+           new_phase(pcb, PPP_PHASE_ESTABLISH);
+    }
+    /* XXX if doing_multilink, should do something to stop
+       network-layer traffic on the link */
+}
+
+void upper_layers_down(ppp_pcb *pcb) {
+    int i;
+    const struct protent *protp;
+
+    for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+        if (protp->protocol != PPP_LCP && protp->lowerdown != NULL)
+           (*protp->lowerdown)(pcb);
+        if (protp->protocol < 0xC000 && protp->close != NULL)
+           (*protp->close)(pcb, "LCP down");
+    }
+    pcb->num_np_open = 0;
+    pcb->num_np_up = 0;
+}
+
+/*
+ * The link is established.
+ * Proceed to the Dead, Authenticate or Network phase as appropriate.
+ */
+void link_established(ppp_pcb *pcb) {
+#if PPP_AUTH_SUPPORT
+    int auth;
+#if PPP_SERVER
+#if PAP_SUPPORT
+    lcp_options *wo = &pcb->lcp_wantoptions;
+#endif /* PAP_SUPPORT */
+    lcp_options *go = &pcb->lcp_gotoptions;
+#endif /* PPP_SERVER */
+    lcp_options *ho = &pcb->lcp_hisoptions;
+#endif /* PPP_AUTH_SUPPORT */
+    int i;
+    const struct protent *protp;
+
+    /*
+     * Tell higher-level protocols that LCP is up.
+     */
+    if (!doing_multilink) {
+       for (i = 0; (protp = protocols[i]) != NULL; ++i)
+           if (protp->protocol != PPP_LCP
+               && protp->lowerup != NULL)
+               (*protp->lowerup)(pcb);
+    }
+
+#if PPP_AUTH_SUPPORT
+#if PPP_SERVER
+#if PPP_ALLOWED_ADDRS
+    if (!auth_required && noauth_addrs != NULL)
+       set_allowed_addrs(unit, NULL, NULL);
+#endif /* PPP_ALLOWED_ADDRS */
+
+    if (pcb->settings.auth_required && !(0
+#if PAP_SUPPORT
+       || go->neg_upap
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+       || go->neg_chap
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+       || go->neg_eap
+#endif /* EAP_SUPPORT */
+       )) {
+
+#if PPP_ALLOWED_ADDRS
+       /*
+        * We wanted the peer to authenticate itself, and it refused:
+        * if we have some address(es) it can use without auth, fine,
+        * otherwise treat it as though it authenticated with PAP using
+        * a username of "" and a password of "".  If that's not OK,
+        * boot it out.
+        */
+       if (noauth_addrs != NULL) {
+           set_allowed_addrs(unit, NULL, NULL);
+       } else
+#endif /* PPP_ALLOWED_ADDRS */
+       if (!pcb->settings.null_login
+#if PAP_SUPPORT
+           || !wo->neg_upap
+#endif /* PAP_SUPPORT */
+           ) {
+           ppp_warn("peer refused to authenticate: terminating link");
+#if 0 /* UNUSED */
+           status = EXIT_PEER_AUTH_FAILED;
+#endif /* UNUSED */
+           pcb->err_code = PPPERR_AUTHFAIL;
+           lcp_close(pcb, "peer refused to authenticate");
+           return;
+       }
+    }
+#endif /* PPP_SERVER */
+
+    new_phase(pcb, PPP_PHASE_AUTHENTICATE);
+    auth = 0;
+#if PPP_SERVER
+#if EAP_SUPPORT
+    if (go->neg_eap) {
+       eap_authpeer(pcb, PPP_OUR_NAME);
+       auth |= EAP_PEER;
+    } else
+#endif /* EAP_SUPPORT */
+#if CHAP_SUPPORT
+    if (go->neg_chap) {
+       chap_auth_peer(pcb, PPP_OUR_NAME, CHAP_DIGEST(go->chap_mdtype));
+       auth |= CHAP_PEER;
+    } else
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+    if (go->neg_upap) {
+       upap_authpeer(pcb);
+       auth |= PAP_PEER;
+    } else
+#endif /* PAP_SUPPORT */
+    {}
+#endif /* PPP_SERVER */
+
+#if EAP_SUPPORT
+    if (ho->neg_eap) {
+       eap_authwithpeer(pcb, pcb->settings.user);
+       auth |= EAP_WITHPEER;
+    } else
+#endif /* EAP_SUPPORT */
+#if CHAP_SUPPORT
+    if (ho->neg_chap) {
+       chap_auth_with_peer(pcb, pcb->settings.user, CHAP_DIGEST(ho->chap_mdtype));
+       auth |= CHAP_WITHPEER;
+    } else
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+    if (ho->neg_upap) {
+       upap_authwithpeer(pcb, pcb->settings.user, pcb->settings.passwd);
+       auth |= PAP_WITHPEER;
+    } else
+#endif /* PAP_SUPPORT */
+    {}
+
+    pcb->auth_pending = auth;
+    pcb->auth_done = 0;
+
+    if (!auth)
+#endif /* PPP_AUTH_SUPPORT */
+       network_phase(pcb);
+}
+
+/*
+ * Proceed to the network phase.
+ */
+static void network_phase(ppp_pcb *pcb) {
+#if CBCP_SUPPORT
+    ppp_pcb *pcb = &ppp_pcb_list[unit];
+#endif
+#if 0 /* UNUSED */
+    lcp_options *go = &lcp_gotoptions[unit];
+#endif /* UNUSED */
+
+#if 0 /* UNUSED */
+    /* Log calling number. */
+    if (*remote_number)
+       ppp_notice("peer from calling number %q authorized", remote_number);
+#endif /* UNUSED */
+
+#if PPP_NOTIFY
+    /*
+     * If the peer had to authenticate, notify it now.
+     */
+    if (0
+#if CHAP_SUPPORT
+       || go->neg_chap
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+       || go->neg_upap
+#endif /* PAP_SUPPORT */
+#if EAP_SUPPORT
+       || go->neg_eap
+#endif /* EAP_SUPPORT */
+       ) {
+       notify(auth_up_notifier, 0);
+    }
+#endif /* PPP_NOTIFY */
+
+#if CBCP_SUPPORT
+    /*
+     * If we negotiated callback, do it now.
+     */
+    if (go->neg_cbcp) {
+       new_phase(pcb, PPP_PHASE_CALLBACK);
+       (*cbcp_protent.open)(pcb);
+       return;
+    }
+#endif
+
+#if PPP_OPTIONS
+    /*
+     * Process extra options from the secrets file
+     */
+    if (extra_options) {
+       options_from_list(extra_options, 1);
+       free_wordlist(extra_options);
+       extra_options = 0;
+    }
+#endif /* PPP_OPTIONS */
+    start_networks(pcb);
+}
+
+void start_networks(ppp_pcb *pcb) {
+#if CCP_SUPPORT || ECP_SUPPORT
+    int i;
+    const struct protent *protp;
+#endif /* CCP_SUPPORT || ECP_SUPPORT */
+
+    new_phase(pcb, PPP_PHASE_NETWORK);
+
+#ifdef HAVE_MULTILINK
+    if (multilink) {
+       if (mp_join_bundle()) {
+           if (multilink_join_hook)
+               (*multilink_join_hook)();
+           if (updetach && !nodetach)
+               detach();
+           return;
+       }
+    }
+#endif /* HAVE_MULTILINK */
+
+#ifdef PPP_FILTER
+    if (!demand)
+       set_filters(&pass_filter, &active_filter);
+#endif
+#if CCP_SUPPORT || ECP_SUPPORT
+    /* Start CCP and ECP */
+    for (i = 0; (protp = protocols[i]) != NULL; ++i)
+       if (
+           (0
+#if ECP_SUPPORT
+           || protp->protocol == PPP_ECP
+#endif /* ECP_SUPPORT */
+#if CCP_SUPPORT
+           || protp->protocol == PPP_CCP
+#endif /* CCP_SUPPORT */
+           )
+           && protp->open != NULL)
+           (*protp->open)(pcb);
+#endif /* CCP_SUPPORT || ECP_SUPPORT */
+
+    /*
+     * Bring up other network protocols iff encryption is not required.
+     */
+    if (1
+#if ECP_SUPPORT
+        && !ecp_gotoptions[unit].required
+#endif /* ECP_SUPPORT */
+#if MPPE_SUPPORT
+        && !pcb->ccp_gotoptions.mppe
+#endif /* MPPE_SUPPORT */
+        )
+       continue_networks(pcb);
+}
+
+void continue_networks(ppp_pcb *pcb) {
+    int i;
+    const struct protent *protp;
+
+    /*
+     * Start the "real" network protocols.
+     */
+    for (i = 0; (protp = protocols[i]) != NULL; ++i)
+       if (protp->protocol < 0xC000
+#if CCP_SUPPORT
+           && protp->protocol != PPP_CCP
+#endif /* CCP_SUPPORT */
+#if ECP_SUPPORT
+           && protp->protocol != PPP_ECP
+#endif /* ECP_SUPPORT */
+           && protp->open != NULL) {
+           (*protp->open)(pcb);
+           ++pcb->num_np_open;
+       }
+
+    if (pcb->num_np_open == 0)
+       /* nothing to do */
+       lcp_close(pcb, "No network protocols running");
+}
+
+#if PPP_AUTH_SUPPORT
+#if PPP_SERVER
+/*
+ * auth_check_passwd - Check the user name and passwd against configuration.
+ *
+ * returns:
+ *      0: Authentication failed.
+ *      1: Authentication succeeded.
+ * In either case, msg points to an appropriate message and msglen to the message len.
+ */
+int auth_check_passwd(ppp_pcb *pcb, char *auser, int userlen, char *apasswd, int passwdlen, const char **msg, int *msglen) {
+  int secretuserlen;
+  int secretpasswdlen;
+
+  if (pcb->settings.user && pcb->settings.passwd) {
+    secretuserlen = (int)strlen(pcb->settings.user);
+    secretpasswdlen = (int)strlen(pcb->settings.passwd);
+    if (secretuserlen == userlen
+        && secretpasswdlen == passwdlen
+        && !memcmp(auser, pcb->settings.user, userlen)
+        && !memcmp(apasswd, pcb->settings.passwd, passwdlen) ) {
+      *msg = "Login ok";
+      *msglen = sizeof("Login ok")-1;
+      return 1;
+    }
+  }
+
+  *msg = "Login incorrect";
+  *msglen = sizeof("Login incorrect")-1;
+  return 0;
+}
+
+/*
+ * The peer has failed to authenticate himself using `protocol'.
+ */
+void auth_peer_fail(ppp_pcb *pcb, int protocol) {
+    LWIP_UNUSED_ARG(protocol);
+    /*
+     * Authentication failure: take the link down
+     */
+#if 0 /* UNUSED */
+    status = EXIT_PEER_AUTH_FAILED;
+#endif /* UNUSED */
+    pcb->err_code = PPPERR_AUTHFAIL;
+    lcp_close(pcb, "Authentication failed");
+}
+
+/*
+ * The peer has been successfully authenticated using `protocol'.
+ */
+void auth_peer_success(ppp_pcb *pcb, int protocol, int prot_flavor, const char *name, int namelen) {
+    int bit;
+#ifndef HAVE_MULTILINK
+    LWIP_UNUSED_ARG(name);
+    LWIP_UNUSED_ARG(namelen);
+#endif /* HAVE_MULTILINK */
+
+    switch (protocol) {
+#if CHAP_SUPPORT
+    case PPP_CHAP:
+       bit = CHAP_PEER;
+       switch (prot_flavor) {
+       case CHAP_MD5:
+           bit |= CHAP_MD5_PEER;
+           break;
+#if MSCHAP_SUPPORT
+       case CHAP_MICROSOFT:
+           bit |= CHAP_MS_PEER;
+           break;
+       case CHAP_MICROSOFT_V2:
+           bit |= CHAP_MS2_PEER;
+           break;
+#endif /* MSCHAP_SUPPORT */
+       default:
+           break;
+       }
+       break;
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+    case PPP_PAP:
+       bit = PAP_PEER;
+       break;
+#endif /* PAP_SUPPORT */
+#if EAP_SUPPORT
+    case PPP_EAP:
+       bit = EAP_PEER;
+       break;
+#endif /* EAP_SUPPORT */
+    default:
+       ppp_warn("auth_peer_success: unknown protocol %x", protocol);
+       return;
+    }
+
+#ifdef HAVE_MULTILINK
+    /*
+     * Save the authenticated name of the peer for later.
+     */
+    if (namelen > (int)sizeof(pcb->peer_authname) - 1)
+       namelen = (int)sizeof(pcb->peer_authname) - 1;
+    MEMCPY(pcb->peer_authname, name, namelen);
+    pcb->peer_authname[namelen] = 0;
+#endif /* HAVE_MULTILINK */
+#if 0 /* UNUSED */
+    script_setenv("PEERNAME", , 0);
+#endif /* UNUSED */
+
+    /* Save the authentication method for later. */
+    pcb->auth_done |= bit;
+
+    /*
+     * If there is no more authentication still to be done,
+     * proceed to the network (or callback) phase.
+     */
+    if ((pcb->auth_pending &= ~bit) == 0)
+        network_phase(pcb);
+}
+#endif /* PPP_SERVER */
+
+/*
+ * We have failed to authenticate ourselves to the peer using `protocol'.
+ */
+void auth_withpeer_fail(ppp_pcb *pcb, int protocol) {
+    LWIP_UNUSED_ARG(protocol);
+    /*
+     * We've failed to authenticate ourselves to our peer.
+     *
+     * Some servers keep sending CHAP challenges, but there
+     * is no point in persisting without any way to get updated
+     * authentication secrets.
+     *
+     * He'll probably take the link down, and there's not much
+     * we can do except wait for that.
+     */
+    pcb->err_code = PPPERR_AUTHFAIL;
+    lcp_close(pcb, "Failed to authenticate ourselves to peer");
+}
+
+/*
+ * We have successfully authenticated ourselves with the peer using `protocol'.
+ */
+void auth_withpeer_success(ppp_pcb *pcb, int protocol, int prot_flavor) {
+    int bit;
+    const char *prot = "";
+
+    switch (protocol) {
+#if CHAP_SUPPORT
+    case PPP_CHAP:
+       bit = CHAP_WITHPEER;
+       prot = "CHAP";
+       switch (prot_flavor) {
+       case CHAP_MD5:
+           bit |= CHAP_MD5_WITHPEER;
+           break;
+#if MSCHAP_SUPPORT
+       case CHAP_MICROSOFT:
+           bit |= CHAP_MS_WITHPEER;
+           break;
+       case CHAP_MICROSOFT_V2:
+           bit |= CHAP_MS2_WITHPEER;
+           break;
+#endif /* MSCHAP_SUPPORT */
+       default:
+           break;
+       }
+       break;
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+    case PPP_PAP:
+       bit = PAP_WITHPEER;
+       prot = "PAP";
+       break;
+#endif /* PAP_SUPPORT */
+#if EAP_SUPPORT
+    case PPP_EAP:
+       bit = EAP_WITHPEER;
+       prot = "EAP";
+       break;
+#endif /* EAP_SUPPORT */
+    default:
+       ppp_warn("auth_withpeer_success: unknown protocol %x", protocol);
+       bit = 0;
+       /* no break */
+    }
+
+    ppp_notice("%s authentication succeeded", prot);
+
+    /* Save the authentication method for later. */
+    pcb->auth_done |= bit;
+
+    /*
+     * If there is no more authentication still being done,
+     * proceed to the network (or callback) phase.
+     */
+    if ((pcb->auth_pending &= ~bit) == 0)
+       network_phase(pcb);
+}
+#endif /* PPP_AUTH_SUPPORT */
+
+
+/*
+ * np_up - a network protocol has come up.
+ */
+void np_up(ppp_pcb *pcb, int proto) {
+#if PPP_IDLETIMELIMIT
+    int tlim;
+#endif /* PPP_IDLETIMELIMIT */
+    LWIP_UNUSED_ARG(proto);
+
+    if (pcb->num_np_up == 0) {
+       /*
+        * At this point we consider that the link has come up successfully.
+        */
+       new_phase(pcb, PPP_PHASE_RUNNING);
+
+#if PPP_IDLETIMELIMIT
+#if 0 /* UNUSED */
+       if (idle_time_hook != 0)
+           tlim = (*idle_time_hook)(NULL);
+       else
+#endif /* UNUSED */
+           tlim = pcb->settings.idle_time_limit;
+       if (tlim > 0)
+           TIMEOUT(check_idle, (void*)pcb, tlim);
+#endif /* PPP_IDLETIMELIMIT */
+
+#if PPP_MAXCONNECT
+       /*
+        * Set a timeout to close the connection once the maximum
+        * connect time has expired.
+        */
+       if (pcb->settings.maxconnect > 0)
+           TIMEOUT(connect_time_expired, (void*)pcb, pcb->settings.maxconnect);
+#endif /* PPP_MAXCONNECT */
+
+#ifdef MAXOCTETS
+       if (maxoctets > 0)
+           TIMEOUT(check_maxoctets, NULL, maxoctets_timeout);
+#endif
+
+#if 0 /* Unused */
+       /*
+        * Detach now, if the updetach option was given.
+        */
+       if (updetach && !nodetach)
+           detach();
+#endif /* Unused */
+    }
+    ++pcb->num_np_up;
+}
+
+/*
+ * np_down - a network protocol has gone down.
+ */
+void np_down(ppp_pcb *pcb, int proto) {
+    LWIP_UNUSED_ARG(proto);
+    if (--pcb->num_np_up == 0) {
+#if PPP_IDLETIMELIMIT
+       UNTIMEOUT(check_idle, (void*)pcb);
+#endif /* PPP_IDLETIMELIMIT */
+#if PPP_MAXCONNECT
+       UNTIMEOUT(connect_time_expired, NULL);
+#endif /* PPP_MAXCONNECT */
+#ifdef MAXOCTETS
+       UNTIMEOUT(check_maxoctets, NULL);
+#endif
+       new_phase(pcb, PPP_PHASE_NETWORK);
+    }
+}
+
+/*
+ * np_finished - a network protocol has finished using the link.
+ */
+void np_finished(ppp_pcb *pcb, int proto) {
+    LWIP_UNUSED_ARG(proto);
+    if (--pcb->num_np_open <= 0) {
+       /* no further use for the link: shut up shop. */
+       lcp_close(pcb, "No network protocols running");
+    }
+}
+
+#ifdef MAXOCTETS
+static void
+check_maxoctets(arg)
+    void *arg;
+{
+#if PPP_STATS_SUPPORT
+    unsigned int used;
+
+    update_link_stats(ifunit);
+    link_stats_valid=0;
+
+    switch(maxoctets_dir) {
+       case PPP_OCTETS_DIRECTION_IN:
+           used = link_stats.bytes_in;
+           break;
+       case PPP_OCTETS_DIRECTION_OUT:
+           used = link_stats.bytes_out;
+           break;
+       case PPP_OCTETS_DIRECTION_MAXOVERAL:
+       case PPP_OCTETS_DIRECTION_MAXSESSION:
+           used = (link_stats.bytes_in > link_stats.bytes_out) ? link_stats.bytes_in : link_stats.bytes_out;
+           break;
+       default:
+           used = link_stats.bytes_in+link_stats.bytes_out;
+           break;
+    }
+    if (used > maxoctets) {
+       ppp_notice("Traffic limit reached. Limit: %u Used: %u", maxoctets, used);
+       status = EXIT_TRAFFIC_LIMIT;
+       lcp_close(pcb, "Traffic limit");
+#if 0 /* UNUSED */
+       need_holdoff = 0;
+#endif /* UNUSED */
+    } else {
+        TIMEOUT(check_maxoctets, NULL, maxoctets_timeout);
+    }
+#endif /* PPP_STATS_SUPPORT */
+}
+#endif /* MAXOCTETS */
+
+#if PPP_IDLETIMELIMIT
+/*
+ * check_idle - check whether the link has been idle for long
+ * enough that we can shut it down.
+ */
+static void check_idle(void *arg) {
+    ppp_pcb *pcb = (ppp_pcb*)arg;
+    struct ppp_idle idle;
+    time_t itime;
+    int tlim;
+
+    if (!get_idle_time(pcb, &idle))
+       return;
+#if 0 /* UNUSED */
+    if (idle_time_hook != 0) {
+       tlim = idle_time_hook(&idle);
+    } else {
+#endif /* UNUSED */
+       itime = LWIP_MIN(idle.xmit_idle, idle.recv_idle);
+       tlim = pcb->settings.idle_time_limit - itime;
+#if 0 /* UNUSED */
+    }
+#endif /* UNUSED */
+    if (tlim <= 0) {
+       /* link is idle: shut it down. */
+       ppp_notice("Terminating connection due to lack of activity.");
+       pcb->err_code = PPPERR_IDLETIMEOUT;
+       lcp_close(pcb, "Link inactive");
+#if 0 /* UNUSED */
+       need_holdoff = 0;
+#endif /* UNUSED */
+    } else {
+       TIMEOUT(check_idle, (void*)pcb, tlim);
+    }
+}
+#endif /* PPP_IDLETIMELIMIT */
+
+#if PPP_MAXCONNECT
+/*
+ * connect_time_expired - log a message and close the connection.
+ */
+static void connect_time_expired(void *arg) {
+    ppp_pcb *pcb = (ppp_pcb*)arg;
+    ppp_info("Connect time expired");
+    pcb->err_code = PPPERR_CONNECTTIME;
+    lcp_close(pcb, "Connect time expired");    /* Close connection */
+}
+#endif /* PPP_MAXCONNECT */
+
+#if PPP_OPTIONS
+/*
+ * auth_check_options - called to check authentication options.
+ */
+void
+auth_check_options()
+{
+    lcp_options *wo = &lcp_wantoptions[0];
+    int can_auth;
+    int lacks_ip;
+
+    /* Default our_name to hostname, and user to our_name */
+    if (our_name[0] == 0 || usehostname)
+       strlcpy(our_name, hostname, sizeof(our_name));
+    /* If a blank username was explicitly given as an option, trust
+       the user and don't use our_name */
+    if (ppp_settings.user[0] == 0 && !explicit_user)
+       strlcpy(ppp_settings.user, our_name, sizeof(ppp_settings.user));
+
+    /*
+     * If we have a default route, require the peer to authenticate
+     * unless the noauth option was given or the real user is root.
+     */
+    if (!auth_required && !allow_any_ip && have_route_to(0) && !privileged) {
+       auth_required = 1;
+       default_auth = 1;
+    }
+
+#if CHAP_SUPPORT
+    /* If we selected any CHAP flavors, we should probably negotiate it. :-) */
+    if (wo->chap_mdtype)
+       wo->neg_chap = 1;
+#endif /* CHAP_SUPPORT */
+
+    /* If authentication is required, ask peer for CHAP, PAP, or EAP. */
+    if (auth_required) {
+       allow_any_ip = 0;
+       if (1
+#if CHAP_SUPPORT
+           && !wo->neg_chap
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+           && !wo->neg_upap
+#endif /* PAP_SUPPORT */
+#if EAP_SUPPORT
+           && !wo->neg_eap
+#endif /* EAP_SUPPORT */
+           ) {
+#if CHAP_SUPPORT
+           wo->neg_chap = CHAP_MDTYPE_SUPPORTED != MDTYPE_NONE;
+           wo->chap_mdtype = CHAP_MDTYPE_SUPPORTED;
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+           wo->neg_upap = 1;
+#endif /* PAP_SUPPORT */
+#if EAP_SUPPORT
+           wo->neg_eap = 1;
+#endif /* EAP_SUPPORT */
+       }
+    } else {
+#if CHAP_SUPPORT
+       wo->neg_chap = 0;
+       wo->chap_mdtype = MDTYPE_NONE;
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+       wo->neg_upap = 0;
+#endif /* PAP_SUPPORT */
+#if EAP_SUPPORT
+       wo->neg_eap = 0;
+#endif /* EAP_SUPPORT */
+    }
+
+    /*
+     * Check whether we have appropriate secrets to use
+     * to authenticate the peer.  Note that EAP can authenticate by way
+     * of a CHAP-like exchanges as well as SRP.
+     */
+    lacks_ip = 0;
+#if PAP_SUPPORT
+    can_auth = wo->neg_upap && (uselogin || have_pap_secret(&lacks_ip));
+#else
+    can_auth = 0;
+#endif /* PAP_SUPPORT */
+    if (!can_auth && (0
+#if CHAP_SUPPORT
+       || wo->neg_chap
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+       || wo->neg_eap
+#endif /* EAP_SUPPORT */
+       )) {
+#if CHAP_SUPPORT
+       can_auth = have_chap_secret((explicit_remote? remote_name: NULL),
+                                   our_name, 1, &lacks_ip);
+#else
+       can_auth = 0;
+#endif
+    }
+    if (!can_auth
+#if EAP_SUPPORT
+       && wo->neg_eap
+#endif /* EAP_SUPPORT */
+       ) {
+       can_auth = have_srp_secret((explicit_remote? remote_name: NULL),
+                                   our_name, 1, &lacks_ip);
+    }
+
+    if (auth_required && !can_auth && noauth_addrs == NULL) {
+       if (default_auth) {
+           option_error(
+"By default the remote system is required to authenticate itself");
+           option_error(
+"(because this system has a default route to the internet)");
+       } else if (explicit_remote)
+           option_error(
+"The remote system (%s) is required to authenticate itself",
+                        remote_name);
+       else
+           option_error(
+"The remote system is required to authenticate itself");
+       option_error(
+"but I couldn't find any suitable secret (password) for it to use to do so.");
+       if (lacks_ip)
+           option_error(
+"(None of the available passwords would let it use an IP address.)");
+
+       exit(1);
+    }
+
+    /*
+     * Early check for remote number authorization.
+     */
+    if (!auth_number()) {
+       ppp_warn("calling number %q is not authorized", remote_number);
+       exit(EXIT_CNID_AUTH_FAILED);
+    }
+}
+#endif /* PPP_OPTIONS */
+
+#if 0 /* UNUSED */
+/*
+ * auth_reset - called when LCP is starting negotiations to recheck
+ * authentication options, i.e. whether we have appropriate secrets
+ * to use for authenticating ourselves and/or the peer.
+ */
+void
+auth_reset(unit)
+    int unit;
+{
+    lcp_options *go = &lcp_gotoptions[unit];
+    lcp_options *ao = &lcp_allowoptions[unit];
+    int hadchap;
+
+    hadchap = -1;
+    ao->neg_upap = !refuse_pap && (passwd[0] != 0 || get_pap_passwd(NULL));
+    ao->neg_chap = (!refuse_chap || !refuse_mschap || !refuse_mschap_v2)
+       && (passwd[0] != 0 ||
+           (hadchap = have_chap_secret(user, (explicit_remote? remote_name:
+                                              NULL), 0, NULL)));
+    ao->neg_eap = !refuse_eap && (
+       passwd[0] != 0 ||
+       (hadchap == 1 || (hadchap == -1 && have_chap_secret(user,
+           (explicit_remote? remote_name: NULL), 0, NULL))) ||
+       have_srp_secret(user, (explicit_remote? remote_name: NULL), 0, NULL));
+
+    hadchap = -1;
+    if (go->neg_upap && !uselogin && !have_pap_secret(NULL))
+       go->neg_upap = 0;
+    if (go->neg_chap) {
+       if (!(hadchap = have_chap_secret((explicit_remote? remote_name: NULL),
+                             our_name, 1, NULL)))
+           go->neg_chap = 0;
+    }
+    if (go->neg_eap &&
+       (hadchap == 0 || (hadchap == -1 &&
+           !have_chap_secret((explicit_remote? remote_name: NULL), our_name,
+               1, NULL))) &&
+       !have_srp_secret((explicit_remote? remote_name: NULL), our_name, 1,
+           NULL))
+       go->neg_eap = 0;
+}
+
+/*
+ * check_passwd - Check the user name and passwd against the PAP secrets
+ * file.  If requested, also check against the system password database,
+ * and login the user if OK.
+ *
+ * returns:
+ *     UPAP_AUTHNAK: Authentication failed.
+ *     UPAP_AUTHACK: Authentication succeeded.
+ * In either case, msg points to an appropriate message.
+ */
+int
+check_passwd(unit, auser, userlen, apasswd, passwdlen, msg)
+    int unit;
+    char *auser;
+    int userlen;
+    char *apasswd;
+    int passwdlen;
+    char **msg;
+{
+  return UPAP_AUTHNAK;
+    int ret;
+    char *filename;
+    FILE *f;
+    struct wordlist *addrs = NULL, *opts = NULL;
+    char passwd[256], user[256];
+    char secret[MAXWORDLEN];
+    static int attempts = 0;
+
+    /*
+     * Make copies of apasswd and auser, then null-terminate them.
+     * If there are unprintable characters in the password, make
+     * them visible.
+     */
+    slprintf(ppp_settings.passwd, sizeof(ppp_settings.passwd), "%.*v", passwdlen, apasswd);
+    slprintf(ppp_settings.user, sizeof(ppp_settings.user), "%.*v", userlen, auser);
+    *msg = "";
+
+    /*
+     * Check if a plugin wants to handle this.
+     */
+    if (pap_auth_hook) {
+       ret = (*pap_auth_hook)(ppp_settings.user, ppp_settings.passwd, msg, &addrs, &opts);
+       if (ret >= 0) {
+           /* note: set_allowed_addrs() saves opts (but not addrs):
+              don't free it! */
+           if (ret)
+               set_allowed_addrs(unit, addrs, opts);
+           else if (opts != 0)
+               free_wordlist(opts);
+           if (addrs != 0)
+               free_wordlist(addrs);
+           BZERO(ppp_settings.passwd, sizeof(ppp_settings.passwd));
+           return ret? UPAP_AUTHACK: UPAP_AUTHNAK;
+       }
+    }
+
+    /*
+     * Open the file of pap secrets and scan for a suitable secret
+     * for authenticating this user.
+     */
+    filename = _PATH_UPAPFILE;
+    addrs = opts = NULL;
+    ret = UPAP_AUTHNAK;
+    f = fopen(filename, "r");
+    if (f == NULL) {
+       ppp_error("Can't open PAP password file %s: %m", filename);
+
+    } else {
+       check_access(f, filename);
+       if (scan_authfile(f, ppp_settings.user, our_name, secret, &addrs, &opts, filename, 0) < 0) {
+           ppp_warn("no PAP secret found for %s", user);
+       } else {
+           /*
+            * If the secret is "@login", it means to check
+            * the password against the login database.
+            */
+           int login_secret = strcmp(secret, "@login") == 0;
+           ret = UPAP_AUTHACK;
+           if (uselogin || login_secret) {
+               /* login option or secret is @login */
+               if (session_full(ppp_settings.user, ppp_settings.passwd, devnam, msg) == 0) {
+                   ret = UPAP_AUTHNAK;
+               }
+           } else if (session_mgmt) {
+               if (session_check(ppp_settings.user, NULL, devnam, NULL) == 0) {
+                   ppp_warn("Peer %q failed PAP Session verification", user);
+                   ret = UPAP_AUTHNAK;
+               }
+           }
+           if (secret[0] != 0 && !login_secret) {
+               /* password given in pap-secrets - must match */
+               if ((cryptpap || strcmp(ppp_settings.passwd, secret) != 0)
+                   && strcmp(crypt(ppp_settings.passwd, secret), secret) != 0)
+                   ret = UPAP_AUTHNAK;
+           }
+       }
+       fclose(f);
+    }
+
+    if (ret == UPAP_AUTHNAK) {
+        if (**msg == 0)
+           *msg = "Login incorrect";
+       /*
+        * XXX can we ever get here more than once??
+        * Frustrate passwd stealer programs.
+        * Allow 10 tries, but start backing off after 3 (stolen from login).
+        * On 10'th, drop the connection.
+        */
+       if (attempts++ >= 10) {
+           ppp_warn("%d LOGIN FAILURES ON %s, %s", attempts, devnam, user);
+           lcp_close(pcb, "login failed");
+       }
+       if (attempts > 3)
+           sleep((u_int) (attempts - 3) * 5);
+       if (opts != NULL)
+           free_wordlist(opts);
+
+    } else {
+       attempts = 0;                   /* Reset count */
+       if (**msg == 0)
+           *msg = "Login ok";
+       set_allowed_addrs(unit, addrs, opts);
+    }
+
+    if (addrs != NULL)
+       free_wordlist(addrs);
+    BZERO(ppp_settings.passwd, sizeof(ppp_settings.passwd));
+    BZERO(secret, sizeof(secret));
+
+    return ret;
+}
+
+/*
+ * null_login - Check if a username of "" and a password of "" are
+ * acceptable, and iff so, set the list of acceptable IP addresses
+ * and return 1.
+ */
+static int
+null_login(unit)
+    int unit;
+{
+    char *filename;
+    FILE *f;
+    int i, ret;
+    struct wordlist *addrs, *opts;
+    char secret[MAXWORDLEN];
+
+    /*
+     * Check if a plugin wants to handle this.
+     */
+    ret = -1;
+    if (null_auth_hook)
+       ret = (*null_auth_hook)(&addrs, &opts);
+
+    /*
+     * Open the file of pap secrets and scan for a suitable secret.
+     */
+    if (ret <= 0) {
+       filename = _PATH_UPAPFILE;
+       addrs = NULL;
+       f = fopen(filename, "r");
+       if (f == NULL)
+           return 0;
+       check_access(f, filename);
+
+       i = scan_authfile(f, "", our_name, secret, &addrs, &opts, filename, 0);
+       ret = i >= 0 && secret[0] == 0;
+       BZERO(secret, sizeof(secret));
+       fclose(f);
+    }
+
+    if (ret)
+       set_allowed_addrs(unit, addrs, opts);
+    else if (opts != 0)
+       free_wordlist(opts);
+    if (addrs != 0)
+       free_wordlist(addrs);
+
+    return ret;
+}
+
+/*
+ * get_pap_passwd - get a password for authenticating ourselves with
+ * our peer using PAP.  Returns 1 on success, 0 if no suitable password
+ * could be found.
+ * Assumes passwd points to MAXSECRETLEN bytes of space (if non-null).
+ */
+static int
+get_pap_passwd(passwd)
+    char *passwd;
+{
+    char *filename;
+    FILE *f;
+    int ret;
+    char secret[MAXWORDLEN];
+
+    /*
+     * Check whether a plugin wants to supply this.
+     */
+    if (pap_passwd_hook) {
+       ret = (*pap_passwd_hook)(ppp_settings,user, ppp_settings.passwd);
+       if (ret >= 0)
+           return ret;
+    }
+
+    filename = _PATH_UPAPFILE;
+    f = fopen(filename, "r");
+    if (f == NULL)
+       return 0;
+    check_access(f, filename);
+    ret = scan_authfile(f, user,
+                       (remote_name[0]? remote_name: NULL),
+                       secret, NULL, NULL, filename, 0);
+    fclose(f);
+    if (ret < 0)
+       return 0;
+    if (passwd != NULL)
+       strlcpy(passwd, secret, MAXSECRETLEN);
+    BZERO(secret, sizeof(secret));
+    return 1;
+}
+
+/*
+ * have_pap_secret - check whether we have a PAP file with any
+ * secrets that we could possibly use for authenticating the peer.
+ */
+static int
+have_pap_secret(lacks_ipp)
+    int *lacks_ipp;
+{
+    FILE *f;
+    int ret;
+    char *filename;
+    struct wordlist *addrs;
+
+    /* let the plugin decide, if there is one */
+    if (pap_check_hook) {
+       ret = (*pap_check_hook)();
+       if (ret >= 0)
+           return ret;
+    }
+
+    filename = _PATH_UPAPFILE;
+    f = fopen(filename, "r");
+    if (f == NULL)
+       return 0;
+
+    ret = scan_authfile(f, (explicit_remote? remote_name: NULL), our_name,
+                       NULL, &addrs, NULL, filename, 0);
+    fclose(f);
+    if (ret >= 0 && !some_ip_ok(addrs)) {
+       if (lacks_ipp != 0)
+           *lacks_ipp = 1;
+       ret = -1;
+    }
+    if (addrs != 0)
+       free_wordlist(addrs);
+
+    return ret >= 0;
+}
+
+/*
+ * have_chap_secret - check whether we have a CHAP file with a
+ * secret that we could possibly use for authenticating `client'
+ * on `server'.  Either can be the null string, meaning we don't
+ * know the identity yet.
+ */
+static int
+have_chap_secret(client, server, need_ip, lacks_ipp)
+    char *client;
+    char *server;
+    int need_ip;
+    int *lacks_ipp;
+{
+    FILE *f;
+    int ret;
+    char *filename;
+    struct wordlist *addrs;
+
+    if (chap_check_hook) {
+       ret = (*chap_check_hook)();
+       if (ret >= 0) {
+           return ret;
+       }
+    }
+
+    filename = _PATH_CHAPFILE;
+    f = fopen(filename, "r");
+    if (f == NULL)
+       return 0;
+
+    if (client != NULL && client[0] == 0)
+       client = NULL;
+    else if (server != NULL && server[0] == 0)
+       server = NULL;
+
+    ret = scan_authfile(f, client, server, NULL, &addrs, NULL, filename, 0);
+    fclose(f);
+    if (ret >= 0 && need_ip && !some_ip_ok(addrs)) {
+       if (lacks_ipp != 0)
+           *lacks_ipp = 1;
+       ret = -1;
+    }
+    if (addrs != 0)
+       free_wordlist(addrs);
+
+    return ret >= 0;
+}
+
+/*
+ * have_srp_secret - check whether we have a SRP file with a
+ * secret that we could possibly use for authenticating `client'
+ * on `server'.  Either can be the null string, meaning we don't
+ * know the identity yet.
+ */
+static int
+have_srp_secret(client, server, need_ip, lacks_ipp)
+    char *client;
+    char *server;
+    int need_ip;
+    int *lacks_ipp;
+{
+    FILE *f;
+    int ret;
+    char *filename;
+    struct wordlist *addrs;
+
+    filename = _PATH_SRPFILE;
+    f = fopen(filename, "r");
+    if (f == NULL)
+       return 0;
+
+    if (client != NULL && client[0] == 0)
+       client = NULL;
+    else if (server != NULL && server[0] == 0)
+       server = NULL;
+
+    ret = scan_authfile(f, client, server, NULL, &addrs, NULL, filename, 0);
+    fclose(f);
+    if (ret >= 0 && need_ip && !some_ip_ok(addrs)) {
+       if (lacks_ipp != 0)
+           *lacks_ipp = 1;
+       ret = -1;
+    }
+    if (addrs != 0)
+       free_wordlist(addrs);
+
+    return ret >= 0;
+}
+#endif /* UNUSED */
+
+#if PPP_AUTH_SUPPORT
+/*
+ * get_secret - open the CHAP secret file and return the secret
+ * for authenticating the given client on the given server.
+ * (We could be either client or server).
+ */
+int get_secret(ppp_pcb *pcb, const char *client, const char *server, char *secret, int *secret_len, int am_server) {
+  int len;
+  LWIP_UNUSED_ARG(server);
+  LWIP_UNUSED_ARG(am_server);
+
+  if (!client || !client[0] || !pcb->settings.user || !pcb->settings.passwd || strcmp(client, pcb->settings.user)) {
+    return 0;
+  }
+
+  len = (int)strlen(pcb->settings.passwd);
+  if (len > MAXSECRETLEN) {
+    ppp_error("Secret for %s on %s is too long", client, server);
+    len = MAXSECRETLEN;
+  }
+
+  MEMCPY(secret, pcb->settings.passwd, len);
+  *secret_len = len;
+  return 1;
+
+#if 0 /* UNUSED */
+    FILE *f;
+    int ret, len;
+    char *filename;
+    struct wordlist *addrs, *opts;
+    char secbuf[MAXWORDLEN];
+    struct wordlist *addrs;
+    addrs = NULL;
+
+    if (!am_server && ppp_settings.passwd[0] != 0) {
+       strlcpy(secbuf, ppp_settings.passwd, sizeof(secbuf));
+    } else if (!am_server && chap_passwd_hook) {
+       if ( (*chap_passwd_hook)(client, secbuf) < 0) {
+           ppp_error("Unable to obtain CHAP password for %s on %s from plugin",
+                 client, server);
+           return 0;
+       }
+    } else {
+       filename = _PATH_CHAPFILE;
+       addrs = NULL;
+       secbuf[0] = 0;
+
+       f = fopen(filename, "r");
+       if (f == NULL) {
+           ppp_error("Can't open chap secret file %s: %m", filename);
+           return 0;
+       }
+       check_access(f, filename);
+
+       ret = scan_authfile(f, client, server, secbuf, &addrs, &opts, filename, 0);
+       fclose(f);
+       if (ret < 0)
+           return 0;
+
+       if (am_server)
+           set_allowed_addrs(unit, addrs, opts);
+       else if (opts != 0)
+           free_wordlist(opts);
+       if (addrs != 0)
+           free_wordlist(addrs);
+    }
+
+    len = strlen(secbuf);
+    if (len > MAXSECRETLEN) {
+       ppp_error("Secret for %s on %s is too long", client, server);
+       len = MAXSECRETLEN;
+    }
+    MEMCPY(secret, secbuf, len);
+    BZERO(secbuf, sizeof(secbuf));
+    *secret_len = len;
+
+    return 1;
+#endif /* UNUSED */
+}
+#endif /* PPP_AUTH_SUPPORT */
+
+
+#if 0 /* UNUSED */
+/*
+ * get_srp_secret - open the SRP secret file and return the secret
+ * for authenticating the given client on the given server.
+ * (We could be either client or server).
+ */
+int
+get_srp_secret(unit, client, server, secret, am_server)
+    int unit;
+    char *client;
+    char *server;
+    char *secret;
+    int am_server;
+{
+    FILE *fp;
+    int ret;
+    char *filename;
+    struct wordlist *addrs, *opts;
+
+    if (!am_server && ppp_settings.passwd[0] != '\0') {
+       strlcpy(secret, ppp_settings.passwd, MAXWORDLEN);
+    } else {
+       filename = _PATH_SRPFILE;
+       addrs = NULL;
+
+       fp = fopen(filename, "r");
+       if (fp == NULL) {
+           ppp_error("Can't open srp secret file %s: %m", filename);
+           return 0;
+       }
+       check_access(fp, filename);
+
+       secret[0] = '\0';
+       ret = scan_authfile(fp, client, server, secret, &addrs, &opts,
+           filename, am_server);
+       fclose(fp);
+       if (ret < 0)
+           return 0;
+
+       if (am_server)
+           set_allowed_addrs(unit, addrs, opts);
+       else if (opts != NULL)
+           free_wordlist(opts);
+       if (addrs != NULL)
+           free_wordlist(addrs);
+    }
+
+    return 1;
+}
+
+/*
+ * set_allowed_addrs() - set the list of allowed addresses.
+ * Also looks for `--' indicating options to apply for this peer
+ * and leaves the following words in extra_options.
+ */
+static void
+set_allowed_addrs(unit, addrs, opts)
+    int unit;
+    struct wordlist *addrs;
+    struct wordlist *opts;
+{
+    int n;
+    struct wordlist *ap, **plink;
+    struct permitted_ip *ip;
+    char *ptr_word, *ptr_mask;
+    struct hostent *hp;
+    struct netent *np;
+    u32_t a, mask, ah, offset;
+    struct ipcp_options *wo = &ipcp_wantoptions[unit];
+    u32_t suggested_ip = 0;
+
+    if (addresses[unit] != NULL)
+       free(addresses[unit]);
+    addresses[unit] = NULL;
+    if (extra_options != NULL)
+       free_wordlist(extra_options);
+    extra_options = opts;
+
+    /*
+     * Count the number of IP addresses given.
+     */
+    n = wordlist_count(addrs) + wordlist_count(noauth_addrs);
+    if (n == 0)
+       return;
+    ip = (struct permitted_ip *) malloc((n + 1) * sizeof(struct permitted_ip));
+    if (ip == 0)
+       return;
+
+    /* temporarily append the noauth_addrs list to addrs */
+    for (plink = &addrs; *plink != NULL; plink = &(*plink)->next)
+       ;
+    *plink = noauth_addrs;
+
+    n = 0;
+    for (ap = addrs; ap != NULL; ap = ap->next) {
+       /* "-" means no addresses authorized, "*" means any address allowed */
+       ptr_word = ap->word;
+       if (strcmp(ptr_word, "-") == 0)
+           break;
+       if (strcmp(ptr_word, "*") == 0) {
+           ip[n].permit = 1;
+           ip[n].base = ip[n].mask = 0;
+           ++n;
+           break;
+       }
+
+       ip[n].permit = 1;
+       if (*ptr_word == '!') {
+           ip[n].permit = 0;
+           ++ptr_word;
+       }
+
+       mask = ~ (u32_t) 0;
+       offset = 0;
+       ptr_mask = strchr (ptr_word, '/');
+       if (ptr_mask != NULL) {
+           int bit_count;
+           char *endp;
+
+           bit_count = (int) strtol (ptr_mask+1, &endp, 10);
+           if (bit_count <= 0 || bit_count > 32) {
+               ppp_warn("invalid address length %v in auth. address list",
+                    ptr_mask+1);
+               continue;
+           }
+           bit_count = 32 - bit_count; /* # bits in host part */
+           if (*endp == '+') {
+               offset = ifunit + 1;
+               ++endp;
+           }
+           if (*endp != 0) {
+               ppp_warn("invalid address length syntax: %v", ptr_mask+1);
+               continue;
+           }
+           *ptr_mask = '\0';
+           mask <<= bit_count;
+       }
+
+       hp = gethostbyname(ptr_word);
+       if (hp != NULL && hp->h_addrtype == AF_INET) {
+           a = *(u32_t *)hp->h_addr;
+       } else {
+           np = getnetbyname (ptr_word);
+           if (np != NULL && np->n_addrtype == AF_INET) {
+               a = lwip_htonl ((u32_t)np->n_net);
+               if (ptr_mask == NULL) {
+                   /* calculate appropriate mask for net */
+                   ah = lwip_ntohl(a);
+                   if (IN_CLASSA(ah))
+                       mask = IN_CLASSA_NET;
+                   else if (IN_CLASSB(ah))
+                       mask = IN_CLASSB_NET;
+                   else if (IN_CLASSC(ah))
+                       mask = IN_CLASSC_NET;
+               }
+           } else {
+               a = inet_addr (ptr_word);
+           }
+       }
+
+       if (ptr_mask != NULL)
+           *ptr_mask = '/';
+
+       if (a == (u32_t)-1L) {
+           ppp_warn("unknown host %s in auth. address list", ap->word);
+           continue;
+       }
+       if (offset != 0) {
+           if (offset >= ~mask) {
+               ppp_warn("interface unit %d too large for subnet %v",
+                    ifunit, ptr_word);
+               continue;
+           }
+           a = lwip_htonl((lwip_ntohl(a) & mask) + offset);
+           mask = ~(u32_t)0;
+       }
+       ip[n].mask = lwip_htonl(mask);
+       ip[n].base = a & ip[n].mask;
+       ++n;
+       if (~mask == 0 && suggested_ip == 0)
+           suggested_ip = a;
+    }
+    *plink = NULL;
+
+    ip[n].permit = 0;          /* make the last entry forbid all addresses */
+    ip[n].base = 0;            /* to terminate the list */
+    ip[n].mask = 0;
+
+    addresses[unit] = ip;
+
+    /*
+     * If the address given for the peer isn't authorized, or if
+     * the user hasn't given one, AND there is an authorized address
+     * which is a single host, then use that if we find one.
+     */
+    if (suggested_ip != 0
+       && (wo->hisaddr == 0 || !auth_ip_addr(unit, wo->hisaddr))) {
+       wo->hisaddr = suggested_ip;
+       /*
+        * Do we insist on this address?  No, if there are other
+        * addresses authorized than the suggested one.
+        */
+       if (n > 1)
+           wo->accept_remote = 1;
+    }
+}
+
+/*
+ * auth_ip_addr - check whether the peer is authorized to use
+ * a given IP address.  Returns 1 if authorized, 0 otherwise.
+ */
+int
+auth_ip_addr(unit, addr)
+    int unit;
+    u32_t addr;
+{
+    int ok;
+
+    /* don't allow loopback or multicast address */
+    if (bad_ip_adrs(addr))
+       return 0;
+
+    if (allowed_address_hook) {
+       ok = allowed_address_hook(addr);
+       if (ok >= 0) return ok;
+    }
+
+    if (addresses[unit] != NULL) {
+       ok = ip_addr_check(addr, addresses[unit]);
+       if (ok >= 0)
+           return ok;
+    }
+
+    if (auth_required)
+       return 0;               /* no addresses authorized */
+    return allow_any_ip || privileged || !have_route_to(addr);
+}
+
+static int
+ip_addr_check(addr, addrs)
+    u32_t addr;
+    struct permitted_ip *addrs;
+{
+    for (; ; ++addrs)
+       if ((addr & addrs->mask) == addrs->base)
+           return addrs->permit;
+}
+
+/*
+ * bad_ip_adrs - return 1 if the IP address is one we don't want
+ * to use, such as an address in the loopback net or a multicast address.
+ * addr is in network byte order.
+ */
+int
+bad_ip_adrs(addr)
+    u32_t addr;
+{
+    addr = lwip_ntohl(addr);
+    return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET
+       || IN_MULTICAST(addr) || IN_BADCLASS(addr);
+}
+
+/*
+ * some_ip_ok - check a wordlist to see if it authorizes any
+ * IP address(es).
+ */
+static int
+some_ip_ok(addrs)
+    struct wordlist *addrs;
+{
+    for (; addrs != 0; addrs = addrs->next) {
+       if (addrs->word[0] == '-')
+           break;
+       if (addrs->word[0] != '!')
+           return 1;           /* some IP address is allowed */
+    }
+    return 0;
+}
+
+/*
+ * auth_number - check whether the remote number is allowed to connect.
+ * Returns 1 if authorized, 0 otherwise.
+ */
+int
+auth_number()
+{
+    struct wordlist *wp = permitted_numbers;
+    int l;
+
+    /* Allow all if no authorization list. */
+    if (!wp)
+       return 1;
+
+    /* Allow if we have a match in the authorization list. */
+    while (wp) {
+       /* trailing '*' wildcard */
+       l = strlen(wp->word);
+       if ((wp->word)[l - 1] == '*')
+           l--;
+       if (!strncasecmp(wp->word, remote_number, l))
+           return 1;
+       wp = wp->next;
+    }
+
+    return 0;
+}
+
+/*
+ * check_access - complain if a secret file has too-liberal permissions.
+ */
+static void
+check_access(f, filename)
+    FILE *f;
+    char *filename;
+{
+    struct stat sbuf;
+
+    if (fstat(fileno(f), &sbuf) < 0) {
+       ppp_warn("cannot stat secret file %s: %m", filename);
+    } else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) {
+       ppp_warn("Warning - secret file %s has world and/or group access",
+            filename);
+    }
+}
+
+/*
+ * scan_authfile - Scan an authorization file for a secret suitable
+ * for authenticating `client' on `server'.  The return value is -1
+ * if no secret is found, otherwise >= 0.  The return value has
+ * NONWILD_CLIENT set if the secret didn't have "*" for the client, and
+ * NONWILD_SERVER set if the secret didn't have "*" for the server.
+ * Any following words on the line up to a "--" (i.e. address authorization
+ * info) are placed in a wordlist and returned in *addrs.  Any
+ * following words (extra options) are placed in a wordlist and
+ * returned in *opts.
+ * We assume secret is NULL or points to MAXWORDLEN bytes of space.
+ * Flags are non-zero if we need two colons in the secret in order to
+ * match.
+ */
+static int
+scan_authfile(f, client, server, secret, addrs, opts, filename, flags)
+    FILE *f;
+    char *client;
+    char *server;
+    char *secret;
+    struct wordlist **addrs;
+    struct wordlist **opts;
+    char *filename;
+    int flags;
+{
+    int newline, xxx;
+    int got_flag, best_flag;
+    FILE *sf;
+    struct wordlist *ap, *addr_list, *alist, **app;
+    char word[MAXWORDLEN];
+    char atfile[MAXWORDLEN];
+    char lsecret[MAXWORDLEN];
+    char *cp;
+
+    if (addrs != NULL)
+       *addrs = NULL;
+    if (opts != NULL)
+       *opts = NULL;
+    addr_list = NULL;
+    if (!getword(f, word, &newline, filename))
+       return -1;              /* file is empty??? */
+    newline = 1;
+    best_flag = -1;
+    for (;;) {
+       /*
+        * Skip until we find a word at the start of a line.
+        */
+       while (!newline && getword(f, word, &newline, filename))
+           ;
+       if (!newline)
+           break;              /* got to end of file */
+
+       /*
+        * Got a client - check if it's a match or a wildcard.
+        */
+       got_flag = 0;
+       if (client != NULL && strcmp(word, client) != 0 && !ISWILD(word)) {
+           newline = 0;
+           continue;
+       }
+       if (!ISWILD(word))
+           got_flag = NONWILD_CLIENT;
+
+       /*
+        * Now get a server and check if it matches.
+        */
+       if (!getword(f, word, &newline, filename))
+           break;
+       if (newline)
+           continue;
+       if (!ISWILD(word)) {
+           if (server != NULL && strcmp(word, server) != 0)
+               continue;
+           got_flag |= NONWILD_SERVER;
+       }
+
+       /*
+        * Got some sort of a match - see if it's better than what
+        * we have already.
+        */
+       if (got_flag <= best_flag)
+           continue;
+
+       /*
+        * Get the secret.
+        */
+       if (!getword(f, word, &newline, filename))
+           break;
+       if (newline)
+           continue;
+
+       /*
+        * SRP-SHA1 authenticator should never be reading secrets from
+        * a file.  (Authenticatee may, though.)
+        */
+       if (flags && ((cp = strchr(word, ':')) == NULL ||
+           strchr(cp + 1, ':') == NULL))
+           continue;
+
+       if (secret != NULL) {
+           /*
+            * Special syntax: @/pathname means read secret from file.
+            */
+           if (word[0] == '@' && word[1] == '/') {
+               strlcpy(atfile, word+1, sizeof(atfile));
+               if ((sf = fopen(atfile, "r")) == NULL) {
+                   ppp_warn("can't open indirect secret file %s", atfile);
+                   continue;
+               }
+               check_access(sf, atfile);
+               if (!getword(sf, word, &xxx, atfile)) {
+                   ppp_warn("no secret in indirect secret file %s", atfile);
+                   fclose(sf);
+                   continue;
+               }
+               fclose(sf);
+           }
+           strlcpy(lsecret, word, sizeof(lsecret));
+       }
+
+       /*
+        * Now read address authorization info and make a wordlist.
+        */
+       app = &alist;
+       for (;;) {
+           if (!getword(f, word, &newline, filename) || newline)
+               break;
+           ap = (struct wordlist *)
+                   malloc(sizeof(struct wordlist) + strlen(word) + 1);
+           if (ap == NULL)
+               novm("authorized addresses");
+           ap->word = (char *) (ap + 1);
+           strcpy(ap->word, word);
+           *app = ap;
+           app = &ap->next;
+       }
+       *app = NULL;
+
+       /*
+        * This is the best so far; remember it.
+        */
+       best_flag = got_flag;
+       if (addr_list)
+           free_wordlist(addr_list);
+       addr_list = alist;
+       if (secret != NULL)
+           strlcpy(secret, lsecret, MAXWORDLEN);
+
+       if (!newline)
+           break;
+    }
+
+    /* scan for a -- word indicating the start of options */
+    for (app = &addr_list; (ap = *app) != NULL; app = &ap->next)
+       if (strcmp(ap->word, "--") == 0)
+           break;
+    /* ap = start of options */
+    if (ap != NULL) {
+       ap = ap->next;          /* first option */
+       free(*app);                     /* free the "--" word */
+       *app = NULL;            /* terminate addr list */
+    }
+    if (opts != NULL)
+       *opts = ap;
+    else if (ap != NULL)
+       free_wordlist(ap);
+    if (addrs != NULL)
+       *addrs = addr_list;
+    else if (addr_list != NULL)
+       free_wordlist(addr_list);
+
+    return best_flag;
+}
+
+/*
+ * wordlist_count - return the number of items in a wordlist
+ */
+static int
+wordlist_count(wp)
+    struct wordlist *wp;
+{
+    int n;
+
+    for (n = 0; wp != NULL; wp = wp->next)
+       ++n;
+    return n;
+}
+
+/*
+ * free_wordlist - release memory allocated for a wordlist.
+ */
+static void
+free_wordlist(wp)
+    struct wordlist *wp;
+{
+    struct wordlist *next;
+
+    while (wp != NULL) {
+       next = wp->next;
+       free(wp);
+       wp = next;
+    }
+}
+#endif /* UNUSED */
+
+#endif /* PPP_SUPPORT */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/ccp.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/ccp.c
new file mode 100644 (file)
index 0000000..f8519eb
--- /dev/null
@@ -0,0 +1,1740 @@
+/*
+ * ccp.c - PPP Compression Control Protocol.
+ *
+ * Copyright (c) 1994-2002 Paul Mackerras. All rights reserved.
+ *
+ * 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. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && CCP_SUPPORT  /* don't build if not configured for use in lwipopts.h */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/fsm.h"
+#include "netif/ppp/ccp.h"
+
+#if MPPE_SUPPORT
+#include "netif/ppp/lcp.h"     /* lcp_close(), lcp_fsm */
+#include "netif/ppp/mppe.h"    /* mppe_init() */
+#endif /* MPPE_SUPPORT */
+
+/*
+ * Unfortunately there is a bug in zlib which means that using a
+ * size of 8 (window size = 256) for Deflate compression will cause
+ * buffer overruns and kernel crashes in the deflate module.
+ * Until this is fixed we only accept sizes in the range 9 .. 15.
+ * Thanks to James Carlson for pointing this out.
+ */
+#define DEFLATE_MIN_WORKS      9
+
+/*
+ * Command-line options.
+ */
+#if PPP_OPTIONS
+static int setbsdcomp (char **);
+static int setdeflate (char **);
+static char bsd_value[8];
+static char deflate_value[8];
+
+/*
+ * Option variables.
+ */
+#if MPPE_SUPPORT
+bool refuse_mppe_stateful = 1;         /* Allow stateful mode? */
+#endif /* MPPE_SUPPORT */
+
+static option_t ccp_option_list[] = {
+    { "noccp", o_bool, &ccp_protent.enabled_flag,
+      "Disable CCP negotiation" },
+    { "-ccp", o_bool, &ccp_protent.enabled_flag,
+      "Disable CCP negotiation", OPT_ALIAS },
+
+    { "bsdcomp", o_special, (void *)setbsdcomp,
+      "Request BSD-Compress packet compression",
+      OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, bsd_value },
+    { "nobsdcomp", o_bool, &ccp_wantoptions[0].bsd_compress,
+      "don't allow BSD-Compress", OPT_PRIOSUB | OPT_A2CLR,
+      &ccp_allowoptions[0].bsd_compress },
+    { "-bsdcomp", o_bool, &ccp_wantoptions[0].bsd_compress,
+      "don't allow BSD-Compress", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR,
+      &ccp_allowoptions[0].bsd_compress },
+
+    { "deflate", o_special, (void *)setdeflate,
+      "request Deflate compression",
+      OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, deflate_value },
+    { "nodeflate", o_bool, &ccp_wantoptions[0].deflate,
+      "don't allow Deflate compression", OPT_PRIOSUB | OPT_A2CLR,
+      &ccp_allowoptions[0].deflate },
+    { "-deflate", o_bool, &ccp_wantoptions[0].deflate,
+      "don't allow Deflate compression", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR,
+      &ccp_allowoptions[0].deflate },
+
+    { "nodeflatedraft", o_bool, &ccp_wantoptions[0].deflate_draft,
+      "don't use draft deflate #", OPT_A2COPY,
+      &ccp_allowoptions[0].deflate_draft },
+
+    { "predictor1", o_bool, &ccp_wantoptions[0].predictor_1,
+      "request Predictor-1", OPT_PRIO | 1 },
+    { "nopredictor1", o_bool, &ccp_wantoptions[0].predictor_1,
+      "don't allow Predictor-1", OPT_PRIOSUB | OPT_A2CLR,
+      &ccp_allowoptions[0].predictor_1 },
+    { "-predictor1", o_bool, &ccp_wantoptions[0].predictor_1,
+      "don't allow Predictor-1", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR,
+      &ccp_allowoptions[0].predictor_1 },
+
+#if MPPE_SUPPORT
+    /* MPPE options are symmetrical ... we only set wantoptions here */
+    { "require-mppe", o_bool, &ccp_wantoptions[0].mppe,
+      "require MPPE encryption",
+      OPT_PRIO | MPPE_OPT_40 | MPPE_OPT_128 },
+    { "+mppe", o_bool, &ccp_wantoptions[0].mppe,
+      "require MPPE encryption",
+      OPT_ALIAS | OPT_PRIO | MPPE_OPT_40 | MPPE_OPT_128 },
+    { "nomppe", o_bool, &ccp_wantoptions[0].mppe,
+      "don't allow MPPE encryption", OPT_PRIO },
+    { "-mppe", o_bool, &ccp_wantoptions[0].mppe,
+      "don't allow MPPE encryption", OPT_ALIAS | OPT_PRIO },
+
+    /* We use ccp_allowoptions[0].mppe as a junk var ... it is reset later */
+    { "require-mppe-40", o_bool, &ccp_allowoptions[0].mppe,
+      "require MPPE 40-bit encryption", OPT_PRIO | OPT_A2OR | MPPE_OPT_40,
+      &ccp_wantoptions[0].mppe },
+    { "+mppe-40", o_bool, &ccp_allowoptions[0].mppe,
+      "require MPPE 40-bit encryption", OPT_PRIO | OPT_A2OR | MPPE_OPT_40,
+      &ccp_wantoptions[0].mppe },
+    { "nomppe-40", o_bool, &ccp_allowoptions[0].mppe,
+      "don't allow MPPE 40-bit encryption",
+      OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_40, &ccp_wantoptions[0].mppe },
+    { "-mppe-40", o_bool, &ccp_allowoptions[0].mppe,
+      "don't allow MPPE 40-bit encryption",
+      OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_40,
+      &ccp_wantoptions[0].mppe },
+
+    { "require-mppe-128", o_bool, &ccp_allowoptions[0].mppe,
+      "require MPPE 128-bit encryption", OPT_PRIO | OPT_A2OR | MPPE_OPT_128,
+      &ccp_wantoptions[0].mppe },
+    { "+mppe-128", o_bool, &ccp_allowoptions[0].mppe,
+      "require MPPE 128-bit encryption",
+      OPT_ALIAS | OPT_PRIO | OPT_A2OR | MPPE_OPT_128,
+      &ccp_wantoptions[0].mppe },
+    { "nomppe-128", o_bool, &ccp_allowoptions[0].mppe,
+      "don't allow MPPE 128-bit encryption",
+      OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_128, &ccp_wantoptions[0].mppe },
+    { "-mppe-128", o_bool, &ccp_allowoptions[0].mppe,
+      "don't allow MPPE 128-bit encryption",
+      OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_128,
+      &ccp_wantoptions[0].mppe },
+
+    /* strange one; we always request stateless, but will we allow stateful? */
+    { "mppe-stateful", o_bool, &refuse_mppe_stateful,
+      "allow MPPE stateful mode", OPT_PRIO },
+    { "nomppe-stateful", o_bool, &refuse_mppe_stateful,
+      "disallow MPPE stateful mode", OPT_PRIO | 1 },
+#endif /* MPPE_SUPPORT */
+
+    { NULL }
+};
+#endif /* PPP_OPTIONS */
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ccp_init(ppp_pcb *pcb);
+static void ccp_open(ppp_pcb *pcb);
+static void ccp_close(ppp_pcb *pcb, const char *reason);
+static void ccp_lowerup(ppp_pcb *pcb);
+static void ccp_lowerdown(ppp_pcb *pcb);
+static void ccp_input(ppp_pcb *pcb, u_char *pkt, int len);
+static void ccp_protrej(ppp_pcb *pcb);
+#if PRINTPKT_SUPPORT
+static int ccp_printpkt(const u_char *p, int plen, void (*printer) (void *, const char *, ...), void *arg);
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_DATAINPUT
+static void ccp_datainput(ppp_pcb *pcb, u_char *pkt, int len);
+#endif /* PPP_DATAINPUT */
+
+const struct protent ccp_protent = {
+    PPP_CCP,
+    ccp_init,
+    ccp_input,
+    ccp_protrej,
+    ccp_lowerup,
+    ccp_lowerdown,
+    ccp_open,
+    ccp_close,
+#if PRINTPKT_SUPPORT
+    ccp_printpkt,
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_DATAINPUT
+    ccp_datainput,
+#endif /* PPP_DATAINPUT */
+#if PRINTPKT_SUPPORT
+    "CCP",
+    "Compressed",
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_OPTIONS
+    ccp_option_list,
+    NULL,
+#endif /* PPP_OPTIONS */
+#if DEMAND_SUPPORT
+    NULL,
+    NULL
+#endif /* DEMAND_SUPPORT */
+};
+
+/*
+ * Callbacks for fsm code.
+ */
+static void ccp_resetci (fsm *);
+static int  ccp_cilen (fsm *);
+static void ccp_addci (fsm *, u_char *, int *);
+static int  ccp_ackci (fsm *, u_char *, int);
+static int  ccp_nakci (fsm *, u_char *, int, int);
+static int  ccp_rejci (fsm *, u_char *, int);
+static int  ccp_reqci (fsm *, u_char *, int *, int);
+static void ccp_up (fsm *);
+static void ccp_down (fsm *);
+static int  ccp_extcode (fsm *, int, int, u_char *, int);
+static void ccp_rack_timeout (void *);
+static const char *method_name (ccp_options *, ccp_options *);
+
+static const fsm_callbacks ccp_callbacks = {
+    ccp_resetci,
+    ccp_cilen,
+    ccp_addci,
+    ccp_ackci,
+    ccp_nakci,
+    ccp_rejci,
+    ccp_reqci,
+    ccp_up,
+    ccp_down,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    ccp_extcode,
+    "CCP"
+};
+
+/*
+ * Do we want / did we get any compression?
+ */
+static int ccp_anycompress(ccp_options *opt) {
+    return (0
+#if DEFLATE_SUPPORT
+       || (opt)->deflate
+#endif /* DEFLATE_SUPPORT */
+#if BSDCOMPRESS_SUPPORT
+       || (opt)->bsd_compress
+#endif /* BSDCOMPRESS_SUPPORT */
+#if PREDICTOR_SUPPORT
+       || (opt)->predictor_1 || (opt)->predictor_2
+#endif /* PREDICTOR_SUPPORT */
+#if MPPE_SUPPORT
+       || (opt)->mppe
+#endif /* MPPE_SUPPORT */
+       );
+}
+
+/*
+ * Local state (mainly for handling reset-reqs and reset-acks).
+ */
+#define RACK_PENDING   1       /* waiting for reset-ack */
+#define RREQ_REPEAT    2       /* send another reset-req if no reset-ack */
+
+#define RACKTIMEOUT    1       /* second */
+
+#if PPP_OPTIONS
+/*
+ * Option parsing
+ */
+static int
+setbsdcomp(argv)
+    char **argv;
+{
+    int rbits, abits;
+    char *str, *endp;
+
+    str = *argv;
+    abits = rbits = strtol(str, &endp, 0);
+    if (endp != str && *endp == ',') {
+       str = endp + 1;
+       abits = strtol(str, &endp, 0);
+    }
+    if (*endp != 0 || endp == str) {
+       option_error("invalid parameter '%s' for bsdcomp option", *argv);
+       return 0;
+    }
+    if ((rbits != 0 && (rbits < BSD_MIN_BITS || rbits > BSD_MAX_BITS))
+       || (abits != 0 && (abits < BSD_MIN_BITS || abits > BSD_MAX_BITS))) {
+       option_error("bsdcomp option values must be 0 or %d .. %d",
+                    BSD_MIN_BITS, BSD_MAX_BITS);
+       return 0;
+    }
+    if (rbits > 0) {
+       ccp_wantoptions[0].bsd_compress = 1;
+       ccp_wantoptions[0].bsd_bits = rbits;
+    } else
+       ccp_wantoptions[0].bsd_compress = 0;
+    if (abits > 0) {
+       ccp_allowoptions[0].bsd_compress = 1;
+       ccp_allowoptions[0].bsd_bits = abits;
+    } else
+       ccp_allowoptions[0].bsd_compress = 0;
+    ppp_slprintf(bsd_value, sizeof(bsd_value),
+            rbits == abits? "%d": "%d,%d", rbits, abits);
+
+    return 1;
+}
+
+static int
+setdeflate(argv)
+    char **argv;
+{
+    int rbits, abits;
+    char *str, *endp;
+
+    str = *argv;
+    abits = rbits = strtol(str, &endp, 0);
+    if (endp != str && *endp == ',') {
+       str = endp + 1;
+       abits = strtol(str, &endp, 0);
+    }
+    if (*endp != 0 || endp == str) {
+       option_error("invalid parameter '%s' for deflate option", *argv);
+       return 0;
+    }
+    if ((rbits != 0 && (rbits < DEFLATE_MIN_SIZE || rbits > DEFLATE_MAX_SIZE))
+       || (abits != 0 && (abits < DEFLATE_MIN_SIZE
+                         || abits > DEFLATE_MAX_SIZE))) {
+       option_error("deflate option values must be 0 or %d .. %d",
+                    DEFLATE_MIN_SIZE, DEFLATE_MAX_SIZE);
+       return 0;
+    }
+    if (rbits == DEFLATE_MIN_SIZE || abits == DEFLATE_MIN_SIZE) {
+       if (rbits == DEFLATE_MIN_SIZE)
+           rbits = DEFLATE_MIN_WORKS;
+       if (abits == DEFLATE_MIN_SIZE)
+           abits = DEFLATE_MIN_WORKS;
+       warn("deflate option value of %d changed to %d to avoid zlib bug",
+            DEFLATE_MIN_SIZE, DEFLATE_MIN_WORKS);
+    }
+    if (rbits > 0) {
+       ccp_wantoptions[0].deflate = 1;
+       ccp_wantoptions[0].deflate_size = rbits;
+    } else
+       ccp_wantoptions[0].deflate = 0;
+    if (abits > 0) {
+       ccp_allowoptions[0].deflate = 1;
+       ccp_allowoptions[0].deflate_size = abits;
+    } else
+       ccp_allowoptions[0].deflate = 0;
+    ppp_slprintf(deflate_value, sizeof(deflate_value),
+            rbits == abits? "%d": "%d,%d", rbits, abits);
+
+    return 1;
+}
+#endif /* PPP_OPTIONS */
+
+/*
+ * ccp_init - initialize CCP.
+ */
+static void ccp_init(ppp_pcb *pcb) {
+    fsm *f = &pcb->ccp_fsm;
+
+    f->pcb = pcb;
+    f->protocol = PPP_CCP;
+    f->callbacks = &ccp_callbacks;
+    fsm_init(f);
+
+#if 0 /* Not necessary, everything is cleared in ppp_new() */
+    memset(wo, 0, sizeof(*wo));
+    memset(go, 0, sizeof(*go));
+    memset(ao, 0, sizeof(*ao));
+    memset(ho, 0, sizeof(*ho));
+#endif /* 0 */
+
+#if DEFLATE_SUPPORT
+    wo->deflate = 1;
+    wo->deflate_size = DEFLATE_MAX_SIZE;
+    wo->deflate_correct = 1;
+    wo->deflate_draft = 1;
+    ao->deflate = 1;
+    ao->deflate_size = DEFLATE_MAX_SIZE;
+    ao->deflate_correct = 1;
+    ao->deflate_draft = 1;
+#endif /* DEFLATE_SUPPORT */
+
+#if BSDCOMPRESS_SUPPORT
+    wo->bsd_compress = 1;
+    wo->bsd_bits = BSD_MAX_BITS;
+    ao->bsd_compress = 1;
+    ao->bsd_bits = BSD_MAX_BITS;
+#endif /* BSDCOMPRESS_SUPPORT */
+
+#if PREDICTOR_SUPPORT
+    ao->predictor_1 = 1;
+#endif /* PREDICTOR_SUPPORT */
+}
+
+/*
+ * ccp_open - CCP is allowed to come up.
+ */
+static void ccp_open(ppp_pcb *pcb) {
+    fsm *f = &pcb->ccp_fsm;
+    ccp_options *go = &pcb->ccp_gotoptions;
+
+    if (f->state != PPP_FSM_OPENED)
+       ccp_set(pcb, 1, 0, 0, 0);
+
+    /*
+     * Find out which compressors the kernel supports before
+     * deciding whether to open in silent mode.
+     */
+    ccp_resetci(f);
+    if (!ccp_anycompress(go))
+       f->flags |= OPT_SILENT;
+
+    fsm_open(f);
+}
+
+/*
+ * ccp_close - Terminate CCP.
+ */
+static void ccp_close(ppp_pcb *pcb, const char *reason) {
+    fsm *f = &pcb->ccp_fsm;
+    ccp_set(pcb, 0, 0, 0, 0);
+    fsm_close(f, reason);
+}
+
+/*
+ * ccp_lowerup - we may now transmit CCP packets.
+ */
+static void ccp_lowerup(ppp_pcb *pcb) {
+    fsm *f = &pcb->ccp_fsm;
+    fsm_lowerup(f);
+}
+
+/*
+ * ccp_lowerdown - we may not transmit CCP packets.
+ */
+static void ccp_lowerdown(ppp_pcb *pcb) {
+    fsm *f = &pcb->ccp_fsm;
+    fsm_lowerdown(f);
+}
+
+/*
+ * ccp_input - process a received CCP packet.
+ */
+static void ccp_input(ppp_pcb *pcb, u_char *p, int len) {
+    fsm *f = &pcb->ccp_fsm;
+    ccp_options *go = &pcb->ccp_gotoptions;
+    int oldstate;
+
+    /*
+     * Check for a terminate-request so we can print a message.
+     */
+    oldstate = f->state;
+    fsm_input(f, p, len);
+    if (oldstate == PPP_FSM_OPENED && p[0] == TERMREQ && f->state != PPP_FSM_OPENED) {
+       ppp_notice("Compression disabled by peer.");
+#if MPPE_SUPPORT
+       if (go->mppe) {
+           ppp_error("MPPE disabled, closing LCP");
+           lcp_close(pcb, "MPPE disabled by peer");
+       }
+#endif /* MPPE_SUPPORT */
+    }
+
+    /*
+     * If we get a terminate-ack and we're not asking for compression,
+     * close CCP.
+     */
+    if (oldstate == PPP_FSM_REQSENT && p[0] == TERMACK
+       && !ccp_anycompress(go))
+       ccp_close(pcb, "No compression negotiated");
+}
+
+/*
+ * Handle a CCP-specific code.
+ */
+static int ccp_extcode(fsm *f, int code, int id, u_char *p, int len) {
+    ppp_pcb *pcb = f->pcb;
+    LWIP_UNUSED_ARG(p);
+    LWIP_UNUSED_ARG(len);
+
+    switch (code) {
+    case CCP_RESETREQ:
+       if (f->state != PPP_FSM_OPENED)
+           break;
+       ccp_reset_comp(pcb);
+       /* send a reset-ack, which the transmitter will see and
+          reset its compression state. */
+       fsm_sdata(f, CCP_RESETACK, id, NULL, 0);
+       break;
+
+    case CCP_RESETACK:
+       if ((pcb->ccp_localstate & RACK_PENDING) && id == f->reqid) {
+           pcb->ccp_localstate &= ~(RACK_PENDING | RREQ_REPEAT);
+           UNTIMEOUT(ccp_rack_timeout, f);
+           ccp_reset_decomp(pcb);
+       }
+       break;
+
+    default:
+       return 0;
+    }
+
+    return 1;
+}
+
+/*
+ * ccp_protrej - peer doesn't talk CCP.
+ */
+static void ccp_protrej(ppp_pcb *pcb) {
+    fsm *f = &pcb->ccp_fsm;
+#if MPPE_SUPPORT
+    ccp_options *go = &pcb->ccp_gotoptions;
+#endif /* MPPE_SUPPORT */
+
+    ccp_set(pcb, 0, 0, 0, 0);
+    fsm_lowerdown(f);
+
+#if MPPE_SUPPORT
+    if (go->mppe) {
+       ppp_error("MPPE required but peer negotiation failed");
+       lcp_close(pcb, "MPPE required but peer negotiation failed");
+    }
+#endif /* MPPE_SUPPORT */
+
+}
+
+/*
+ * ccp_resetci - initialize at start of negotiation.
+ */
+static void ccp_resetci(fsm *f) {
+    ppp_pcb *pcb = f->pcb;
+    ccp_options *go = &pcb->ccp_gotoptions;
+    ccp_options *wo = &pcb->ccp_wantoptions;
+#if MPPE_SUPPORT
+    ccp_options *ao = &pcb->ccp_allowoptions;
+#endif /* MPPE_SUPPORT */
+#if DEFLATE_SUPPORT || BSDCOMPRESS_SUPPORT || PREDICTOR_SUPPORT
+    u_char opt_buf[CCP_MAX_OPTION_LENGTH];
+#endif /* DEFLATE_SUPPORT || BSDCOMPRESS_SUPPORT || PREDICTOR_SUPPORT */
+#if DEFLATE_SUPPORT || BSDCOMPRESS_SUPPORT
+    int res;
+#endif /* DEFLATE_SUPPORT || BSDCOMPRESS_SUPPORT */
+
+#if MPPE_SUPPORT
+    if (pcb->settings.require_mppe) {
+       wo->mppe = ao->mppe =
+                   (pcb->settings.refuse_mppe_40 ? 0 : MPPE_OPT_40)
+                 | (pcb->settings.refuse_mppe_128 ? 0 : MPPE_OPT_128);
+    }
+#endif /* MPPE_SUPPORT */
+
+    *go = *wo;
+    pcb->ccp_all_rejected = 0;
+
+#if MPPE_SUPPORT
+    if (go->mppe) {
+       int auth_mschap_bits = pcb->auth_done;
+       int numbits;
+
+       /*
+        * Start with a basic sanity check: mschap[v2] auth must be in
+        * exactly one direction.  RFC 3079 says that the keys are
+        * 'derived from the credentials of the peer that initiated the call',
+        * however the PPP protocol doesn't have such a concept, and pppd
+        * cannot get this info externally.  Instead we do the best we can.
+        * NB: If MPPE is required, all other compression opts are invalid.
+        *     So, we return right away if we can't do it.
+        */
+
+       /* Leave only the mschap auth bits set */
+       auth_mschap_bits &= (CHAP_MS_WITHPEER  | CHAP_MS_PEER |
+                            CHAP_MS2_WITHPEER | CHAP_MS2_PEER);
+       /* Count the mschap auths */
+       auth_mschap_bits >>= CHAP_MS_SHIFT;
+       numbits = 0;
+       do {
+           numbits += auth_mschap_bits & 1;
+           auth_mschap_bits >>= 1;
+       } while (auth_mschap_bits);
+       if (numbits > 1) {
+           ppp_error("MPPE required, but auth done in both directions.");
+           lcp_close(pcb, "MPPE required but not available");
+           return;
+       }
+       if (!numbits) {
+           ppp_error("MPPE required, but MS-CHAP[v2] auth not performed.");
+           lcp_close(pcb, "MPPE required but not available");
+           return;
+       }
+
+       /* A plugin (eg radius) may not have obtained key material. */
+       if (!pcb->mppe_keys_set) {
+           ppp_error("MPPE required, but keys are not available.  "
+                 "Possible plugin problem?");
+           lcp_close(pcb, "MPPE required but not available");
+           return;
+       }
+
+       /* LM auth not supported for MPPE */
+       if (pcb->auth_done & (CHAP_MS_WITHPEER | CHAP_MS_PEER)) {
+           /* This might be noise */
+           if (go->mppe & MPPE_OPT_40) {
+               ppp_notice("Disabling 40-bit MPPE; MS-CHAP LM not supported");
+               go->mppe &= ~MPPE_OPT_40;
+               wo->mppe &= ~MPPE_OPT_40;
+           }
+       }
+
+       /* Last check: can we actually negotiate something? */
+       if (!(go->mppe & (MPPE_OPT_40 | MPPE_OPT_128))) {
+           /* Could be misconfig, could be 40-bit disabled above. */
+           ppp_error("MPPE required, but both 40-bit and 128-bit disabled.");
+           lcp_close(pcb, "MPPE required but not available");
+           return;
+       }
+
+       /* sync options */
+       ao->mppe = go->mppe;
+       /* MPPE is not compatible with other compression types */
+#if BSDCOMPRESS_SUPPORT
+       ao->bsd_compress = go->bsd_compress = 0;
+#endif /* BSDCOMPRESS_SUPPORT */
+#if PREDICTOR_SUPPORT
+       ao->predictor_1  = go->predictor_1  = 0;
+       ao->predictor_2  = go->predictor_2  = 0;
+#endif /* PREDICTOR_SUPPORT */
+#if DEFLATE_SUPPORT
+       ao->deflate      = go->deflate      = 0;
+#endif /* DEFLATE_SUPPORT */
+    }
+#endif /* MPPE_SUPPORT */
+
+    /*
+     * Check whether the kernel knows about the various
+     * compression methods we might request.
+     */
+#if BSDCOMPRESS_SUPPORT
+    /* FIXME: we don't need to test if BSD compress is available
+     * if BSDCOMPRESS_SUPPORT is set, it is.
+     */
+    if (go->bsd_compress) {
+       opt_buf[0] = CI_BSD_COMPRESS;
+       opt_buf[1] = CILEN_BSD_COMPRESS;
+       for (;;) {
+           if (go->bsd_bits < BSD_MIN_BITS) {
+               go->bsd_compress = 0;
+               break;
+           }
+           opt_buf[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits);
+           res = ccp_test(pcb, opt_buf, CILEN_BSD_COMPRESS, 0);
+           if (res > 0) {
+               break;
+           } else if (res < 0) {
+               go->bsd_compress = 0;
+               break;
+           }
+           go->bsd_bits--;
+       }
+    }
+#endif /* BSDCOMPRESS_SUPPORT */
+#if DEFLATE_SUPPORT
+    /* FIXME: we don't need to test if deflate is available
+     * if DEFLATE_SUPPORT is set, it is.
+     */
+    if (go->deflate) {
+       if (go->deflate_correct) {
+           opt_buf[0] = CI_DEFLATE;
+           opt_buf[1] = CILEN_DEFLATE;
+           opt_buf[3] = DEFLATE_CHK_SEQUENCE;
+           for (;;) {
+               if (go->deflate_size < DEFLATE_MIN_WORKS) {
+                   go->deflate_correct = 0;
+                   break;
+               }
+               opt_buf[2] = DEFLATE_MAKE_OPT(go->deflate_size);
+               res = ccp_test(pcb, opt_buf, CILEN_DEFLATE, 0);
+               if (res > 0) {
+                   break;
+               } else if (res < 0) {
+                   go->deflate_correct = 0;
+                   break;
+               }
+               go->deflate_size--;
+           }
+       }
+       if (go->deflate_draft) {
+           opt_buf[0] = CI_DEFLATE_DRAFT;
+           opt_buf[1] = CILEN_DEFLATE;
+           opt_buf[3] = DEFLATE_CHK_SEQUENCE;
+           for (;;) {
+               if (go->deflate_size < DEFLATE_MIN_WORKS) {
+                   go->deflate_draft = 0;
+                   break;
+               }
+               opt_buf[2] = DEFLATE_MAKE_OPT(go->deflate_size);
+               res = ccp_test(pcb, opt_buf, CILEN_DEFLATE, 0);
+               if (res > 0) {
+                   break;
+               } else if (res < 0) {
+                   go->deflate_draft = 0;
+                   break;
+               }
+               go->deflate_size--;
+           }
+       }
+       if (!go->deflate_correct && !go->deflate_draft)
+           go->deflate = 0;
+    }
+#endif /* DEFLATE_SUPPORT */
+#if PREDICTOR_SUPPORT
+    /* FIXME: we don't need to test if predictor is available,
+     * if PREDICTOR_SUPPORT is set, it is.
+     */
+    if (go->predictor_1) {
+       opt_buf[0] = CI_PREDICTOR_1;
+       opt_buf[1] = CILEN_PREDICTOR_1;
+       if (ccp_test(pcb, opt_buf, CILEN_PREDICTOR_1, 0) <= 0)
+           go->predictor_1 = 0;
+    }
+    if (go->predictor_2) {
+       opt_buf[0] = CI_PREDICTOR_2;
+       opt_buf[1] = CILEN_PREDICTOR_2;
+       if (ccp_test(pcb, opt_buf, CILEN_PREDICTOR_2, 0) <= 0)
+           go->predictor_2 = 0;
+    }
+#endif /* PREDICTOR_SUPPORT */
+}
+
+/*
+ * ccp_cilen - Return total length of our configuration info.
+ */
+static int ccp_cilen(fsm *f) {
+    ppp_pcb *pcb = f->pcb;
+    ccp_options *go = &pcb->ccp_gotoptions;
+
+    return 0
+#if BSDCOMPRESS_SUPPORT
+       + (go->bsd_compress? CILEN_BSD_COMPRESS: 0)
+#endif /* BSDCOMPRESS_SUPPORT */
+#if DEFLATE_SUPPORT
+       + (go->deflate && go->deflate_correct? CILEN_DEFLATE: 0)
+       + (go->deflate && go->deflate_draft? CILEN_DEFLATE: 0)
+#endif /* DEFLATE_SUPPORT */
+#if PREDICTOR_SUPPORT
+       + (go->predictor_1? CILEN_PREDICTOR_1: 0)
+       + (go->predictor_2? CILEN_PREDICTOR_2: 0)
+#endif /* PREDICTOR_SUPPORT */
+#if MPPE_SUPPORT
+       + (go->mppe? CILEN_MPPE: 0)
+#endif /* MPPE_SUPPORT */
+       ;
+}
+
+/*
+ * ccp_addci - put our requests in a packet.
+ */
+static void ccp_addci(fsm *f, u_char *p, int *lenp) {
+    ppp_pcb *pcb = f->pcb;
+    ccp_options *go = &pcb->ccp_gotoptions;
+    u_char *p0 = p;
+
+    /*
+     * Add the compression types that we can receive, in decreasing
+     * preference order.
+     */
+#if MPPE_SUPPORT
+    if (go->mppe) {
+       p[0] = CI_MPPE;
+       p[1] = CILEN_MPPE;
+       MPPE_OPTS_TO_CI(go->mppe, &p[2]);
+       mppe_init(pcb, &pcb->mppe_decomp, go->mppe);
+       p += CILEN_MPPE;
+    }
+#endif /* MPPE_SUPPORT */
+#if DEFLATE_SUPPORT
+    if (go->deflate) {
+       if (go->deflate_correct) {
+           p[0] = CI_DEFLATE;
+           p[1] = CILEN_DEFLATE;
+           p[2] = DEFLATE_MAKE_OPT(go->deflate_size);
+           p[3] = DEFLATE_CHK_SEQUENCE;
+           p += CILEN_DEFLATE;
+       }
+       if (go->deflate_draft) {
+           p[0] = CI_DEFLATE_DRAFT;
+           p[1] = CILEN_DEFLATE;
+           p[2] = p[2 - CILEN_DEFLATE];
+           p[3] = DEFLATE_CHK_SEQUENCE;
+           p += CILEN_DEFLATE;
+       }
+    }
+#endif /* DEFLATE_SUPPORT */
+#if BSDCOMPRESS_SUPPORT
+    if (go->bsd_compress) {
+       p[0] = CI_BSD_COMPRESS;
+       p[1] = CILEN_BSD_COMPRESS;
+       p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits);
+       p += CILEN_BSD_COMPRESS;
+    }
+#endif /* BSDCOMPRESS_SUPPORT */
+#if PREDICTOR_SUPPORT
+    /* XXX Should Predictor 2 be preferable to Predictor 1? */
+    if (go->predictor_1) {
+       p[0] = CI_PREDICTOR_1;
+       p[1] = CILEN_PREDICTOR_1;
+       p += CILEN_PREDICTOR_1;
+    }
+    if (go->predictor_2) {
+       p[0] = CI_PREDICTOR_2;
+       p[1] = CILEN_PREDICTOR_2;
+       p += CILEN_PREDICTOR_2;
+    }
+#endif /* PREDICTOR_SUPPORT */
+
+    go->method = (p > p0)? p0[0]: 0;
+
+    *lenp = p - p0;
+}
+
+/*
+ * ccp_ackci - process a received configure-ack, and return
+ * 1 iff the packet was OK.
+ */
+static int ccp_ackci(fsm *f, u_char *p, int len) {
+    ppp_pcb *pcb = f->pcb;
+    ccp_options *go = &pcb->ccp_gotoptions;
+#if BSDCOMPRESS_SUPPORT || PREDICTOR_SUPPORT
+    u_char *p0 = p;
+#endif /* BSDCOMPRESS_SUPPORT || PREDICTOR_SUPPORT */
+
+#if MPPE_SUPPORT
+    if (go->mppe) {
+       u_char opt_buf[CILEN_MPPE];
+
+       opt_buf[0] = CI_MPPE;
+       opt_buf[1] = CILEN_MPPE;
+       MPPE_OPTS_TO_CI(go->mppe, &opt_buf[2]);
+       if (len < CILEN_MPPE || memcmp(opt_buf, p, CILEN_MPPE))
+           return 0;
+       p += CILEN_MPPE;
+       len -= CILEN_MPPE;
+       /* XXX Cope with first/fast ack */
+       if (len == 0)
+           return 1;
+    }
+#endif /* MPPE_SUPPORT */
+#if DEFLATE_SUPPORT
+    if (go->deflate) {
+       if (len < CILEN_DEFLATE
+           || p[0] != (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT)
+           || p[1] != CILEN_DEFLATE
+           || p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+           || p[3] != DEFLATE_CHK_SEQUENCE)
+           return 0;
+       p += CILEN_DEFLATE;
+       len -= CILEN_DEFLATE;
+       /* XXX Cope with first/fast ack */
+       if (len == 0)
+           return 1;
+       if (go->deflate_correct && go->deflate_draft) {
+           if (len < CILEN_DEFLATE
+               || p[0] != CI_DEFLATE_DRAFT
+               || p[1] != CILEN_DEFLATE
+               || p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+               || p[3] != DEFLATE_CHK_SEQUENCE)
+               return 0;
+           p += CILEN_DEFLATE;
+           len -= CILEN_DEFLATE;
+       }
+    }
+#endif /* DEFLATE_SUPPORT */
+#if BSDCOMPRESS_SUPPORT
+    if (go->bsd_compress) {
+       if (len < CILEN_BSD_COMPRESS
+           || p[0] != CI_BSD_COMPRESS || p[1] != CILEN_BSD_COMPRESS
+           || p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits))
+           return 0;
+       p += CILEN_BSD_COMPRESS;
+       len -= CILEN_BSD_COMPRESS;
+       /* XXX Cope with first/fast ack */
+       if (p == p0 && len == 0)
+           return 1;
+    }
+#endif /* BSDCOMPRESS_SUPPORT */
+#if PREDICTOR_SUPPORT
+    if (go->predictor_1) {
+       if (len < CILEN_PREDICTOR_1
+           || p[0] != CI_PREDICTOR_1 || p[1] != CILEN_PREDICTOR_1)
+           return 0;
+       p += CILEN_PREDICTOR_1;
+       len -= CILEN_PREDICTOR_1;
+       /* XXX Cope with first/fast ack */
+       if (p == p0 && len == 0)
+           return 1;
+    }
+    if (go->predictor_2) {
+       if (len < CILEN_PREDICTOR_2
+           || p[0] != CI_PREDICTOR_2 || p[1] != CILEN_PREDICTOR_2)
+           return 0;
+       p += CILEN_PREDICTOR_2;
+       len -= CILEN_PREDICTOR_2;
+       /* XXX Cope with first/fast ack */
+       if (p == p0 && len == 0)
+           return 1;
+    }
+#endif /* PREDICTOR_SUPPORT */
+
+    if (len != 0)
+       return 0;
+    return 1;
+}
+
+/*
+ * ccp_nakci - process received configure-nak.
+ * Returns 1 iff the nak was OK.
+ */
+static int ccp_nakci(fsm *f, u_char *p, int len, int treat_as_reject) {
+    ppp_pcb *pcb = f->pcb;
+    ccp_options *go = &pcb->ccp_gotoptions;
+    ccp_options no;            /* options we've seen already */
+    ccp_options try_;          /* options to ask for next time */
+    LWIP_UNUSED_ARG(treat_as_reject);
+#if !MPPE_SUPPORT && !DEFLATE_SUPPORT && !BSDCOMPRESS_SUPPORT
+    LWIP_UNUSED_ARG(p);
+    LWIP_UNUSED_ARG(len);
+#endif /* !MPPE_SUPPORT && !DEFLATE_SUPPORT && !BSDCOMPRESS_SUPPORT */
+
+    memset(&no, 0, sizeof(no));
+    try_ = *go;
+
+#if MPPE_SUPPORT
+    if (go->mppe && len >= CILEN_MPPE
+       && p[0] == CI_MPPE && p[1] == CILEN_MPPE) {
+       no.mppe = 1;
+       /*
+        * Peer wants us to use a different strength or other setting.
+        * Fail if we aren't willing to use his suggestion.
+        */
+       MPPE_CI_TO_OPTS(&p[2], try_.mppe);
+       if ((try_.mppe & MPPE_OPT_STATEFUL) && pcb->settings.refuse_mppe_stateful) {
+           ppp_error("Refusing MPPE stateful mode offered by peer");
+           try_.mppe = 0;
+       } else if (((go->mppe | MPPE_OPT_STATEFUL) & try_.mppe) != try_.mppe) {
+           /* Peer must have set options we didn't request (suggest) */
+           try_.mppe = 0;
+       }
+
+       if (!try_.mppe) {
+           ppp_error("MPPE required but peer negotiation failed");
+           lcp_close(pcb, "MPPE required but peer negotiation failed");
+       }
+    }
+#endif /* MPPE_SUPPORT */
+#if DEFLATE_SUPPORT
+    if (go->deflate && len >= CILEN_DEFLATE
+       && p[0] == (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT)
+       && p[1] == CILEN_DEFLATE) {
+       no.deflate = 1;
+       /*
+        * Peer wants us to use a different code size or something.
+        * Stop asking for Deflate if we don't understand his suggestion.
+        */
+       if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL
+           || DEFLATE_SIZE(p[2]) < DEFLATE_MIN_WORKS
+           || p[3] != DEFLATE_CHK_SEQUENCE)
+           try_.deflate = 0;
+       else if (DEFLATE_SIZE(p[2]) < go->deflate_size)
+           try_.deflate_size = DEFLATE_SIZE(p[2]);
+       p += CILEN_DEFLATE;
+       len -= CILEN_DEFLATE;
+       if (go->deflate_correct && go->deflate_draft
+           && len >= CILEN_DEFLATE && p[0] == CI_DEFLATE_DRAFT
+           && p[1] == CILEN_DEFLATE) {
+           p += CILEN_DEFLATE;
+           len -= CILEN_DEFLATE;
+       }
+    }
+#endif /* DEFLATE_SUPPORT */
+#if BSDCOMPRESS_SUPPORT
+    if (go->bsd_compress && len >= CILEN_BSD_COMPRESS
+       && p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) {
+       no.bsd_compress = 1;
+       /*
+        * Peer wants us to use a different number of bits
+        * or a different version.
+        */
+       if (BSD_VERSION(p[2]) != BSD_CURRENT_VERSION)
+           try_.bsd_compress = 0;
+       else if (BSD_NBITS(p[2]) < go->bsd_bits)
+           try_.bsd_bits = BSD_NBITS(p[2]);
+       p += CILEN_BSD_COMPRESS;
+       len -= CILEN_BSD_COMPRESS;
+    }
+#endif /* BSDCOMPRESS_SUPPORT */
+
+    /*
+     * Predictor-1 and 2 have no options, so they can't be Naked.
+     *
+     * There may be remaining options but we ignore them.
+     */
+
+    if (f->state != PPP_FSM_OPENED)
+       *go = try_;
+    return 1;
+}
+
+/*
+ * ccp_rejci - reject some of our suggested compression methods.
+ */
+static int ccp_rejci(fsm *f, u_char *p, int len) {
+    ppp_pcb *pcb = f->pcb;
+    ccp_options *go = &pcb->ccp_gotoptions;
+    ccp_options try_;          /* options to request next time */
+
+    try_ = *go;
+
+    /*
+     * Cope with empty configure-rejects by ceasing to send
+     * configure-requests.
+     */
+    if (len == 0 && pcb->ccp_all_rejected)
+       return -1;
+
+#if MPPE_SUPPORT
+    if (go->mppe && len >= CILEN_MPPE
+       && p[0] == CI_MPPE && p[1] == CILEN_MPPE) {
+       ppp_error("MPPE required but peer refused");
+       lcp_close(pcb, "MPPE required but peer refused");
+       p += CILEN_MPPE;
+       len -= CILEN_MPPE;
+    }
+#endif /* MPPE_SUPPORT */
+#if DEFLATE_SUPPORT
+    if (go->deflate_correct && len >= CILEN_DEFLATE
+       && p[0] == CI_DEFLATE && p[1] == CILEN_DEFLATE) {
+       if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+           || p[3] != DEFLATE_CHK_SEQUENCE)
+           return 0;           /* Rej is bad */
+       try_.deflate_correct = 0;
+       p += CILEN_DEFLATE;
+       len -= CILEN_DEFLATE;
+    }
+    if (go->deflate_draft && len >= CILEN_DEFLATE
+       && p[0] == CI_DEFLATE_DRAFT && p[1] == CILEN_DEFLATE) {
+       if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+           || p[3] != DEFLATE_CHK_SEQUENCE)
+           return 0;           /* Rej is bad */
+       try_.deflate_draft = 0;
+       p += CILEN_DEFLATE;
+       len -= CILEN_DEFLATE;
+    }
+    if (!try_.deflate_correct && !try_.deflate_draft)
+       try_.deflate = 0;
+#endif /* DEFLATE_SUPPORT */
+#if BSDCOMPRESS_SUPPORT
+    if (go->bsd_compress && len >= CILEN_BSD_COMPRESS
+       && p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) {
+       if (p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits))
+           return 0;
+       try_.bsd_compress = 0;
+       p += CILEN_BSD_COMPRESS;
+       len -= CILEN_BSD_COMPRESS;
+    }
+#endif /* BSDCOMPRESS_SUPPORT */
+#if PREDICTOR_SUPPORT
+    if (go->predictor_1 && len >= CILEN_PREDICTOR_1
+       && p[0] == CI_PREDICTOR_1 && p[1] == CILEN_PREDICTOR_1) {
+       try_.predictor_1 = 0;
+       p += CILEN_PREDICTOR_1;
+       len -= CILEN_PREDICTOR_1;
+    }
+    if (go->predictor_2 && len >= CILEN_PREDICTOR_2
+       && p[0] == CI_PREDICTOR_2 && p[1] == CILEN_PREDICTOR_2) {
+       try_.predictor_2 = 0;
+       p += CILEN_PREDICTOR_2;
+       len -= CILEN_PREDICTOR_2;
+    }
+#endif /* PREDICTOR_SUPPORT */
+
+    if (len != 0)
+       return 0;
+
+    if (f->state != PPP_FSM_OPENED)
+       *go = try_;
+
+    return 1;
+}
+
+/*
+ * ccp_reqci - processed a received configure-request.
+ * Returns CONFACK, CONFNAK or CONFREJ and the packet modified
+ * appropriately.
+ */
+static int ccp_reqci(fsm *f, u_char *p, int *lenp, int dont_nak) {
+    ppp_pcb *pcb = f->pcb;
+    ccp_options *ho = &pcb->ccp_hisoptions;
+    ccp_options *ao = &pcb->ccp_allowoptions;
+    int ret, newret;
+#if DEFLATE_SUPPORT || BSDCOMPRESS_SUPPORT
+    int res;
+    int nb;
+#endif /* DEFLATE_SUPPORT || BSDCOMPRESS_SUPPORT */
+    u_char *p0, *retp;
+    int len, clen, type;
+#if MPPE_SUPPORT
+    u8_t rej_for_ci_mppe = 1;  /* Are we rejecting based on a bad/missing */
+                               /* CI_MPPE, or due to other options?       */
+#endif /* MPPE_SUPPORT */
+
+    ret = CONFACK;
+    retp = p0 = p;
+    len = *lenp;
+
+    memset(ho, 0, sizeof(ccp_options));
+    ho->method = (len > 0)? p[0]: 0;
+
+    while (len > 0) {
+       newret = CONFACK;
+       if (len < 2 || p[1] < 2 || p[1] > len) {
+           /* length is bad */
+           clen = len;
+           newret = CONFREJ;
+
+       } else {
+           type = p[0];
+           clen = p[1];
+
+           switch (type) {
+#if MPPE_SUPPORT
+           case CI_MPPE:
+               if (!ao->mppe || clen != CILEN_MPPE) {
+                   newret = CONFREJ;
+                   break;
+               }
+               MPPE_CI_TO_OPTS(&p[2], ho->mppe);
+
+               /* Nak if anything unsupported or unknown are set. */
+               if (ho->mppe & MPPE_OPT_UNSUPPORTED) {
+                   newret = CONFNAK;
+                   ho->mppe &= ~MPPE_OPT_UNSUPPORTED;
+               }
+               if (ho->mppe & MPPE_OPT_UNKNOWN) {
+                   newret = CONFNAK;
+                   ho->mppe &= ~MPPE_OPT_UNKNOWN;
+               }
+
+               /* Check state opt */
+               if (ho->mppe & MPPE_OPT_STATEFUL) {
+                   /*
+                    * We can Nak and request stateless, but it's a
+                    * lot easier to just assume the peer will request
+                    * it if he can do it; stateful mode is bad over
+                    * the Internet -- which is where we expect MPPE.
+                    */
+                  if (pcb->settings.refuse_mppe_stateful) {
+                       ppp_error("Refusing MPPE stateful mode offered by peer");
+                       newret = CONFREJ;
+                       break;
+                   }
+               }
+
+               /* Find out which of {S,L} are set. */
+               if ((ho->mppe & MPPE_OPT_128)
+                    && (ho->mppe & MPPE_OPT_40)) {
+                   /* Both are set, negotiate the strongest. */
+                   newret = CONFNAK;
+                   if (ao->mppe & MPPE_OPT_128)
+                       ho->mppe &= ~MPPE_OPT_40;
+                   else if (ao->mppe & MPPE_OPT_40)
+                       ho->mppe &= ~MPPE_OPT_128;
+                   else {
+                       newret = CONFREJ;
+                       break;
+                   }
+               } else if (ho->mppe & MPPE_OPT_128) {
+                   if (!(ao->mppe & MPPE_OPT_128)) {
+                       newret = CONFREJ;
+                       break;
+                   }
+               } else if (ho->mppe & MPPE_OPT_40) {
+                   if (!(ao->mppe & MPPE_OPT_40)) {
+                       newret = CONFREJ;
+                       break;
+                   }
+               } else {
+                   /* Neither are set. */
+                   /* We cannot accept this.  */
+                   newret = CONFNAK;
+                   /* Give the peer our idea of what can be used,
+                      so it can choose and confirm */
+                   ho->mppe = ao->mppe;
+               }
+
+               /* rebuild the opts */
+               MPPE_OPTS_TO_CI(ho->mppe, &p[2]);
+               if (newret == CONFACK) {
+                   int mtu;
+
+                   mppe_init(pcb, &pcb->mppe_comp, ho->mppe);
+                   /*
+                    * We need to decrease the interface MTU by MPPE_PAD
+                    * because MPPE frames **grow**.  The kernel [must]
+                    * allocate MPPE_PAD extra bytes in xmit buffers.
+                    */
+                   mtu = netif_get_mtu(pcb);
+                   if (mtu)
+                       netif_set_mtu(pcb, mtu - MPPE_PAD);
+                   else
+                       newret = CONFREJ;
+               }
+
+               /*
+                * We have accepted MPPE or are willing to negotiate
+                * MPPE parameters.  A CONFREJ is due to subsequent
+                * (non-MPPE) processing.
+                */
+               rej_for_ci_mppe = 0;
+               break;
+#endif /* MPPE_SUPPORT */
+#if DEFLATE_SUPPORT
+           case CI_DEFLATE:
+           case CI_DEFLATE_DRAFT:
+               if (!ao->deflate || clen != CILEN_DEFLATE
+                   || (!ao->deflate_correct && type == CI_DEFLATE)
+                   || (!ao->deflate_draft && type == CI_DEFLATE_DRAFT)) {
+                   newret = CONFREJ;
+                   break;
+               }
+
+               ho->deflate = 1;
+               ho->deflate_size = nb = DEFLATE_SIZE(p[2]);
+               if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL
+                   || p[3] != DEFLATE_CHK_SEQUENCE
+                   || nb > ao->deflate_size || nb < DEFLATE_MIN_WORKS) {
+                   newret = CONFNAK;
+                   if (!dont_nak) {
+                       p[2] = DEFLATE_MAKE_OPT(ao->deflate_size);
+                       p[3] = DEFLATE_CHK_SEQUENCE;
+                       /* fall through to test this #bits below */
+                   } else
+                       break;
+               }
+
+               /*
+                * Check whether we can do Deflate with the window
+                * size they want.  If the window is too big, reduce
+                * it until the kernel can cope and nak with that.
+                * We only check this for the first option.
+                */
+               if (p == p0) {
+                   for (;;) {
+                       res = ccp_test(pcb, p, CILEN_DEFLATE, 1);
+                       if (res > 0)
+                           break;              /* it's OK now */
+                       if (res < 0 || nb == DEFLATE_MIN_WORKS || dont_nak) {
+                           newret = CONFREJ;
+                           p[2] = DEFLATE_MAKE_OPT(ho->deflate_size);
+                           break;
+                       }
+                       newret = CONFNAK;
+                       --nb;
+                       p[2] = DEFLATE_MAKE_OPT(nb);
+                   }
+               }
+               break;
+#endif /* DEFLATE_SUPPORT */
+#if BSDCOMPRESS_SUPPORT
+           case CI_BSD_COMPRESS:
+               if (!ao->bsd_compress || clen != CILEN_BSD_COMPRESS) {
+                   newret = CONFREJ;
+                   break;
+               }
+
+               ho->bsd_compress = 1;
+               ho->bsd_bits = nb = BSD_NBITS(p[2]);
+               if (BSD_VERSION(p[2]) != BSD_CURRENT_VERSION
+                   || nb > ao->bsd_bits || nb < BSD_MIN_BITS) {
+                   newret = CONFNAK;
+                   if (!dont_nak) {
+                       p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, ao->bsd_bits);
+                       /* fall through to test this #bits below */
+                   } else
+                       break;
+               }
+
+               /*
+                * Check whether we can do BSD-Compress with the code
+                * size they want.  If the code size is too big, reduce
+                * it until the kernel can cope and nak with that.
+                * We only check this for the first option.
+                */
+               if (p == p0) {
+                   for (;;) {
+                       res = ccp_test(pcb, p, CILEN_BSD_COMPRESS, 1);
+                       if (res > 0)
+                           break;
+                       if (res < 0 || nb == BSD_MIN_BITS || dont_nak) {
+                           newret = CONFREJ;
+                           p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION,
+                                               ho->bsd_bits);
+                           break;
+                       }
+                       newret = CONFNAK;
+                       --nb;
+                       p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, nb);
+                   }
+               }
+               break;
+#endif /* BSDCOMPRESS_SUPPORT */
+#if PREDICTOR_SUPPORT
+           case CI_PREDICTOR_1:
+               if (!ao->predictor_1 || clen != CILEN_PREDICTOR_1) {
+                   newret = CONFREJ;
+                   break;
+               }
+
+               ho->predictor_1 = 1;
+               if (p == p0
+                   && ccp_test(pcb, p, CILEN_PREDICTOR_1, 1) <= 0) {
+                   newret = CONFREJ;
+               }
+               break;
+
+           case CI_PREDICTOR_2:
+               if (!ao->predictor_2 || clen != CILEN_PREDICTOR_2) {
+                   newret = CONFREJ;
+                   break;
+               }
+
+               ho->predictor_2 = 1;
+               if (p == p0
+                   && ccp_test(pcb, p, CILEN_PREDICTOR_2, 1) <= 0) {
+                   newret = CONFREJ;
+               }
+               break;
+#endif /* PREDICTOR_SUPPORT */
+
+           default:
+               newret = CONFREJ;
+           }
+       }
+
+       if (newret == CONFNAK && dont_nak)
+           newret = CONFREJ;
+       if (!(newret == CONFACK || (newret == CONFNAK && ret == CONFREJ))) {
+           /* we're returning this option */
+           if (newret == CONFREJ && ret == CONFNAK)
+               retp = p0;
+           ret = newret;
+           if (p != retp)
+               MEMCPY(retp, p, clen);
+           retp += clen;
+       }
+
+       p += clen;
+       len -= clen;
+    }
+
+    if (ret != CONFACK) {
+       if (ret == CONFREJ && *lenp == retp - p0)
+           pcb->ccp_all_rejected = 1;
+       else
+           *lenp = retp - p0;
+    }
+#if MPPE_SUPPORT
+    if (ret == CONFREJ && ao->mppe && rej_for_ci_mppe) {
+       ppp_error("MPPE required but peer negotiation failed");
+       lcp_close(pcb, "MPPE required but peer negotiation failed");
+    }
+#endif /* MPPE_SUPPORT */
+    return ret;
+}
+
+/*
+ * Make a string name for a compression method (or 2).
+ */
+static const char *method_name(ccp_options *opt, ccp_options *opt2) {
+    static char result[64];
+#if !DEFLATE_SUPPORT && !BSDCOMPRESS_SUPPORT
+    LWIP_UNUSED_ARG(opt2);
+#endif /* !DEFLATE_SUPPORT && !BSDCOMPRESS_SUPPORT */
+
+    if (!ccp_anycompress(opt))
+       return "(none)";
+    switch (opt->method) {
+#if MPPE_SUPPORT
+    case CI_MPPE:
+    {
+       char *p = result;
+       char *q = result + sizeof(result); /* 1 past result */
+
+       ppp_slprintf(p, q - p, "MPPE ");
+       p += 5;
+       if (opt->mppe & MPPE_OPT_128) {
+           ppp_slprintf(p, q - p, "128-bit ");
+           p += 8;
+       }
+       if (opt->mppe & MPPE_OPT_40) {
+           ppp_slprintf(p, q - p, "40-bit ");
+           p += 7;
+       }
+       if (opt->mppe & MPPE_OPT_STATEFUL)
+           ppp_slprintf(p, q - p, "stateful");
+       else
+           ppp_slprintf(p, q - p, "stateless");
+
+       break;
+    }
+#endif /* MPPE_SUPPORT */
+#if DEFLATE_SUPPORT
+    case CI_DEFLATE:
+    case CI_DEFLATE_DRAFT:
+       if (opt2 != NULL && opt2->deflate_size != opt->deflate_size)
+           ppp_slprintf(result, sizeof(result), "Deflate%s (%d/%d)",
+                    (opt->method == CI_DEFLATE_DRAFT? "(old#)": ""),
+                    opt->deflate_size, opt2->deflate_size);
+       else
+           ppp_slprintf(result, sizeof(result), "Deflate%s (%d)",
+                    (opt->method == CI_DEFLATE_DRAFT? "(old#)": ""),
+                    opt->deflate_size);
+       break;
+#endif /* DEFLATE_SUPPORT */
+#if BSDCOMPRESS_SUPPORT
+    case CI_BSD_COMPRESS:
+       if (opt2 != NULL && opt2->bsd_bits != opt->bsd_bits)
+           ppp_slprintf(result, sizeof(result), "BSD-Compress (%d/%d)",
+                    opt->bsd_bits, opt2->bsd_bits);
+       else
+           ppp_slprintf(result, sizeof(result), "BSD-Compress (%d)",
+                    opt->bsd_bits);
+       break;
+#endif /* BSDCOMPRESS_SUPPORT */
+#if PREDICTOR_SUPPORT
+    case CI_PREDICTOR_1:
+       return "Predictor 1";
+    case CI_PREDICTOR_2:
+       return "Predictor 2";
+#endif /* PREDICTOR_SUPPORT */
+    default:
+       ppp_slprintf(result, sizeof(result), "Method %d", opt->method);
+    }
+    return result;
+}
+
+/*
+ * CCP has come up - inform the kernel driver and log a message.
+ */
+static void ccp_up(fsm *f) {
+    ppp_pcb *pcb = f->pcb;
+    ccp_options *go = &pcb->ccp_gotoptions;
+    ccp_options *ho = &pcb->ccp_hisoptions;
+    char method1[64];
+
+    ccp_set(pcb, 1, 1, go->method, ho->method);
+    if (ccp_anycompress(go)) {
+       if (ccp_anycompress(ho)) {
+           if (go->method == ho->method) {
+               ppp_notice("%s compression enabled", method_name(go, ho));
+           } else {
+               ppp_strlcpy(method1, method_name(go, NULL), sizeof(method1));
+               ppp_notice("%s / %s compression enabled",
+                      method1, method_name(ho, NULL));
+           }
+       } else
+           ppp_notice("%s receive compression enabled", method_name(go, NULL));
+    } else if (ccp_anycompress(ho))
+       ppp_notice("%s transmit compression enabled", method_name(ho, NULL));
+#if MPPE_SUPPORT
+    if (go->mppe) {
+       continue_networks(pcb);         /* Bring up IP et al */
+    }
+#endif /* MPPE_SUPPORT */
+}
+
+/*
+ * CCP has gone down - inform the kernel driver.
+ */
+static void ccp_down(fsm *f) {
+    ppp_pcb *pcb = f->pcb;
+#if MPPE_SUPPORT
+    ccp_options *go = &pcb->ccp_gotoptions;
+#endif /* MPPE_SUPPORT */
+
+    if (pcb->ccp_localstate & RACK_PENDING)
+       UNTIMEOUT(ccp_rack_timeout, f);
+    pcb->ccp_localstate = 0;
+    ccp_set(pcb, 1, 0, 0, 0);
+#if MPPE_SUPPORT
+    if (go->mppe) {
+       go->mppe = 0;
+       if (pcb->lcp_fsm.state == PPP_FSM_OPENED) {
+           /* If LCP is not already going down, make sure it does. */
+           ppp_error("MPPE disabled");
+           lcp_close(pcb, "MPPE disabled");
+       }
+    }
+#endif /* MPPE_SUPPORT */
+}
+
+#if PRINTPKT_SUPPORT
+/*
+ * Print the contents of a CCP packet.
+ */
+static const char* const ccp_codenames[] = {
+    "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+    "TermReq", "TermAck", "CodeRej",
+    NULL, NULL, NULL, NULL, NULL, NULL,
+    "ResetReq", "ResetAck",
+};
+
+static int ccp_printpkt(const u_char *p, int plen, void (*printer) (void *, const char *, ...), void *arg) {
+    const u_char *p0, *optend;
+    int code, id, len;
+    int optlen;
+
+    p0 = p;
+    if (plen < HEADERLEN)
+       return 0;
+    code = p[0];
+    id = p[1];
+    len = (p[2] << 8) + p[3];
+    if (len < HEADERLEN || len > plen)
+       return 0;
+
+    if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(ccp_codenames) && ccp_codenames[code-1] != NULL)
+       printer(arg, " %s", ccp_codenames[code-1]);
+    else
+       printer(arg, " code=0x%x", code);
+    printer(arg, " id=0x%x", id);
+    len -= HEADERLEN;
+    p += HEADERLEN;
+
+    switch (code) {
+    case CONFREQ:
+    case CONFACK:
+    case CONFNAK:
+    case CONFREJ:
+       /* print list of possible compression methods */
+       while (len >= 2) {
+           code = p[0];
+           optlen = p[1];
+           if (optlen < 2 || optlen > len)
+               break;
+           printer(arg, " <");
+           len -= optlen;
+           optend = p + optlen;
+           switch (code) {
+#if MPPE_SUPPORT
+           case CI_MPPE:
+               if (optlen >= CILEN_MPPE) {
+                   u_char mppe_opts;
+
+                   MPPE_CI_TO_OPTS(&p[2], mppe_opts);
+                   printer(arg, "mppe %s %s %s %s %s %s%s",
+                           (p[2] & MPPE_H_BIT)? "+H": "-H",
+                           (p[5] & MPPE_M_BIT)? "+M": "-M",
+                           (p[5] & MPPE_S_BIT)? "+S": "-S",
+                           (p[5] & MPPE_L_BIT)? "+L": "-L",
+                           (p[5] & MPPE_D_BIT)? "+D": "-D",
+                           (p[5] & MPPE_C_BIT)? "+C": "-C",
+                           (mppe_opts & MPPE_OPT_UNKNOWN)? " +U": "");
+                   if (mppe_opts & MPPE_OPT_UNKNOWN)
+                       printer(arg, " (%.2x %.2x %.2x %.2x)",
+                               p[2], p[3], p[4], p[5]);
+                   p += CILEN_MPPE;
+               }
+               break;
+#endif /* MPPE_SUPPORT */
+#if DEFLATE_SUPPORT
+           case CI_DEFLATE:
+           case CI_DEFLATE_DRAFT:
+               if (optlen >= CILEN_DEFLATE) {
+                   printer(arg, "deflate%s %d",
+                           (code == CI_DEFLATE_DRAFT? "(old#)": ""),
+                           DEFLATE_SIZE(p[2]));
+                   if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL)
+                       printer(arg, " method %d", DEFLATE_METHOD(p[2]));
+                   if (p[3] != DEFLATE_CHK_SEQUENCE)
+                       printer(arg, " check %d", p[3]);
+                   p += CILEN_DEFLATE;
+               }
+               break;
+#endif /* DEFLATE_SUPPORT */
+#if BSDCOMPRESS_SUPPORT
+           case CI_BSD_COMPRESS:
+               if (optlen >= CILEN_BSD_COMPRESS) {
+                   printer(arg, "bsd v%d %d", BSD_VERSION(p[2]),
+                           BSD_NBITS(p[2]));
+                   p += CILEN_BSD_COMPRESS;
+               }
+               break;
+#endif /* BSDCOMPRESS_SUPPORT */
+#if PREDICTOR_SUPPORT
+           case CI_PREDICTOR_1:
+               if (optlen >= CILEN_PREDICTOR_1) {
+                   printer(arg, "predictor 1");
+                   p += CILEN_PREDICTOR_1;
+               }
+               break;
+           case CI_PREDICTOR_2:
+               if (optlen >= CILEN_PREDICTOR_2) {
+                   printer(arg, "predictor 2");
+                   p += CILEN_PREDICTOR_2;
+               }
+               break;
+#endif /* PREDICTOR_SUPPORT */
+           default:
+                break;
+           }
+           while (p < optend)
+               printer(arg, " %.2x", *p++);
+           printer(arg, ">");
+       }
+       break;
+
+    case TERMACK:
+    case TERMREQ:
+       if (len > 0 && *p >= ' ' && *p < 0x7f) {
+           ppp_print_string(p, len, printer, arg);
+           p += len;
+           len = 0;
+       }
+       break;
+    default:
+        break;
+    }
+
+    /* dump out the rest of the packet in hex */
+    while (--len >= 0)
+       printer(arg, " %.2x", *p++);
+
+    return p - p0;
+}
+#endif /* PRINTPKT_SUPPORT */
+
+#if PPP_DATAINPUT
+/*
+ * We have received a packet that the decompressor failed to
+ * decompress.  Here we would expect to issue a reset-request, but
+ * Motorola has a patent on resetting the compressor as a result of
+ * detecting an error in the decompressed data after decompression.
+ * (See US patent 5,130,993; international patent publication number
+ * WO 91/10289; Australian patent 73296/91.)
+ *
+ * So we ask the kernel whether the error was detected after
+ * decompression; if it was, we take CCP down, thus disabling
+ * compression :-(, otherwise we issue the reset-request.
+ */
+static void ccp_datainput(ppp_pcb *pcb, u_char *pkt, int len) {
+    fsm *f;
+#if MPPE_SUPPORT
+    ccp_options *go = &pcb->ccp_gotoptions;
+#endif /* MPPE_SUPPORT */
+    LWIP_UNUSED_ARG(pkt);
+    LWIP_UNUSED_ARG(len);
+
+    f = &pcb->ccp_fsm;
+    if (f->state == PPP_FSM_OPENED) {
+       if (ccp_fatal_error(pcb)) {
+           /*
+            * Disable compression by taking CCP down.
+            */
+           ppp_error("Lost compression sync: disabling compression");
+           ccp_close(pcb, "Lost compression sync");
+#if MPPE_SUPPORT
+           /*
+            * If we were doing MPPE, we must also take the link down.
+            */
+           if (go->mppe) {
+               ppp_error("Too many MPPE errors, closing LCP");
+               lcp_close(pcb, "Too many MPPE errors");
+           }
+#endif /* MPPE_SUPPORT */
+       } else {
+           /*
+            * Send a reset-request to reset the peer's compressor.
+            * We don't do that if we are still waiting for an
+            * acknowledgement to a previous reset-request.
+            */
+           if (!(pcb->ccp_localstate & RACK_PENDING)) {
+               fsm_sdata(f, CCP_RESETREQ, f->reqid = ++f->id, NULL, 0);
+               TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT);
+               pcb->ccp_localstate |= RACK_PENDING;
+           } else
+               pcb->ccp_localstate |= RREQ_REPEAT;
+       }
+    }
+}
+#endif /* PPP_DATAINPUT */
+
+/*
+ * We have received a packet that the decompressor failed to
+ * decompress. Issue a reset-request.
+ */
+void ccp_resetrequest(ppp_pcb *pcb) {
+    fsm *f = &pcb->ccp_fsm;
+
+    if (f->state != PPP_FSM_OPENED)
+       return;
+
+    /*
+     * Send a reset-request to reset the peer's compressor.
+     * We don't do that if we are still waiting for an
+     * acknowledgement to a previous reset-request.
+     */
+    if (!(pcb->ccp_localstate & RACK_PENDING)) {
+       fsm_sdata(f, CCP_RESETREQ, f->reqid = ++f->id, NULL, 0);
+       TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT);
+       pcb->ccp_localstate |= RACK_PENDING;
+    } else
+       pcb->ccp_localstate |= RREQ_REPEAT;
+}
+
+/*
+ * Timeout waiting for reset-ack.
+ */
+static void ccp_rack_timeout(void *arg) {
+    fsm *f = (fsm*)arg;
+    ppp_pcb *pcb = f->pcb;
+
+    if (f->state == PPP_FSM_OPENED && (pcb->ccp_localstate & RREQ_REPEAT)) {
+       fsm_sdata(f, CCP_RESETREQ, f->reqid, NULL, 0);
+       TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT);
+       pcb->ccp_localstate &= ~RREQ_REPEAT;
+    } else
+       pcb->ccp_localstate &= ~RACK_PENDING;
+}
+
+#endif /* PPP_SUPPORT && CCP_SUPPORT */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/chap-md5.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/chap-md5.c
new file mode 100644 (file)
index 0000000..88f069f
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * chap-md5.c - New CHAP/MD5 implementation.
+ *
+ * Copyright (c) 2003 Paul Mackerras. All rights reserved.
+ *
+ * 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. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && CHAP_SUPPORT  /* don't build if not configured for use in lwipopts.h */
+
+#if 0 /* UNUSED */
+#include <stdlib.h>
+#include <string.h>
+#endif /* UNUSED */
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/chap-new.h"
+#include "netif/ppp/chap-md5.h"
+#include "netif/ppp/magic.h"
+#include "netif/ppp/pppcrypt.h"
+
+#define MD5_HASH_SIZE          16
+#define MD5_MIN_CHALLENGE      17
+#define MD5_MAX_CHALLENGE      24
+#define MD5_MIN_MAX_POWER_OF_TWO_CHALLENGE     3   /* 2^3-1 = 7, 17+7 = 24 */
+
+#if PPP_SERVER
+static void chap_md5_generate_challenge(ppp_pcb *pcb, unsigned char *cp) {
+       int clen;
+       LWIP_UNUSED_ARG(pcb);
+
+       clen = MD5_MIN_CHALLENGE + magic_pow(MD5_MIN_MAX_POWER_OF_TWO_CHALLENGE);
+       *cp++ = clen;
+       magic_random_bytes(cp, clen);
+}
+
+static int chap_md5_verify_response(ppp_pcb *pcb, int id, const char *name,
+                        const unsigned char *secret, int secret_len,
+                        const unsigned char *challenge, const unsigned char *response,
+                        char *message, int message_space) {
+       lwip_md5_context ctx;
+       unsigned char idbyte = id;
+       unsigned char hash[MD5_HASH_SIZE];
+       int challenge_len, response_len;
+       LWIP_UNUSED_ARG(name);
+       LWIP_UNUSED_ARG(pcb);
+
+       challenge_len = *challenge++;
+       response_len = *response++;
+       if (response_len == MD5_HASH_SIZE) {
+               /* Generate hash of ID, secret, challenge */
+               lwip_md5_init(&ctx);
+               lwip_md5_starts(&ctx);
+               lwip_md5_update(&ctx, &idbyte, 1);
+               lwip_md5_update(&ctx, secret, secret_len);
+               lwip_md5_update(&ctx, challenge, challenge_len);
+               lwip_md5_finish(&ctx, hash);
+               lwip_md5_free(&ctx);
+
+               /* Test if our hash matches the peer's response */
+               if (memcmp(hash, response, MD5_HASH_SIZE) == 0) {
+                       ppp_slprintf(message, message_space, "Access granted");
+                       return 1;
+               }
+       }
+       ppp_slprintf(message, message_space, "Access denied");
+       return 0;
+}
+#endif /* PPP_SERVER */
+
+static void chap_md5_make_response(ppp_pcb *pcb, unsigned char *response, int id, const char *our_name,
+                      const unsigned char *challenge, const char *secret, int secret_len,
+                      unsigned char *private_) {
+       lwip_md5_context ctx;
+       unsigned char idbyte = id;
+       int challenge_len = *challenge++;
+       LWIP_UNUSED_ARG(our_name);
+       LWIP_UNUSED_ARG(private_);
+       LWIP_UNUSED_ARG(pcb);
+
+       lwip_md5_init(&ctx);
+       lwip_md5_starts(&ctx);
+       lwip_md5_update(&ctx, &idbyte, 1);
+       lwip_md5_update(&ctx, (const u_char *)secret, secret_len);
+       lwip_md5_update(&ctx, challenge, challenge_len);
+       lwip_md5_finish(&ctx, &response[1]);
+       lwip_md5_free(&ctx);
+       response[0] = MD5_HASH_SIZE;
+}
+
+const struct chap_digest_type md5_digest = {
+       CHAP_MD5,               /* code */
+#if PPP_SERVER
+       chap_md5_generate_challenge,
+       chap_md5_verify_response,
+#endif /* PPP_SERVER */
+       chap_md5_make_response,
+       NULL,                   /* check_success */
+       NULL,                   /* handle_failure */
+};
+
+#endif /* PPP_SUPPORT && CHAP_SUPPORT */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/chap-new.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/chap-new.c
new file mode 100644 (file)
index 0000000..485122d
--- /dev/null
@@ -0,0 +1,677 @@
+/*
+ * chap-new.c - New CHAP implementation.
+ *
+ * Copyright (c) 2003 Paul Mackerras. All rights reserved.
+ *
+ * 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. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && CHAP_SUPPORT  /* don't build if not configured for use in lwipopts.h */
+
+#if 0 /* UNUSED */
+#include <stdlib.h>
+#include <string.h>
+#endif /* UNUSED */
+
+#include "netif/ppp/ppp_impl.h"
+
+#if 0 /* UNUSED */
+#include "session.h"
+#endif /* UNUSED */
+
+#include "netif/ppp/chap-new.h"
+#include "netif/ppp/chap-md5.h"
+#if MSCHAP_SUPPORT
+#include "netif/ppp/chap_ms.h"
+#endif
+#include "netif/ppp/magic.h"
+
+#if 0 /* UNUSED */
+/* Hook for a plugin to validate CHAP challenge */
+int (*chap_verify_hook)(const char *name, const char *ourname, int id,
+                       const struct chap_digest_type *digest,
+                       const unsigned char *challenge, const unsigned char *response,
+                       char *message, int message_space) = NULL;
+#endif /* UNUSED */
+
+#if PPP_OPTIONS
+/*
+ * Command-line options.
+ */
+static option_t chap_option_list[] = {
+       { "chap-restart", o_int, &chap_timeout_time,
+         "Set timeout for CHAP", OPT_PRIO },
+       { "chap-max-challenge", o_int, &pcb->settings.chap_max_transmits,
+         "Set max #xmits for challenge", OPT_PRIO },
+       { "chap-interval", o_int, &pcb->settings.chap_rechallenge_time,
+         "Set interval for rechallenge", OPT_PRIO },
+       { NULL }
+};
+#endif /* PPP_OPTIONS */
+
+
+/* Values for flags in chap_client_state and chap_server_state */
+#define LOWERUP                        1
+#define AUTH_STARTED           2
+#define AUTH_DONE              4
+#define AUTH_FAILED            8
+#define TIMEOUT_PENDING                0x10
+#define CHALLENGE_VALID                0x20
+
+/*
+ * Prototypes.
+ */
+static void chap_init(ppp_pcb *pcb);
+static void chap_lowerup(ppp_pcb *pcb);
+static void chap_lowerdown(ppp_pcb *pcb);
+#if PPP_SERVER
+static void chap_timeout(void *arg);
+static void chap_generate_challenge(ppp_pcb *pcb);
+static void chap_handle_response(ppp_pcb *pcb, int code,
+               unsigned char *pkt, int len);
+static int chap_verify_response(ppp_pcb *pcb, const char *name, const char *ourname, int id,
+               const struct chap_digest_type *digest,
+               const unsigned char *challenge, const unsigned char *response,
+               char *message, int message_space);
+#endif /* PPP_SERVER */
+static void chap_respond(ppp_pcb *pcb, int id,
+               unsigned char *pkt, int len);
+static void chap_handle_status(ppp_pcb *pcb, int code, int id,
+               unsigned char *pkt, int len);
+static void chap_protrej(ppp_pcb *pcb);
+static void chap_input(ppp_pcb *pcb, unsigned char *pkt, int pktlen);
+#if PRINTPKT_SUPPORT
+static int chap_print_pkt(const unsigned char *p, int plen,
+               void (*printer) (void *, const char *, ...), void *arg);
+#endif /* PRINTPKT_SUPPORT */
+
+/* List of digest types that we know about */
+static const struct chap_digest_type* const chap_digests[] = {
+    &md5_digest,
+#if MSCHAP_SUPPORT
+    &chapms_digest,
+    &chapms2_digest,
+#endif /* MSCHAP_SUPPORT */
+    NULL
+};
+
+/*
+ * chap_init - reset to initial state.
+ */
+static void chap_init(ppp_pcb *pcb) {
+       LWIP_UNUSED_ARG(pcb);
+
+#if 0 /* Not necessary, everything is cleared in ppp_new() */
+       memset(&pcb->chap_client, 0, sizeof(chap_client_state));
+#if PPP_SERVER
+       memset(&pcb->chap_server, 0, sizeof(chap_server_state));
+#endif /* PPP_SERVER */
+#endif /* 0 */
+}
+
+/*
+ * chap_lowerup - we can start doing stuff now.
+ */
+static void chap_lowerup(ppp_pcb *pcb) {
+
+       pcb->chap_client.flags |= LOWERUP;
+#if PPP_SERVER
+       pcb->chap_server.flags |= LOWERUP;
+       if (pcb->chap_server.flags & AUTH_STARTED)
+               chap_timeout(pcb);
+#endif /* PPP_SERVER */
+}
+
+static void chap_lowerdown(ppp_pcb *pcb) {
+
+       pcb->chap_client.flags = 0;
+#if PPP_SERVER
+       if (pcb->chap_server.flags & TIMEOUT_PENDING)
+               UNTIMEOUT(chap_timeout, pcb);
+       pcb->chap_server.flags = 0;
+#endif /* PPP_SERVER */
+}
+
+#if PPP_SERVER
+/*
+ * chap_auth_peer - Start authenticating the peer.
+ * If the lower layer is already up, we start sending challenges,
+ * otherwise we wait for the lower layer to come up.
+ */
+void chap_auth_peer(ppp_pcb *pcb, const char *our_name, int digest_code) {
+       const struct chap_digest_type *dp;
+       int i;
+
+       if (pcb->chap_server.flags & AUTH_STARTED) {
+               ppp_error("CHAP: peer authentication already started!");
+               return;
+       }
+       for (i = 0; (dp = chap_digests[i]) != NULL; ++i)
+               if (dp->code == digest_code)
+                       break;
+       if (dp == NULL)
+               ppp_fatal("CHAP digest 0x%x requested but not available",
+                     digest_code);
+
+       pcb->chap_server.digest = dp;
+       pcb->chap_server.name = our_name;
+       /* Start with a random ID value */
+       pcb->chap_server.id = magic();
+       pcb->chap_server.flags |= AUTH_STARTED;
+       if (pcb->chap_server.flags & LOWERUP)
+               chap_timeout(pcb);
+}
+#endif /* PPP_SERVER */
+
+/*
+ * chap_auth_with_peer - Prepare to authenticate ourselves to the peer.
+ * There isn't much to do until we receive a challenge.
+ */
+void chap_auth_with_peer(ppp_pcb *pcb, const char *our_name, int digest_code) {
+       const struct chap_digest_type *dp;
+       int i;
+
+       if(NULL == our_name)
+               return;
+
+       if (pcb->chap_client.flags & AUTH_STARTED) {
+               ppp_error("CHAP: authentication with peer already started!");
+               return;
+       }
+       for (i = 0; (dp = chap_digests[i]) != NULL; ++i)
+               if (dp->code == digest_code)
+                       break;
+
+       if (dp == NULL)
+               ppp_fatal("CHAP digest 0x%x requested but not available",
+                     digest_code);
+
+       pcb->chap_client.digest = dp;
+       pcb->chap_client.name = our_name;
+       pcb->chap_client.flags |= AUTH_STARTED;
+}
+
+#if PPP_SERVER
+/*
+ * chap_timeout - It's time to send another challenge to the peer.
+ * This could be either a retransmission of a previous challenge,
+ * or a new challenge to start re-authentication.
+ */
+static void chap_timeout(void *arg) {
+       ppp_pcb *pcb = (ppp_pcb*)arg;
+       struct pbuf *p;
+
+       pcb->chap_server.flags &= ~TIMEOUT_PENDING;
+       if ((pcb->chap_server.flags & CHALLENGE_VALID) == 0) {
+               pcb->chap_server.challenge_xmits = 0;
+               chap_generate_challenge(pcb);
+               pcb->chap_server.flags |= CHALLENGE_VALID;
+       } else if (pcb->chap_server.challenge_xmits >= pcb->settings.chap_max_transmits) {
+               pcb->chap_server.flags &= ~CHALLENGE_VALID;
+               pcb->chap_server.flags |= AUTH_DONE | AUTH_FAILED;
+               auth_peer_fail(pcb, PPP_CHAP);
+               return;
+       }
+
+       p = pbuf_alloc(PBUF_RAW, (u16_t)(pcb->chap_server.challenge_pktlen), PPP_CTRL_PBUF_TYPE);
+       if(NULL == p)
+               return;
+       if(p->tot_len != p->len) {
+               pbuf_free(p);
+               return;
+       }
+       MEMCPY(p->payload, pcb->chap_server.challenge, pcb->chap_server.challenge_pktlen);
+       ppp_write(pcb, p);
+       ++pcb->chap_server.challenge_xmits;
+       pcb->chap_server.flags |= TIMEOUT_PENDING;
+       TIMEOUT(chap_timeout, arg, pcb->settings.chap_timeout_time);
+}
+
+/*
+ * chap_generate_challenge - generate a challenge string and format
+ * the challenge packet in pcb->chap_server.challenge_pkt.
+ */
+static void chap_generate_challenge(ppp_pcb *pcb) {
+       int clen = 1, nlen, len;
+       unsigned char *p;
+
+       p = pcb->chap_server.challenge;
+       MAKEHEADER(p, PPP_CHAP);
+       p += CHAP_HDRLEN;
+       pcb->chap_server.digest->generate_challenge(pcb, p);
+       clen = *p;
+       nlen = strlen(pcb->chap_server.name);
+       memcpy(p + 1 + clen, pcb->chap_server.name, nlen);
+
+       len = CHAP_HDRLEN + 1 + clen + nlen;
+       pcb->chap_server.challenge_pktlen = PPP_HDRLEN + len;
+
+       p = pcb->chap_server.challenge + PPP_HDRLEN;
+       p[0] = CHAP_CHALLENGE;
+       p[1] = ++pcb->chap_server.id;
+       p[2] = len >> 8;
+       p[3] = len;
+}
+
+/*
+ * chap_handle_response - check the response to our challenge.
+ */
+static void  chap_handle_response(ppp_pcb *pcb, int id,
+                    unsigned char *pkt, int len) {
+       int response_len, ok, mlen;
+       const unsigned char *response;
+       unsigned char *outp;
+       struct pbuf *p;
+       const char *name = NULL;        /* initialized to shut gcc up */
+#if 0 /* UNUSED */
+       int (*verifier)(const char *, const char *, int, const struct chap_digest_type *,
+               const unsigned char *, const unsigned char *, char *, int);
+#endif /* UNUSED */
+       char rname[MAXNAMELEN+1];
+       char message[256];
+
+       if ((pcb->chap_server.flags & LOWERUP) == 0)
+               return;
+       if (id != pcb->chap_server.challenge[PPP_HDRLEN+1] || len < 2)
+               return;
+       if (pcb->chap_server.flags & CHALLENGE_VALID) {
+               response = pkt;
+               GETCHAR(response_len, pkt);
+               len -= response_len + 1;        /* length of name */
+               name = (char *)pkt + response_len;
+               if (len < 0)
+                       return;
+
+               if (pcb->chap_server.flags & TIMEOUT_PENDING) {
+                       pcb->chap_server.flags &= ~TIMEOUT_PENDING;
+                       UNTIMEOUT(chap_timeout, pcb);
+               }
+#if PPP_REMOTENAME
+               if (pcb->settings.explicit_remote) {
+                       name = pcb->remote_name;
+               } else
+#endif /* PPP_REMOTENAME */
+               {
+                       /* Null terminate and clean remote name. */
+                       ppp_slprintf(rname, sizeof(rname), "%.*v", len, name);
+                       name = rname;
+               }
+
+#if 0 /* UNUSED */
+               if (chap_verify_hook)
+                       verifier = chap_verify_hook;
+               else
+                       verifier = chap_verify_response;
+               ok = (*verifier)(name, pcb->chap_server.name, id, pcb->chap_server.digest,
+                                pcb->chap_server.challenge + PPP_HDRLEN + CHAP_HDRLEN,
+                                response, pcb->chap_server.message, sizeof(pcb->chap_server.message));
+#endif /* UNUSED */
+               ok = chap_verify_response(pcb, name, pcb->chap_server.name, id, pcb->chap_server.digest,
+                    pcb->chap_server.challenge + PPP_HDRLEN + CHAP_HDRLEN,
+                    response, message, sizeof(message));
+#if 0 /* UNUSED */
+               if (!ok || !auth_number()) {
+#endif /* UNUSED */
+               if (!ok) {
+                       pcb->chap_server.flags |= AUTH_FAILED;
+                       ppp_warn("Peer %q failed CHAP authentication", name);
+               }
+       } else if ((pcb->chap_server.flags & AUTH_DONE) == 0)
+               return;
+
+       /* send the response */
+       mlen = strlen(message);
+       len = CHAP_HDRLEN + mlen;
+       p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN +len), PPP_CTRL_PBUF_TYPE);
+       if(NULL == p)
+               return;
+       if(p->tot_len != p->len) {
+               pbuf_free(p);
+               return;
+       }
+
+       outp = (unsigned char *)p->payload;
+       MAKEHEADER(outp, PPP_CHAP);
+
+       outp[0] = (pcb->chap_server.flags & AUTH_FAILED)? CHAP_FAILURE: CHAP_SUCCESS;
+       outp[1] = id;
+       outp[2] = len >> 8;
+       outp[3] = len;
+       if (mlen > 0)
+               memcpy(outp + CHAP_HDRLEN, message, mlen);
+       ppp_write(pcb, p);
+
+       if (pcb->chap_server.flags & CHALLENGE_VALID) {
+               pcb->chap_server.flags &= ~CHALLENGE_VALID;
+               if (!(pcb->chap_server.flags & AUTH_DONE) && !(pcb->chap_server.flags & AUTH_FAILED)) {
+
+#if 0 /* UNUSED */
+                   /*
+                    * Auth is OK, so now we need to check session restrictions
+                    * to ensure everything is OK, but only if we used a
+                    * plugin, and only if we're configured to check.  This
+                    * allows us to do PAM checks on PPP servers that
+                    * authenticate against ActiveDirectory, and use AD for
+                    * account info (like when using Winbind integrated with
+                    * PAM).
+                    */
+                   if (session_mgmt &&
+                       session_check(name, NULL, devnam, NULL) == 0) {
+                       pcb->chap_server.flags |= AUTH_FAILED;
+                       ppp_warn("Peer %q failed CHAP Session verification", name);
+                   }
+#endif /* UNUSED */
+
+               }
+               if (pcb->chap_server.flags & AUTH_FAILED) {
+                       auth_peer_fail(pcb, PPP_CHAP);
+               } else {
+                       if ((pcb->chap_server.flags & AUTH_DONE) == 0)
+                               auth_peer_success(pcb, PPP_CHAP,
+                                                 pcb->chap_server.digest->code,
+                                                 name, strlen(name));
+                       if (pcb->settings.chap_rechallenge_time) {
+                               pcb->chap_server.flags |= TIMEOUT_PENDING;
+                               TIMEOUT(chap_timeout, pcb,
+                                       pcb->settings.chap_rechallenge_time);
+                       }
+               }
+               pcb->chap_server.flags |= AUTH_DONE;
+       }
+}
+
+/*
+ * chap_verify_response - check whether the peer's response matches
+ * what we think it should be.  Returns 1 if it does (authentication
+ * succeeded), or 0 if it doesn't.
+ */
+static int chap_verify_response(ppp_pcb *pcb, const char *name, const char *ourname, int id,
+                    const struct chap_digest_type *digest,
+                    const unsigned char *challenge, const unsigned char *response,
+                    char *message, int message_space) {
+       int ok;
+       unsigned char secret[MAXSECRETLEN];
+       int secret_len;
+
+       /* Get the secret that the peer is supposed to know */
+       if (!get_secret(pcb, name, ourname, (char *)secret, &secret_len, 1)) {
+               ppp_error("No CHAP secret found for authenticating %q", name);
+               return 0;
+       }
+       ok = digest->verify_response(pcb, id, name, secret, secret_len, challenge,
+                                    response, message, message_space);
+       memset(secret, 0, sizeof(secret));
+
+       return ok;
+}
+#endif /* PPP_SERVER */
+
+/*
+ * chap_respond - Generate and send a response to a challenge.
+ */
+static void chap_respond(ppp_pcb *pcb, int id,
+            unsigned char *pkt, int len) {
+       int clen, nlen;
+       int secret_len;
+       struct pbuf *p;
+       u_char *outp;
+       char rname[MAXNAMELEN+1];
+       char secret[MAXSECRETLEN+1];
+
+       p = pbuf_alloc(PBUF_RAW, (u16_t)(RESP_MAX_PKTLEN), PPP_CTRL_PBUF_TYPE);
+       if(NULL == p)
+               return;
+       if(p->tot_len != p->len) {
+               pbuf_free(p);
+               return;
+       }
+
+       if ((pcb->chap_client.flags & (LOWERUP | AUTH_STARTED)) != (LOWERUP | AUTH_STARTED))
+               return;         /* not ready */
+       if (len < 2 || len < pkt[0] + 1)
+               return;         /* too short */
+       clen = pkt[0];
+       nlen = len - (clen + 1);
+
+       /* Null terminate and clean remote name. */
+       ppp_slprintf(rname, sizeof(rname), "%.*v", nlen, pkt + clen + 1);
+
+#if PPP_REMOTENAME
+       /* Microsoft doesn't send their name back in the PPP packet */
+       if (pcb->settings.explicit_remote || (pcb->settings.remote_name[0] != 0 && rname[0] == 0))
+               strlcpy(rname, pcb->settings.remote_name, sizeof(rname));
+#endif /* PPP_REMOTENAME */
+
+       /* get secret for authenticating ourselves with the specified host */
+       if (!get_secret(pcb, pcb->chap_client.name, rname, secret, &secret_len, 0)) {
+               secret_len = 0; /* assume null secret if can't find one */
+               ppp_warn("No CHAP secret found for authenticating us to %q", rname);
+       }
+
+       outp = (u_char*)p->payload;
+       MAKEHEADER(outp, PPP_CHAP);
+       outp += CHAP_HDRLEN;
+
+       pcb->chap_client.digest->make_response(pcb, outp, id, pcb->chap_client.name, pkt,
+                                 secret, secret_len, pcb->chap_client.priv);
+       memset(secret, 0, secret_len);
+
+       clen = *outp;
+       nlen = strlen(pcb->chap_client.name);
+       memcpy(outp + clen + 1, pcb->chap_client.name, nlen);
+
+       outp = (u_char*)p->payload + PPP_HDRLEN;
+       len = CHAP_HDRLEN + clen + 1 + nlen;
+       outp[0] = CHAP_RESPONSE;
+       outp[1] = id;
+       outp[2] = len >> 8;
+       outp[3] = len;
+
+       pbuf_realloc(p, PPP_HDRLEN + len);
+       ppp_write(pcb, p);
+}
+
+static void chap_handle_status(ppp_pcb *pcb, int code, int id,
+                  unsigned char *pkt, int len) {
+       const char *msg = NULL;
+       LWIP_UNUSED_ARG(id);
+
+       if ((pcb->chap_client.flags & (AUTH_DONE|AUTH_STARTED|LOWERUP))
+           != (AUTH_STARTED|LOWERUP))
+               return;
+       pcb->chap_client.flags |= AUTH_DONE;
+
+       if (code == CHAP_SUCCESS) {
+               /* used for MS-CHAP v2 mutual auth, yuck */
+               if (pcb->chap_client.digest->check_success != NULL) {
+                       if (!(*pcb->chap_client.digest->check_success)(pcb, pkt, len, pcb->chap_client.priv))
+                               code = CHAP_FAILURE;
+               } else
+                       msg = "CHAP authentication succeeded";
+       } else {
+               if (pcb->chap_client.digest->handle_failure != NULL)
+                       (*pcb->chap_client.digest->handle_failure)(pcb, pkt, len);
+               else
+                       msg = "CHAP authentication failed";
+       }
+       if (msg) {
+               if (len > 0)
+                       ppp_info("%s: %.*v", msg, len, pkt);
+               else
+                       ppp_info("%s", msg);
+       }
+       if (code == CHAP_SUCCESS)
+               auth_withpeer_success(pcb, PPP_CHAP, pcb->chap_client.digest->code);
+       else {
+               pcb->chap_client.flags |= AUTH_FAILED;
+               ppp_error("CHAP authentication failed");
+               auth_withpeer_fail(pcb, PPP_CHAP);
+       }
+}
+
+static void chap_input(ppp_pcb *pcb, unsigned char *pkt, int pktlen) {
+       unsigned char code, id;
+       int len;
+
+       if (pktlen < CHAP_HDRLEN)
+               return;
+       GETCHAR(code, pkt);
+       GETCHAR(id, pkt);
+       GETSHORT(len, pkt);
+       if (len < CHAP_HDRLEN || len > pktlen)
+               return;
+       len -= CHAP_HDRLEN;
+
+       switch (code) {
+       case CHAP_CHALLENGE:
+               chap_respond(pcb, id, pkt, len);
+               break;
+#if PPP_SERVER
+       case CHAP_RESPONSE:
+               chap_handle_response(pcb, id, pkt, len);
+               break;
+#endif /* PPP_SERVER */
+       case CHAP_FAILURE:
+       case CHAP_SUCCESS:
+               chap_handle_status(pcb, code, id, pkt, len);
+               break;
+       default:
+               break;
+       }
+}
+
+static void chap_protrej(ppp_pcb *pcb) {
+
+#if PPP_SERVER
+       if (pcb->chap_server.flags & TIMEOUT_PENDING) {
+               pcb->chap_server.flags &= ~TIMEOUT_PENDING;
+               UNTIMEOUT(chap_timeout, pcb);
+       }
+       if (pcb->chap_server.flags & AUTH_STARTED) {
+               pcb->chap_server.flags = 0;
+               auth_peer_fail(pcb, PPP_CHAP);
+       }
+#endif /* PPP_SERVER */
+       if ((pcb->chap_client.flags & (AUTH_STARTED|AUTH_DONE)) == AUTH_STARTED) {
+               pcb->chap_client.flags &= ~AUTH_STARTED;
+               ppp_error("CHAP authentication failed due to protocol-reject");
+               auth_withpeer_fail(pcb, PPP_CHAP);
+       }
+}
+
+#if PRINTPKT_SUPPORT
+/*
+ * chap_print_pkt - print the contents of a CHAP packet.
+ */
+static const char* const chap_code_names[] = {
+       "Challenge", "Response", "Success", "Failure"
+};
+
+static int chap_print_pkt(const unsigned char *p, int plen,
+              void (*printer) (void *, const char *, ...), void *arg) {
+       int code, id, len;
+       int clen, nlen;
+       unsigned char x;
+
+       if (plen < CHAP_HDRLEN)
+               return 0;
+       GETCHAR(code, p);
+       GETCHAR(id, p);
+       GETSHORT(len, p);
+       if (len < CHAP_HDRLEN || len > plen)
+               return 0;
+
+       if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(chap_code_names))
+               printer(arg, " %s", chap_code_names[code-1]);
+       else
+               printer(arg, " code=0x%x", code);
+       printer(arg, " id=0x%x", id);
+       len -= CHAP_HDRLEN;
+       switch (code) {
+       case CHAP_CHALLENGE:
+       case CHAP_RESPONSE:
+               if (len < 1)
+                       break;
+               clen = p[0];
+               if (len < clen + 1)
+                       break;
+               ++p;
+               nlen = len - clen - 1;
+               printer(arg, " <");
+               for (; clen > 0; --clen) {
+                       GETCHAR(x, p);
+                       printer(arg, "%.2x", x);
+               }
+               printer(arg, ">, name = ");
+               ppp_print_string(p, nlen, printer, arg);
+               break;
+       case CHAP_FAILURE:
+       case CHAP_SUCCESS:
+               printer(arg, " ");
+               ppp_print_string(p, len, printer, arg);
+               break;
+       default:
+               for (clen = len; clen > 0; --clen) {
+                       GETCHAR(x, p);
+                       printer(arg, " %.2x", x);
+               }
+               /* no break */
+       }
+
+       return len + CHAP_HDRLEN;
+}
+#endif /* PRINTPKT_SUPPORT */
+
+const struct protent chap_protent = {
+       PPP_CHAP,
+       chap_init,
+       chap_input,
+       chap_protrej,
+       chap_lowerup,
+       chap_lowerdown,
+       NULL,           /* open */
+       NULL,           /* close */
+#if PRINTPKT_SUPPORT
+       chap_print_pkt,
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_DATAINPUT
+       NULL,           /* datainput */
+#endif /* PPP_DATAINPUT */
+#if PRINTPKT_SUPPORT
+       "CHAP",         /* name */
+       NULL,           /* data_name */
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_OPTIONS
+       chap_option_list,
+       NULL,           /* check_options */
+#endif /* PPP_OPTIONS */
+#if DEMAND_SUPPORT
+       NULL,
+       NULL
+#endif /* DEMAND_SUPPORT */
+};
+
+#endif /* PPP_SUPPORT && CHAP_SUPPORT */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/chap_ms.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/chap_ms.c
new file mode 100644 (file)
index 0000000..5a989c9
--- /dev/null
@@ -0,0 +1,962 @@
+/*
+ * chap_ms.c - Microsoft MS-CHAP compatible implementation.
+ *
+ * Copyright (c) 1995 Eric Rosenquist.  All rights reserved.
+ *
+ * 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.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997
+ *
+ *   Implemented LANManager type password response to MS-CHAP challenges.
+ *   Now pppd provides both NT style and LANMan style blocks, and the
+ *   prefered is set by option "ms-lanman". Default is to use NT.
+ *   The hash text (StdText) was taken from Win95 RASAPI32.DLL.
+ *
+ *   You should also use DOMAIN\\USERNAME as described in README.MSCHAP80
+ */
+
+/*
+ * Modifications by Frank Cusack, frank@google.com, March 2002.
+ *
+ *   Implemented MS-CHAPv2 functionality, heavily based on sample
+ *   implementation in RFC 2759.  Implemented MPPE functionality,
+ *   heavily based on sample implementation in RFC 3079.
+ *
+ * Copyright (c) 2002 Google, Inc.  All rights reserved.
+ *
+ * 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.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && MSCHAP_SUPPORT  /* don't build if not configured for use in lwipopts.h */
+
+#if 0 /* UNUSED */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#endif /* UNUSED */
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/chap-new.h"
+#include "netif/ppp/chap_ms.h"
+#include "netif/ppp/pppcrypt.h"
+#include "netif/ppp/magic.h"
+#if MPPE_SUPPORT
+#include "netif/ppp/mppe.h" /* For mppe_sha1_pad*, mppe_set_key() */
+#endif /* MPPE_SUPPORT */
+
+#define SHA1_SIGNATURE_SIZE    20
+#define MD4_SIGNATURE_SIZE     16      /* 16 bytes in a MD4 message digest */
+#define MAX_NT_PASSWORD                256     /* Max (Unicode) chars in an NT pass */
+
+#define MS_CHAP_RESPONSE_LEN   49      /* Response length for MS-CHAP */
+#define MS_CHAP2_RESPONSE_LEN  49      /* Response length for MS-CHAPv2 */
+#define MS_AUTH_RESPONSE_LENGTH        40      /* MS-CHAPv2 authenticator response, */
+                                       /* as ASCII */
+
+/* Error codes for MS-CHAP failure messages. */
+#define MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS   646
+#define MS_CHAP_ERROR_ACCT_DISABLED            647
+#define MS_CHAP_ERROR_PASSWD_EXPIRED           648
+#define MS_CHAP_ERROR_NO_DIALIN_PERMISSION     649
+#define MS_CHAP_ERROR_AUTHENTICATION_FAILURE   691
+#define MS_CHAP_ERROR_CHANGING_PASSWORD                709
+
+/*
+ * Offsets within the response field for MS-CHAP
+ */
+#define MS_CHAP_LANMANRESP     0
+#define MS_CHAP_LANMANRESP_LEN 24
+#define MS_CHAP_NTRESP         24
+#define MS_CHAP_NTRESP_LEN     24
+#define MS_CHAP_USENT          48
+
+/*
+ * Offsets within the response field for MS-CHAP2
+ */
+#define MS_CHAP2_PEER_CHALLENGE        0
+#define MS_CHAP2_PEER_CHAL_LEN 16
+#define MS_CHAP2_RESERVED_LEN  8
+#define MS_CHAP2_NTRESP                24
+#define MS_CHAP2_NTRESP_LEN    24
+#define MS_CHAP2_FLAGS         48
+
+#if MPPE_SUPPORT
+#if 0 /* UNUSED */
+/* These values are the RADIUS attribute values--see RFC 2548. */
+#define MPPE_ENC_POL_ENC_ALLOWED 1
+#define MPPE_ENC_POL_ENC_REQUIRED 2
+#define MPPE_ENC_TYPES_RC4_40 2
+#define MPPE_ENC_TYPES_RC4_128 4
+
+/* used by plugins (using above values) */
+extern void set_mppe_enc_types(int, int);
+#endif /* UNUSED */
+#endif /* MPPE_SUPPORT */
+
+/* Are we the authenticator or authenticatee?  For MS-CHAPv2 key derivation. */
+#define MS_CHAP2_AUTHENTICATEE 0
+#define MS_CHAP2_AUTHENTICATOR 1
+
+static void    ascii2unicode (const char[], int, u_char[]);
+static void    NTPasswordHash (u_char *, int, u_char[MD4_SIGNATURE_SIZE]);
+static void    ChallengeResponse (const u_char *, const u_char *, u_char[24]);
+static void    ChallengeHash (const u_char[16], const u_char *, const char *, u_char[8]);
+static void    ChapMS_NT (const u_char *, const char *, int, u_char[24]);
+static void    ChapMS2_NT (const u_char *, const u_char[16], const char *, const char *, int,
+                               u_char[24]);
+static void    GenerateAuthenticatorResponsePlain
+                       (const char*, int, u_char[24], const u_char[16], const u_char *,
+                            const char *, u_char[41]);
+#ifdef MSLANMAN
+static void    ChapMS_LANMan (u_char *, char *, int, u_char *);
+#endif
+
+static void GenerateAuthenticatorResponse(const u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
+                       u_char NTResponse[24], const u_char PeerChallenge[16],
+                       const u_char *rchallenge, const char *username,
+                       u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1]);
+
+#if MPPE_SUPPORT
+static void    Set_Start_Key (ppp_pcb *pcb, const u_char *, const char *, int);
+static void    SetMasterKeys (ppp_pcb *pcb, const char *, int, u_char[24], int);
+#endif /* MPPE_SUPPORT */
+
+static void ChapMS (ppp_pcb *pcb, const u_char *, const char *, int, u_char *);
+static void ChapMS2 (ppp_pcb *pcb, const u_char *, const u_char *, const char *, const char *, int,
+                 u_char *, u_char[MS_AUTH_RESPONSE_LENGTH+1], int);
+
+#ifdef MSLANMAN
+bool   ms_lanman = 0;          /* Use LanMan password instead of NT */
+                               /* Has meaning only with MS-CHAP challenges */
+#endif
+
+#if MPPE_SUPPORT
+#ifdef DEBUGMPPEKEY
+/* For MPPE debug */
+/* Use "[]|}{?/><,`!2&&(" (sans quotes) for RFC 3079 MS-CHAPv2 test value */
+static char *mschap_challenge = NULL;
+/* Use "!@\#$%^&*()_+:3|~" (sans quotes, backslash is to escape #) for ... */
+static char *mschap2_peer_challenge = NULL;
+#endif
+
+#include "netif/ppp/fsm.h"             /* Need to poke MPPE options */
+#include "netif/ppp/ccp.h"
+#endif /* MPPE_SUPPORT */
+
+#if PPP_OPTIONS
+/*
+ * Command-line options.
+ */
+static option_t chapms_option_list[] = {
+#ifdef MSLANMAN
+       { "ms-lanman", o_bool, &ms_lanman,
+         "Use LanMan passwd when using MS-CHAP", 1 },
+#endif
+#ifdef DEBUGMPPEKEY
+       { "mschap-challenge", o_string, &mschap_challenge,
+         "specify CHAP challenge" },
+       { "mschap2-peer-challenge", o_string, &mschap2_peer_challenge,
+         "specify CHAP peer challenge" },
+#endif
+       { NULL }
+};
+#endif /* PPP_OPTIONS */
+
+#if PPP_SERVER
+/*
+ * chapms_generate_challenge - generate a challenge for MS-CHAP.
+ * For MS-CHAP the challenge length is fixed at 8 bytes.
+ * The length goes in challenge[0] and the actual challenge starts
+ * at challenge[1].
+ */
+static void chapms_generate_challenge(ppp_pcb *pcb, unsigned char *challenge) {
+       LWIP_UNUSED_ARG(pcb);
+
+       *challenge++ = 8;
+#ifdef DEBUGMPPEKEY
+       if (mschap_challenge && strlen(mschap_challenge) == 8)
+               memcpy(challenge, mschap_challenge, 8);
+       else
+#endif
+               magic_random_bytes(challenge, 8);
+}
+
+static void chapms2_generate_challenge(ppp_pcb *pcb, unsigned char *challenge) {
+       LWIP_UNUSED_ARG(pcb);
+
+       *challenge++ = 16;
+#ifdef DEBUGMPPEKEY
+       if (mschap_challenge && strlen(mschap_challenge) == 16)
+               memcpy(challenge, mschap_challenge, 16);
+       else
+#endif
+               magic_random_bytes(challenge, 16);
+}
+
+static int chapms_verify_response(ppp_pcb *pcb, int id, const char *name,
+                      const unsigned char *secret, int secret_len,
+                      const unsigned char *challenge, const unsigned char *response,
+                      char *message, int message_space) {
+       unsigned char md[MS_CHAP_RESPONSE_LEN];
+       int diff;
+       int challenge_len, response_len;
+       LWIP_UNUSED_ARG(id);
+       LWIP_UNUSED_ARG(name);
+
+       challenge_len = *challenge++;   /* skip length, is 8 */
+       response_len = *response++;
+       if (response_len != MS_CHAP_RESPONSE_LEN)
+               goto bad;
+
+#ifndef MSLANMAN
+       if (!response[MS_CHAP_USENT]) {
+               /* Should really propagate this into the error packet. */
+               ppp_notice("Peer request for LANMAN auth not supported");
+               goto bad;
+       }
+#endif
+
+       /* Generate the expected response. */
+       ChapMS(pcb, (const u_char *)challenge, (const char *)secret, secret_len, md);
+
+#ifdef MSLANMAN
+       /* Determine which part of response to verify against */
+       if (!response[MS_CHAP_USENT])
+               diff = memcmp(&response[MS_CHAP_LANMANRESP],
+                             &md[MS_CHAP_LANMANRESP], MS_CHAP_LANMANRESP_LEN);
+       else
+#endif
+               diff = memcmp(&response[MS_CHAP_NTRESP], &md[MS_CHAP_NTRESP],
+                             MS_CHAP_NTRESP_LEN);
+
+       if (diff == 0) {
+               ppp_slprintf(message, message_space, "Access granted");
+               return 1;
+       }
+
+ bad:
+       /* See comments below for MS-CHAP V2 */
+       ppp_slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0",
+                challenge_len, challenge);
+       return 0;
+}
+
+static int chapms2_verify_response(ppp_pcb *pcb, int id, const char *name,
+                       const unsigned char *secret, int secret_len,
+                       const unsigned char *challenge, const unsigned char *response,
+                       char *message, int message_space) {
+       unsigned char md[MS_CHAP2_RESPONSE_LEN];
+       char saresponse[MS_AUTH_RESPONSE_LENGTH+1];
+       int challenge_len, response_len;
+       LWIP_UNUSED_ARG(id);
+
+       challenge_len = *challenge++;   /* skip length, is 16 */
+       response_len = *response++;
+       if (response_len != MS_CHAP2_RESPONSE_LEN)
+               goto bad;       /* not even the right length */
+
+       /* Generate the expected response and our mutual auth. */
+       ChapMS2(pcb, (const u_char*)challenge, (const u_char*)&response[MS_CHAP2_PEER_CHALLENGE], name,
+               (const char *)secret, secret_len, md,
+               (unsigned char *)saresponse, MS_CHAP2_AUTHENTICATOR);
+
+       /* compare MDs and send the appropriate status */
+       /*
+        * Per RFC 2759, success message must be formatted as
+        *     "S=<auth_string> M=<message>"
+        * where
+        *     <auth_string> is the Authenticator Response (mutual auth)
+        *     <message> is a text message
+        *
+        * However, some versions of Windows (win98 tested) do not know
+        * about the M=<message> part (required per RFC 2759) and flag
+        * it as an error (reported incorrectly as an encryption error
+        * to the user).  Since the RFC requires it, and it can be
+        * useful information, we supply it if the peer is a conforming
+        * system.  Luckily (?), win98 sets the Flags field to 0x04
+        * (contrary to RFC requirements) so we can use that to
+        * distinguish between conforming and non-conforming systems.
+        *
+        * Special thanks to Alex Swiridov <say@real.kharkov.ua> for
+        * help debugging this.
+        */
+       if (memcmp(&md[MS_CHAP2_NTRESP], &response[MS_CHAP2_NTRESP],
+                  MS_CHAP2_NTRESP_LEN) == 0) {
+               if (response[MS_CHAP2_FLAGS])
+                       ppp_slprintf(message, message_space, "S=%s", saresponse);
+               else
+                       ppp_slprintf(message, message_space, "S=%s M=%s",
+                                saresponse, "Access granted");
+               return 1;
+       }
+
+ bad:
+       /*
+        * Failure message must be formatted as
+        *     "E=e R=r C=c V=v M=m"
+        * where
+        *     e = error code (we use 691, ERROR_AUTHENTICATION_FAILURE)
+        *     r = retry (we use 1, ok to retry)
+        *     c = challenge to use for next response, we reuse previous
+        *     v = Change Password version supported, we use 0
+        *     m = text message
+        *
+        * The M=m part is only for MS-CHAPv2.  Neither win2k nor
+        * win98 (others untested) display the message to the user anyway.
+        * They also both ignore the E=e code.
+        *
+        * Note that it's safe to reuse the same challenge as we don't
+        * actually accept another response based on the error message
+        * (and no clients try to resend a response anyway).
+        *
+        * Basically, this whole bit is useless code, even the small
+        * implementation here is only because of overspecification.
+        */
+       ppp_slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
+                challenge_len, challenge, "Access denied");
+       return 0;
+}
+#endif /* PPP_SERVER */
+
+static void chapms_make_response(ppp_pcb *pcb, unsigned char *response, int id, const char *our_name,
+                    const unsigned char *challenge, const char *secret, int secret_len,
+                    unsigned char *private_) {
+       LWIP_UNUSED_ARG(id);
+       LWIP_UNUSED_ARG(our_name);
+       LWIP_UNUSED_ARG(private_);
+       challenge++;    /* skip length, should be 8 */
+       *response++ = MS_CHAP_RESPONSE_LEN;
+       ChapMS(pcb, challenge, secret, secret_len, response);
+}
+
+static void chapms2_make_response(ppp_pcb *pcb, unsigned char *response, int id, const char *our_name,
+                     const unsigned char *challenge, const char *secret, int secret_len,
+                     unsigned char *private_) {
+       LWIP_UNUSED_ARG(id);
+       challenge++;    /* skip length, should be 16 */
+       *response++ = MS_CHAP2_RESPONSE_LEN;
+       ChapMS2(pcb, challenge,
+#ifdef DEBUGMPPEKEY
+               mschap2_peer_challenge,
+#else
+               NULL,
+#endif
+               our_name, secret, secret_len, response, private_,
+               MS_CHAP2_AUTHENTICATEE);
+}
+
+static int chapms2_check_success(ppp_pcb *pcb, unsigned char *msg, int len, unsigned char *private_) {
+       LWIP_UNUSED_ARG(pcb);
+
+       if ((len < MS_AUTH_RESPONSE_LENGTH + 2) ||
+           strncmp((char *)msg, "S=", 2) != 0) {
+               /* Packet does not start with "S=" */
+               ppp_error("MS-CHAPv2 Success packet is badly formed.");
+               return 0;
+       }
+       msg += 2;
+       len -= 2;
+       if (len < MS_AUTH_RESPONSE_LENGTH
+           || memcmp(msg, private_, MS_AUTH_RESPONSE_LENGTH)) {
+               /* Authenticator Response did not match expected. */
+               ppp_error("MS-CHAPv2 mutual authentication failed.");
+               return 0;
+       }
+       /* Authenticator Response matches. */
+       msg += MS_AUTH_RESPONSE_LENGTH; /* Eat it */
+       len -= MS_AUTH_RESPONSE_LENGTH;
+       if ((len >= 3) && !strncmp((char *)msg, " M=", 3)) {
+               msg += 3; /* Eat the delimiter */
+       } else if (len) {
+               /* Packet has extra text which does not begin " M=" */
+               ppp_error("MS-CHAPv2 Success packet is badly formed.");
+               return 0;
+       }
+       return 1;
+}
+
+static void chapms_handle_failure(ppp_pcb *pcb, unsigned char *inp, int len) {
+       int err;
+       const char *p;
+       char msg[64];
+       LWIP_UNUSED_ARG(pcb);
+
+       /* We want a null-terminated string for strxxx(). */
+       len = LWIP_MIN(len, 63);
+       MEMCPY(msg, inp, len);
+       msg[len] = 0;
+       p = msg;
+
+       /*
+        * Deal with MS-CHAP formatted failure messages; just print the
+        * M=<message> part (if any).  For MS-CHAP we're not really supposed
+        * to use M=<message>, but it shouldn't hurt.  See
+        * chapms[2]_verify_response.
+        */
+       if (!strncmp(p, "E=", 2))
+               err = strtol(p+2, NULL, 10); /* Remember the error code. */
+       else
+               goto print_msg; /* Message is badly formatted. */
+
+       if (len && ((p = strstr(p, " M=")) != NULL)) {
+               /* M=<message> field found. */
+               p += 3;
+       } else {
+               /* No M=<message>; use the error code. */
+               switch (err) {
+               case MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS:
+                       p = "E=646 Restricted logon hours";
+                       break;
+
+               case MS_CHAP_ERROR_ACCT_DISABLED:
+                       p = "E=647 Account disabled";
+                       break;
+
+               case MS_CHAP_ERROR_PASSWD_EXPIRED:
+                       p = "E=648 Password expired";
+                       break;
+
+               case MS_CHAP_ERROR_NO_DIALIN_PERMISSION:
+                       p = "E=649 No dialin permission";
+                       break;
+
+               case MS_CHAP_ERROR_AUTHENTICATION_FAILURE:
+                       p = "E=691 Authentication failure";
+                       break;
+
+               case MS_CHAP_ERROR_CHANGING_PASSWORD:
+                       /* Should never see this, we don't support Change Password. */
+                       p = "E=709 Error changing password";
+                       break;
+
+               default:
+                       ppp_error("Unknown MS-CHAP authentication failure: %.*v",
+                             len, inp);
+                       return;
+               }
+       }
+print_msg:
+       if (p != NULL)
+               ppp_error("MS-CHAP authentication failed: %v", p);
+}
+
+static void ChallengeResponse(const u_char *challenge,
+                 const u_char PasswordHash[MD4_SIGNATURE_SIZE],
+                 u_char response[24]) {
+    u_char    ZPasswordHash[21];
+    lwip_des_context des;
+    u_char des_key[8];
+
+    BZERO(ZPasswordHash, sizeof(ZPasswordHash));
+    MEMCPY(ZPasswordHash, PasswordHash, MD4_SIGNATURE_SIZE);
+
+#if 0
+    dbglog("ChallengeResponse - ZPasswordHash %.*B",
+          sizeof(ZPasswordHash), ZPasswordHash);
+#endif
+
+    pppcrypt_56_to_64_bit_key(ZPasswordHash + 0, des_key);
+    lwip_des_init(&des);
+    lwip_des_setkey_enc(&des, des_key);
+    lwip_des_crypt_ecb(&des, challenge, response +0);
+    lwip_des_free(&des);
+
+    pppcrypt_56_to_64_bit_key(ZPasswordHash + 7, des_key);
+    lwip_des_init(&des);
+    lwip_des_setkey_enc(&des, des_key);
+    lwip_des_crypt_ecb(&des, challenge, response +8);
+    lwip_des_free(&des);
+
+    pppcrypt_56_to_64_bit_key(ZPasswordHash + 14, des_key);
+    lwip_des_init(&des);
+    lwip_des_setkey_enc(&des, des_key);
+    lwip_des_crypt_ecb(&des, challenge, response +16);
+    lwip_des_free(&des);
+
+#if 0
+    dbglog("ChallengeResponse - response %.24B", response);
+#endif
+}
+
+static void ChallengeHash(const u_char PeerChallenge[16], const u_char *rchallenge,
+             const char *username, u_char Challenge[8]) {
+    lwip_sha1_context  sha1Context;
+    u_char     sha1Hash[SHA1_SIGNATURE_SIZE];
+    const char *user;
+
+    /* remove domain from "domain\username" */
+    if ((user = strrchr(username, '\\')) != NULL)
+       ++user;
+    else
+       user = username;
+
+    lwip_sha1_init(&sha1Context);
+    lwip_sha1_starts(&sha1Context);
+    lwip_sha1_update(&sha1Context, PeerChallenge, 16);
+    lwip_sha1_update(&sha1Context, rchallenge, 16);
+    lwip_sha1_update(&sha1Context, (const unsigned char*)user, strlen(user));
+    lwip_sha1_finish(&sha1Context, sha1Hash);
+    lwip_sha1_free(&sha1Context);
+
+    MEMCPY(Challenge, sha1Hash, 8);
+}
+
+/*
+ * Convert the ASCII version of the password to Unicode.
+ * This implicitly supports 8-bit ISO8859/1 characters.
+ * This gives us the little-endian representation, which
+ * is assumed by all M$ CHAP RFCs.  (Unicode byte ordering
+ * is machine-dependent.)
+ */
+static void ascii2unicode(const char ascii[], int ascii_len, u_char unicode[]) {
+    int i;
+
+    BZERO(unicode, ascii_len * 2);
+    for (i = 0; i < ascii_len; i++)
+       unicode[i * 2] = (u_char) ascii[i];
+}
+
+static void NTPasswordHash(u_char *secret, int secret_len, u_char hash[MD4_SIGNATURE_SIZE]) {
+    lwip_md4_context           md4Context;
+
+    lwip_md4_init(&md4Context);
+    lwip_md4_starts(&md4Context);
+    lwip_md4_update(&md4Context, secret, secret_len);
+    lwip_md4_finish(&md4Context, hash);
+    lwip_md4_free(&md4Context);
+}
+
+static void ChapMS_NT(const u_char *rchallenge, const char *secret, int secret_len,
+         u_char NTResponse[24]) {
+    u_char     unicodePassword[MAX_NT_PASSWORD * 2];
+    u_char     PasswordHash[MD4_SIGNATURE_SIZE];
+
+    /* Hash the Unicode version of the secret (== password). */
+    ascii2unicode(secret, secret_len, unicodePassword);
+    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+
+    ChallengeResponse(rchallenge, PasswordHash, NTResponse);
+}
+
+static void ChapMS2_NT(const u_char *rchallenge, const u_char PeerChallenge[16], const char *username,
+          const char *secret, int secret_len, u_char NTResponse[24]) {
+    u_char     unicodePassword[MAX_NT_PASSWORD * 2];
+    u_char     PasswordHash[MD4_SIGNATURE_SIZE];
+    u_char     Challenge[8];
+
+    ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
+
+    /* Hash the Unicode version of the secret (== password). */
+    ascii2unicode(secret, secret_len, unicodePassword);
+    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+
+    ChallengeResponse(Challenge, PasswordHash, NTResponse);
+}
+
+#ifdef MSLANMAN
+static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
+
+static void ChapMS_LANMan(u_char *rchallenge, char *secret, int secret_len,
+             unsigned char *response) {
+    int                        i;
+    u_char             UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
+    u_char             PasswordHash[MD4_SIGNATURE_SIZE];
+    lwip_des_context des;
+    u_char des_key[8];
+
+    /* LANMan password is case insensitive */
+    BZERO(UcasePassword, sizeof(UcasePassword));
+    for (i = 0; i < secret_len; i++)
+       UcasePassword[i] = (u_char)toupper(secret[i]);
+
+    pppcrypt_56_to_64_bit_key(UcasePassword +0, des_key);
+    lwip_des_init(&des);
+    lwip_des_setkey_enc(&des, des_key);
+    lwip_des_crypt_ecb(&des, StdText, PasswordHash +0);
+    lwip_des_free(&des);
+
+    pppcrypt_56_to_64_bit_key(UcasePassword +7, des_key);
+    lwip_des_init(&des);
+    lwip_des_setkey_enc(&des, des_key);
+    lwip_des_crypt_ecb(&des, StdText, PasswordHash +8);
+    lwip_des_free(&des);
+
+    ChallengeResponse(rchallenge, PasswordHash, &response[MS_CHAP_LANMANRESP]);
+}
+#endif
+
+
+static void GenerateAuthenticatorResponse(const u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
+                             u_char NTResponse[24], const u_char PeerChallenge[16],
+                             const u_char *rchallenge, const char *username,
+                             u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1]) {
+    /*
+     * "Magic" constants used in response generation, from RFC 2759.
+     */
+    static const u_char Magic1[39] = /* "Magic server to client signing constant" */
+       { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
+         0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
+         0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
+         0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 };
+    static const u_char Magic2[41] = /* "Pad to make it do more than one iteration" */
+       { 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
+         0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
+         0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
+         0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
+         0x6E };
+
+    int                i;
+    lwip_sha1_context  sha1Context;
+    u_char     Digest[SHA1_SIGNATURE_SIZE];
+    u_char     Challenge[8];
+
+    lwip_sha1_init(&sha1Context);
+    lwip_sha1_starts(&sha1Context);
+    lwip_sha1_update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
+    lwip_sha1_update(&sha1Context, NTResponse, 24);
+    lwip_sha1_update(&sha1Context, Magic1, sizeof(Magic1));
+    lwip_sha1_finish(&sha1Context, Digest);
+    lwip_sha1_free(&sha1Context);
+
+    ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
+
+    lwip_sha1_init(&sha1Context);
+    lwip_sha1_starts(&sha1Context);
+    lwip_sha1_update(&sha1Context, Digest, sizeof(Digest));
+    lwip_sha1_update(&sha1Context, Challenge, sizeof(Challenge));
+    lwip_sha1_update(&sha1Context, Magic2, sizeof(Magic2));
+    lwip_sha1_finish(&sha1Context, Digest);
+    lwip_sha1_free(&sha1Context);
+
+    /* Convert to ASCII hex string. */
+    for (i = 0; i < LWIP_MAX((MS_AUTH_RESPONSE_LENGTH / 2), (int)sizeof(Digest)); i++)
+       sprintf((char *)&authResponse[i * 2], "%02X", Digest[i]);
+}
+
+
+static void GenerateAuthenticatorResponsePlain(
+                const char *secret, int secret_len,
+                u_char NTResponse[24], const u_char PeerChallenge[16],
+                const u_char *rchallenge, const char *username,
+                u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1]) {
+    u_char     unicodePassword[MAX_NT_PASSWORD * 2];
+    u_char     PasswordHash[MD4_SIGNATURE_SIZE];
+    u_char     PasswordHashHash[MD4_SIGNATURE_SIZE];
+
+    /* Hash (x2) the Unicode version of the secret (== password). */
+    ascii2unicode(secret, secret_len, unicodePassword);
+    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+    NTPasswordHash(PasswordHash, sizeof(PasswordHash),
+                  PasswordHashHash);
+
+    GenerateAuthenticatorResponse(PasswordHashHash, NTResponse, PeerChallenge,
+                                 rchallenge, username, authResponse);
+}
+
+
+#if MPPE_SUPPORT
+/*
+ * Set mppe_xxxx_key from MS-CHAP credentials. (see RFC 3079)
+ */
+static void Set_Start_Key(ppp_pcb *pcb, const u_char *rchallenge, const char *secret, int secret_len) {
+    u_char     unicodePassword[MAX_NT_PASSWORD * 2];
+    u_char     PasswordHash[MD4_SIGNATURE_SIZE];
+    u_char     PasswordHashHash[MD4_SIGNATURE_SIZE];
+    lwip_sha1_context  sha1Context;
+    u_char     Digest[SHA1_SIGNATURE_SIZE];    /* >= MPPE_MAX_KEY_LEN */
+
+    /* Hash (x2) the Unicode version of the secret (== password). */
+    ascii2unicode(secret, secret_len, unicodePassword);
+    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+    NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
+
+    lwip_sha1_init(&sha1Context);
+    lwip_sha1_starts(&sha1Context);
+    lwip_sha1_update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
+    lwip_sha1_update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
+    lwip_sha1_update(&sha1Context, rchallenge, 8);
+    lwip_sha1_finish(&sha1Context, Digest);
+    lwip_sha1_free(&sha1Context);
+
+    /* Same key in both directions. */
+    mppe_set_key(pcb, &pcb->mppe_comp, Digest);
+    mppe_set_key(pcb, &pcb->mppe_decomp, Digest);
+
+    pcb->mppe_keys_set = 1;
+}
+
+/*
+ * Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079)
+ */
+static void SetMasterKeys(ppp_pcb *pcb, const char *secret, int secret_len, u_char NTResponse[24], int IsServer) {
+    u_char     unicodePassword[MAX_NT_PASSWORD * 2];
+    u_char     PasswordHash[MD4_SIGNATURE_SIZE];
+    u_char     PasswordHashHash[MD4_SIGNATURE_SIZE];
+    lwip_sha1_context  sha1Context;
+    u_char     MasterKey[SHA1_SIGNATURE_SIZE]; /* >= MPPE_MAX_KEY_LEN */
+    u_char     Digest[SHA1_SIGNATURE_SIZE];    /* >= MPPE_MAX_KEY_LEN */
+    const u_char *s;
+
+    /* "This is the MPPE Master Key" */
+    static const u_char Magic1[27] =
+       { 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
+         0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
+         0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 };
+    /* "On the client side, this is the send key; "
+       "on the server side, it is the receive key." */
+    static const u_char Magic2[84] =
+       { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+         0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+         0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+         0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
+         0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+         0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
+         0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+         0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+         0x6b, 0x65, 0x79, 0x2e };
+    /* "On the client side, this is the receive key; "
+       "on the server side, it is the send key." */
+    static const u_char Magic3[84] =
+       { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+         0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+         0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+         0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+         0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
+         0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
+         0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
+         0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
+         0x6b, 0x65, 0x79, 0x2e };
+
+    /* Hash (x2) the Unicode version of the secret (== password). */
+    ascii2unicode(secret, secret_len, unicodePassword);
+    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+    NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
+
+    lwip_sha1_init(&sha1Context);
+    lwip_sha1_starts(&sha1Context);
+    lwip_sha1_update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
+    lwip_sha1_update(&sha1Context, NTResponse, 24);
+    lwip_sha1_update(&sha1Context, Magic1, sizeof(Magic1));
+    lwip_sha1_finish(&sha1Context, MasterKey);
+    lwip_sha1_free(&sha1Context);
+
+    /*
+     * generate send key
+     */
+    if (IsServer)
+       s = Magic3;
+    else
+       s = Magic2;
+    lwip_sha1_init(&sha1Context);
+    lwip_sha1_starts(&sha1Context);
+    lwip_sha1_update(&sha1Context, MasterKey, 16);
+    lwip_sha1_update(&sha1Context, mppe_sha1_pad1, SHA1_PAD_SIZE);
+    lwip_sha1_update(&sha1Context, s, 84);
+    lwip_sha1_update(&sha1Context, mppe_sha1_pad2, SHA1_PAD_SIZE);
+    lwip_sha1_finish(&sha1Context, Digest);
+    lwip_sha1_free(&sha1Context);
+
+    mppe_set_key(pcb, &pcb->mppe_comp, Digest);
+
+    /*
+     * generate recv key
+     */
+    if (IsServer)
+       s = Magic2;
+    else
+       s = Magic3;
+    lwip_sha1_init(&sha1Context);
+    lwip_sha1_starts(&sha1Context);
+    lwip_sha1_update(&sha1Context, MasterKey, 16);
+    lwip_sha1_update(&sha1Context, mppe_sha1_pad1, SHA1_PAD_SIZE);
+    lwip_sha1_update(&sha1Context, s, 84);
+    lwip_sha1_update(&sha1Context, mppe_sha1_pad2, SHA1_PAD_SIZE);
+    lwip_sha1_finish(&sha1Context, Digest);
+    lwip_sha1_free(&sha1Context);
+
+    mppe_set_key(pcb, &pcb->mppe_decomp, Digest);
+
+    pcb->mppe_keys_set = 1;
+}
+
+#endif /* MPPE_SUPPORT */
+
+
+static void ChapMS(ppp_pcb *pcb, const u_char *rchallenge, const char *secret, int secret_len,
+       unsigned char *response) {
+#if !MPPE_SUPPORT
+    LWIP_UNUSED_ARG(pcb);
+#endif /* !MPPE_SUPPORT */
+    BZERO(response, MS_CHAP_RESPONSE_LEN);
+
+    ChapMS_NT(rchallenge, secret, secret_len, &response[MS_CHAP_NTRESP]);
+
+#ifdef MSLANMAN
+    ChapMS_LANMan(rchallenge, secret, secret_len,
+                 &response[MS_CHAP_LANMANRESP]);
+
+    /* preferred method is set by option  */
+    response[MS_CHAP_USENT] = !ms_lanman;
+#else
+    response[MS_CHAP_USENT] = 1;
+#endif
+
+#if MPPE_SUPPORT
+    Set_Start_Key(pcb, rchallenge, secret, secret_len);
+#endif /* MPPE_SUPPORT */
+}
+
+
+/*
+ * If PeerChallenge is NULL, one is generated and the PeerChallenge
+ * field of response is filled in.  Call this way when generating a response.
+ * If PeerChallenge is supplied, it is copied into the PeerChallenge field.
+ * Call this way when verifying a response (or debugging).
+ * Do not call with PeerChallenge = response.
+ *
+ * The PeerChallenge field of response is then used for calculation of the
+ * Authenticator Response.
+ */
+static void ChapMS2(ppp_pcb *pcb, const u_char *rchallenge, const u_char *PeerChallenge,
+       const char *user, const char *secret, int secret_len, unsigned char *response,
+       u_char authResponse[], int authenticator) {
+    /* ARGSUSED */
+    LWIP_UNUSED_ARG(authenticator);
+#if !MPPE_SUPPORT
+    LWIP_UNUSED_ARG(pcb);
+#endif /* !MPPE_SUPPORT */
+
+    BZERO(response, MS_CHAP2_RESPONSE_LEN);
+
+    /* Generate the Peer-Challenge if requested, or copy it if supplied. */
+    if (!PeerChallenge)
+       magic_random_bytes(&response[MS_CHAP2_PEER_CHALLENGE], MS_CHAP2_PEER_CHAL_LEN);
+    else
+       MEMCPY(&response[MS_CHAP2_PEER_CHALLENGE], PeerChallenge,
+             MS_CHAP2_PEER_CHAL_LEN);
+
+    /* Generate the NT-Response */
+    ChapMS2_NT(rchallenge, &response[MS_CHAP2_PEER_CHALLENGE], user,
+              secret, secret_len, &response[MS_CHAP2_NTRESP]);
+
+    /* Generate the Authenticator Response. */
+    GenerateAuthenticatorResponsePlain(secret, secret_len,
+                                      &response[MS_CHAP2_NTRESP],
+                                      &response[MS_CHAP2_PEER_CHALLENGE],
+                                      rchallenge, user, authResponse);
+
+#if MPPE_SUPPORT
+    SetMasterKeys(pcb, secret, secret_len,
+                 &response[MS_CHAP2_NTRESP], authenticator);
+#endif /* MPPE_SUPPORT */
+}
+
+#if 0 /* UNUSED */
+#if MPPE_SUPPORT
+/*
+ * Set MPPE options from plugins.
+ */
+void set_mppe_enc_types(int policy, int types) {
+    /* Early exit for unknown policies. */
+    if (policy != MPPE_ENC_POL_ENC_ALLOWED ||
+       policy != MPPE_ENC_POL_ENC_REQUIRED)
+       return;
+
+    /* Don't modify MPPE if it's optional and wasn't already configured. */
+    if (policy == MPPE_ENC_POL_ENC_ALLOWED && !ccp_wantoptions[0].mppe)
+       return;
+
+    /*
+     * Disable undesirable encryption types.  Note that we don't ENABLE
+     * any encryption types, to avoid overriding manual configuration.
+     */
+    switch(types) {
+       case MPPE_ENC_TYPES_RC4_40:
+           ccp_wantoptions[0].mppe &= ~MPPE_OPT_128;   /* disable 128-bit */
+           break;
+       case MPPE_ENC_TYPES_RC4_128:
+           ccp_wantoptions[0].mppe &= ~MPPE_OPT_40;    /* disable 40-bit */
+           break;
+       default:
+           break;
+    }
+}
+#endif /* MPPE_SUPPORT */
+#endif /* UNUSED */
+
+const struct chap_digest_type chapms_digest = {
+       CHAP_MICROSOFT,         /* code */
+#if PPP_SERVER
+       chapms_generate_challenge,
+       chapms_verify_response,
+#endif /* PPP_SERVER */
+       chapms_make_response,
+       NULL,                   /* check_success */
+       chapms_handle_failure,
+};
+
+const struct chap_digest_type chapms2_digest = {
+       CHAP_MICROSOFT_V2,      /* code */
+#if PPP_SERVER
+       chapms2_generate_challenge,
+       chapms2_verify_response,
+#endif /* PPP_SERVER */
+       chapms2_make_response,
+       chapms2_check_success,
+       chapms_handle_failure,
+};
+
+#endif /* PPP_SUPPORT && MSCHAP_SUPPORT */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/demand.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/demand.c
new file mode 100644 (file)
index 0000000..26c6c30
--- /dev/null
@@ -0,0 +1,465 @@
+/*
+ * demand.c - Support routines for demand-dialling.
+ *
+ * Copyright (c) 1996-2002 Paul Mackerras. All rights reserved.
+ *
+ * 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. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && DEMAND_SUPPORT  /* don't build if not configured for use in lwipopts.h */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifdef PPP_FILTER
+#include <pcap-bpf.h>
+#endif
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/fsm.h"
+#include "netif/ppp/ipcp.h"
+#include "netif/ppp/lcp.h"
+
+char *frame;
+int framelen;
+int framemax;
+int escape_flag;
+int flush_flag;
+int fcs;
+
+struct packet {
+    int length;
+    struct packet *next;
+    unsigned char data[1];
+};
+
+struct packet *pend_q;
+struct packet *pend_qtail;
+
+static int active_packet (unsigned char *, int);
+
+/*
+ * demand_conf - configure the interface for doing dial-on-demand.
+ */
+void
+demand_conf()
+{
+    int i;
+    const struct protent *protp;
+
+/*    framemax = lcp_allowoptions[0].mru;
+    if (framemax < PPP_MRU) */
+       framemax = PPP_MRU;
+    framemax += PPP_HDRLEN + PPP_FCSLEN;
+    frame = malloc(framemax);
+    if (frame == NULL)
+       novm("demand frame");
+    framelen = 0;
+    pend_q = NULL;
+    escape_flag = 0;
+    flush_flag = 0;
+    fcs = PPP_INITFCS;
+
+    netif_set_mtu(pcb, LWIP_MIN(lcp_allowoptions[0].mru, PPP_MRU));
+    if (ppp_send_config(pcb, PPP_MRU, (u32_t) 0, 0, 0) < 0
+       || ppp_recv_config(pcb, PPP_MRU, (u32_t) 0, 0, 0) < 0)
+           fatal("Couldn't set up demand-dialled PPP interface: %m");
+
+#ifdef PPP_FILTER
+    set_filters(&pass_filter, &active_filter);
+#endif
+
+    /*
+     * Call the demand_conf procedure for each protocol that's got one.
+     */
+    for (i = 0; (protp = protocols[i]) != NULL; ++i)
+       if (protp->demand_conf != NULL)
+           ((*protp->demand_conf)(pcb));
+/* FIXME: find a way to die() here */
+#if 0
+           if (!((*protp->demand_conf)(pcb)))
+               die(1);
+#endif
+}
+
+
+/*
+ * demand_block - set each network protocol to block further packets.
+ */
+void
+demand_block()
+{
+    int i;
+    const struct protent *protp;
+
+    for (i = 0; (protp = protocols[i]) != NULL; ++i)
+       if (protp->demand_conf != NULL)
+           sifnpmode(pcb, protp->protocol & ~0x8000, NPMODE_QUEUE);
+    get_loop_output();
+}
+
+/*
+ * demand_discard - set each network protocol to discard packets
+ * with an error.
+ */
+void
+demand_discard()
+{
+    struct packet *pkt, *nextpkt;
+    int i;
+    const struct protent *protp;
+
+    for (i = 0; (protp = protocols[i]) != NULL; ++i)
+       if (protp->demand_conf != NULL)
+           sifnpmode(pcb, protp->protocol & ~0x8000, NPMODE_ERROR);
+    get_loop_output();
+
+    /* discard all saved packets */
+    for (pkt = pend_q; pkt != NULL; pkt = nextpkt) {
+       nextpkt = pkt->next;
+       free(pkt);
+    }
+    pend_q = NULL;
+    framelen = 0;
+    flush_flag = 0;
+    escape_flag = 0;
+    fcs = PPP_INITFCS;
+}
+
+/*
+ * demand_unblock - set each enabled network protocol to pass packets.
+ */
+void
+demand_unblock()
+{
+    int i;
+    const struct protent *protp;
+
+    for (i = 0; (protp = protocols[i]) != NULL; ++i)
+       if (protp->demand_conf != NULL)
+           sifnpmode(pcb, protp->protocol & ~0x8000, NPMODE_PASS);
+}
+
+/*
+ * FCS lookup table as calculated by genfcstab.
+ */
+static u_short fcstab[256] = {
+       0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+       0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+       0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+       0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+       0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+       0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+       0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+       0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+       0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+       0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+       0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+       0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+       0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+       0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+       0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+       0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+       0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+       0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+       0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+       0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+       0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+       0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+       0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+       0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+       0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+       0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+       0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+       0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+       0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+       0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+       0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+       0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+/*
+ * loop_chars - process characters received from the loopback.
+ * Calls loop_frame when a complete frame has been accumulated.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ */
+int
+loop_chars(p, n)
+    unsigned char *p;
+    int n;
+{
+    int c, rv;
+
+    rv = 0;
+
+/* check for synchronous connection... */
+
+    if ( (p[0] == 0xFF) && (p[1] == 0x03) ) {
+        rv = loop_frame(p,n);
+        return rv;
+    }
+
+    for (; n > 0; --n) {
+       c = *p++;
+       if (c == PPP_FLAG) {
+           if (!escape_flag && !flush_flag
+               && framelen > 2 && fcs == PPP_GOODFCS) {
+               framelen -= 2;
+               if (loop_frame((unsigned char *)frame, framelen))
+                   rv = 1;
+           }
+           framelen = 0;
+           flush_flag = 0;
+           escape_flag = 0;
+           fcs = PPP_INITFCS;
+           continue;
+       }
+       if (flush_flag)
+           continue;
+       if (escape_flag) {
+           c ^= PPP_TRANS;
+           escape_flag = 0;
+       } else if (c == PPP_ESCAPE) {
+           escape_flag = 1;
+           continue;
+       }
+       if (framelen >= framemax) {
+           flush_flag = 1;
+           continue;
+       }
+       frame[framelen++] = c;
+       fcs = PPP_FCS(fcs, c);
+    }
+    return rv;
+}
+
+/*
+ * loop_frame - given a frame obtained from the loopback,
+ * decide whether to bring up the link or not, and, if we want
+ * to transmit this frame later, put it on the pending queue.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ * We assume that the kernel driver has already applied the
+ * pass_filter, so we won't get packets it rejected.
+ * We apply the active_filter to see if we want this packet to
+ * bring up the link.
+ */
+int
+loop_frame(frame, len)
+    unsigned char *frame;
+    int len;
+{
+    struct packet *pkt;
+
+    /* dbglog("from loop: %P", frame, len); */
+    if (len < PPP_HDRLEN)
+       return 0;
+    if ((PPP_PROTOCOL(frame) & 0x8000) != 0)
+       return 0;               /* shouldn't get any of these anyway */
+    if (!active_packet(frame, len))
+       return 0;
+
+    pkt = (struct packet *) malloc(sizeof(struct packet) + len);
+    if (pkt != NULL) {
+       pkt->length = len;
+       pkt->next = NULL;
+       memcpy(pkt->data, frame, len);
+       if (pend_q == NULL)
+           pend_q = pkt;
+       else
+           pend_qtail->next = pkt;
+       pend_qtail = pkt;
+    }
+    return 1;
+}
+
+/*
+ * demand_rexmit - Resend all those frames which we got via the
+ * loopback, now that the real serial link is up.
+ */
+void
+demand_rexmit(proto, newip)
+    int proto;
+    u32_t newip;
+{
+    struct packet *pkt, *prev, *nextpkt;
+    unsigned short checksum;
+    unsigned short pkt_checksum = 0;
+    unsigned iphdr;
+    struct timeval tv;
+    char cv = 0;
+    char ipstr[16];
+
+    prev = NULL;
+    pkt = pend_q;
+    pend_q = NULL;
+    tv.tv_sec = 1;
+    tv.tv_usec = 0;
+    select(0,NULL,NULL,NULL,&tv);      /* Sleep for 1 Seconds */
+    for (; pkt != NULL; pkt = nextpkt) {
+       nextpkt = pkt->next;
+       if (PPP_PROTOCOL(pkt->data) == proto) {
+            if ( (proto == PPP_IP) && newip ) {
+               /* Get old checksum */
+
+               iphdr = (pkt->data[4] & 15) << 2;
+               checksum = *((unsigned short *) (pkt->data+14));
+                if (checksum == 0xFFFF) {
+                    checksum = 0;
+                }
+
+ 
+                if (pkt->data[13] == 17) {
+                    pkt_checksum =  *((unsigned short *) (pkt->data+10+iphdr));
+                   if (pkt_checksum) {
+                        cv = 1;
+                        if (pkt_checksum == 0xFFFF) {
+                            pkt_checksum = 0;
+                        }
+                    }
+                    else {
+                       cv = 0;
+                    }
+                }
+
+               if (pkt->data[13] == 6) {
+                   pkt_checksum = *((unsigned short *) (pkt->data+20+iphdr));
+                   cv = 1;
+                    if (pkt_checksum == 0xFFFF) {
+                        pkt_checksum = 0;
+                    }
+               }
+
+               /* Delete old Source-IP-Address */
+                checksum -= *((unsigned short *) (pkt->data+16)) ^ 0xFFFF;
+                checksum -= *((unsigned short *) (pkt->data+18)) ^ 0xFFFF;
+
+               pkt_checksum -= *((unsigned short *) (pkt->data+16)) ^ 0xFFFF;
+               pkt_checksum -= *((unsigned short *) (pkt->data+18)) ^ 0xFFFF;
+
+               /* Change Source-IP-Address */
+                * ((u32_t *) (pkt->data + 16)) = newip;
+
+               /* Add new Source-IP-Address */
+                checksum += *((unsigned short *) (pkt->data+16)) ^ 0xFFFF;
+                checksum += *((unsigned short *) (pkt->data+18)) ^ 0xFFFF;
+
+                pkt_checksum += *((unsigned short *) (pkt->data+16)) ^ 0xFFFF;
+                pkt_checksum += *((unsigned short *) (pkt->data+18)) ^ 0xFFFF;
+
+               /* Write new checksum */
+                if (!checksum) {
+                    checksum = 0xFFFF;
+                }
+                *((unsigned short *) (pkt->data+14)) = checksum;
+               if (pkt->data[13] == 6) {
+                   *((unsigned short *) (pkt->data+20+iphdr)) = pkt_checksum;
+               }
+               if (cv && (pkt->data[13] == 17) ) {
+                   *((unsigned short *) (pkt->data+10+iphdr)) = pkt_checksum;
+               }
+
+               /* Log Packet */
+               strcpy(ipstr,inet_ntoa(*( (struct in_addr *) (pkt->data+16))));
+               if (pkt->data[13] == 1) {
+                   syslog(LOG_INFO,"Open ICMP %s -> %s\n",
+                       ipstr,
+                       inet_ntoa(*( (struct in_addr *) (pkt->data+20))));
+               } else {
+                   syslog(LOG_INFO,"Open %s %s:%d -> %s:%d\n",
+                       pkt->data[13] == 6 ? "TCP" : "UDP",
+                       ipstr,
+                       ntohs(*( (short *) (pkt->data+iphdr+4))),
+                       inet_ntoa(*( (struct in_addr *) (pkt->data+20))),
+                       ntohs(*( (short *) (pkt->data+iphdr+6))));
+                }
+            }
+           output(pcb, pkt->data, pkt->length);
+           free(pkt);
+       } else {
+           if (prev == NULL)
+               pend_q = pkt;
+           else
+               prev->next = pkt;
+           prev = pkt;
+       }
+    }
+    pend_qtail = prev;
+    if (prev != NULL)
+       prev->next = NULL;
+}
+
+/*
+ * Scan a packet to decide whether it is an "active" packet,
+ * that is, whether it is worth bringing up the link for.
+ */
+static int
+active_packet(p, len)
+    unsigned char *p;
+    int len;
+{
+    int proto, i;
+    const struct protent *protp;
+
+    if (len < PPP_HDRLEN)
+       return 0;
+    proto = PPP_PROTOCOL(p);
+#ifdef PPP_FILTER
+    p[0] = 1;          /* outbound packet indicator */
+    if ((pass_filter.bf_len != 0
+        && bpf_filter(pass_filter.bf_insns, p, len, len) == 0)
+       || (active_filter.bf_len != 0
+           && bpf_filter(active_filter.bf_insns, p, len, len) == 0)) {
+       p[0] = 0xff;
+       return 0;
+    }
+    p[0] = 0xff;
+#endif
+    for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+       if (protp->protocol < 0xC000 && (protp->protocol & ~0x8000) == proto) {
+           if (protp->active_pkt == NULL)
+               return 1;
+           return (*protp->active_pkt)(p, len);
+       }
+    }
+    return 0;                  /* not a supported protocol !!?? */
+}
+
+#endif /* PPP_SUPPORT && DEMAND_SUPPORT */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/eap.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/eap.c
new file mode 100644 (file)
index 0000000..8fb5636
--- /dev/null
@@ -0,0 +1,2423 @@
+/*
+ * eap.c - Extensible Authentication Protocol for PPP (RFC 2284)
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Non-exclusive rights to redistribute, modify, translate, and use
+ * this software in source and binary forms, in whole or in part, is
+ * hereby granted, provided that the above copyright notice is
+ * duplicated in any source form, and that neither the name of the
+ * copyright holder nor the author is used to endorse or promote
+ * products derived from this software.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Original version by James Carlson
+ *
+ * This implementation of EAP supports MD5-Challenge and SRP-SHA1
+ * authentication styles.  Note that support of MD5-Challenge is a
+ * requirement of RFC 2284, and that it's essentially just a
+ * reimplementation of regular RFC 1994 CHAP using EAP messages.
+ *
+ * As an authenticator ("server"), there are multiple phases for each
+ * style.  In the first phase of each style, the unauthenticated peer
+ * name is queried using the EAP Identity request type.  If the
+ * "remotename" option is used, then this phase is skipped, because
+ * the peer's name is presumed to be known.
+ *
+ * For MD5-Challenge, there are two phases, and the second phase
+ * consists of sending the challenge itself and handling the
+ * associated response.
+ *
+ * For SRP-SHA1, there are four phases.  The second sends 's', 'N',
+ * and 'g'.  The reply contains 'A'.  The third sends 'B', and the
+ * reply contains 'M1'.  The forth sends the 'M2' value.
+ *
+ * As an authenticatee ("client"), there's just a single phase --
+ * responding to the queries generated by the peer.  EAP is an
+ * authenticator-driven protocol.
+ *
+ * Based on draft-ietf-pppext-eap-srp-03.txt.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && EAP_SUPPORT  /* don't build if not configured for use in lwipopts.h */
+
+#include "netif/ppp/ppp_impl.h"
+#include "netif/ppp/eap.h"
+#include "netif/ppp/magic.h"
+#include "netif/ppp/pppcrypt.h"
+
+#ifdef USE_SRP
+#include <t_pwd.h>
+#include <t_server.h>
+#include <t_client.h>
+#endif /* USE_SRP */
+
+#ifndef SHA_DIGESTSIZE
+#define        SHA_DIGESTSIZE 20
+#endif
+
+#ifdef USE_SRP
+static char *pn_secret = NULL;         /* Pseudonym generating secret */
+#endif
+
+#if PPP_OPTIONS
+/*
+ * Command-line options.
+ */
+static option_t eap_option_list[] = {
+    { "eap-restart", o_int, &eap_states[0].es_server.ea_timeout,
+      "Set retransmit timeout for EAP Requests (server)" },
+    { "eap-max-sreq", o_int, &eap_states[0].es_server.ea_maxrequests,
+      "Set max number of EAP Requests sent (server)" },
+    { "eap-timeout", o_int, &eap_states[0].es_client.ea_timeout,
+      "Set time limit for peer EAP authentication" },
+    { "eap-max-rreq", o_int, &eap_states[0].es_client.ea_maxrequests,
+      "Set max number of EAP Requests allows (client)" },
+    { "eap-interval", o_int, &eap_states[0].es_rechallenge,
+      "Set interval for EAP rechallenge" },
+#ifdef USE_SRP
+    { "srp-interval", o_int, &eap_states[0].es_lwrechallenge,
+      "Set interval for SRP lightweight rechallenge" },
+    { "srp-pn-secret", o_string, &pn_secret,
+      "Long term pseudonym generation secret" },
+    { "srp-use-pseudonym", o_bool, &eap_states[0].es_usepseudo,
+      "Use pseudonym if offered one by server", 1 },
+#endif
+    { NULL }
+};
+#endif /* PPP_OPTIONS */
+
+/*
+ * Protocol entry points.
+ */
+static void eap_init(ppp_pcb *pcb);
+static void eap_input(ppp_pcb *pcb, u_char *inp, int inlen);
+static void eap_protrej(ppp_pcb *pcb);
+static void eap_lowerup(ppp_pcb *pcb);
+static void eap_lowerdown(ppp_pcb *pcb);
+#if PRINTPKT_SUPPORT
+static int  eap_printpkt(const u_char *inp, int inlen,
+    void (*)(void *arg, const char *fmt, ...), void *arg);
+#endif /* PRINTPKT_SUPPORT */
+
+const struct protent eap_protent = {
+       PPP_EAP,                /* protocol number */
+       eap_init,               /* initialization procedure */
+       eap_input,              /* process a received packet */
+       eap_protrej,            /* process a received protocol-reject */
+       eap_lowerup,            /* lower layer has gone up */
+       eap_lowerdown,          /* lower layer has gone down */
+       NULL,                   /* open the protocol */
+       NULL,                   /* close the protocol */
+#if PRINTPKT_SUPPORT
+       eap_printpkt,           /* print a packet in readable form */
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_DATAINPUT
+       NULL,                   /* process a received data packet */
+#endif /* PPP_DATAINPUT */
+#if PRINTPKT_SUPPORT
+       "EAP",                  /* text name of protocol */
+       NULL,                   /* text name of corresponding data protocol */
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_OPTIONS
+       eap_option_list,        /* list of command-line options */
+       NULL,                   /* check requested options; assign defaults */
+#endif /* PPP_OPTIONS */
+#if DEMAND_SUPPORT
+       NULL,                   /* configure interface for demand-dial */
+       NULL                    /* say whether to bring up link for this pkt */
+#endif /* DEMAND_SUPPORT */
+};
+
+#ifdef USE_SRP
+/*
+ * A well-known 2048 bit modulus.
+ */
+static const u_char wkmodulus[] = {
+       0xAC, 0x6B, 0xDB, 0x41, 0x32, 0x4A, 0x9A, 0x9B,
+       0xF1, 0x66, 0xDE, 0x5E, 0x13, 0x89, 0x58, 0x2F,
+       0xAF, 0x72, 0xB6, 0x65, 0x19, 0x87, 0xEE, 0x07,
+       0xFC, 0x31, 0x92, 0x94, 0x3D, 0xB5, 0x60, 0x50,
+       0xA3, 0x73, 0x29, 0xCB, 0xB4, 0xA0, 0x99, 0xED,
+       0x81, 0x93, 0xE0, 0x75, 0x77, 0x67, 0xA1, 0x3D,
+       0xD5, 0x23, 0x12, 0xAB, 0x4B, 0x03, 0x31, 0x0D,
+       0xCD, 0x7F, 0x48, 0xA9, 0xDA, 0x04, 0xFD, 0x50,
+       0xE8, 0x08, 0x39, 0x69, 0xED, 0xB7, 0x67, 0xB0,
+       0xCF, 0x60, 0x95, 0x17, 0x9A, 0x16, 0x3A, 0xB3,
+       0x66, 0x1A, 0x05, 0xFB, 0xD5, 0xFA, 0xAA, 0xE8,
+       0x29, 0x18, 0xA9, 0x96, 0x2F, 0x0B, 0x93, 0xB8,
+       0x55, 0xF9, 0x79, 0x93, 0xEC, 0x97, 0x5E, 0xEA,
+       0xA8, 0x0D, 0x74, 0x0A, 0xDB, 0xF4, 0xFF, 0x74,
+       0x73, 0x59, 0xD0, 0x41, 0xD5, 0xC3, 0x3E, 0xA7,
+       0x1D, 0x28, 0x1E, 0x44, 0x6B, 0x14, 0x77, 0x3B,
+       0xCA, 0x97, 0xB4, 0x3A, 0x23, 0xFB, 0x80, 0x16,
+       0x76, 0xBD, 0x20, 0x7A, 0x43, 0x6C, 0x64, 0x81,
+       0xF1, 0xD2, 0xB9, 0x07, 0x87, 0x17, 0x46, 0x1A,
+       0x5B, 0x9D, 0x32, 0xE6, 0x88, 0xF8, 0x77, 0x48,
+       0x54, 0x45, 0x23, 0xB5, 0x24, 0xB0, 0xD5, 0x7D,
+       0x5E, 0xA7, 0x7A, 0x27, 0x75, 0xD2, 0xEC, 0xFA,
+       0x03, 0x2C, 0xFB, 0xDB, 0xF5, 0x2F, 0xB3, 0x78,
+       0x61, 0x60, 0x27, 0x90, 0x04, 0xE5, 0x7A, 0xE6,
+       0xAF, 0x87, 0x4E, 0x73, 0x03, 0xCE, 0x53, 0x29,
+       0x9C, 0xCC, 0x04, 0x1C, 0x7B, 0xC3, 0x08, 0xD8,
+       0x2A, 0x56, 0x98, 0xF3, 0xA8, 0xD0, 0xC3, 0x82,
+       0x71, 0xAE, 0x35, 0xF8, 0xE9, 0xDB, 0xFB, 0xB6,
+       0x94, 0xB5, 0xC8, 0x03, 0xD8, 0x9F, 0x7A, 0xE4,
+       0x35, 0xDE, 0x23, 0x6D, 0x52, 0x5F, 0x54, 0x75,
+       0x9B, 0x65, 0xE3, 0x72, 0xFC, 0xD6, 0x8E, 0xF2,
+       0x0F, 0xA7, 0x11, 0x1F, 0x9E, 0x4A, 0xFF, 0x73
+};
+#endif
+
+#if PPP_SERVER
+/* Local forward declarations. */
+static void eap_server_timeout(void *arg);
+#endif /* PPP_SERVER */
+
+/*
+ * Convert EAP state code to printable string for debug.
+ */
+static const char * eap_state_name(enum eap_state_code esc)
+{
+       static const char *state_names[] = { EAP_STATES };
+
+       return (state_names[(int)esc]);
+}
+
+/*
+ * eap_init - Initialize state for an EAP user.  This is currently
+ * called once by main() during start-up.
+ */
+static void eap_init(ppp_pcb *pcb) {
+
+       BZERO(&pcb->eap, sizeof(eap_state));
+#if PPP_SERVER
+       pcb->eap.es_server.ea_id = magic();
+#endif /* PPP_SERVER */
+}
+
+/*
+ * eap_client_timeout - Give up waiting for the peer to send any
+ * Request messages.
+ */
+static void eap_client_timeout(void *arg) {
+       ppp_pcb *pcb = (ppp_pcb*)arg;
+
+       if (!eap_client_active(pcb))
+               return;
+
+       ppp_error("EAP: timeout waiting for Request from peer");
+       auth_withpeer_fail(pcb, PPP_EAP);
+       pcb->eap.es_client.ea_state = eapBadAuth;
+}
+
+/*
+ * eap_authwithpeer - Authenticate to our peer (behave as client).
+ *
+ * Start client state and wait for requests.  This is called only
+ * after eap_lowerup.
+ */
+void eap_authwithpeer(ppp_pcb *pcb, const char *localname) {
+
+       if(NULL == localname)
+               return;
+
+       /* Save the peer name we're given */
+       pcb->eap.es_client.ea_name = localname;
+       pcb->eap.es_client.ea_namelen = strlen(localname);
+
+       pcb->eap.es_client.ea_state = eapListen;
+
+       /*
+        * Start a timer so that if the other end just goes
+        * silent, we don't sit here waiting forever.
+        */
+       if (pcb->settings.eap_req_time > 0)
+               TIMEOUT(eap_client_timeout, pcb,
+                   pcb->settings.eap_req_time);
+}
+
+#if PPP_SERVER
+/*
+ * Format a standard EAP Failure message and send it to the peer.
+ * (Server operation)
+ */
+static void eap_send_failure(ppp_pcb *pcb) {
+       struct pbuf *p;
+       u_char *outp;
+
+       p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + EAP_HEADERLEN), PPP_CTRL_PBUF_TYPE);
+       if(NULL == p)
+               return;
+       if(p->tot_len != p->len) {
+               pbuf_free(p);
+               return;
+       }
+
+       outp = (u_char*)p->payload;
+
+       MAKEHEADER(outp, PPP_EAP);
+
+       PUTCHAR(EAP_FAILURE, outp);
+       pcb->eap.es_server.ea_id++;
+       PUTCHAR(pcb->eap.es_server.ea_id, outp);
+       PUTSHORT(EAP_HEADERLEN, outp);
+
+       ppp_write(pcb, p);
+
+       pcb->eap.es_server.ea_state = eapBadAuth;
+       auth_peer_fail(pcb, PPP_EAP);
+}
+
+/*
+ * Format a standard EAP Success message and send it to the peer.
+ * (Server operation)
+ */
+static void eap_send_success(ppp_pcb *pcb) {
+       struct pbuf *p;
+       u_char *outp;
+
+       p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + EAP_HEADERLEN), PPP_CTRL_PBUF_TYPE);
+       if(NULL == p)
+               return;
+       if(p->tot_len != p->len) {
+               pbuf_free(p);
+               return;
+       }
+
+       outp = (u_char*)p->payload;
+    
+       MAKEHEADER(outp, PPP_EAP);
+
+       PUTCHAR(EAP_SUCCESS, outp);
+       pcb->eap.es_server.ea_id++;
+       PUTCHAR(pcb->eap.es_server.ea_id, outp);
+       PUTSHORT(EAP_HEADERLEN, outp);
+
+       ppp_write(pcb, p);
+
+       auth_peer_success(pcb, PPP_EAP, 0,
+           pcb->eap.es_server.ea_peer, pcb->eap.es_server.ea_peerlen);
+}
+#endif /* PPP_SERVER */
+
+#ifdef USE_SRP
+/*
+ * Set DES key according to pseudonym-generating secret and current
+ * date.
+ */
+static bool
+pncrypt_setkey(int timeoffs)
+{
+       struct tm *tp;
+       char tbuf[9];
+       SHA1_CTX ctxt;
+       u_char dig[SHA_DIGESTSIZE];
+       time_t reftime;
+
+       if (pn_secret == NULL)
+               return (0);
+       reftime = time(NULL) + timeoffs;
+       tp = localtime(&reftime);
+       SHA1Init(&ctxt);
+       SHA1Update(&ctxt, pn_secret, strlen(pn_secret));
+       strftime(tbuf, sizeof (tbuf), "%Y%m%d", tp);
+       SHA1Update(&ctxt, tbuf, strlen(tbuf));
+       SHA1Final(dig, &ctxt);
+       /* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */
+       return (DesSetkey(dig));
+}
+
+static char base64[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+struct b64state {
+       u32_t bs_bits;
+       int bs_offs;
+};
+
+static int
+b64enc(bs, inp, inlen, outp)
+struct b64state *bs;
+u_char *inp;
+int inlen;
+u_char *outp;
+{
+       int outlen = 0;
+
+       while (inlen > 0) {
+               bs->bs_bits = (bs->bs_bits << 8) | *inp++;
+               inlen--;
+               bs->bs_offs += 8;
+               if (bs->bs_offs >= 24) {
+                       *outp++ = base64[(bs->bs_bits >> 18) & 0x3F];
+                       *outp++ = base64[(bs->bs_bits >> 12) & 0x3F];
+                       *outp++ = base64[(bs->bs_bits >> 6) & 0x3F];
+                       *outp++ = base64[bs->bs_bits & 0x3F];
+                       outlen += 4;
+                       bs->bs_offs = 0;
+                       bs->bs_bits = 0;
+               }
+       }
+       return (outlen);
+}
+
+static int
+b64flush(bs, outp)
+struct b64state *bs;
+u_char *outp;
+{
+       int outlen = 0;
+
+       if (bs->bs_offs == 8) {
+               *outp++ = base64[(bs->bs_bits >> 2) & 0x3F];
+               *outp++ = base64[(bs->bs_bits << 4) & 0x3F];
+               outlen = 2;
+       } else if (bs->bs_offs == 16) {
+               *outp++ = base64[(bs->bs_bits >> 10) & 0x3F];
+               *outp++ = base64[(bs->bs_bits >> 4) & 0x3F];
+               *outp++ = base64[(bs->bs_bits << 2) & 0x3F];
+               outlen = 3;
+       }
+       bs->bs_offs = 0;
+       bs->bs_bits = 0;
+       return (outlen);
+}
+
+static int
+b64dec(bs, inp, inlen, outp)
+struct b64state *bs;
+u_char *inp;
+int inlen;
+u_char *outp;
+{
+       int outlen = 0;
+       char *cp;
+
+       while (inlen > 0) {
+               if ((cp = strchr(base64, *inp++)) == NULL)
+                       break;
+               bs->bs_bits = (bs->bs_bits << 6) | (cp - base64);
+               inlen--;
+               bs->bs_offs += 6;
+               if (bs->bs_offs >= 8) {
+                       *outp++ = bs->bs_bits >> (bs->bs_offs - 8);
+                       outlen++;
+                       bs->bs_offs -= 8;
+               }
+       }
+       return (outlen);
+}
+#endif /* USE_SRP */
+
+#if PPP_SERVER
+/*
+ * Assume that current waiting server state is complete and figure
+ * next state to use based on available authentication data.  'status'
+ * indicates if there was an error in handling the last query.  It is
+ * 0 for success and non-zero for failure.
+ */
+static void eap_figure_next_state(ppp_pcb *pcb, int status) {
+#ifdef USE_SRP
+       unsigned char secbuf[MAXSECRETLEN], clear[8], *sp, *dp;
+       struct t_pw tpw;
+       struct t_confent *tce, mytce;
+       char *cp, *cp2;
+       struct t_server *ts;
+       int id, i, plen, toffs;
+       u_char vals[2];
+       struct b64state bs;
+#endif /* USE_SRP */
+
+       pcb->settings.eap_timeout_time = pcb->eap.es_savedtime;
+       switch (pcb->eap.es_server.ea_state) {
+       case eapBadAuth:
+               return;
+
+       case eapIdentify:
+#ifdef USE_SRP
+               /* Discard any previous session. */
+               ts = (struct t_server *)pcb->eap.es_server.ea_session;
+               if (ts != NULL) {
+                       t_serverclose(ts);
+                       pcb->eap.es_server.ea_session = NULL;
+                       pcb->eap.es_server.ea_skey = NULL;
+               }
+#endif /* USE_SRP */
+               if (status != 0) {
+                       pcb->eap.es_server.ea_state = eapBadAuth;
+                       break;
+               }
+#ifdef USE_SRP
+               /* If we've got a pseudonym, try to decode to real name. */
+               if (pcb->eap.es_server.ea_peerlen > SRP_PSEUDO_LEN &&
+                   strncmp(pcb->eap.es_server.ea_peer, SRP_PSEUDO_ID,
+                       SRP_PSEUDO_LEN) == 0 &&
+                   (pcb->eap.es_server.ea_peerlen - SRP_PSEUDO_LEN) * 3 / 4 <
+                   sizeof (secbuf)) {
+                       BZERO(&bs, sizeof (bs));
+                       plen = b64dec(&bs,
+                           pcb->eap.es_server.ea_peer + SRP_PSEUDO_LEN,
+                           pcb->eap.es_server.ea_peerlen - SRP_PSEUDO_LEN,
+                           secbuf);
+                       toffs = 0;
+                       for (i = 0; i < 5; i++) {
+                               pncrypt_setkey(toffs);
+                               toffs -= 86400;
+                               /* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */
+                               if (!DesDecrypt(secbuf, clear)) {
+                                       ppp_dbglog("no DES here; cannot decode "
+                                           "pseudonym");
+                                       return;
+                               }
+                               id = *(unsigned char *)clear;
+                               if (id + 1 <= plen && id + 9 > plen)
+                                       break;
+                       }
+                       if (plen % 8 == 0 && i < 5) {
+                               /*
+                                * Note that this is always shorter than the
+                                * original stored string, so there's no need
+                                * to realloc.
+                                */
+                               if ((i = plen = *(unsigned char *)clear) > 7)
+                                       i = 7;
+                               pcb->eap.es_server.ea_peerlen = plen;
+                               dp = (unsigned char *)pcb->eap.es_server.ea_peer;
+                               MEMCPY(dp, clear + 1, i);
+                               plen -= i;
+                               dp += i;
+                               sp = secbuf + 8;
+                               while (plen > 0) {
+                                       /* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */
+                                       (void) DesDecrypt(sp, dp);
+                                       sp += 8;
+                                       dp += 8;
+                                       plen -= 8;
+                               }
+                               pcb->eap.es_server.ea_peer[
+                                       pcb->eap.es_server.ea_peerlen] = '\0';
+                               ppp_dbglog("decoded pseudonym to \"%.*q\"",
+                                   pcb->eap.es_server.ea_peerlen,
+                                   pcb->eap.es_server.ea_peer);
+                       } else {
+                               ppp_dbglog("failed to decode real name");
+                               /* Stay in eapIdentfy state; requery */
+                               break;
+                       }
+               }
+               /* Look up user in secrets database. */
+               if (get_srp_secret(pcb->eap.es_unit, pcb->eap.es_server.ea_peer,
+                   pcb->eap.es_server.ea_name, (char *)secbuf, 1) != 0) {
+                       /* Set up default in case SRP entry is bad */
+                       pcb->eap.es_server.ea_state = eapMD5Chall;
+                       /* Get t_confent based on index in srp-secrets */
+                       id = strtol((char *)secbuf, &cp, 10);
+                       if (*cp++ != ':' || id < 0)
+                               break;
+                       if (id == 0) {
+                               mytce.index = 0;
+                               mytce.modulus.data = (u_char *)wkmodulus;
+                               mytce.modulus.len = sizeof (wkmodulus);
+                               mytce.generator.data = (u_char *)"\002";
+                               mytce.generator.len = 1;
+                               tce = &mytce;
+                       } else if ((tce = gettcid(id)) != NULL) {
+                               /*
+                                * Client will have to verify this modulus/
+                                * generator combination, and that will take
+                                * a while.  Lengthen the timeout here.
+                                */
+                               if (pcb->settings.eap_timeout_time > 0 &&
+                                   pcb->settings.eap_timeout_time < 30)
+                                       pcb->settings.eap_timeout_time = 30;
+                       } else {
+                               break;
+                       }
+                       if ((cp2 = strchr(cp, ':')) == NULL)
+                               break;
+                       *cp2++ = '\0';
+                       tpw.pebuf.name = pcb->eap.es_server.ea_peer;
+                       tpw.pebuf.password.len = t_fromb64((char *)tpw.pwbuf,
+                           cp);
+                       tpw.pebuf.password.data = tpw.pwbuf;
+                       tpw.pebuf.salt.len = t_fromb64((char *)tpw.saltbuf,
+                           cp2);
+                       tpw.pebuf.salt.data = tpw.saltbuf;
+                       if ((ts = t_serveropenraw(&tpw.pebuf, tce)) == NULL)
+                               break;
+                       pcb->eap.es_server.ea_session = (void *)ts;
+                       pcb->eap.es_server.ea_state = eapSRP1;
+                       vals[0] = pcb->eap.es_server.ea_id + 1;
+                       vals[1] = EAPT_SRP;
+                       t_serveraddexdata(ts, vals, 2);
+                       /* Generate B; must call before t_servergetkey() */
+                       t_servergenexp(ts);
+                       break;
+               }
+#endif /* USE_SRP */
+               pcb->eap.es_server.ea_state = eapMD5Chall;
+               break;
+
+       case eapSRP1:
+#ifdef USE_SRP
+               ts = (struct t_server *)pcb->eap.es_server.ea_session;
+               if (ts != NULL && status != 0) {
+                       t_serverclose(ts);
+                       pcb->eap.es_server.ea_session = NULL;
+                       pcb->eap.es_server.ea_skey = NULL;
+               }
+#endif /* USE_SRP */
+               if (status == 1) {
+                       pcb->eap.es_server.ea_state = eapMD5Chall;
+               } else if (status != 0 || pcb->eap.es_server.ea_session == NULL) {
+                       pcb->eap.es_server.ea_state = eapBadAuth;
+               } else {
+                       pcb->eap.es_server.ea_state = eapSRP2;
+               }
+               break;
+
+       case eapSRP2:
+#ifdef USE_SRP
+               ts = (struct t_server *)pcb->eap.es_server.ea_session;
+               if (ts != NULL && status != 0) {
+                       t_serverclose(ts);
+                       pcb->eap.es_server.ea_session = NULL;
+                       pcb->eap.es_server.ea_skey = NULL;
+               }
+#endif /* USE_SRP */
+               if (status != 0 || pcb->eap.es_server.ea_session == NULL) {
+                       pcb->eap.es_server.ea_state = eapBadAuth;
+               } else {
+                       pcb->eap.es_server.ea_state = eapSRP3;
+               }
+               break;
+
+       case eapSRP3:
+       case eapSRP4:
+#ifdef USE_SRP
+               ts = (struct t_server *)pcb->eap.es_server.ea_session;
+               if (ts != NULL && status != 0) {
+                       t_serverclose(ts);
+                       pcb->eap.es_server.ea_session = NULL;
+                       pcb->eap.es_server.ea_skey = NULL;
+               }
+#endif /* USE_SRP */
+               if (status != 0 || pcb->eap.es_server.ea_session == NULL) {
+                       pcb->eap.es_server.ea_state = eapBadAuth;
+               } else {
+                       pcb->eap.es_server.ea_state = eapOpen;
+               }
+               break;
+
+       case eapMD5Chall:
+               if (status != 0) {
+                       pcb->eap.es_server.ea_state = eapBadAuth;
+               } else {
+                       pcb->eap.es_server.ea_state = eapOpen;
+               }
+               break;
+
+       default:
+               pcb->eap.es_server.ea_state = eapBadAuth;
+               break;
+       }
+       if (pcb->eap.es_server.ea_state == eapBadAuth)
+               eap_send_failure(pcb);
+}
+
+/*
+ * Format an EAP Request message and send it to the peer.  Message
+ * type depends on current state.  (Server operation)
+ */
+static void eap_send_request(ppp_pcb *pcb) {
+       struct pbuf *p;
+       u_char *outp;
+       u_char *lenloc;
+       int outlen;
+       int len;
+       const char *str;
+#ifdef USE_SRP
+       struct t_server *ts;
+       u_char clear[8], cipher[8], dig[SHA_DIGESTSIZE], *optr, *cp;
+       int i, j;
+       struct b64state b64;
+       SHA1_CTX ctxt;
+#endif /* USE_SRP */
+
+       /* Handle both initial auth and restart */
+       if (pcb->eap.es_server.ea_state < eapIdentify &&
+           pcb->eap.es_server.ea_state != eapInitial) {
+               pcb->eap.es_server.ea_state = eapIdentify;
+#if PPP_REMOTENAME
+               if (pcb->settings.explicit_remote && pcb->remote_name) {
+                       /*
+                        * If we already know the peer's
+                        * unauthenticated name, then there's no
+                        * reason to ask.  Go to next state instead.
+                        */
+                       int len = (int)strlen(pcb->remote_name);
+                       if (len > MAXNAMELEN) {
+                               len = MAXNAMELEN;
+                       }
+                       MEMCPY(pcb->eap.es_server.ea_peer, pcb->remote_name, len);
+                       pcb->eap.es_server.ea_peer[len] = '\0';
+                       pcb->eap.es_server.ea_peerlen = len;
+                       eap_figure_next_state(pcb, 0);
+               }
+#endif /* PPP_REMOTENAME */
+       }
+
+       if (pcb->settings.eap_max_transmits > 0 &&
+           pcb->eap.es_server.ea_requests >= pcb->settings.eap_max_transmits) {
+               if (pcb->eap.es_server.ea_responses > 0)
+                       ppp_error("EAP: too many Requests sent");
+               else
+                       ppp_error("EAP: no response to Requests");
+               eap_send_failure(pcb);
+               return;
+       }
+
+       p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_CTRL_PBUF_MAX_SIZE), PPP_CTRL_PBUF_TYPE);
+       if(NULL == p)
+               return;
+       if(p->tot_len != p->len) {
+               pbuf_free(p);
+               return;
+       }
+
+       outp = (u_char*)p->payload;
+    
+       MAKEHEADER(outp, PPP_EAP);
+
+       PUTCHAR(EAP_REQUEST, outp);
+       PUTCHAR(pcb->eap.es_server.ea_id, outp);
+       lenloc = outp;
+       INCPTR(2, outp);
+
+       switch (pcb->eap.es_server.ea_state) {
+       case eapIdentify:
+               PUTCHAR(EAPT_IDENTITY, outp);
+               str = "Name";
+               len = strlen(str);
+               MEMCPY(outp, str, len);
+               INCPTR(len, outp);
+               break;
+
+       case eapMD5Chall:
+               PUTCHAR(EAPT_MD5CHAP, outp);
+               /*
+                * pick a random challenge length between
+                * EAP_MIN_CHALLENGE_LENGTH and EAP_MAX_CHALLENGE_LENGTH
+                */
+               pcb->eap.es_challen = EAP_MIN_CHALLENGE_LENGTH +
+                   magic_pow(EAP_MIN_MAX_POWER_OF_TWO_CHALLENGE_LENGTH);
+               PUTCHAR(pcb->eap.es_challen, outp);
+               magic_random_bytes(pcb->eap.es_challenge, pcb->eap.es_challen);
+               MEMCPY(outp, pcb->eap.es_challenge, pcb->eap.es_challen);
+               INCPTR(pcb->eap.es_challen, outp);
+               MEMCPY(outp, pcb->eap.es_server.ea_name, pcb->eap.es_server.ea_namelen);
+               INCPTR(pcb->eap.es_server.ea_namelen, outp);
+               break;
+
+#ifdef USE_SRP
+       case eapSRP1:
+               PUTCHAR(EAPT_SRP, outp);
+               PUTCHAR(EAPSRP_CHALLENGE, outp);
+
+               PUTCHAR(pcb->eap.es_server.ea_namelen, outp);
+               MEMCPY(outp, pcb->eap.es_server.ea_name, pcb->eap.es_server.ea_namelen);
+               INCPTR(pcb->eap.es_server.ea_namelen, outp);
+
+               ts = (struct t_server *)pcb->eap.es_server.ea_session;
+               assert(ts != NULL);
+               PUTCHAR(ts->s.len, outp);
+               MEMCPY(outp, ts->s.data, ts->s.len);
+               INCPTR(ts->s.len, outp);
+
+               if (ts->g.len == 1 && ts->g.data[0] == 2) {
+                       PUTCHAR(0, outp);
+               } else {
+                       PUTCHAR(ts->g.len, outp);
+                       MEMCPY(outp, ts->g.data, ts->g.len);
+                       INCPTR(ts->g.len, outp);
+               }
+
+               if (ts->n.len != sizeof (wkmodulus) ||
+                   BCMP(ts->n.data, wkmodulus, sizeof (wkmodulus)) != 0) {
+                       MEMCPY(outp, ts->n.data, ts->n.len);
+                       INCPTR(ts->n.len, outp);
+               }
+               break;
+
+       case eapSRP2:
+               PUTCHAR(EAPT_SRP, outp);
+               PUTCHAR(EAPSRP_SKEY, outp);
+
+               ts = (struct t_server *)pcb->eap.es_server.ea_session;
+               assert(ts != NULL);
+               MEMCPY(outp, ts->B.data, ts->B.len);
+               INCPTR(ts->B.len, outp);
+               break;
+
+       case eapSRP3:
+               PUTCHAR(EAPT_SRP, outp);
+               PUTCHAR(EAPSRP_SVALIDATOR, outp);
+               PUTLONG(SRPVAL_EBIT, outp);
+               ts = (struct t_server *)pcb->eap.es_server.ea_session;
+               assert(ts != NULL);
+               MEMCPY(outp, t_serverresponse(ts), SHA_DIGESTSIZE);
+               INCPTR(SHA_DIGESTSIZE, outp);
+
+               if (pncrypt_setkey(0)) {
+                       /* Generate pseudonym */
+                       optr = outp;
+                       cp = (unsigned char *)pcb->eap.es_server.ea_peer;
+                       if ((j = i = pcb->eap.es_server.ea_peerlen) > 7)
+                               j = 7;
+                       clear[0] = i;
+                       MEMCPY(clear + 1, cp, j);
+                       i -= j;
+                       cp += j;
+                       /* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */
+                       if (!DesEncrypt(clear, cipher)) {
+                               ppp_dbglog("no DES here; not generating pseudonym");
+                               break;
+                       }
+                       BZERO(&b64, sizeof (b64));
+                       outp++;         /* space for pseudonym length */
+                       outp += b64enc(&b64, cipher, 8, outp);
+                       while (i >= 8) {
+                               /* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */
+                               (void) DesEncrypt(cp, cipher);
+                               outp += b64enc(&b64, cipher, 8, outp);
+                               cp += 8;
+                               i -= 8;
+                       }
+                       if (i > 0) {
+                               MEMCPY(clear, cp, i);
+                               cp += i;
+                               magic_random_bytes(cp, 8-i);
+                               /* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */
+                               (void) DesEncrypt(clear, cipher);
+                               outp += b64enc(&b64, cipher, 8, outp);
+                       }
+                       outp += b64flush(&b64, outp);
+
+                       /* Set length and pad out to next 20 octet boundary */
+                       i = outp - optr - 1;
+                       *optr = i;
+                       i %= SHA_DIGESTSIZE;
+                       if (i != 0) {
+                               magic_random_bytes(outp, SHA_DIGESTSIZE-i);
+                               INCPTR(SHA_DIGESTSIZE-i, outp);
+                       }
+
+                       /* Obscure the pseudonym with SHA1 hash */
+                       SHA1Init(&ctxt);
+                       SHA1Update(&ctxt, &pcb->eap.es_server.ea_id, 1);
+                       SHA1Update(&ctxt, pcb->eap.es_server.ea_skey,
+                           SESSION_KEY_LEN);
+                       SHA1Update(&ctxt, pcb->eap.es_server.ea_peer,
+                           pcb->eap.es_server.ea_peerlen);
+                       while (optr < outp) {
+                               SHA1Final(dig, &ctxt);
+                               cp = dig;
+                               while (cp < dig + SHA_DIGESTSIZE)
+                                       *optr++ ^= *cp++;
+                               SHA1Init(&ctxt);
+                               SHA1Update(&ctxt, &pcb->eap.es_server.ea_id, 1);
+                               SHA1Update(&ctxt, pcb->eap.es_server.ea_skey,
+                                   SESSION_KEY_LEN);
+                               SHA1Update(&ctxt, optr - SHA_DIGESTSIZE,
+                                   SHA_DIGESTSIZE);
+                       }
+               }
+               break;
+
+       case eapSRP4:
+               PUTCHAR(EAPT_SRP, outp);
+               PUTCHAR(EAPSRP_LWRECHALLENGE, outp);
+               pcb->eap.es_challen = EAP_MIN_CHALLENGE_LENGTH +
+                   magic_pow(EAP_MIN_MAX_POWER_OF_TWO_CHALLENGE_LENGTH);
+               magic_random_bytes(pcb->eap.es_challenge, pcb->eap.es_challen);
+               MEMCPY(outp, pcb->eap.es_challenge, pcb->eap.es_challen);
+               INCPTR(pcb->eap.es_challen, outp);
+               break;
+#endif /* USE_SRP */
+
+       default:
+               return;
+       }
+
+       outlen = (outp - (unsigned char*)p->payload) - PPP_HDRLEN;
+       PUTSHORT(outlen, lenloc);
+
+       pbuf_realloc(p, outlen + PPP_HDRLEN);
+       ppp_write(pcb, p);
+
+       pcb->eap.es_server.ea_requests++;
+
+       if (pcb->settings.eap_timeout_time > 0)
+               TIMEOUT(eap_server_timeout, pcb, pcb->settings.eap_timeout_time);
+}
+
+/*
+ * eap_authpeer - Authenticate our peer (behave as server).
+ *
+ * Start server state and send first request.  This is called only
+ * after eap_lowerup.
+ */
+void eap_authpeer(ppp_pcb *pcb, const char *localname) {
+
+       /* Save the name we're given. */
+       pcb->eap.es_server.ea_name = localname;
+       pcb->eap.es_server.ea_namelen = strlen(localname);
+
+       pcb->eap.es_savedtime = pcb->settings.eap_timeout_time;
+
+       /* Lower layer up yet? */
+       if (pcb->eap.es_server.ea_state == eapInitial ||
+           pcb->eap.es_server.ea_state == eapPending) {
+               pcb->eap.es_server.ea_state = eapPending;
+               return;
+       }
+
+       pcb->eap.es_server.ea_state = eapPending;
+
+       /* ID number not updated here intentionally; hashed into M1 */
+       eap_send_request(pcb);
+}
+
+/*
+ * eap_server_timeout - Retransmission timer for sending Requests
+ * expired.
+ */
+static void eap_server_timeout(void *arg) {
+       ppp_pcb *pcb = (ppp_pcb*)arg;
+
+       if (!eap_server_active(pcb))
+               return;
+
+       /* EAP ID number must not change on timeout. */
+       eap_send_request(pcb);
+}
+
+/*
+ * When it's time to send rechallenge the peer, this timeout is
+ * called.  Once the rechallenge is successful, the response handler
+ * will restart the timer.  If it fails, then the link is dropped.
+ */
+static void eap_rechallenge(void *arg) {
+       ppp_pcb *pcb = (ppp_pcb*)arg;
+
+       if (pcb->eap.es_server.ea_state != eapOpen &&
+           pcb->eap.es_server.ea_state != eapSRP4)
+               return;
+
+       pcb->eap.es_server.ea_requests = 0;
+       pcb->eap.es_server.ea_state = eapIdentify;
+       eap_figure_next_state(pcb, 0);
+       pcb->eap.es_server.ea_id++;
+       eap_send_request(pcb);
+}
+
+static void srp_lwrechallenge(void *arg) {
+       ppp_pcb *pcb = (ppp_pcb*)arg;
+
+       if (pcb->eap.es_server.ea_state != eapOpen ||
+           pcb->eap.es_server.ea_type != EAPT_SRP)
+               return;
+
+       pcb->eap.es_server.ea_requests = 0;
+       pcb->eap.es_server.ea_state = eapSRP4;
+       pcb->eap.es_server.ea_id++;
+       eap_send_request(pcb);
+}
+#endif /* PPP_SERVER */
+
+/*
+ * eap_lowerup - The lower layer is now up.
+ *
+ * This is called before either eap_authpeer or eap_authwithpeer.  See
+ * link_established() in auth.c.  All that's necessary here is to
+ * return to closed state so that those two routines will do the right
+ * thing.
+ */
+static void eap_lowerup(ppp_pcb *pcb) {
+       pcb->eap.es_client.ea_state = eapClosed;
+#if PPP_SERVER
+       pcb->eap.es_server.ea_state = eapClosed;
+#endif /* PPP_SERVER */
+}
+
+/*
+ * eap_lowerdown - The lower layer is now down.
+ *
+ * Cancel all timeouts and return to initial state.
+ */
+static void eap_lowerdown(ppp_pcb *pcb) {
+
+       if (eap_client_active(pcb) && pcb->settings.eap_req_time > 0) {
+               UNTIMEOUT(eap_client_timeout, pcb);
+       }
+#if PPP_SERVER
+       if (eap_server_active(pcb)) {
+               if (pcb->settings.eap_timeout_time > 0) {
+                       UNTIMEOUT(eap_server_timeout, pcb);
+               }
+       } else {
+               if ((pcb->eap.es_server.ea_state == eapOpen ||
+                   pcb->eap.es_server.ea_state == eapSRP4) &&
+                   pcb->eap.es_rechallenge > 0) {
+                       UNTIMEOUT(eap_rechallenge, (void *)pcb);
+               }
+               if (pcb->eap.es_server.ea_state == eapOpen &&
+                   pcb->eap.es_lwrechallenge > 0) {
+                       UNTIMEOUT(srp_lwrechallenge, (void *)pcb);
+               }
+       }
+
+       pcb->eap.es_client.ea_state = pcb->eap.es_server.ea_state = eapInitial;
+       pcb->eap.es_client.ea_requests = pcb->eap.es_server.ea_requests = 0;
+#endif /* PPP_SERVER */
+}
+
+/*
+ * eap_protrej - Peer doesn't speak this protocol.
+ *
+ * This shouldn't happen.  If it does, it represents authentication
+ * failure.
+ */
+static void eap_protrej(ppp_pcb *pcb) {
+
+       if (eap_client_active(pcb)) {
+               ppp_error("EAP authentication failed due to Protocol-Reject");
+               auth_withpeer_fail(pcb, PPP_EAP);
+       }
+#if PPP_SERVER
+       if (eap_server_active(pcb)) {
+               ppp_error("EAP authentication of peer failed on Protocol-Reject");
+               auth_peer_fail(pcb, PPP_EAP);
+       }
+#endif /* PPP_SERVER */
+       eap_lowerdown(pcb);
+}
+
+/*
+ * Format and send a regular EAP Response message.
+ */
+static void eap_send_response(ppp_pcb *pcb, u_char id, u_char typenum, const u_char *str, int lenstr) {
+       struct pbuf *p;
+       u_char *outp;
+       int msglen;
+
+       msglen = EAP_HEADERLEN + sizeof (u_char) + lenstr;
+       p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE);
+       if(NULL == p)
+               return;
+       if(p->tot_len != p->len) {
+               pbuf_free(p);
+               return;
+       }
+
+       outp = (u_char*)p->payload;
+
+       MAKEHEADER(outp, PPP_EAP);
+
+       PUTCHAR(EAP_RESPONSE, outp);
+       PUTCHAR(id, outp);
+       pcb->eap.es_client.ea_id = id;
+       PUTSHORT(msglen, outp);
+       PUTCHAR(typenum, outp);
+       if (lenstr > 0) {
+               MEMCPY(outp, str, lenstr);
+       }
+
+       ppp_write(pcb, p);
+}
+
+/*
+ * Format and send an MD5-Challenge EAP Response message.
+ */
+static void eap_chap_response(ppp_pcb *pcb, u_char id, u_char *hash, const char *name, int namelen) {
+       struct pbuf *p;
+       u_char *outp;
+       int msglen;
+
+       msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + MD5_SIGNATURE_SIZE +
+           namelen;
+       p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE);
+       if(NULL == p)
+               return;
+       if(p->tot_len != p->len) {
+               pbuf_free(p);
+               return;
+       }
+
+       outp = (u_char*)p->payload;
+    
+       MAKEHEADER(outp, PPP_EAP);
+
+       PUTCHAR(EAP_RESPONSE, outp);
+       PUTCHAR(id, outp);
+       pcb->eap.es_client.ea_id = id;
+       PUTSHORT(msglen, outp);
+       PUTCHAR(EAPT_MD5CHAP, outp);
+       PUTCHAR(MD5_SIGNATURE_SIZE, outp);
+       MEMCPY(outp, hash, MD5_SIGNATURE_SIZE);
+       INCPTR(MD5_SIGNATURE_SIZE, outp);
+       if (namelen > 0) {
+               MEMCPY(outp, name, namelen);
+       }
+
+       ppp_write(pcb, p);
+}
+
+#ifdef USE_SRP
+/*
+ * Format and send a SRP EAP Response message.
+ */
+static void
+eap_srp_response(esp, id, subtypenum, str, lenstr)
+eap_state *esp;
+u_char id;
+u_char subtypenum;
+u_char *str;
+int lenstr;
+{
+       ppp_pcb *pcb = &ppp_pcb_list[pcb->eap.es_unit];
+       struct pbuf *p;
+       u_char *outp;
+       int msglen;
+
+       msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + lenstr;
+       p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE);
+       if(NULL == p)
+               return;
+       if(p->tot_len != p->len) {
+               pbuf_free(p);
+               return;
+       }
+
+       outp = p->payload;
+
+       MAKEHEADER(outp, PPP_EAP);
+
+       PUTCHAR(EAP_RESPONSE, outp);
+       PUTCHAR(id, outp);
+       pcb->eap.es_client.ea_id = id;
+       PUTSHORT(msglen, outp);
+       PUTCHAR(EAPT_SRP, outp);
+       PUTCHAR(subtypenum, outp);
+       if (lenstr > 0) {
+               MEMCPY(outp, str, lenstr);
+       }
+
+       ppp_write(pcb, p);
+}
+
+/*
+ * Format and send a SRP EAP Client Validator Response message.
+ */
+static void
+eap_srpval_response(esp, id, flags, str)
+eap_state *esp;
+u_char id;
+u32_t flags;
+u_char *str;
+{
+       ppp_pcb *pcb = &ppp_pcb_list[pcb->eap.es_unit];
+       struct pbuf *p;
+       u_char *outp;
+       int msglen;
+
+       msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + sizeof (u32_t) +
+           SHA_DIGESTSIZE;
+       p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE);
+       if(NULL == p)
+               return;
+       if(p->tot_len != p->len) {
+               pbuf_free(p);
+               return;
+       }
+
+       outp = p->payload;
+
+       MAKEHEADER(outp, PPP_EAP);
+
+       PUTCHAR(EAP_RESPONSE, outp);
+       PUTCHAR(id, outp);
+       pcb->eap.es_client.ea_id = id;
+       PUTSHORT(msglen, outp);
+       PUTCHAR(EAPT_SRP, outp);
+       PUTCHAR(EAPSRP_CVALIDATOR, outp);
+       PUTLONG(flags, outp);
+       MEMCPY(outp, str, SHA_DIGESTSIZE);
+
+       ppp_write(pcb, p);
+}
+#endif /* USE_SRP */
+
+static void eap_send_nak(ppp_pcb *pcb, u_char id, u_char type) {
+       struct pbuf *p;
+       u_char *outp;
+       int msglen;
+
+       msglen = EAP_HEADERLEN + 2 * sizeof (u_char);
+       p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE);
+       if(NULL == p)
+               return;
+       if(p->tot_len != p->len) {
+               pbuf_free(p);
+               return;
+       }
+
+       outp = (u_char*)p->payload;
+
+       MAKEHEADER(outp, PPP_EAP);
+
+       PUTCHAR(EAP_RESPONSE, outp);
+       PUTCHAR(id, outp);
+       pcb->eap.es_client.ea_id = id;
+       PUTSHORT(msglen, outp);
+       PUTCHAR(EAPT_NAK, outp);
+       PUTCHAR(type, outp);
+
+       ppp_write(pcb, p);
+}
+
+#ifdef USE_SRP
+static char *
+name_of_pn_file()
+{
+       char *user, *path, *file;
+       struct passwd *pw;
+       size_t pl;
+       static bool pnlogged = 0;
+
+       pw = getpwuid(getuid());
+       if (pw == NULL || (user = pw->pw_dir) == NULL || user[0] == 0) {
+               errno = EINVAL;
+               return (NULL);
+       }
+       file = _PATH_PSEUDONYM;
+       pl = strlen(user) + strlen(file) + 2;
+       path = malloc(pl);
+       if (path == NULL)
+               return (NULL);
+       (void) slprintf(path, pl, "%s/%s", user, file);
+       if (!pnlogged) {
+               ppp_dbglog("pseudonym file: %s", path);
+               pnlogged = 1;
+       }
+       return (path);
+}
+
+static int
+open_pn_file(modebits)
+mode_t modebits;
+{
+       char *path;
+       int fd, err;
+
+       if ((path = name_of_pn_file()) == NULL)
+               return (-1);
+       fd = open(path, modebits, S_IRUSR | S_IWUSR);
+       err = errno;
+       free(path);
+       errno = err;
+       return (fd);
+}
+
+static void
+remove_pn_file()
+{
+       char *path;
+
+       if ((path = name_of_pn_file()) != NULL) {
+               (void) unlink(path);
+               (void) free(path);
+       }
+}
+
+static void
+write_pseudonym(esp, inp, len, id)
+eap_state *esp;
+u_char *inp;
+int len, id;
+{
+       u_char val;
+       u_char *datp, *digp;
+       SHA1_CTX ctxt;
+       u_char dig[SHA_DIGESTSIZE];
+       int dsize, fd, olen = len;
+
+       /*
+        * Do the decoding by working backwards.  This eliminates the need
+        * to save the decoded output in a separate buffer.
+        */
+       val = id;
+       while (len > 0) {
+               if ((dsize = len % SHA_DIGESTSIZE) == 0)
+                       dsize = SHA_DIGESTSIZE;
+               len -= dsize;
+               datp = inp + len;
+               SHA1Init(&ctxt);
+               SHA1Update(&ctxt, &val, 1);
+               SHA1Update(&ctxt, pcb->eap.es_client.ea_skey, SESSION_KEY_LEN);
+               if (len > 0) {
+                       SHA1Update(&ctxt, datp, SHA_DIGESTSIZE);
+               } else {
+                       SHA1Update(&ctxt, pcb->eap.es_client.ea_name,
+                           pcb->eap.es_client.ea_namelen);
+               }
+               SHA1Final(dig, &ctxt);
+               for (digp = dig; digp < dig + SHA_DIGESTSIZE; digp++)
+                       *datp++ ^= *digp;
+       }
+
+       /* Now check that the result is sane */
+       if (olen <= 0 || *inp + 1 > olen) {
+               ppp_dbglog("EAP: decoded pseudonym is unusable <%.*B>", olen, inp);
+               return;
+       }
+
+       /* Save it away */
+       fd = open_pn_file(O_WRONLY | O_CREAT | O_TRUNC);
+       if (fd < 0) {
+               ppp_dbglog("EAP: error saving pseudonym: %m");
+               return;
+       }
+       len = write(fd, inp + 1, *inp);
+       if (close(fd) != -1 && len == *inp) {
+               ppp_dbglog("EAP: saved pseudonym");
+               pcb->eap.es_usedpseudo = 0;
+       } else {
+               ppp_dbglog("EAP: failed to save pseudonym");
+               remove_pn_file();
+       }
+}
+#endif /* USE_SRP */
+
+/*
+ * eap_request - Receive EAP Request message (client mode).
+ */
+static void eap_request(ppp_pcb *pcb, u_char *inp, int id, int len) {
+       u_char typenum;
+       u_char vallen;
+       int secret_len;
+       char secret[MAXSECRETLEN];
+       char rhostname[MAXNAMELEN];
+       lwip_md5_context mdContext;
+       u_char hash[MD5_SIGNATURE_SIZE];
+#ifdef USE_SRP
+       struct t_client *tc;
+       struct t_num sval, gval, Nval, *Ap, Bval;
+       u_char vals[2];
+       SHA1_CTX ctxt;
+       u_char dig[SHA_DIGESTSIZE];
+       int fd;
+#endif /* USE_SRP */
+
+       /*
+        * Note: we update es_client.ea_id *only if* a Response
+        * message is being generated.  Otherwise, we leave it the
+        * same for duplicate detection purposes.
+        */
+
+       pcb->eap.es_client.ea_requests++;
+       if (pcb->settings.eap_allow_req != 0 &&
+           pcb->eap.es_client.ea_requests > pcb->settings.eap_allow_req) {
+               ppp_info("EAP: received too many Request messages");
+               if (pcb->settings.eap_req_time > 0) {
+                       UNTIMEOUT(eap_client_timeout, pcb);
+               }
+               auth_withpeer_fail(pcb, PPP_EAP);
+               return;
+       }
+
+       if (len <= 0) {
+               ppp_error("EAP: empty Request message discarded");
+               return;
+       }
+
+       GETCHAR(typenum, inp);
+       len--;
+
+       switch (typenum) {
+       case EAPT_IDENTITY:
+               if (len > 0)
+                       ppp_info("EAP: Identity prompt \"%.*q\"", len, inp);
+#ifdef USE_SRP
+               if (pcb->eap.es_usepseudo &&
+                   (pcb->eap.es_usedpseudo == 0 ||
+                       (pcb->eap.es_usedpseudo == 1 &&
+                           id == pcb->eap.es_client.ea_id))) {
+                       pcb->eap.es_usedpseudo = 1;
+                       /* Try to get a pseudonym */
+                       if ((fd = open_pn_file(O_RDONLY)) >= 0) {
+                               strcpy(rhostname, SRP_PSEUDO_ID);
+                               len = read(fd, rhostname + SRP_PSEUDO_LEN,
+                                   sizeof (rhostname) - SRP_PSEUDO_LEN);
+                               /* XXX NAI unsupported */
+                               if (len > 0) {
+                                       eap_send_response(pcb, id, typenum,
+                                           rhostname, len + SRP_PSEUDO_LEN);
+                               }
+                               (void) close(fd);
+                               if (len > 0)
+                                       break;
+                       }
+               }
+               /* Stop using pseudonym now. */
+               if (pcb->eap.es_usepseudo && pcb->eap.es_usedpseudo != 2) {
+                       remove_pn_file();
+                       pcb->eap.es_usedpseudo = 2;
+               }
+#endif /* USE_SRP */
+               eap_send_response(pcb, id, typenum, (const u_char*)pcb->eap.es_client.ea_name,
+                   pcb->eap.es_client.ea_namelen);
+               break;
+
+       case EAPT_NOTIFICATION:
+               if (len > 0)
+                       ppp_info("EAP: Notification \"%.*q\"", len, inp);
+               eap_send_response(pcb, id, typenum, NULL, 0);
+               break;
+
+       case EAPT_NAK:
+               /*
+                * Avoid the temptation to send Response Nak in reply
+                * to Request Nak here.  It can only lead to trouble.
+                */
+               ppp_warn("EAP: unexpected Nak in Request; ignored");
+               /* Return because we're waiting for something real. */
+               return;
+
+       case EAPT_MD5CHAP:
+               if (len < 1) {
+                       ppp_error("EAP: received MD5-Challenge with no data");
+                       /* Bogus request; wait for something real. */
+                       return;
+               }
+               GETCHAR(vallen, inp);
+               len--;
+               if (vallen < 8 || vallen > len) {
+                       ppp_error("EAP: MD5-Challenge with bad length %d (8..%d)",
+                           vallen, len);
+                       /* Try something better. */
+                       eap_send_nak(pcb, id, EAPT_SRP);
+                       break;
+               }
+
+               /* Not so likely to happen. */
+               if (vallen >= len + sizeof (rhostname)) {
+                       ppp_dbglog("EAP: trimming really long peer name down");
+                       MEMCPY(rhostname, inp + vallen, sizeof (rhostname) - 1);
+                       rhostname[sizeof (rhostname) - 1] = '\0';
+               } else {
+                       MEMCPY(rhostname, inp + vallen, len - vallen);
+                       rhostname[len - vallen] = '\0';
+               }
+
+#if PPP_REMOTENAME
+               /* In case the remote doesn't give us his name. */
+               if (pcb->settings.explicit_remote ||
+                   (pcb->settings.remote_name[0] != '\0' && vallen == len))
+                       strlcpy(rhostname, pcb->settings.remote_name, sizeof (rhostname));
+#endif /* PPP_REMOTENAME */
+
+               /*
+                * Get the secret for authenticating ourselves with
+                * the specified host.
+                */
+               if (!get_secret(pcb, pcb->eap.es_client.ea_name,
+                   rhostname, secret, &secret_len, 0)) {
+                       ppp_dbglog("EAP: no MD5 secret for auth to %q", rhostname);
+                       eap_send_nak(pcb, id, EAPT_SRP);
+                       break;
+               }
+               lwip_md5_init(&mdContext);
+               lwip_md5_starts(&mdContext);
+               typenum = id;
+               lwip_md5_update(&mdContext, &typenum, 1);
+               lwip_md5_update(&mdContext, (u_char *)secret, secret_len);
+               BZERO(secret, sizeof (secret));
+               lwip_md5_update(&mdContext, inp, vallen);
+               lwip_md5_finish(&mdContext, hash);
+               lwip_md5_free(&mdContext);
+               eap_chap_response(pcb, id, hash, pcb->eap.es_client.ea_name,
+                   pcb->eap.es_client.ea_namelen);
+               break;
+
+#ifdef USE_SRP
+       case EAPT_SRP:
+               if (len < 1) {
+                       ppp_error("EAP: received empty SRP Request");
+                       /* Bogus request; wait for something real. */
+                       return;
+               }
+
+               /* Get subtype */
+               GETCHAR(vallen, inp);
+               len--;
+               switch (vallen) {
+               case EAPSRP_CHALLENGE:
+                       tc = NULL;
+                       if (pcb->eap.es_client.ea_session != NULL) {
+                               tc = (struct t_client *)pcb->eap.es_client.
+                                   ea_session;
+                               /*
+                                * If this is a new challenge, then start
+                                * over with a new client session context.
+                                * Otherwise, just resend last response.
+                                */
+                               if (id != pcb->eap.es_client.ea_id) {
+                                       t_clientclose(tc);
+                                       pcb->eap.es_client.ea_session = NULL;
+                                       tc = NULL;
+                               }
+                       }
+                       /* No session key just yet */
+                       pcb->eap.es_client.ea_skey = NULL;
+                       if (tc == NULL) {
+                               int rhostnamelen;
+
+                               GETCHAR(vallen, inp);
+                               len--;
+                               if (vallen >= len) {
+                                       ppp_error("EAP: badly-formed SRP Challenge"
+                                           " (name)");
+                                       /* Ignore badly-formed messages */
+                                       return;
+                               }
+                               MEMCPY(rhostname, inp, vallen);
+                               rhostname[vallen] = '\0';
+                               INCPTR(vallen, inp);
+                               len -= vallen;
+
+                               /*
+                                * In case the remote doesn't give us his name,
+                                * use configured name.
+                                */
+                               if (explicit_remote ||
+                                   (remote_name[0] != '\0' && vallen == 0)) {
+                                       strlcpy(rhostname, remote_name,
+                                           sizeof (rhostname));
+                               }
+
+                               rhostnamelen = (int)strlen(rhostname);
+                               if (rhostnamelen > MAXNAMELEN) {
+                                       rhostnamelen = MAXNAMELEN;
+                               }
+                               MEMCPY(pcb->eap.es_client.ea_peer, rhostname, rhostnamelen);
+                               pcb->eap.es_client.ea_peer[rhostnamelen] = '\0';
+                               pcb->eap.es_client.ea_peerlen = rhostnamelen;
+
+                               GETCHAR(vallen, inp);
+                               len--;
+                               if (vallen >= len) {
+                                       ppp_error("EAP: badly-formed SRP Challenge"
+                                           " (s)");
+                                       /* Ignore badly-formed messages */
+                                       return;
+                               }
+                               sval.data = inp;
+                               sval.len = vallen;
+                               INCPTR(vallen, inp);
+                               len -= vallen;
+
+                               GETCHAR(vallen, inp);
+                               len--;
+                               if (vallen > len) {
+                                       ppp_error("EAP: badly-formed SRP Challenge"
+                                           " (g)");
+                                       /* Ignore badly-formed messages */
+                                       return;
+                               }
+                               /* If no generator present, then use value 2 */
+                               if (vallen == 0) {
+                                       gval.data = (u_char *)"\002";
+                                       gval.len = 1;
+                               } else {
+                                       gval.data = inp;
+                                       gval.len = vallen;
+                               }
+                               INCPTR(vallen, inp);
+                               len -= vallen;
+
+                               /*
+                                * If no modulus present, then use well-known
+                                * value.
+                                */
+                               if (len == 0) {
+                                       Nval.data = (u_char *)wkmodulus;
+                                       Nval.len = sizeof (wkmodulus);
+                               } else {
+                                       Nval.data = inp;
+                                       Nval.len = len;
+                               }
+                               tc = t_clientopen(pcb->eap.es_client.ea_name,
+                                   &Nval, &gval, &sval);
+                               if (tc == NULL) {
+                                       eap_send_nak(pcb, id, EAPT_MD5CHAP);
+                                       break;
+                               }
+                               pcb->eap.es_client.ea_session = (void *)tc;
+
+                               /* Add Challenge ID & type to verifier */
+                               vals[0] = id;
+                               vals[1] = EAPT_SRP;
+                               t_clientaddexdata(tc, vals, 2);
+                       }
+                       Ap = t_clientgenexp(tc);
+                       eap_srp_response(esp, id, EAPSRP_CKEY, Ap->data,
+                           Ap->len);
+                       break;
+
+               case EAPSRP_SKEY:
+                       tc = (struct t_client *)pcb->eap.es_client.ea_session;
+                       if (tc == NULL) {
+                               ppp_warn("EAP: peer sent Subtype 2 without 1");
+                               eap_send_nak(pcb, id, EAPT_MD5CHAP);
+                               break;
+                       }
+                       if (pcb->eap.es_client.ea_skey != NULL) {
+                               /*
+                                * ID number should not change here.  Warn
+                                * if it does (but otherwise ignore).
+                                */
+                               if (id != pcb->eap.es_client.ea_id) {
+                                       ppp_warn("EAP: ID changed from %d to %d "
+                                           "in SRP Subtype 2 rexmit",
+                                           pcb->eap.es_client.ea_id, id);
+                               }
+                       } else {
+                               if (get_srp_secret(pcb->eap.es_unit,
+                                   pcb->eap.es_client.ea_name,
+                                   pcb->eap.es_client.ea_peer, secret, 0) == 0) {
+                                       /*
+                                        * Can't work with this peer because
+                                        * the secret is missing.  Just give
+                                        * up.
+                                        */
+                                       eap_send_nak(pcb, id, EAPT_MD5CHAP);
+                                       break;
+                               }
+                               Bval.data = inp;
+                               Bval.len = len;
+                               t_clientpasswd(tc, secret);
+                               BZERO(secret, sizeof (secret));
+                               pcb->eap.es_client.ea_skey =
+                                   t_clientgetkey(tc, &Bval);
+                               if (pcb->eap.es_client.ea_skey == NULL) {
+                                       /* Server is rogue; stop now */
+                                       ppp_error("EAP: SRP server is rogue");
+                                       goto client_failure;
+                               }
+                       }
+                       eap_srpval_response(esp, id, SRPVAL_EBIT,
+                           t_clientresponse(tc));
+                       break;
+
+               case EAPSRP_SVALIDATOR:
+                       tc = (struct t_client *)pcb->eap.es_client.ea_session;
+                       if (tc == NULL || pcb->eap.es_client.ea_skey == NULL) {
+                               ppp_warn("EAP: peer sent Subtype 3 without 1/2");
+                               eap_send_nak(pcb, id, EAPT_MD5CHAP);
+                               break;
+                       }
+                       /*
+                        * If we're already open, then this ought to be a
+                        * duplicate.  Otherwise, check that the server is
+                        * who we think it is.
+                        */
+                       if (pcb->eap.es_client.ea_state == eapOpen) {
+                               if (id != pcb->eap.es_client.ea_id) {
+                                       ppp_warn("EAP: ID changed from %d to %d "
+                                           "in SRP Subtype 3 rexmit",
+                                           pcb->eap.es_client.ea_id, id);
+                               }
+                       } else {
+                               len -= sizeof (u32_t) + SHA_DIGESTSIZE;
+                               if (len < 0 || t_clientverify(tc, inp +
+                                       sizeof (u32_t)) != 0) {
+                                       ppp_error("EAP: SRP server verification "
+                                           "failed");
+                                       goto client_failure;
+                               }
+                               GETLONG(pcb->eap.es_client.ea_keyflags, inp);
+                               /* Save pseudonym if user wants it. */
+                               if (len > 0 && pcb->eap.es_usepseudo) {
+                                       INCPTR(SHA_DIGESTSIZE, inp);
+                                       write_pseudonym(esp, inp, len, id);
+                               }
+                       }
+                       /*
+                        * We've verified our peer.  We're now mostly done,
+                        * except for waiting on the regular EAP Success
+                        * message.
+                        */
+                       eap_srp_response(esp, id, EAPSRP_ACK, NULL, 0);
+                       break;
+
+               case EAPSRP_LWRECHALLENGE:
+                       if (len < 4) {
+                               ppp_warn("EAP: malformed Lightweight rechallenge");
+                               return;
+                       }
+                       SHA1Init(&ctxt);
+                       vals[0] = id;
+                       SHA1Update(&ctxt, vals, 1);
+                       SHA1Update(&ctxt, pcb->eap.es_client.ea_skey,
+                           SESSION_KEY_LEN);
+                       SHA1Update(&ctxt, inp, len);
+                       SHA1Update(&ctxt, pcb->eap.es_client.ea_name,
+                           pcb->eap.es_client.ea_namelen);
+                       SHA1Final(dig, &ctxt);
+                       eap_srp_response(esp, id, EAPSRP_LWRECHALLENGE, dig,
+                           SHA_DIGESTSIZE);
+                       break;
+
+               default:
+                       ppp_error("EAP: unknown SRP Subtype %d", vallen);
+                       eap_send_nak(pcb, id, EAPT_MD5CHAP);
+                       break;
+               }
+               break;
+#endif /* USE_SRP */
+
+       default:
+               ppp_info("EAP: unknown authentication type %d; Naking", typenum);
+               eap_send_nak(pcb, id, EAPT_SRP);
+               break;
+       }
+
+       if (pcb->settings.eap_req_time > 0) {
+               UNTIMEOUT(eap_client_timeout, pcb);
+               TIMEOUT(eap_client_timeout, pcb,
+                   pcb->settings.eap_req_time);
+       }
+       return;
+
+#ifdef USE_SRP
+client_failure:
+       pcb->eap.es_client.ea_state = eapBadAuth;
+       if (pcb->settings.eap_req_time > 0) {
+               UNTIMEOUT(eap_client_timeout, (void *)esp);
+       }
+       pcb->eap.es_client.ea_session = NULL;
+       t_clientclose(tc);
+       auth_withpeer_fail(pcb, PPP_EAP);
+#endif /* USE_SRP */
+}
+
+#if PPP_SERVER
+/*
+ * eap_response - Receive EAP Response message (server mode).
+ */
+static void eap_response(ppp_pcb *pcb, u_char *inp, int id, int len) {
+       u_char typenum;
+       u_char vallen;
+       int secret_len;
+       char secret[MAXSECRETLEN];
+       char rhostname[MAXNAMELEN];
+       lwip_md5_context mdContext;
+       u_char hash[MD5_SIGNATURE_SIZE];
+#ifdef USE_SRP
+       struct t_server *ts;
+       struct t_num A;
+       SHA1_CTX ctxt;
+       u_char dig[SHA_DIGESTSIZE];
+#endif /* USE_SRP */
+
+       if (pcb->eap.es_server.ea_id != id) {
+               ppp_dbglog("EAP: discarding Response %d; expected ID %d", id,
+                   pcb->eap.es_server.ea_id);
+               return;
+       }
+
+       pcb->eap.es_server.ea_responses++;
+
+       if (len <= 0) {
+               ppp_error("EAP: empty Response message discarded");
+               return;
+       }
+
+       GETCHAR(typenum, inp);
+       len--;
+
+       switch (typenum) {
+       case EAPT_IDENTITY:
+               if (pcb->eap.es_server.ea_state != eapIdentify) {
+                       ppp_dbglog("EAP discarding unwanted Identify \"%.q\"", len,
+                           inp);
+                       break;
+               }
+               ppp_info("EAP: unauthenticated peer name \"%.*q\"", len, inp);
+               if (len > MAXNAMELEN) {
+                 len = MAXNAMELEN;
+               }
+               MEMCPY(pcb->eap.es_server.ea_peer, inp, len);
+               pcb->eap.es_server.ea_peer[len] = '\0';
+               pcb->eap.es_server.ea_peerlen = len;
+               eap_figure_next_state(pcb, 0);
+               break;
+
+       case EAPT_NOTIFICATION:
+               ppp_dbglog("EAP unexpected Notification; response discarded");
+               break;
+
+       case EAPT_NAK:
+               if (len < 1) {
+                       ppp_info("EAP: Nak Response with no suggested protocol");
+                       eap_figure_next_state(pcb, 1);
+                       break;
+               }
+
+               GETCHAR(vallen, inp);
+               len--;
+
+               if (
+#if PPP_REMOTENAME
+               !pcb->explicit_remote &&
+#endif /* PPP_REMOTENAME */
+               pcb->eap.es_server.ea_state == eapIdentify){
+                       /* Peer cannot Nak Identify Request */
+                       eap_figure_next_state(pcb, 1);
+                       break;
+               }
+
+               switch (vallen) {
+               case EAPT_SRP:
+                       /* Run through SRP validator selection again. */
+                       pcb->eap.es_server.ea_state = eapIdentify;
+                       eap_figure_next_state(pcb, 0);
+                       break;
+
+               case EAPT_MD5CHAP:
+                       pcb->eap.es_server.ea_state = eapMD5Chall;
+                       break;
+
+               default:
+                       ppp_dbglog("EAP: peer requesting unknown Type %d", vallen);
+                       switch (pcb->eap.es_server.ea_state) {
+                       case eapSRP1:
+                       case eapSRP2:
+                       case eapSRP3:
+                               pcb->eap.es_server.ea_state = eapMD5Chall;
+                               break;
+                       case eapMD5Chall:
+                       case eapSRP4:
+                               pcb->eap.es_server.ea_state = eapIdentify;
+                               eap_figure_next_state(pcb, 0);
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               }
+               break;
+
+       case EAPT_MD5CHAP:
+               if (pcb->eap.es_server.ea_state != eapMD5Chall) {
+                       ppp_error("EAP: unexpected MD5-Response");
+                       eap_figure_next_state(pcb, 1);
+                       break;
+               }
+               if (len < 1) {
+                       ppp_error("EAP: received MD5-Response with no data");
+                       eap_figure_next_state(pcb, 1);
+                       break;
+               }
+               GETCHAR(vallen, inp);
+               len--;
+               if (vallen != 16 || vallen > len) {
+                       ppp_error("EAP: MD5-Response with bad length %d", vallen);
+                       eap_figure_next_state(pcb, 1);
+                       break;
+               }
+
+               /* Not so likely to happen. */
+               if (vallen >= len + sizeof (rhostname)) {
+                       ppp_dbglog("EAP: trimming really long peer name down");
+                       MEMCPY(rhostname, inp + vallen, sizeof (rhostname) - 1);
+                       rhostname[sizeof (rhostname) - 1] = '\0';
+               } else {
+                       MEMCPY(rhostname, inp + vallen, len - vallen);
+                       rhostname[len - vallen] = '\0';
+               }
+
+#if PPP_REMOTENAME
+               /* In case the remote doesn't give us his name. */
+               if (explicit_remote ||
+                   (remote_name[0] != '\0' && vallen == len))
+                       strlcpy(rhostname, remote_name, sizeof (rhostname));
+#endif /* PPP_REMOTENAME */
+
+               /*
+                * Get the secret for authenticating the specified
+                * host.
+                */
+               if (!get_secret(pcb, rhostname,
+                   pcb->eap.es_server.ea_name, secret, &secret_len, 1)) {
+                       ppp_dbglog("EAP: no MD5 secret for auth of %q", rhostname);
+                       eap_send_failure(pcb);
+                       break;
+               }
+               lwip_md5_init(&mdContext);
+               lwip_md5_starts(&mdContext);
+               lwip_md5_update(&mdContext, &pcb->eap.es_server.ea_id, 1);
+               lwip_md5_update(&mdContext, (u_char *)secret, secret_len);
+               BZERO(secret, sizeof (secret));
+               lwip_md5_update(&mdContext, pcb->eap.es_challenge, pcb->eap.es_challen);
+               lwip_md5_finish(&mdContext, hash);
+               lwip_md5_free(&mdContext);
+               if (BCMP(hash, inp, MD5_SIGNATURE_SIZE) != 0) {
+                       eap_send_failure(pcb);
+                       break;
+               }
+               pcb->eap.es_server.ea_type = EAPT_MD5CHAP;
+               eap_send_success(pcb);
+               eap_figure_next_state(pcb, 0);
+               if (pcb->eap.es_rechallenge != 0)
+                       TIMEOUT(eap_rechallenge, pcb, pcb->eap.es_rechallenge);
+               break;
+
+#ifdef USE_SRP
+       case EAPT_SRP:
+               if (len < 1) {
+                       ppp_error("EAP: empty SRP Response");
+                       eap_figure_next_state(pcb, 1);
+                       break;
+               }
+               GETCHAR(typenum, inp);
+               len--;
+               switch (typenum) {
+               case EAPSRP_CKEY:
+                       if (pcb->eap.es_server.ea_state != eapSRP1) {
+                               ppp_error("EAP: unexpected SRP Subtype 1 Response");
+                               eap_figure_next_state(pcb, 1);
+                               break;
+                       }
+                       A.data = inp;
+                       A.len = len;
+                       ts = (struct t_server *)pcb->eap.es_server.ea_session;
+                       assert(ts != NULL);
+                       pcb->eap.es_server.ea_skey = t_servergetkey(ts, &A);
+                       if (pcb->eap.es_server.ea_skey == NULL) {
+                               /* Client's A value is bogus; terminate now */
+                               ppp_error("EAP: bogus A value from client");
+                               eap_send_failure(pcb);
+                       } else {
+                               eap_figure_next_state(pcb, 0);
+                       }
+                       break;
+
+               case EAPSRP_CVALIDATOR:
+                       if (pcb->eap.es_server.ea_state != eapSRP2) {
+                               ppp_error("EAP: unexpected SRP Subtype 2 Response");
+                               eap_figure_next_state(pcb, 1);
+                               break;
+                       }
+                       if (len < sizeof (u32_t) + SHA_DIGESTSIZE) {
+                               ppp_error("EAP: M1 length %d < %d", len,
+                                   sizeof (u32_t) + SHA_DIGESTSIZE);
+                               eap_figure_next_state(pcb, 1);
+                               break;
+                       }
+                       GETLONG(pcb->eap.es_server.ea_keyflags, inp);
+                       ts = (struct t_server *)pcb->eap.es_server.ea_session;
+                       assert(ts != NULL);
+                       if (t_serververify(ts, inp)) {
+                               ppp_info("EAP: unable to validate client identity");
+                               eap_send_failure(pcb);
+                               break;
+                       }
+                       eap_figure_next_state(pcb, 0);
+                       break;
+
+               case EAPSRP_ACK:
+                       if (pcb->eap.es_server.ea_state != eapSRP3) {
+                               ppp_error("EAP: unexpected SRP Subtype 3 Response");
+                               eap_send_failure(esp);
+                               break;
+                       }
+                       pcb->eap.es_server.ea_type = EAPT_SRP;
+                       eap_send_success(pcb, esp);
+                       eap_figure_next_state(pcb, 0);
+                       if (pcb->eap.es_rechallenge != 0)
+                               TIMEOUT(eap_rechallenge, pcb,
+                                   pcb->eap.es_rechallenge);
+                       if (pcb->eap.es_lwrechallenge != 0)
+                               TIMEOUT(srp_lwrechallenge, pcb,
+                                   pcb->eap.es_lwrechallenge);
+                       break;
+
+               case EAPSRP_LWRECHALLENGE:
+                       if (pcb->eap.es_server.ea_state != eapSRP4) {
+                               ppp_info("EAP: unexpected SRP Subtype 4 Response");
+                               return;
+                       }
+                       if (len != SHA_DIGESTSIZE) {
+                               ppp_error("EAP: bad Lightweight rechallenge "
+                                   "response");
+                               return;
+                       }
+                       SHA1Init(&ctxt);
+                       vallen = id;
+                       SHA1Update(&ctxt, &vallen, 1);
+                       SHA1Update(&ctxt, pcb->eap.es_server.ea_skey,
+                           SESSION_KEY_LEN);
+                       SHA1Update(&ctxt, pcb->eap.es_challenge, pcb->eap.es_challen);
+                       SHA1Update(&ctxt, pcb->eap.es_server.ea_peer,
+                           pcb->eap.es_server.ea_peerlen);
+                       SHA1Final(dig, &ctxt);
+                       if (BCMP(dig, inp, SHA_DIGESTSIZE) != 0) {
+                               ppp_error("EAP: failed Lightweight rechallenge");
+                               eap_send_failure(pcb);
+                               break;
+                       }
+                       pcb->eap.es_server.ea_state = eapOpen;
+                       if (pcb->eap.es_lwrechallenge != 0)
+                               TIMEOUT(srp_lwrechallenge, esp,
+                                   pcb->eap.es_lwrechallenge);
+                       break;
+               }
+               break;
+#endif /* USE_SRP */
+
+       default:
+               /* This can't happen. */
+               ppp_error("EAP: unknown Response type %d; ignored", typenum);
+               return;
+       }
+
+       if (pcb->settings.eap_timeout_time > 0) {
+               UNTIMEOUT(eap_server_timeout, pcb);
+       }
+
+       if (pcb->eap.es_server.ea_state != eapBadAuth &&
+           pcb->eap.es_server.ea_state != eapOpen) {
+               pcb->eap.es_server.ea_id++;
+               eap_send_request(pcb);
+       }
+}
+#endif /* PPP_SERVER */
+
+/*
+ * eap_success - Receive EAP Success message (client mode).
+ */
+static void eap_success(ppp_pcb *pcb, u_char *inp, int id, int len) {
+       LWIP_UNUSED_ARG(id);
+
+       if (pcb->eap.es_client.ea_state != eapOpen && !eap_client_active(pcb)) {
+               ppp_dbglog("EAP unexpected success message in state %s (%d)",
+                   eap_state_name(pcb->eap.es_client.ea_state),
+                   pcb->eap.es_client.ea_state);
+               return;
+       }
+
+       if (pcb->settings.eap_req_time > 0) {
+               UNTIMEOUT(eap_client_timeout, pcb);
+       }
+
+       if (len > 0) {
+               /* This is odd.  The spec doesn't allow for this. */
+               PRINTMSG(inp, len);
+       }
+
+       pcb->eap.es_client.ea_state = eapOpen;
+       auth_withpeer_success(pcb, PPP_EAP, 0);
+}
+
+/*
+ * eap_failure - Receive EAP Failure message (client mode).
+ */
+static void eap_failure(ppp_pcb *pcb, u_char *inp, int id, int len) {
+       LWIP_UNUSED_ARG(id);
+
+       if (!eap_client_active(pcb)) {
+               ppp_dbglog("EAP unexpected failure message in state %s (%d)",
+                   eap_state_name(pcb->eap.es_client.ea_state),
+                   pcb->eap.es_client.ea_state);
+       }
+
+       if (pcb->settings.eap_req_time > 0) {
+               UNTIMEOUT(eap_client_timeout, pcb);
+       }
+
+       if (len > 0) {
+               /* This is odd.  The spec doesn't allow for this. */
+               PRINTMSG(inp, len);
+       }
+
+       pcb->eap.es_client.ea_state = eapBadAuth;
+
+       ppp_error("EAP: peer reports authentication failure");
+       auth_withpeer_fail(pcb, PPP_EAP);
+}
+
+/*
+ * eap_input - Handle received EAP message.
+ */
+static void eap_input(ppp_pcb *pcb, u_char *inp, int inlen) {
+       u_char code, id;
+       int len;
+
+       /*
+        * Parse header (code, id and length).  If packet too short,
+        * drop it.
+        */
+       if (inlen < EAP_HEADERLEN) {
+               ppp_error("EAP: packet too short: %d < %d", inlen, EAP_HEADERLEN);
+               return;
+       }
+       GETCHAR(code, inp);
+       GETCHAR(id, inp);
+       GETSHORT(len, inp);
+       if (len < EAP_HEADERLEN || len > inlen) {
+               ppp_error("EAP: packet has illegal length field %d (%d..%d)", len,
+                   EAP_HEADERLEN, inlen);
+               return;
+       }
+       len -= EAP_HEADERLEN;
+
+       /* Dispatch based on message code */
+       switch (code) {
+       case EAP_REQUEST:
+               eap_request(pcb, inp, id, len);
+               break;
+
+#if PPP_SERVER
+       case EAP_RESPONSE:
+               eap_response(pcb, inp, id, len);
+               break;
+#endif /* PPP_SERVER */
+
+       case EAP_SUCCESS:
+               eap_success(pcb, inp, id, len);
+               break;
+
+       case EAP_FAILURE:
+               eap_failure(pcb, inp, id, len);
+               break;
+
+       default:                                /* XXX Need code reject */
+               /* Note: it's not legal to send EAP Nak here. */
+               ppp_warn("EAP: unknown code %d received", code);
+               break;
+       }
+}
+
+#if PRINTPKT_SUPPORT
+/*
+ * eap_printpkt - print the contents of an EAP packet.
+ */
+static const char* const eap_codenames[] = {
+       "Request", "Response", "Success", "Failure"
+};
+
+static const char* const eap_typenames[] = {
+       "Identity", "Notification", "Nak", "MD5-Challenge",
+       "OTP", "Generic-Token", NULL, NULL,
+       "RSA", "DSS", "KEA", "KEA-Validate",
+       "TLS", "Defender", "Windows 2000", "Arcot",
+       "Cisco", "Nokia", "SRP"
+};
+
+static int eap_printpkt(const u_char *inp, int inlen, void (*printer) (void *, const char *, ...), void *arg) {
+       int code, id, len, rtype, vallen;
+       const u_char *pstart;
+       u32_t uval;
+
+       if (inlen < EAP_HEADERLEN)
+               return (0);
+       pstart = inp;
+       GETCHAR(code, inp);
+       GETCHAR(id, inp);
+       GETSHORT(len, inp);
+       if (len < EAP_HEADERLEN || len > inlen)
+               return (0);
+
+       if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(eap_codenames))
+               printer(arg, " %s", eap_codenames[code-1]);
+       else
+               printer(arg, " code=0x%x", code);
+       printer(arg, " id=0x%x", id);
+       len -= EAP_HEADERLEN;
+       switch (code) {
+       case EAP_REQUEST:
+               if (len < 1) {
+                       printer(arg, " <missing type>");
+                       break;
+               }
+               GETCHAR(rtype, inp);
+               len--;
+               if (rtype >= 1 && rtype <= (int)LWIP_ARRAYSIZE(eap_typenames))
+                       printer(arg, " %s", eap_typenames[rtype-1]);
+               else
+                       printer(arg, " type=0x%x", rtype);
+               switch (rtype) {
+               case EAPT_IDENTITY:
+               case EAPT_NOTIFICATION:
+                       if (len > 0) {
+                               printer(arg, " <Message ");
+                               ppp_print_string(inp, len, printer, arg);
+                               printer(arg, ">");
+                               INCPTR(len, inp);
+                               len = 0;
+                       } else {
+                               printer(arg, " <No message>");
+                       }
+                       break;
+
+               case EAPT_MD5CHAP:
+                       if (len <= 0)
+                               break;
+                       GETCHAR(vallen, inp);
+                       len--;
+                       if (vallen > len)
+                               goto truncated;
+                       printer(arg, " <Value%.*B>", vallen, inp);
+                       INCPTR(vallen, inp);
+                       len -= vallen;
+                       if (len > 0) {
+                               printer(arg, " <Name ");
+                               ppp_print_string(inp, len, printer, arg);
+                               printer(arg, ">");
+                               INCPTR(len, inp);
+                               len = 0;
+                       } else {
+                               printer(arg, " <No name>");
+                       }
+                       break;
+
+               case EAPT_SRP:
+                       if (len < 3)
+                               goto truncated;
+                       GETCHAR(vallen, inp);
+                       len--;
+                       printer(arg, "-%d", vallen);
+                       switch (vallen) {
+                       case EAPSRP_CHALLENGE:
+                               GETCHAR(vallen, inp);
+                               len--;
+                               if (vallen >= len)
+                                       goto truncated;
+                               if (vallen > 0) {
+                                       printer(arg, " <Name ");
+                                       ppp_print_string(inp, vallen, printer,
+                                           arg);
+                                       printer(arg, ">");
+                               } else {
+                                       printer(arg, " <No name>");
+                               }
+                               INCPTR(vallen, inp);
+                               len -= vallen;
+                               GETCHAR(vallen, inp);
+                               len--;
+                               if (vallen >= len)
+                                       goto truncated;
+                               printer(arg, " <s%.*B>", vallen, inp);
+                               INCPTR(vallen, inp);
+                               len -= vallen;
+                               GETCHAR(vallen, inp);
+                               len--;
+                               if (vallen > len)
+                                       goto truncated;
+                               if (vallen == 0) {
+                                       printer(arg, " <Default g=2>");
+                               } else {
+                                       printer(arg, " <g%.*B>", vallen, inp);
+                               }
+                               INCPTR(vallen, inp);
+                               len -= vallen;
+                               if (len == 0) {
+                                       printer(arg, " <Default N>");
+                               } else {
+                                       printer(arg, " <N%.*B>", len, inp);
+                                       INCPTR(len, inp);
+                                       len = 0;
+                               }
+                               break;
+
+                       case EAPSRP_SKEY:
+                               printer(arg, " <B%.*B>", len, inp);
+                               INCPTR(len, inp);
+                               len = 0;
+                               break;
+
+                       case EAPSRP_SVALIDATOR:
+                               if (len < (int)sizeof (u32_t))
+                                       break;
+                               GETLONG(uval, inp);
+                               len -= sizeof (u32_t);
+                               if (uval & SRPVAL_EBIT) {
+                                       printer(arg, " E");
+                                       uval &= ~SRPVAL_EBIT;
+                               }
+                               if (uval != 0) {
+                                       printer(arg, " f<%X>", uval);
+                               }
+                               if ((vallen = len) > SHA_DIGESTSIZE)
+                                       vallen = SHA_DIGESTSIZE;
+                               printer(arg, " <M2%.*B%s>", len, inp,
+                                   len < SHA_DIGESTSIZE ? "?" : "");
+                               INCPTR(vallen, inp);
+                               len -= vallen;
+                               if (len > 0) {
+                                       printer(arg, " <PN%.*B>", len, inp);
+                                       INCPTR(len, inp);
+                                       len = 0;
+                               }
+                               break;
+
+                       case EAPSRP_LWRECHALLENGE:
+                               printer(arg, " <Challenge%.*B>", len, inp);
+                               INCPTR(len, inp);
+                               len = 0;
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               default:
+                       break;
+               }
+               break;
+
+       case EAP_RESPONSE:
+               if (len < 1)
+                       break;
+               GETCHAR(rtype, inp);
+               len--;
+               if (rtype >= 1 && rtype <= (int)LWIP_ARRAYSIZE(eap_typenames))
+                       printer(arg, " %s", eap_typenames[rtype-1]);
+               else
+                       printer(arg, " type=0x%x", rtype);
+               switch (rtype) {
+               case EAPT_IDENTITY:
+                       if (len > 0) {
+                               printer(arg, " <Name ");
+                               ppp_print_string(inp, len, printer, arg);
+                               printer(arg, ">");
+                               INCPTR(len, inp);
+                               len = 0;
+                       }
+                       break;
+
+               case EAPT_NAK:
+                       if (len <= 0) {
+                               printer(arg, " <missing hint>");
+                               break;
+                       }
+                       GETCHAR(rtype, inp);
+                       len--;
+                       printer(arg, " <Suggested-type %02X", rtype);
+                       if (rtype >= 1 && rtype < (int)LWIP_ARRAYSIZE(eap_typenames))
+                               printer(arg, " (%s)", eap_typenames[rtype-1]);
+                       printer(arg, ">");
+                       break;
+
+               case EAPT_MD5CHAP:
+                       if (len <= 0) {
+                               printer(arg, " <missing length>");
+                               break;
+                       }
+                       GETCHAR(vallen, inp);
+                       len--;
+                       if (vallen > len)
+                               goto truncated;
+                       printer(arg, " <Value%.*B>", vallen, inp);
+                       INCPTR(vallen, inp);
+                       len -= vallen;
+                       if (len > 0) {
+                               printer(arg, " <Name ");
+                               ppp_print_string(inp, len, printer, arg);
+                               printer(arg, ">");
+                               INCPTR(len, inp);
+                               len = 0;
+                       } else {
+                               printer(arg, " <No name>");
+                       }
+                       break;
+
+               case EAPT_SRP:
+                       if (len < 1)
+                               goto truncated;
+                       GETCHAR(vallen, inp);
+                       len--;
+                       printer(arg, "-%d", vallen);
+                       switch (vallen) {
+                       case EAPSRP_CKEY:
+                               printer(arg, " <A%.*B>", len, inp);
+                               INCPTR(len, inp);
+                               len = 0;
+                               break;
+
+                       case EAPSRP_CVALIDATOR:
+                               if (len < (int)sizeof (u32_t))
+                                       break;
+                               GETLONG(uval, inp);
+                               len -= sizeof (u32_t);
+                               if (uval & SRPVAL_EBIT) {
+                                       printer(arg, " E");
+                                       uval &= ~SRPVAL_EBIT;
+                               }
+                               if (uval != 0) {
+                                       printer(arg, " f<%X>", uval);
+                               }
+                               printer(arg, " <M1%.*B%s>", len, inp,
+                                   len == SHA_DIGESTSIZE ? "" : "?");
+                               INCPTR(len, inp);
+                               len = 0;
+                               break;
+
+                       case EAPSRP_ACK:
+                               break;
+
+                       case EAPSRP_LWRECHALLENGE:
+                               printer(arg, " <Response%.*B%s>", len, inp,
+                                   len == SHA_DIGESTSIZE ? "" : "?");
+                               if ((vallen = len) > SHA_DIGESTSIZE)
+                                       vallen = SHA_DIGESTSIZE;
+                               INCPTR(vallen, inp);
+                               len -= vallen;
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               default:
+                       break;
+               }
+               break;
+
+       case EAP_SUCCESS:       /* No payload expected for these! */
+       case EAP_FAILURE:
+       default:
+               break;
+
+       truncated:
+               printer(arg, " <truncated>");
+               break;
+       }
+
+       if (len > 8)
+               printer(arg, "%8B...", inp);
+       else if (len > 0)
+               printer(arg, "%.*B", len, inp);
+       INCPTR(len, inp);
+
+       return (inp - pstart);
+}
+#endif /* PRINTPKT_SUPPORT */
+
+#endif /* PPP_SUPPORT && EAP_SUPPORT */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/ecp.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/ecp.c
new file mode 100644 (file)
index 0000000..4d84f60
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * ecp.c - PPP Encryption Control Protocol.
+ *
+ * Copyright (c) 2002 Google, Inc.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Derived from ccp.c, which is:
+ *
+ * Copyright (c) 1994-2002 Paul Mackerras. All rights reserved.
+ *
+ * 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. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && ECP_SUPPORT  /* don't build if not configured for use in lwipopts.h */
+
+#include <string.h>
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/fsm.h"
+#include "netif/ppp/ecp.h"
+
+#if PPP_OPTIONS
+static option_t ecp_option_list[] = {
+    { "noecp", o_bool, &ecp_protent.enabled_flag,
+      "Disable ECP negotiation" },
+    { "-ecp", o_bool, &ecp_protent.enabled_flag,
+      "Disable ECP negotiation", OPT_ALIAS },
+
+    { NULL }
+};
+#endif /* PPP_OPTIONS */
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ecp_init (int unit);
+/*
+static void ecp_open (int unit);
+static void ecp_close (int unit, char *);
+static void ecp_lowerup (int unit);
+static void ecp_lowerdown (int);
+static void ecp_input (int unit, u_char *pkt, int len);
+static void ecp_protrej (int unit);
+*/
+#if PRINTPKT_SUPPORT
+static int  ecp_printpkt (const u_char *pkt, int len,
+                             void (*printer) (void *, char *, ...),
+                             void *arg);
+#endif /* PRINTPKT_SUPPORT */
+/*
+static void ecp_datainput (int unit, u_char *pkt, int len);
+*/
+
+const struct protent ecp_protent = {
+    PPP_ECP,
+    ecp_init,
+    NULL, /* ecp_input, */
+    NULL, /* ecp_protrej, */
+    NULL, /* ecp_lowerup, */
+    NULL, /* ecp_lowerdown, */
+    NULL, /* ecp_open, */
+    NULL, /* ecp_close, */
+#if PRINTPKT_SUPPORT
+    ecp_printpkt,
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_DATAINPUT
+    NULL, /* ecp_datainput, */
+#endif /* PPP_DATAINPUT */
+#if PRINTPKT_SUPPORT
+    "ECP",
+    "Encrypted",
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_OPTIONS
+    ecp_option_list,
+    NULL,
+#endif /* PPP_OPTIONS */
+#if DEMAND_SUPPORT
+    NULL,
+    NULL
+#endif /* DEMAND_SUPPORT */
+};
+
+fsm ecp_fsm[NUM_PPP];
+ecp_options ecp_wantoptions[NUM_PPP];  /* what to request the peer to use */
+ecp_options ecp_gotoptions[NUM_PPP];   /* what the peer agreed to do */
+ecp_options ecp_allowoptions[NUM_PPP]; /* what we'll agree to do */
+ecp_options ecp_hisoptions[NUM_PPP];   /* what we agreed to do */
+
+static const fsm_callbacks ecp_callbacks = {
+    NULL, /* ecp_resetci, */
+    NULL, /* ecp_cilen, */
+    NULL, /* ecp_addci, */
+    NULL, /* ecp_ackci, */
+    NULL, /* ecp_nakci, */
+    NULL, /* ecp_rejci, */
+    NULL, /* ecp_reqci, */
+    NULL, /* ecp_up, */
+    NULL, /* ecp_down, */
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL, /* ecp_extcode, */
+    "ECP"
+};
+
+/*
+ * ecp_init - initialize ECP.
+ */
+static void
+ecp_init(unit)
+    int unit;
+{
+    fsm *f = &ecp_fsm[unit];
+
+    f->unit = unit;
+    f->protocol = PPP_ECP;
+    f->callbacks = &ecp_callbacks;
+    fsm_init(f);
+
+#if 0 /* Not necessary, everything is cleared in ppp_new() */
+    memset(&ecp_wantoptions[unit],  0, sizeof(ecp_options));
+    memset(&ecp_gotoptions[unit],   0, sizeof(ecp_options));
+    memset(&ecp_allowoptions[unit], 0, sizeof(ecp_options));
+    memset(&ecp_hisoptions[unit],   0, sizeof(ecp_options));
+#endif /* 0 */
+
+}
+
+
+#if PRINTPKT_SUPPORT
+static int
+ecp_printpkt(p, plen, printer, arg)
+    const u_char *p;
+    int plen;
+    void (*printer) (void *, char *, ...);
+    void *arg;
+{
+    return 0;
+}
+#endif /* PRINTPKT_SUPPORT */
+
+#endif /* PPP_SUPPORT && ECP_SUPPORT */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/eui64.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/eui64.c
new file mode 100644 (file)
index 0000000..01493bc
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * eui64.c - EUI64 routines for IPv6CP.
+ *
+ * Copyright (c) 1999 Tommi Komulainen.  All rights reserved.
+ *
+ * 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.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Tommi Komulainen
+ *     <Tommi.Komulainen@iki.fi>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: eui64.c,v 1.6 2002/12/04 23:03:32 paulus Exp $
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPP_IPV6_SUPPORT  /* don't build if not configured for use in lwipopts.h */
+
+#include "netif/ppp/ppp_impl.h"
+#include "netif/ppp/eui64.h"
+
+/*
+ * eui64_ntoa - Make an ascii representation of an interface identifier
+ */
+char *eui64_ntoa(eui64_t e) {
+    static char buf[20];
+
+    sprintf(buf, "%02x%02x:%02x%02x:%02x%02x:%02x%02x",
+            e.e8[0], e.e8[1], e.e8[2], e.e8[3], 
+            e.e8[4], e.e8[5], e.e8[6], e.e8[7]);
+    return buf;
+}
+
+#endif /* PPP_SUPPORT && PPP_IPV6_SUPPORT */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/fsm.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/fsm.c
new file mode 100644 (file)
index 0000000..b1f08af
--- /dev/null
@@ -0,0 +1,799 @@
+/*
+ * fsm.c - {Link, IP} Control Protocol Finite State Machine.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * 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.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+/*
+ * @todo:
+ * Randomize fsm id on link/init.
+ * Deal with variable outgoing MTU.
+ */
+
+#if 0 /* UNUSED */
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#endif /* UNUSED */
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/fsm.h"
+
+static void fsm_timeout (void *);
+static void fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len);
+static void fsm_rconfack(fsm *f, int id, u_char *inp, int len);
+static void fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len);
+static void fsm_rtermreq(fsm *f, int id, u_char *p, int len);
+static void fsm_rtermack(fsm *f);
+static void fsm_rcoderej(fsm *f, u_char *inp, int len);
+static void fsm_sconfreq(fsm *f, int retransmit);
+
+#define PROTO_NAME(f)  ((f)->callbacks->proto_name)
+
+/*
+ * fsm_init - Initialize fsm.
+ *
+ * Initialize fsm state.
+ */
+void fsm_init(fsm *f) {
+    ppp_pcb *pcb = f->pcb;
+    f->state = PPP_FSM_INITIAL;
+    f->flags = 0;
+    f->id = 0;                         /* XXX Start with random id? */
+    f->maxnakloops = pcb->settings.fsm_max_nak_loops;
+    f->term_reason_len = 0;
+}
+
+
+/*
+ * fsm_lowerup - The lower layer is up.
+ */
+void fsm_lowerup(fsm *f) {
+    switch( f->state ){
+    case PPP_FSM_INITIAL:
+       f->state = PPP_FSM_CLOSED;
+       break;
+
+    case PPP_FSM_STARTING:
+       if( f->flags & OPT_SILENT )
+           f->state = PPP_FSM_STOPPED;
+       else {
+           /* Send an initial configure-request */
+           fsm_sconfreq(f, 0);
+           f->state = PPP_FSM_REQSENT;
+       }
+       break;
+
+    default:
+       FSMDEBUG(("%s: Up event in state %d!", PROTO_NAME(f), f->state));
+       /* no break */
+    }
+}
+
+
+/*
+ * fsm_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts and inform upper layers.
+ */
+void fsm_lowerdown(fsm *f) {
+    switch( f->state ){
+    case PPP_FSM_CLOSED:
+       f->state = PPP_FSM_INITIAL;
+       break;
+
+    case PPP_FSM_STOPPED:
+       f->state = PPP_FSM_STARTING;
+       if( f->callbacks->starting )
+           (*f->callbacks->starting)(f);
+       break;
+
+    case PPP_FSM_CLOSING:
+       f->state = PPP_FSM_INITIAL;
+       UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
+       break;
+
+    case PPP_FSM_STOPPING:
+    case PPP_FSM_REQSENT:
+    case PPP_FSM_ACKRCVD:
+    case PPP_FSM_ACKSENT:
+       f->state = PPP_FSM_STARTING;
+       UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
+       break;
+
+    case PPP_FSM_OPENED:
+       if( f->callbacks->down )
+           (*f->callbacks->down)(f);
+       f->state = PPP_FSM_STARTING;
+       break;
+
+    default:
+       FSMDEBUG(("%s: Down event in state %d!", PROTO_NAME(f), f->state));
+       /* no break */
+    }
+}
+
+
+/*
+ * fsm_open - Link is allowed to come up.
+ */
+void fsm_open(fsm *f) {
+    switch( f->state ){
+    case PPP_FSM_INITIAL:
+       f->state = PPP_FSM_STARTING;
+       if( f->callbacks->starting )
+           (*f->callbacks->starting)(f);
+       break;
+
+    case PPP_FSM_CLOSED:
+       if( f->flags & OPT_SILENT )
+           f->state = PPP_FSM_STOPPED;
+       else {
+           /* Send an initial configure-request */
+           fsm_sconfreq(f, 0);
+           f->state = PPP_FSM_REQSENT;
+       }
+       break;
+
+    case PPP_FSM_CLOSING:
+       f->state = PPP_FSM_STOPPING;
+       /* fall through */
+       /* no break */
+    case PPP_FSM_STOPPED:
+    case PPP_FSM_OPENED:
+       if( f->flags & OPT_RESTART ){
+           fsm_lowerdown(f);
+           fsm_lowerup(f);
+       }
+       break;
+    default:
+       break;
+    }
+}
+
+/*
+ * terminate_layer - Start process of shutting down the FSM
+ *
+ * Cancel any timeout running, notify upper layers we're done, and
+ * send a terminate-request message as configured.
+ */
+static void terminate_layer(fsm *f, int nextstate) {
+    ppp_pcb *pcb = f->pcb;
+
+    if( f->state != PPP_FSM_OPENED )
+       UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
+    else if( f->callbacks->down )
+       (*f->callbacks->down)(f);       /* Inform upper layers we're down */
+
+    /* Init restart counter and send Terminate-Request */
+    f->retransmits = pcb->settings.fsm_max_term_transmits;
+    fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+             (const u_char *) f->term_reason, f->term_reason_len);
+
+    if (f->retransmits == 0) {
+       /*
+        * User asked for no terminate requests at all; just close it.
+        * We've already fired off one Terminate-Request just to be nice
+        * to the peer, but we're not going to wait for a reply.
+        */
+       f->state = nextstate == PPP_FSM_CLOSING ? PPP_FSM_CLOSED : PPP_FSM_STOPPED;
+       if( f->callbacks->finished )
+           (*f->callbacks->finished)(f);
+       return;
+    }
+
+    TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time);
+    --f->retransmits;
+
+    f->state = nextstate;
+}
+
+/*
+ * fsm_close - Start closing connection.
+ *
+ * Cancel timeouts and either initiate close or possibly go directly to
+ * the PPP_FSM_CLOSED state.
+ */
+void fsm_close(fsm *f, const char *reason) {
+    f->term_reason = reason;
+    f->term_reason_len = (reason == NULL? 0: (u8_t)LWIP_MIN(strlen(reason), 0xFF) );
+    switch( f->state ){
+    case PPP_FSM_STARTING:
+       f->state = PPP_FSM_INITIAL;
+       break;
+    case PPP_FSM_STOPPED:
+       f->state = PPP_FSM_CLOSED;
+       break;
+    case PPP_FSM_STOPPING:
+       f->state = PPP_FSM_CLOSING;
+       break;
+
+    case PPP_FSM_REQSENT:
+    case PPP_FSM_ACKRCVD:
+    case PPP_FSM_ACKSENT:
+    case PPP_FSM_OPENED:
+       terminate_layer(f, PPP_FSM_CLOSING);
+       break;
+    default:
+       break;
+    }
+}
+
+
+/*
+ * fsm_timeout - Timeout expired.
+ */
+static void fsm_timeout(void *arg) {
+    fsm *f = (fsm *) arg;
+    ppp_pcb *pcb = f->pcb;
+
+    switch (f->state) {
+    case PPP_FSM_CLOSING:
+    case PPP_FSM_STOPPING:
+       if( f->retransmits <= 0 ){
+           /*
+            * We've waited for an ack long enough.  Peer probably heard us.
+            */
+           f->state = (f->state == PPP_FSM_CLOSING)? PPP_FSM_CLOSED: PPP_FSM_STOPPED;
+           if( f->callbacks->finished )
+               (*f->callbacks->finished)(f);
+       } else {
+           /* Send Terminate-Request */
+           fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+                     (const u_char *) f->term_reason, f->term_reason_len);
+           TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time);
+           --f->retransmits;
+       }
+       break;
+
+    case PPP_FSM_REQSENT:
+    case PPP_FSM_ACKRCVD:
+    case PPP_FSM_ACKSENT:
+       if (f->retransmits <= 0) {
+           ppp_warn("%s: timeout sending Config-Requests", PROTO_NAME(f));
+           f->state = PPP_FSM_STOPPED;
+           if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished )
+               (*f->callbacks->finished)(f);
+
+       } else {
+           /* Retransmit the configure-request */
+           if (f->callbacks->retransmit)
+               (*f->callbacks->retransmit)(f);
+           fsm_sconfreq(f, 1);         /* Re-send Configure-Request */
+           if( f->state == PPP_FSM_ACKRCVD )
+               f->state = PPP_FSM_REQSENT;
+       }
+       break;
+
+    default:
+       FSMDEBUG(("%s: Timeout event in state %d!", PROTO_NAME(f), f->state));
+       /* no break */
+    }
+}
+
+
+/*
+ * fsm_input - Input packet.
+ */
+void fsm_input(fsm *f, u_char *inpacket, int l) {
+    u_char *inp;
+    u_char code, id;
+    int len;
+
+    /*
+     * Parse header (code, id and length).
+     * If packet too short, drop it.
+     */
+    inp = inpacket;
+    if (l < HEADERLEN) {
+       FSMDEBUG(("fsm_input(%x): Rcvd short header.", f->protocol));
+       return;
+    }
+    GETCHAR(code, inp);
+    GETCHAR(id, inp);
+    GETSHORT(len, inp);
+    if (len < HEADERLEN) {
+       FSMDEBUG(("fsm_input(%x): Rcvd illegal length.", f->protocol));
+       return;
+    }
+    if (len > l) {
+       FSMDEBUG(("fsm_input(%x): Rcvd short packet.", f->protocol));
+       return;
+    }
+    len -= HEADERLEN;          /* subtract header length */
+
+    if( f->state == PPP_FSM_INITIAL || f->state == PPP_FSM_STARTING ){
+       FSMDEBUG(("fsm_input(%x): Rcvd packet in state %d.",
+                 f->protocol, f->state));
+       return;
+    }
+
+    /*
+     * Action depends on code.
+     */
+    switch (code) {
+    case CONFREQ:
+       fsm_rconfreq(f, id, inp, len);
+       break;
+    
+    case CONFACK:
+       fsm_rconfack(f, id, inp, len);
+       break;
+    
+    case CONFNAK:
+    case CONFREJ:
+       fsm_rconfnakrej(f, code, id, inp, len);
+       break;
+    
+    case TERMREQ:
+       fsm_rtermreq(f, id, inp, len);
+       break;
+    
+    case TERMACK:
+       fsm_rtermack(f);
+       break;
+    
+    case CODEREJ:
+       fsm_rcoderej(f, inp, len);
+       break;
+    
+    default:
+       if( !f->callbacks->extcode
+          || !(*f->callbacks->extcode)(f, code, id, inp, len) )
+           fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN);
+       break;
+    }
+}
+
+
+/*
+ * fsm_rconfreq - Receive Configure-Request.
+ */
+static void fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len) {
+    int code, reject_if_disagree;
+
+    switch( f->state ){
+    case PPP_FSM_CLOSED:
+       /* Go away, we're closed */
+       fsm_sdata(f, TERMACK, id, NULL, 0);
+       return;
+    case PPP_FSM_CLOSING:
+    case PPP_FSM_STOPPING:
+       return;
+
+    case PPP_FSM_OPENED:
+       /* Go down and restart negotiation */
+       if( f->callbacks->down )
+           (*f->callbacks->down)(f);   /* Inform upper layers */
+       fsm_sconfreq(f, 0);             /* Send initial Configure-Request */
+       f->state = PPP_FSM_REQSENT;
+       break;
+
+    case PPP_FSM_STOPPED:
+       /* Negotiation started by our peer */
+       fsm_sconfreq(f, 0);             /* Send initial Configure-Request */
+       f->state = PPP_FSM_REQSENT;
+       break;
+    default:
+       break;
+    }
+
+    /*
+     * Pass the requested configuration options
+     * to protocol-specific code for checking.
+     */
+    if (f->callbacks->reqci){          /* Check CI */
+       reject_if_disagree = (f->nakloops >= f->maxnakloops);
+       code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree);
+    } else if (len)
+       code = CONFREJ;                 /* Reject all CI */
+    else
+       code = CONFACK;
+
+    /* send the Ack, Nak or Rej to the peer */
+    fsm_sdata(f, code, id, inp, len);
+
+    if (code == CONFACK) {
+       if (f->state == PPP_FSM_ACKRCVD) {
+           UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
+           f->state = PPP_FSM_OPENED;
+           if (f->callbacks->up)
+               (*f->callbacks->up)(f); /* Inform upper layers */
+       } else
+           f->state = PPP_FSM_ACKSENT;
+       f->nakloops = 0;
+
+    } else {
+       /* we sent CONFACK or CONFREJ */
+       if (f->state != PPP_FSM_ACKRCVD)
+           f->state = PPP_FSM_REQSENT;
+       if( code == CONFNAK )
+           ++f->nakloops;
+    }
+}
+
+
+/*
+ * fsm_rconfack - Receive Configure-Ack.
+ */
+static void fsm_rconfack(fsm *f, int id, u_char *inp, int len) {
+    ppp_pcb *pcb = f->pcb;
+
+    if (id != f->reqid || f->seen_ack)         /* Expected id? */
+       return;                                 /* Nope, toss... */
+    if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len):
+         (len == 0)) ){
+       /* Ack is bad - ignore it */
+       ppp_error("Received bad configure-ack: %P", inp, len);
+       return;
+    }
+    f->seen_ack = 1;
+    f->rnakloops = 0;
+
+    switch (f->state) {
+    case PPP_FSM_CLOSED:
+    case PPP_FSM_STOPPED:
+       fsm_sdata(f, TERMACK, id, NULL, 0);
+       break;
+
+    case PPP_FSM_REQSENT:
+       f->state = PPP_FSM_ACKRCVD;
+       f->retransmits = pcb->settings.fsm_max_conf_req_transmits;
+       break;
+
+    case PPP_FSM_ACKRCVD:
+       /* Huh? an extra valid Ack? oh well... */
+       UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
+       fsm_sconfreq(f, 0);
+       f->state = PPP_FSM_REQSENT;
+       break;
+
+    case PPP_FSM_ACKSENT:
+       UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
+       f->state = PPP_FSM_OPENED;
+       f->retransmits = pcb->settings.fsm_max_conf_req_transmits;
+       if (f->callbacks->up)
+           (*f->callbacks->up)(f);     /* Inform upper layers */
+       break;
+
+    case PPP_FSM_OPENED:
+       /* Go down and restart negotiation */
+       if (f->callbacks->down)
+           (*f->callbacks->down)(f);   /* Inform upper layers */
+       fsm_sconfreq(f, 0);             /* Send initial Configure-Request */
+       f->state = PPP_FSM_REQSENT;
+       break;
+    default:
+       break;
+    }
+}
+
+
+/*
+ * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
+ */
+static void fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len) {
+    int ret;
+    int treat_as_reject;
+
+    if (id != f->reqid || f->seen_ack) /* Expected id? */
+       return;                         /* Nope, toss... */
+
+    if (code == CONFNAK) {
+       ++f->rnakloops;
+       treat_as_reject = (f->rnakloops >= f->maxnakloops);
+       if (f->callbacks->nakci == NULL
+           || !(ret = f->callbacks->nakci(f, inp, len, treat_as_reject))) {
+           ppp_error("Received bad configure-nak: %P", inp, len);
+           return;
+       }
+    } else {
+       f->rnakloops = 0;
+       if (f->callbacks->rejci == NULL
+           || !(ret = f->callbacks->rejci(f, inp, len))) {
+           ppp_error("Received bad configure-rej: %P", inp, len);
+           return;
+       }
+    }
+
+    f->seen_ack = 1;
+
+    switch (f->state) {
+    case PPP_FSM_CLOSED:
+    case PPP_FSM_STOPPED:
+       fsm_sdata(f, TERMACK, id, NULL, 0);
+       break;
+
+    case PPP_FSM_REQSENT:
+    case PPP_FSM_ACKSENT:
+       /* They didn't agree to what we wanted - try another request */
+       UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
+       if (ret < 0)
+           f->state = PPP_FSM_STOPPED;         /* kludge for stopping CCP */
+       else
+           fsm_sconfreq(f, 0);         /* Send Configure-Request */
+       break;
+
+    case PPP_FSM_ACKRCVD:
+       /* Got a Nak/reject when we had already had an Ack?? oh well... */
+       UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
+       fsm_sconfreq(f, 0);
+       f->state = PPP_FSM_REQSENT;
+       break;
+
+    case PPP_FSM_OPENED:
+       /* Go down and restart negotiation */
+       if (f->callbacks->down)
+           (*f->callbacks->down)(f);   /* Inform upper layers */
+       fsm_sconfreq(f, 0);             /* Send initial Configure-Request */
+       f->state = PPP_FSM_REQSENT;
+       break;
+    default:
+       break;
+    }
+}
+
+
+/*
+ * fsm_rtermreq - Receive Terminate-Req.
+ */
+static void fsm_rtermreq(fsm *f, int id, u_char *p, int len) {
+    ppp_pcb *pcb = f->pcb;
+
+    switch (f->state) {
+    case PPP_FSM_ACKRCVD:
+    case PPP_FSM_ACKSENT:
+       f->state = PPP_FSM_REQSENT;             /* Start over but keep trying */
+       break;
+
+    case PPP_FSM_OPENED:
+       if (len > 0) {
+           ppp_info("%s terminated by peer (%0.*v)", PROTO_NAME(f), len, p);
+       } else
+           ppp_info("%s terminated by peer", PROTO_NAME(f));
+       f->retransmits = 0;
+       f->state = PPP_FSM_STOPPING;
+       if (f->callbacks->down)
+           (*f->callbacks->down)(f);   /* Inform upper layers */
+       TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time);
+       break;
+    default:
+       break;
+    }
+
+    fsm_sdata(f, TERMACK, id, NULL, 0);
+}
+
+
+/*
+ * fsm_rtermack - Receive Terminate-Ack.
+ */
+static void fsm_rtermack(fsm *f) {
+    switch (f->state) {
+    case PPP_FSM_CLOSING:
+       UNTIMEOUT(fsm_timeout, f);
+       f->state = PPP_FSM_CLOSED;
+       if( f->callbacks->finished )
+           (*f->callbacks->finished)(f);
+       break;
+    case PPP_FSM_STOPPING:
+       UNTIMEOUT(fsm_timeout, f);
+       f->state = PPP_FSM_STOPPED;
+       if( f->callbacks->finished )
+           (*f->callbacks->finished)(f);
+       break;
+
+    case PPP_FSM_ACKRCVD:
+       f->state = PPP_FSM_REQSENT;
+       break;
+
+    case PPP_FSM_OPENED:
+       if (f->callbacks->down)
+           (*f->callbacks->down)(f);   /* Inform upper layers */
+       fsm_sconfreq(f, 0);
+       f->state = PPP_FSM_REQSENT;
+       break;
+    default:
+       break;
+    }
+}
+
+
+/*
+ * fsm_rcoderej - Receive an Code-Reject.
+ */
+static void fsm_rcoderej(fsm *f, u_char *inp, int len) {
+    u_char code, id;
+
+    if (len < HEADERLEN) {
+       FSMDEBUG(("fsm_rcoderej: Rcvd short Code-Reject packet!"));
+       return;
+    }
+    GETCHAR(code, inp);
+    GETCHAR(id, inp);
+    ppp_warn("%s: Rcvd Code-Reject for code %d, id %d", PROTO_NAME(f), code, id);
+
+    if( f->state == PPP_FSM_ACKRCVD )
+       f->state = PPP_FSM_REQSENT;
+}
+
+
+/*
+ * fsm_protreject - Peer doesn't speak this protocol.
+ *
+ * Treat this as a catastrophic error (RXJ-).
+ */
+void fsm_protreject(fsm *f) {
+    switch( f->state ){
+    case PPP_FSM_CLOSING:
+       UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
+       /* fall through */
+       /* no break */
+    case PPP_FSM_CLOSED:
+       f->state = PPP_FSM_CLOSED;
+       if( f->callbacks->finished )
+           (*f->callbacks->finished)(f);
+       break;
+
+    case PPP_FSM_STOPPING:
+    case PPP_FSM_REQSENT:
+    case PPP_FSM_ACKRCVD:
+    case PPP_FSM_ACKSENT:
+       UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
+       /* fall through */
+       /* no break */
+    case PPP_FSM_STOPPED:
+       f->state = PPP_FSM_STOPPED;
+       if( f->callbacks->finished )
+           (*f->callbacks->finished)(f);
+       break;
+
+    case PPP_FSM_OPENED:
+       terminate_layer(f, PPP_FSM_STOPPING);
+       break;
+
+    default:
+       FSMDEBUG(("%s: Protocol-reject event in state %d!",
+                 PROTO_NAME(f), f->state));
+       /* no break */
+    }
+}
+
+
+/*
+ * fsm_sconfreq - Send a Configure-Request.
+ */
+static void fsm_sconfreq(fsm *f, int retransmit) {
+    ppp_pcb *pcb = f->pcb;
+    struct pbuf *p;
+    u_char *outp;
+    int cilen;
+
+    if( f->state != PPP_FSM_REQSENT && f->state != PPP_FSM_ACKRCVD && f->state != PPP_FSM_ACKSENT ){
+       /* Not currently negotiating - reset options */
+       if( f->callbacks->resetci )
+           (*f->callbacks->resetci)(f);
+       f->nakloops = 0;
+       f->rnakloops = 0;
+    }
+
+    if( !retransmit ){
+       /* New request - reset retransmission counter, use new ID */
+       f->retransmits = pcb->settings.fsm_max_conf_req_transmits;
+       f->reqid = ++f->id;
+    }
+
+    f->seen_ack = 0;
+
+    /*
+     * Make up the request packet
+     */
+    if( f->callbacks->cilen && f->callbacks->addci ){
+       cilen = (*f->callbacks->cilen)(f);
+       if( cilen > pcb->peer_mru - HEADERLEN )
+           cilen = pcb->peer_mru - HEADERLEN;
+    } else
+       cilen = 0;
+
+    p = pbuf_alloc(PBUF_RAW, (u16_t)(cilen + HEADERLEN + PPP_HDRLEN), PPP_CTRL_PBUF_TYPE);
+    if(NULL == p)
+        return;
+    if(p->tot_len != p->len) {
+        pbuf_free(p);
+        return;
+    }
+
+    /* send the request to our peer */
+    outp = (u_char*)p->payload;
+    MAKEHEADER(outp, f->protocol);
+    PUTCHAR(CONFREQ, outp);
+    PUTCHAR(f->reqid, outp);
+    PUTSHORT(cilen + HEADERLEN, outp);
+    if (cilen != 0) {
+       (*f->callbacks->addci)(f, outp, &cilen);
+       LWIP_ASSERT("cilen == p->len - HEADERLEN - PPP_HDRLEN", cilen == p->len - HEADERLEN - PPP_HDRLEN);
+    }
+
+    ppp_write(pcb, p);
+
+    /* start the retransmit timer */
+    --f->retransmits;
+    TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time);
+}
+
+
+/*
+ * fsm_sdata - Send some data.
+ *
+ * Used for all packets sent to our peer by this module.
+ */
+void fsm_sdata(fsm *f, u_char code, u_char id, const u_char *data, int datalen) {
+    ppp_pcb *pcb = f->pcb;
+    struct pbuf *p;
+    u_char *outp;
+    int outlen;
+
+    /* Adjust length to be smaller than MTU */
+    if (datalen > pcb->peer_mru - HEADERLEN)
+       datalen = pcb->peer_mru - HEADERLEN;
+    outlen = datalen + HEADERLEN;
+
+    p = pbuf_alloc(PBUF_RAW, (u16_t)(outlen + PPP_HDRLEN), PPP_CTRL_PBUF_TYPE);
+    if(NULL == p)
+        return;
+    if(p->tot_len != p->len) {
+        pbuf_free(p);
+        return;
+    }
+
+    outp = (u_char*)p->payload;
+    if (datalen) /* && data != outp + PPP_HDRLEN + HEADERLEN)  -- was only for fsm_sconfreq() */
+       MEMCPY(outp + PPP_HDRLEN + HEADERLEN, data, datalen);
+    MAKEHEADER(outp, f->protocol);
+    PUTCHAR(code, outp);
+    PUTCHAR(id, outp);
+    PUTSHORT(outlen, outp);
+    ppp_write(pcb, p);
+}
+
+#endif /* PPP_SUPPORT */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/ipcp.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/ipcp.c
new file mode 100644 (file)
index 0000000..b7c766e
--- /dev/null
@@ -0,0 +1,2418 @@
+/*
+ * ipcp.c - PPP IP Control Protocol.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * 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.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPP_IPV4_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+/*
+ * @todo:
+ */
+
+#if 0 /* UNUSED */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif /* UNUSED */
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/fsm.h"
+#include "netif/ppp/ipcp.h"
+
+#if 0 /* UNUSED */
+/* global vars */
+u32_t netmask = 0;             /* IP netmask to set on interface */
+#endif /* UNUSED */
+
+#if 0 /* UNUSED */
+bool   disable_defaultip = 0;  /* Don't use hostname for default IP adrs */
+#endif /* UNUSED */
+
+#if 0 /* moved to ppp_settings */
+bool   noremoteip = 0;         /* Let him have no IP address */
+#endif /* moved to ppp_setting */
+
+#if 0 /* UNUSED */
+/* Hook for a plugin to know when IP protocol has come up */
+void (*ip_up_hook) (void) = NULL;
+
+/* Hook for a plugin to know when IP protocol has come down */
+void (*ip_down_hook) (void) = NULL;
+
+/* Hook for a plugin to choose the remote IP address */
+void (*ip_choose_hook) (u32_t *) = NULL;
+#endif /* UNUSED */
+
+#if PPP_NOTIFY
+/* Notifiers for when IPCP goes up and down */
+struct notifier *ip_up_notifier = NULL;
+struct notifier *ip_down_notifier = NULL;
+#endif /* PPP_NOTIFY */
+
+/* local vars */
+#if 0 /* moved to ppp_pcb */
+static int default_route_set[NUM_PPP]; /* Have set up a default route */
+static int proxy_arp_set[NUM_PPP];     /* Have created proxy arp entry */
+static int ipcp_is_up;                 /* have called np_up() */
+static int ipcp_is_open;               /* haven't called np_finished() */
+static bool ask_for_local;             /* request our address from peer */
+#endif /* moved to ppp_pcb */
+#if 0 /* UNUSED */
+static char vj_value[8];               /* string form of vj option value */
+static char netmask_str[20];           /* string form of netmask value */
+#endif /* UNUSED */
+
+/*
+ * Callbacks for fsm code.  (CI = Configuration Information)
+ */
+static void ipcp_resetci(fsm *f);      /* Reset our CI */
+static int  ipcp_cilen(fsm *f);                /* Return length of our CI */
+static void ipcp_addci(fsm *f, u_char *ucp, int *lenp); /* Add our CI */
+static int  ipcp_ackci(fsm *f, u_char *p, int len);    /* Peer ack'd our CI */
+static int  ipcp_nakci(fsm *f, u_char *p, int len, int treat_as_reject);/* Peer nak'd our CI */
+static int  ipcp_rejci(fsm *f, u_char *p, int len);    /* Peer rej'd our CI */
+static int  ipcp_reqci(fsm *f, u_char *inp, int *len, int reject_if_disagree); /* Rcv CI */
+static void ipcp_up(fsm *f);           /* We're UP */
+static void ipcp_down(fsm *f);         /* We're DOWN */
+static void ipcp_finished(fsm *f);     /* Don't need lower layer */
+
+static const fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */
+    ipcp_resetci,              /* Reset our Configuration Information */
+    ipcp_cilen,                        /* Length of our Configuration Information */
+    ipcp_addci,                        /* Add our Configuration Information */
+    ipcp_ackci,                        /* ACK our Configuration Information */
+    ipcp_nakci,                        /* NAK our Configuration Information */
+    ipcp_rejci,                        /* Reject our Configuration Information */
+    ipcp_reqci,                        /* Request peer's Configuration Information */
+    ipcp_up,                   /* Called when fsm reaches OPENED state */
+    ipcp_down,                 /* Called when fsm leaves OPENED state */
+    NULL,                      /* Called when we want the lower layer up */
+    ipcp_finished,             /* Called when we want the lower layer down */
+    NULL,                      /* Called when Protocol-Reject received */
+    NULL,                      /* Retransmission is necessary */
+    NULL,                      /* Called to handle protocol-specific codes */
+    "IPCP"                     /* String name of protocol */
+};
+
+/*
+ * Command-line options.
+ */
+#if PPP_OPTIONS
+static int setvjslots (char **);
+static int setdnsaddr (char **);
+static int setwinsaddr (char **);
+static int setnetmask (char **);
+int setipaddr (char *, char **, int);
+
+static void printipaddr (option_t *, void (*)(void *, char *,...),void *);
+
+static option_t ipcp_option_list[] = {
+    { "noip", o_bool, &ipcp_protent.enabled_flag,
+      "Disable IP and IPCP" },
+    { "-ip", o_bool, &ipcp_protent.enabled_flag,
+      "Disable IP and IPCP", OPT_ALIAS },
+
+    { "novj", o_bool, &ipcp_wantoptions[0].neg_vj,
+      "Disable VJ compression", OPT_A2CLR, &ipcp_allowoptions[0].neg_vj },
+    { "-vj", o_bool, &ipcp_wantoptions[0].neg_vj,
+      "Disable VJ compression", OPT_ALIAS | OPT_A2CLR,
+      &ipcp_allowoptions[0].neg_vj },
+
+    { "novjccomp", o_bool, &ipcp_wantoptions[0].cflag,
+      "Disable VJ connection-ID compression", OPT_A2CLR,
+      &ipcp_allowoptions[0].cflag },
+    { "-vjccomp", o_bool, &ipcp_wantoptions[0].cflag,
+      "Disable VJ connection-ID compression", OPT_ALIAS | OPT_A2CLR,
+      &ipcp_allowoptions[0].cflag },
+
+    { "vj-max-slots", o_special, (void *)setvjslots,
+      "Set maximum VJ header slots",
+      OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, vj_value },
+
+    { "ipcp-accept-local", o_bool, &ipcp_wantoptions[0].accept_local,
+      "Accept peer's address for us", 1 },
+    { "ipcp-accept-remote", o_bool, &ipcp_wantoptions[0].accept_remote,
+      "Accept peer's address for it", 1 },
+
+    { "ipparam", o_string, &ipparam,
+      "Set ip script parameter", OPT_PRIO },
+
+    { "noipdefault", o_bool, &disable_defaultip,
+      "Don't use name for default IP adrs", 1 },
+
+    { "ms-dns", 1, (void *)setdnsaddr,
+      "DNS address for the peer's use" },
+    { "ms-wins", 1, (void *)setwinsaddr,
+      "Nameserver for SMB over TCP/IP for peer" },
+
+    { "ipcp-restart", o_int, &ipcp_fsm[0].timeouttime,
+      "Set timeout for IPCP", OPT_PRIO },
+    { "ipcp-max-terminate", o_int, &ipcp_fsm[0].maxtermtransmits,
+      "Set max #xmits for term-reqs", OPT_PRIO },
+    { "ipcp-max-configure", o_int, &ipcp_fsm[0].maxconfreqtransmits,
+      "Set max #xmits for conf-reqs", OPT_PRIO },
+    { "ipcp-max-failure", o_int, &ipcp_fsm[0].maxnakloops,
+      "Set max #conf-naks for IPCP", OPT_PRIO },
+
+    { "defaultroute", o_bool, &ipcp_wantoptions[0].default_route,
+      "Add default route", OPT_ENABLE|1, &ipcp_allowoptions[0].default_route },
+    { "nodefaultroute", o_bool, &ipcp_allowoptions[0].default_route,
+      "disable defaultroute option", OPT_A2CLR,
+      &ipcp_wantoptions[0].default_route },
+    { "-defaultroute", o_bool, &ipcp_allowoptions[0].default_route,
+      "disable defaultroute option", OPT_ALIAS | OPT_A2CLR,
+      &ipcp_wantoptions[0].default_route },
+
+    { "replacedefaultroute", o_bool,
+                               &ipcp_wantoptions[0].replace_default_route,
+      "Replace default route", 1
+    },
+    { "noreplacedefaultroute", o_bool,
+                               &ipcp_allowoptions[0].replace_default_route,
+      "Never replace default route", OPT_A2COPY,
+                               &ipcp_wantoptions[0].replace_default_route },
+    { "proxyarp", o_bool, &ipcp_wantoptions[0].proxy_arp,
+      "Add proxy ARP entry", OPT_ENABLE|1, &ipcp_allowoptions[0].proxy_arp },
+    { "noproxyarp", o_bool, &ipcp_allowoptions[0].proxy_arp,
+      "disable proxyarp option", OPT_A2CLR,
+      &ipcp_wantoptions[0].proxy_arp },
+    { "-proxyarp", o_bool, &ipcp_allowoptions[0].proxy_arp,
+      "disable proxyarp option", OPT_ALIAS | OPT_A2CLR,
+      &ipcp_wantoptions[0].proxy_arp },
+
+    { "usepeerdns", o_bool, &usepeerdns,
+      "Ask peer for DNS address(es)", 1 },
+
+    { "netmask", o_special, (void *)setnetmask,
+      "set netmask", OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, netmask_str },
+
+    { "ipcp-no-addresses", o_bool, &ipcp_wantoptions[0].old_addrs,
+      "Disable old-style IP-Addresses usage", OPT_A2CLR,
+      &ipcp_allowoptions[0].old_addrs },
+    { "ipcp-no-address", o_bool, &ipcp_wantoptions[0].neg_addr,
+      "Disable IP-Address usage", OPT_A2CLR,
+      &ipcp_allowoptions[0].neg_addr },
+
+    { "noremoteip", o_bool, &noremoteip,
+      "Allow peer to have no IP address", 1 },
+
+    { "nosendip", o_bool, &ipcp_wantoptions[0].neg_addr,
+      "Don't send our IP address to peer", OPT_A2CLR,
+      &ipcp_wantoptions[0].old_addrs},
+
+    { "IP addresses", o_wild, (void *) &setipaddr,
+      "set local and remote IP addresses",
+      OPT_NOARG | OPT_A2PRINTER, (void *) &printipaddr },
+
+    { NULL }
+};
+#endif /* PPP_OPTIONS */
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ipcp_init(ppp_pcb *pcb);
+static void ipcp_open(ppp_pcb *pcb);
+static void ipcp_close(ppp_pcb *pcb, const char *reason);
+static void ipcp_lowerup(ppp_pcb *pcb);
+static void ipcp_lowerdown(ppp_pcb *pcb);
+static void ipcp_input(ppp_pcb *pcb, u_char *p, int len);
+static void ipcp_protrej(ppp_pcb *pcb);
+#if PRINTPKT_SUPPORT
+static int ipcp_printpkt(const u_char *p, int plen,
+               void (*printer) (void *, const char *, ...), void *arg);
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_OPTIONS
+static void ip_check_options (void);
+#endif /* PPP_OPTIONS */
+#if DEMAND_SUPPORT
+static int  ip_demand_conf (int);
+static int  ip_active_pkt (u_char *, int);
+#endif /* DEMAND_SUPPORT */
+#if 0 /* UNUSED */
+static void create_resolv (u32_t, u32_t);
+#endif /* UNUSED */
+
+const struct protent ipcp_protent = {
+    PPP_IPCP,
+    ipcp_init,
+    ipcp_input,
+    ipcp_protrej,
+    ipcp_lowerup,
+    ipcp_lowerdown,
+    ipcp_open,
+    ipcp_close,
+#if PRINTPKT_SUPPORT
+    ipcp_printpkt,
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_DATAINPUT
+    NULL,
+#endif /* PPP_DATAINPUT */
+#if PRINTPKT_SUPPORT
+    "IPCP",
+    "IP",
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_OPTIONS
+    ipcp_option_list,
+    ip_check_options,
+#endif /* PPP_OPTIONS */
+#if DEMAND_SUPPORT
+    ip_demand_conf,
+    ip_active_pkt
+#endif /* DEMAND_SUPPORT */
+};
+
+static void ipcp_clear_addrs(ppp_pcb *pcb, u32_t ouraddr, u32_t hisaddr, u8_t replacedefaultroute);
+
+/*
+ * Lengths of configuration options.
+ */
+#define CILEN_VOID     2
+#define CILEN_COMPRESS 4       /* min length for compression protocol opt. */
+#define CILEN_VJ       6       /* length for RFC1332 Van-Jacobson opt. */
+#define CILEN_ADDR     6       /* new-style single address option */
+#define CILEN_ADDRS    10      /* old-style dual address option */
+
+
+#define CODENAME(x)    ((x) == CONFACK ? "ACK" : \
+                        (x) == CONFNAK ? "NAK" : "REJ")
+
+#if 0 /* UNUSED, already defined by lwIP */
+/*
+ * Make a string representation of a network IP address.
+ */
+char *
+ip_ntoa(ipaddr)
+u32_t ipaddr;
+{
+    static char b[64];
+
+    slprintf(b, sizeof(b), "%I", ipaddr);
+    return b;
+}
+#endif /* UNUSED, already defined by lwIP */
+
+/*
+ * Option parsing.
+ */
+#if PPP_OPTIONS
+/*
+ * setvjslots - set maximum number of connection slots for VJ compression
+ */
+static int
+setvjslots(argv)
+    char **argv;
+{
+    int value;
+
+    if (!int_option(*argv, &value))
+       return 0;
+
+    if (value < 2 || value > 16) {
+       option_error("vj-max-slots value must be between 2 and 16");
+       return 0;
+    }
+    ipcp_wantoptions [0].maxslotindex =
+        ipcp_allowoptions[0].maxslotindex = value - 1;
+    slprintf(vj_value, sizeof(vj_value), "%d", value);
+    return 1;
+}
+
+/*
+ * setdnsaddr - set the dns address(es)
+ */
+static int
+setdnsaddr(argv)
+    char **argv;
+{
+    u32_t dns;
+    struct hostent *hp;
+
+    dns = inet_addr(*argv);
+    if (dns == (u32_t) -1) {
+       if ((hp = gethostbyname(*argv)) == NULL) {
+           option_error("invalid address parameter '%s' for ms-dns option",
+                        *argv);
+           return 0;
+       }
+       dns = *(u32_t *)hp->h_addr;
+    }
+
+    /* We take the last 2 values given, the 2nd-last as the primary
+       and the last as the secondary.  If only one is given it
+       becomes both primary and secondary. */
+    if (ipcp_allowoptions[0].dnsaddr[1] == 0)
+       ipcp_allowoptions[0].dnsaddr[0] = dns;
+    else
+       ipcp_allowoptions[0].dnsaddr[0] = ipcp_allowoptions[0].dnsaddr[1];
+
+    /* always set the secondary address value. */
+    ipcp_allowoptions[0].dnsaddr[1] = dns;
+
+    return (1);
+}
+
+/*
+ * setwinsaddr - set the wins address(es)
+ * This is primrarly used with the Samba package under UNIX or for pointing
+ * the caller to the existing WINS server on a Windows NT platform.
+ */
+static int
+setwinsaddr(argv)
+    char **argv;
+{
+    u32_t wins;
+    struct hostent *hp;
+
+    wins = inet_addr(*argv);
+    if (wins == (u32_t) -1) {
+       if ((hp = gethostbyname(*argv)) == NULL) {
+           option_error("invalid address parameter '%s' for ms-wins option",
+                        *argv);
+           return 0;
+       }
+       wins = *(u32_t *)hp->h_addr;
+    }
+
+    /* We take the last 2 values given, the 2nd-last as the primary
+       and the last as the secondary.  If only one is given it
+       becomes both primary and secondary. */
+    if (ipcp_allowoptions[0].winsaddr[1] == 0)
+       ipcp_allowoptions[0].winsaddr[0] = wins;
+    else
+       ipcp_allowoptions[0].winsaddr[0] = ipcp_allowoptions[0].winsaddr[1];
+
+    /* always set the secondary address value. */
+    ipcp_allowoptions[0].winsaddr[1] = wins;
+
+    return (1);
+}
+
+/*
+ * setipaddr - Set the IP address
+ * If doit is 0, the call is to check whether this option is
+ * potentially an IP address specification.
+ * Not static so that plugins can call it to set the addresses
+ */
+int
+setipaddr(arg, argv, doit)
+    char *arg;
+    char **argv;
+    int doit;
+{
+    struct hostent *hp;
+    char *colon;
+    u32_t local, remote;
+    ipcp_options *wo = &ipcp_wantoptions[0];
+    static int prio_local = 0, prio_remote = 0;
+
+    /*
+     * IP address pair separated by ":".
+     */
+    if ((colon = strchr(arg, ':')) == NULL)
+       return 0;
+    if (!doit)
+       return 1;
+  
+    /*
+     * If colon first character, then no local addr.
+     */
+    if (colon != arg && option_priority >= prio_local) {
+       *colon = '\0';
+       if ((local = inet_addr(arg)) == (u32_t) -1) {
+           if ((hp = gethostbyname(arg)) == NULL) {
+               option_error("unknown host: %s", arg);
+               return 0;
+           }
+           local = *(u32_t *)hp->h_addr;
+       }
+       if (bad_ip_adrs(local)) {
+           option_error("bad local IP address %s", ip_ntoa(local));
+           return 0;
+       }
+       if (local != 0)
+           wo->ouraddr = local;
+       *colon = ':';
+       prio_local = option_priority;
+    }
+  
+    /*
+     * If colon last character, then no remote addr.
+     */
+    if (*++colon != '\0' && option_priority >= prio_remote) {
+       if ((remote = inet_addr(colon)) == (u32_t) -1) {
+           if ((hp = gethostbyname(colon)) == NULL) {
+               option_error("unknown host: %s", colon);
+               return 0;
+           }
+           remote = *(u32_t *)hp->h_addr;
+           if (remote_name[0] == 0)
+               strlcpy(remote_name, colon, sizeof(remote_name));
+       }
+       if (bad_ip_adrs(remote)) {
+           option_error("bad remote IP address %s", ip_ntoa(remote));
+           return 0;
+       }
+       if (remote != 0)
+           wo->hisaddr = remote;
+       prio_remote = option_priority;
+    }
+
+    return 1;
+}
+
+static void
+printipaddr(opt, printer, arg)
+    option_t *opt;
+    void (*printer) (void *, char *, ...);
+    void *arg;
+{
+       ipcp_options *wo = &ipcp_wantoptions[0];
+
+       if (wo->ouraddr != 0)
+               printer(arg, "%I", wo->ouraddr);
+       printer(arg, ":");
+       if (wo->hisaddr != 0)
+               printer(arg, "%I", wo->hisaddr);
+}
+
+/*
+ * setnetmask - set the netmask to be used on the interface.
+ */
+static int
+setnetmask(argv)
+    char **argv;
+{
+    u32_t mask;
+    int n;
+    char *p;
+
+    /*
+     * Unfortunately, if we use inet_addr, we can't tell whether
+     * a result of all 1s is an error or a valid 255.255.255.255.
+     */
+    p = *argv;
+    n = parse_dotted_ip(p, &mask);
+
+    mask = lwip_htonl(mask);
+
+    if (n == 0 || p[n] != 0 || (netmask & ~mask) != 0) {
+       option_error("invalid netmask value '%s'", *argv);
+       return 0;
+    }
+
+    netmask = mask;
+    slprintf(netmask_str, sizeof(netmask_str), "%I", mask);
+
+    return (1);
+}
+
+int
+parse_dotted_ip(p, vp)
+    char *p;
+    u32_t *vp;
+{
+    int n;
+    u32_t v, b;
+    char *endp, *p0 = p;
+
+    v = 0;
+    for (n = 3;; --n) {
+       b = strtoul(p, &endp, 0);
+       if (endp == p)
+           return 0;
+       if (b > 255) {
+           if (n < 3)
+               return 0;
+           /* accept e.g. 0xffffff00 */
+           *vp = b;
+           return endp - p0;
+       }
+       v |= b << (n * 8);
+       p = endp;
+       if (n == 0)
+           break;
+       if (*p != '.')
+           return 0;
+       ++p;
+    }
+    *vp = v;
+    return p - p0;
+}
+#endif /* PPP_OPTIONS */
+
+/*
+ * ipcp_init - Initialize IPCP.
+ */
+static void ipcp_init(ppp_pcb *pcb) {
+    fsm *f = &pcb->ipcp_fsm;
+
+    ipcp_options *wo = &pcb->ipcp_wantoptions;
+    ipcp_options *ao = &pcb->ipcp_allowoptions;
+
+    f->pcb = pcb;
+    f->protocol = PPP_IPCP;
+    f->callbacks = &ipcp_callbacks;
+    fsm_init(f);
+
+    /*
+     * Some 3G modems use repeated IPCP NAKs as a way of stalling
+     * until they can contact a server on the network, so we increase
+     * the default number of NAKs we accept before we start treating
+     * them as rejects.
+     */
+    f->maxnakloops = 100;
+
+#if 0 /* Not necessary, everything is cleared in ppp_new() */
+    memset(wo, 0, sizeof(*wo));
+    memset(ao, 0, sizeof(*ao));
+#endif /* 0 */
+
+    wo->neg_addr = wo->old_addrs = 1;
+#if VJ_SUPPORT
+    wo->neg_vj = 1;
+    wo->vj_protocol = IPCP_VJ_COMP;
+    wo->maxslotindex = MAX_STATES - 1; /* really max index */
+    wo->cflag = 1;
+#endif /* VJ_SUPPORT */
+
+#if 0 /* UNUSED */
+    /* wanting default route by default */
+    wo->default_route = 1;
+#endif /* UNUSED */
+
+    ao->neg_addr = ao->old_addrs = 1;
+#if VJ_SUPPORT
+    /* max slots and slot-id compression are currently hardwired in */
+    /* ppp_if.c to 16 and 1, this needs to be changed (among other */
+    /* things) gmc */
+
+    ao->neg_vj = 1;
+    ao->maxslotindex = MAX_STATES - 1;
+    ao->cflag = 1;
+#endif /* #if VJ_SUPPORT */
+
+#if 0 /* UNUSED */
+    /*
+     * XXX These control whether the user may use the proxyarp
+     * and defaultroute options.
+     */
+    ao->proxy_arp = 1;
+    ao->default_route = 1;
+#endif /* UNUSED */
+}
+
+
+/*
+ * ipcp_open - IPCP is allowed to come up.
+ */
+static void ipcp_open(ppp_pcb *pcb) {
+    fsm *f = &pcb->ipcp_fsm;
+    fsm_open(f);
+    pcb->ipcp_is_open = 1;
+}
+
+
+/*
+ * ipcp_close - Take IPCP down.
+ */
+static void ipcp_close(ppp_pcb *pcb, const char *reason) {
+    fsm *f = &pcb->ipcp_fsm;
+    fsm_close(f, reason);
+}
+
+
+/*
+ * ipcp_lowerup - The lower layer is up.
+ */
+static void ipcp_lowerup(ppp_pcb *pcb) {
+    fsm *f = &pcb->ipcp_fsm;
+    fsm_lowerup(f);
+}
+
+
+/*
+ * ipcp_lowerdown - The lower layer is down.
+ */
+static void ipcp_lowerdown(ppp_pcb *pcb) {
+    fsm *f = &pcb->ipcp_fsm;
+    fsm_lowerdown(f);
+}
+
+
+/*
+ * ipcp_input - Input IPCP packet.
+ */
+static void ipcp_input(ppp_pcb *pcb, u_char *p, int len) {
+    fsm *f = &pcb->ipcp_fsm;
+    fsm_input(f, p, len);
+}
+
+
+/*
+ * ipcp_protrej - A Protocol-Reject was received for IPCP.
+ *
+ * Pretend the lower layer went down, so we shut up.
+ */
+static void ipcp_protrej(ppp_pcb *pcb) {
+    fsm *f = &pcb->ipcp_fsm;
+    fsm_lowerdown(f);
+}
+
+
+/*
+ * ipcp_resetci - Reset our CI.
+ * Called by fsm_sconfreq, Send Configure Request.
+ */
+static void ipcp_resetci(fsm *f) {
+    ppp_pcb *pcb = f->pcb;
+    ipcp_options *wo = &pcb->ipcp_wantoptions;
+    ipcp_options *go = &pcb->ipcp_gotoptions;
+    ipcp_options *ao = &pcb->ipcp_allowoptions;
+
+    wo->req_addr = (wo->neg_addr || wo->old_addrs) &&
+       (ao->neg_addr || ao->old_addrs);
+    if (wo->ouraddr == 0)
+       wo->accept_local = 1;
+    if (wo->hisaddr == 0)
+       wo->accept_remote = 1;
+#if LWIP_DNS
+    wo->req_dns1 = wo->req_dns2 = pcb->settings.usepeerdns;    /* Request DNS addresses from the peer */
+#endif /* LWIP_DNS */
+    *go = *wo;
+    if (!pcb->ask_for_local)
+       go->ouraddr = 0;
+#if 0 /* UNUSED */
+    if (ip_choose_hook) {
+       ip_choose_hook(&wo->hisaddr);
+       if (wo->hisaddr) {
+           wo->accept_remote = 0;
+       }
+    }
+#endif /* UNUSED */
+    BZERO(&pcb->ipcp_hisoptions, sizeof(ipcp_options));
+}
+
+
+/*
+ * ipcp_cilen - Return length of our CI.
+ * Called by fsm_sconfreq, Send Configure Request.
+ */
+static int ipcp_cilen(fsm *f) {
+    ppp_pcb *pcb = f->pcb;
+    ipcp_options *go = &pcb->ipcp_gotoptions;
+#if VJ_SUPPORT
+    ipcp_options *wo = &pcb->ipcp_wantoptions;
+#endif /* VJ_SUPPORT */
+    ipcp_options *ho = &pcb->ipcp_hisoptions;
+
+#define LENCIADDRS(neg)                (neg ? CILEN_ADDRS : 0)
+#if VJ_SUPPORT
+#define LENCIVJ(neg, old)      (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0)
+#endif /* VJ_SUPPORT */
+#define LENCIADDR(neg)         (neg ? CILEN_ADDR : 0)
+#if LWIP_DNS
+#define LENCIDNS(neg)          LENCIADDR(neg)
+#endif /* LWIP_DNS */
+#if 0 /* UNUSED - WINS */
+#define LENCIWINS(neg)         LENCIADDR(neg)
+#endif /* UNUSED - WINS */
+
+    /*
+     * First see if we want to change our options to the old
+     * forms because we have received old forms from the peer.
+     */
+    if (go->neg_addr && go->old_addrs && !ho->neg_addr && ho->old_addrs)
+       go->neg_addr = 0;
+
+#if VJ_SUPPORT
+    if (wo->neg_vj && !go->neg_vj && !go->old_vj) {
+       /* try an older style of VJ negotiation */
+       /* use the old style only if the peer did */
+       if (ho->neg_vj && ho->old_vj) {
+           go->neg_vj = 1;
+           go->old_vj = 1;
+           go->vj_protocol = ho->vj_protocol;
+       }
+    }
+#endif /* VJ_SUPPORT */
+
+    return (LENCIADDRS(!go->neg_addr && go->old_addrs) +
+#if VJ_SUPPORT
+           LENCIVJ(go->neg_vj, go->old_vj) +
+#endif /* VJ_SUPPORT */
+           LENCIADDR(go->neg_addr) +
+#if LWIP_DNS
+           LENCIDNS(go->req_dns1) +
+           LENCIDNS(go->req_dns2) +
+#endif /* LWIP_DNS */
+#if 0 /* UNUSED - WINS */
+           LENCIWINS(go->winsaddr[0]) +
+           LENCIWINS(go->winsaddr[1]) +
+#endif /* UNUSED - WINS */
+           0);
+}
+
+
+/*
+ * ipcp_addci - Add our desired CIs to a packet.
+ * Called by fsm_sconfreq, Send Configure Request.
+ */
+static void ipcp_addci(fsm *f, u_char *ucp, int *lenp) {
+    ppp_pcb *pcb = f->pcb;
+    ipcp_options *go = &pcb->ipcp_gotoptions;
+    int len = *lenp;
+
+#define ADDCIADDRS(opt, neg, val1, val2) \
+    if (neg) { \
+       if (len >= CILEN_ADDRS) { \
+           u32_t l; \
+           PUTCHAR(opt, ucp); \
+           PUTCHAR(CILEN_ADDRS, ucp); \
+           l = lwip_ntohl(val1); \
+           PUTLONG(l, ucp); \
+           l = lwip_ntohl(val2); \
+           PUTLONG(l, ucp); \
+           len -= CILEN_ADDRS; \
+       } else \
+           go->old_addrs = 0; \
+    }
+
+#if VJ_SUPPORT
+#define ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \
+    if (neg) { \
+       int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \
+       if (len >= vjlen) { \
+           PUTCHAR(opt, ucp); \
+           PUTCHAR(vjlen, ucp); \
+           PUTSHORT(val, ucp); \
+           if (!old) { \
+               PUTCHAR(maxslotindex, ucp); \
+               PUTCHAR(cflag, ucp); \
+           } \
+           len -= vjlen; \
+       } else \
+           neg = 0; \
+    }
+#endif /* VJ_SUPPORT */
+
+#define ADDCIADDR(opt, neg, val) \
+    if (neg) { \
+       if (len >= CILEN_ADDR) { \
+           u32_t l; \
+           PUTCHAR(opt, ucp); \
+           PUTCHAR(CILEN_ADDR, ucp); \
+           l = lwip_ntohl(val); \
+           PUTLONG(l, ucp); \
+           len -= CILEN_ADDR; \
+       } else \
+           neg = 0; \
+    }
+
+#if LWIP_DNS
+#define ADDCIDNS(opt, neg, addr) \
+    if (neg) { \
+       if (len >= CILEN_ADDR) { \
+           u32_t l; \
+           PUTCHAR(opt, ucp); \
+           PUTCHAR(CILEN_ADDR, ucp); \
+           l = lwip_ntohl(addr); \
+           PUTLONG(l, ucp); \
+           len -= CILEN_ADDR; \
+       } else \
+           neg = 0; \
+    }
+#endif /* LWIP_DNS */
+
+#if 0 /* UNUSED - WINS */
+#define ADDCIWINS(opt, addr) \
+    if (addr) { \
+       if (len >= CILEN_ADDR) { \
+           u32_t l; \
+           PUTCHAR(opt, ucp); \
+           PUTCHAR(CILEN_ADDR, ucp); \
+           l = lwip_ntohl(addr); \
+           PUTLONG(l, ucp); \
+           len -= CILEN_ADDR; \
+       } else \
+           addr = 0; \
+    }
+#endif /* UNUSED - WINS */
+
+    ADDCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs, go->ouraddr,
+              go->hisaddr);
+
+#if VJ_SUPPORT
+    ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
+           go->maxslotindex, go->cflag);
+#endif /* VJ_SUPPORT */
+
+    ADDCIADDR(CI_ADDR, go->neg_addr, go->ouraddr);
+
+#if LWIP_DNS
+    ADDCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]);
+
+    ADDCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]);
+#endif /* LWIP_DNS */
+
+#if 0 /* UNUSED - WINS */
+    ADDCIWINS(CI_MS_WINS1, go->winsaddr[0]);
+
+    ADDCIWINS(CI_MS_WINS2, go->winsaddr[1]);
+#endif /* UNUSED - WINS */
+    
+    *lenp -= len;
+}
+
+
+/*
+ * ipcp_ackci - Ack our CIs.
+ * Called by fsm_rconfack, Receive Configure ACK.
+ *
+ * Returns:
+ *     0 - Ack was bad.
+ *     1 - Ack was good.
+ */
+static int ipcp_ackci(fsm *f, u_char *p, int len) {
+    ppp_pcb *pcb = f->pcb;
+    ipcp_options *go = &pcb->ipcp_gotoptions;
+    u_short cilen, citype;
+    u32_t cilong;
+#if VJ_SUPPORT
+    u_short cishort;
+    u_char cimaxslotindex, cicflag;
+#endif /* VJ_SUPPORT */
+
+    /*
+     * CIs must be in exactly the same order that we sent...
+     * Check packet length and CI length at each step.
+     * If we find any deviations, then this packet is bad.
+     */
+
+#define ACKCIADDRS(opt, neg, val1, val2) \
+    if (neg) { \
+       u32_t l; \
+       if ((len -= CILEN_ADDRS) < 0) \
+           goto bad; \
+       GETCHAR(citype, p); \
+       GETCHAR(cilen, p); \
+       if (cilen != CILEN_ADDRS || \
+           citype != opt) \
+           goto bad; \
+       GETLONG(l, p); \
+       cilong = lwip_htonl(l); \
+       if (val1 != cilong) \
+           goto bad; \
+       GETLONG(l, p); \
+       cilong = lwip_htonl(l); \
+       if (val2 != cilong) \
+           goto bad; \
+    }
+
+#if VJ_SUPPORT
+#define ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \
+    if (neg) { \
+       int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \
+       if ((len -= vjlen) < 0) \
+           goto bad; \
+       GETCHAR(citype, p); \
+       GETCHAR(cilen, p); \
+       if (cilen != vjlen || \
+           citype != opt)  \
+           goto bad; \
+       GETSHORT(cishort, p); \
+       if (cishort != val) \
+           goto bad; \
+       if (!old) { \
+           GETCHAR(cimaxslotindex, p); \
+           if (cimaxslotindex != maxslotindex) \
+               goto bad; \
+           GETCHAR(cicflag, p); \
+           if (cicflag != cflag) \
+               goto bad; \
+       } \
+    }
+#endif /* VJ_SUPPORT */
+
+#define ACKCIADDR(opt, neg, val) \
+    if (neg) { \
+       u32_t l; \
+       if ((len -= CILEN_ADDR) < 0) \
+           goto bad; \
+       GETCHAR(citype, p); \
+       GETCHAR(cilen, p); \
+       if (cilen != CILEN_ADDR || \
+           citype != opt) \
+           goto bad; \
+       GETLONG(l, p); \
+       cilong = lwip_htonl(l); \
+       if (val != cilong) \
+           goto bad; \
+    }
+
+#if LWIP_DNS
+#define ACKCIDNS(opt, neg, addr) \
+    if (neg) { \
+       u32_t l; \
+       if ((len -= CILEN_ADDR) < 0) \
+           goto bad; \
+       GETCHAR(citype, p); \
+       GETCHAR(cilen, p); \
+       if (cilen != CILEN_ADDR || citype != opt) \
+           goto bad; \
+       GETLONG(l, p); \
+       cilong = lwip_htonl(l); \
+       if (addr != cilong) \
+           goto bad; \
+    }
+#endif /* LWIP_DNS */
+
+#if 0 /* UNUSED - WINS */
+#define ACKCIWINS(opt, addr) \
+    if (addr) { \
+       u32_t l; \
+       if ((len -= CILEN_ADDR) < 0) \
+           goto bad; \
+       GETCHAR(citype, p); \
+       GETCHAR(cilen, p); \
+       if (cilen != CILEN_ADDR || citype != opt) \
+           goto bad; \
+       GETLONG(l, p); \
+       cilong = lwip_htonl(l); \
+       if (addr != cilong) \
+           goto bad; \
+    }
+#endif /* UNUSED - WINS */
+
+    ACKCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs, go->ouraddr,
+              go->hisaddr);
+
+#if VJ_SUPPORT
+    ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
+           go->maxslotindex, go->cflag);
+#endif /* VJ_SUPPORT */
+
+    ACKCIADDR(CI_ADDR, go->neg_addr, go->ouraddr);
+
+#if LWIP_DNS
+    ACKCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]);
+
+    ACKCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]);
+#endif /* LWIP_DNS */
+
+#if 0 /* UNUSED - WINS */
+    ACKCIWINS(CI_MS_WINS1, go->winsaddr[0]);
+
+    ACKCIWINS(CI_MS_WINS2, go->winsaddr[1]);
+#endif /* UNUSED - WINS */
+
+    /*
+     * If there are any remaining CIs, then this packet is bad.
+     */
+    if (len != 0)
+       goto bad;
+    return (1);
+
+bad:
+    IPCPDEBUG(("ipcp_ackci: received bad Ack!"));
+    return (0);
+}
+
+/*
+ * ipcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if IPCP is in the OPENED state.
+ * Calback from fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
+ *
+ * Returns:
+ *     0 - Nak was bad.
+ *     1 - Nak was good.
+ */
+static int ipcp_nakci(fsm *f, u_char *p, int len, int treat_as_reject) {
+    ppp_pcb *pcb = f->pcb;
+    ipcp_options *go = &pcb->ipcp_gotoptions;
+    u_char citype, cilen, *next;
+#if VJ_SUPPORT
+    u_char cimaxslotindex, cicflag;
+    u_short cishort;
+#endif /* VJ_SUPPORT */
+    u32_t ciaddr1, ciaddr2, l;
+#if LWIP_DNS
+    u32_t cidnsaddr;
+#endif /* LWIP_DNS */
+    ipcp_options no;           /* options we've seen Naks for */
+    ipcp_options try_;         /* options to request next time */
+
+    BZERO(&no, sizeof(no));
+    try_ = *go;
+
+    /*
+     * Any Nak'd CIs must be in exactly the same order that we sent.
+     * Check packet length and CI length at each step.
+     * If we find any deviations, then this packet is bad.
+     */
+#define NAKCIADDRS(opt, neg, code) \
+    if ((neg) && \
+       (cilen = p[1]) == CILEN_ADDRS && \
+       len >= cilen && \
+       p[0] == opt) { \
+       len -= cilen; \
+       INCPTR(2, p); \
+       GETLONG(l, p); \
+       ciaddr1 = lwip_htonl(l); \
+       GETLONG(l, p); \
+       ciaddr2 = lwip_htonl(l); \
+       no.old_addrs = 1; \
+       code \
+    }
+
+#if VJ_SUPPORT
+#define NAKCIVJ(opt, neg, code) \
+    if (go->neg && \
+       ((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \
+       len >= cilen && \
+       p[0] == opt) { \
+       len -= cilen; \
+       INCPTR(2, p); \
+       GETSHORT(cishort, p); \
+       no.neg = 1; \
+        code \
+    }
+#endif /* VJ_SUPPORT */
+
+#define NAKCIADDR(opt, neg, code) \
+    if (go->neg && \
+       (cilen = p[1]) == CILEN_ADDR && \
+       len >= cilen && \
+       p[0] == opt) { \
+       len -= cilen; \
+       INCPTR(2, p); \
+       GETLONG(l, p); \
+       ciaddr1 = lwip_htonl(l); \
+       no.neg = 1; \
+       code \
+    }
+
+#if LWIP_DNS
+#define NAKCIDNS(opt, neg, code) \
+    if (go->neg && \
+       ((cilen = p[1]) == CILEN_ADDR) && \
+       len >= cilen && \
+       p[0] == opt) { \
+       len -= cilen; \
+       INCPTR(2, p); \
+       GETLONG(l, p); \
+       cidnsaddr = lwip_htonl(l); \
+       no.neg = 1; \
+       code \
+    }
+#endif /* LWIP_DNS */
+
+    /*
+     * Accept the peer's idea of {our,his} address, if different
+     * from our idea, only if the accept_{local,remote} flag is set.
+     */
+    NAKCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs,
+              if (treat_as_reject) {
+                  try_.old_addrs = 0;
+              } else {
+                  if (go->accept_local && ciaddr1) {
+                      /* take his idea of our address */
+                      try_.ouraddr = ciaddr1;
+                  }
+                  if (go->accept_remote && ciaddr2) {
+                      /* take his idea of his address */
+                      try_.hisaddr = ciaddr2;
+                  }
+              }
+       );
+
+#if VJ_SUPPORT
+    /*
+     * Accept the peer's value of maxslotindex provided that it
+     * is less than what we asked for.  Turn off slot-ID compression
+     * if the peer wants.  Send old-style compress-type option if
+     * the peer wants.
+     */
+    NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
+           if (treat_as_reject) {
+               try_.neg_vj = 0;
+           } else if (cilen == CILEN_VJ) {
+               GETCHAR(cimaxslotindex, p);
+               GETCHAR(cicflag, p);
+               if (cishort == IPCP_VJ_COMP) {
+                   try_.old_vj = 0;
+                   if (cimaxslotindex < go->maxslotindex)
+                       try_.maxslotindex = cimaxslotindex;
+                   if (!cicflag)
+                       try_.cflag = 0;
+               } else {
+                   try_.neg_vj = 0;
+               }
+           } else {
+               if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) {
+                   try_.old_vj = 1;
+                   try_.vj_protocol = cishort;
+               } else {
+                   try_.neg_vj = 0;
+               }
+           }
+           );
+#endif /* VJ_SUPPORT */
+
+    NAKCIADDR(CI_ADDR, neg_addr,
+             if (treat_as_reject) {
+                 try_.neg_addr = 0;
+                 try_.old_addrs = 0;
+             } else if (go->accept_local && ciaddr1) {
+                 /* take his idea of our address */
+                 try_.ouraddr = ciaddr1;
+             }
+             );
+
+#if LWIP_DNS
+    NAKCIDNS(CI_MS_DNS1, req_dns1,
+            if (treat_as_reject) {
+                try_.req_dns1 = 0;
+            } else {
+                try_.dnsaddr[0] = cidnsaddr;
+            }
+            );
+
+    NAKCIDNS(CI_MS_DNS2, req_dns2,
+            if (treat_as_reject) {
+                try_.req_dns2 = 0;
+            } else {
+                try_.dnsaddr[1] = cidnsaddr;
+            }
+            );
+#endif /* #if LWIP_DNS */
+
+    /*
+     * There may be remaining CIs, if the peer is requesting negotiation
+     * on an option that we didn't include in our request packet.
+     * If they want to negotiate about IP addresses, we comply.
+     * If they want us to ask for compression, we refuse.
+     * If they want us to ask for ms-dns, we do that, since some
+     * peers get huffy if we don't.
+     */
+    while (len >= CILEN_VOID) {
+       GETCHAR(citype, p);
+       GETCHAR(cilen, p);
+       if ( cilen < CILEN_VOID || (len -= cilen) < 0 )
+           goto bad;
+       next = p + cilen - 2;
+
+       switch (citype) {
+#if VJ_SUPPORT
+       case CI_COMPRESSTYPE:
+           if (go->neg_vj || no.neg_vj ||
+               (cilen != CILEN_VJ && cilen != CILEN_COMPRESS))
+               goto bad;
+           no.neg_vj = 1;
+           break;
+#endif /* VJ_SUPPORT */
+       case CI_ADDRS:
+           if ((!go->neg_addr && go->old_addrs) || no.old_addrs
+               || cilen != CILEN_ADDRS)
+               goto bad;
+           try_.neg_addr = 0;
+           GETLONG(l, p);
+           ciaddr1 = lwip_htonl(l);
+           if (ciaddr1 && go->accept_local)
+               try_.ouraddr = ciaddr1;
+           GETLONG(l, p);
+           ciaddr2 = lwip_htonl(l);
+           if (ciaddr2 && go->accept_remote)
+               try_.hisaddr = ciaddr2;
+           no.old_addrs = 1;
+           break;
+       case CI_ADDR:
+           if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR)
+               goto bad;
+           try_.old_addrs = 0;
+           GETLONG(l, p);
+           ciaddr1 = lwip_htonl(l);
+           if (ciaddr1 && go->accept_local)
+               try_.ouraddr = ciaddr1;
+           if (try_.ouraddr != 0)
+               try_.neg_addr = 1;
+           no.neg_addr = 1;
+           break;
+#if LWIP_DNS
+       case CI_MS_DNS1:
+           if (go->req_dns1 || no.req_dns1 || cilen != CILEN_ADDR)
+               goto bad;
+           GETLONG(l, p);
+           try_.dnsaddr[0] = lwip_htonl(l);
+           try_.req_dns1 = 1;
+           no.req_dns1 = 1;
+           break;
+       case CI_MS_DNS2:
+           if (go->req_dns2 || no.req_dns2 || cilen != CILEN_ADDR)
+               goto bad;
+           GETLONG(l, p);
+           try_.dnsaddr[1] = lwip_htonl(l);
+           try_.req_dns2 = 1;
+           no.req_dns2 = 1;
+           break;
+#endif /* LWIP_DNS */
+#if 0 /* UNUSED - WINS */
+       case CI_MS_WINS1:
+       case CI_MS_WINS2:
+           if (cilen != CILEN_ADDR)
+               goto bad;
+           GETLONG(l, p);
+           ciaddr1 = lwip_htonl(l);
+           if (ciaddr1)
+               try_.winsaddr[citype == CI_MS_WINS2] = ciaddr1;
+           break;
+#endif /* UNUSED - WINS */
+       default:
+           break;
+       }
+       p = next;
+    }
+
+    /*
+     * OK, the Nak is good.  Now we can update state.
+     * If there are any remaining options, we ignore them.
+     */
+    if (f->state != PPP_FSM_OPENED)
+       *go = try_;
+
+    return 1;
+
+bad:
+    IPCPDEBUG(("ipcp_nakci: received bad Nak!"));
+    return 0;
+}
+
+
+/*
+ * ipcp_rejci - Reject some of our CIs.
+ * Callback from fsm_rconfnakrej.
+ */
+static int ipcp_rejci(fsm *f, u_char *p, int len) {
+    ppp_pcb *pcb = f->pcb;
+    ipcp_options *go = &pcb->ipcp_gotoptions;
+    u_char cilen;
+#if VJ_SUPPORT
+    u_char cimaxslotindex, ciflag;
+    u_short cishort;
+#endif /* VJ_SUPPORT */
+    u32_t cilong;
+    ipcp_options try_;         /* options to request next time */
+
+    try_ = *go;
+    /*
+     * Any Rejected CIs must be in exactly the same order that we sent.
+     * Check packet length and CI length at each step.
+     * If we find any deviations, then this packet is bad.
+     */
+#define REJCIADDRS(opt, neg, val1, val2) \
+    if ((neg) && \
+       (cilen = p[1]) == CILEN_ADDRS && \
+       len >= cilen && \
+       p[0] == opt) { \
+       u32_t l; \
+       len -= cilen; \
+       INCPTR(2, p); \
+       GETLONG(l, p); \
+       cilong = lwip_htonl(l); \
+       /* Check rejected value. */ \
+       if (cilong != val1) \
+           goto bad; \
+       GETLONG(l, p); \
+       cilong = lwip_htonl(l); \
+       /* Check rejected value. */ \
+       if (cilong != val2) \
+           goto bad; \
+       try_.old_addrs = 0; \
+    }
+
+#if VJ_SUPPORT
+#define REJCIVJ(opt, neg, val, old, maxslot, cflag) \
+    if (go->neg && \
+       p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \
+       len >= p[1] && \
+       p[0] == opt) { \
+       len -= p[1]; \
+       INCPTR(2, p); \
+       GETSHORT(cishort, p); \
+       /* Check rejected value. */  \
+       if (cishort != val) \
+           goto bad; \
+       if (!old) { \
+          GETCHAR(cimaxslotindex, p); \
+          if (cimaxslotindex != maxslot) \
+            goto bad; \
+          GETCHAR(ciflag, p); \
+          if (ciflag != cflag) \
+            goto bad; \
+        } \
+       try_.neg = 0; \
+     }
+#endif /* VJ_SUPPORT */
+
+#define REJCIADDR(opt, neg, val) \
+    if (go->neg && \
+       (cilen = p[1]) == CILEN_ADDR && \
+       len >= cilen && \
+       p[0] == opt) { \
+       u32_t l; \
+       len -= cilen; \
+       INCPTR(2, p); \
+       GETLONG(l, p); \
+       cilong = lwip_htonl(l); \
+       /* Check rejected value. */ \
+       if (cilong != val) \
+           goto bad; \
+       try_.neg = 0; \
+    }
+
+#if LWIP_DNS
+#define REJCIDNS(opt, neg, dnsaddr) \
+    if (go->neg && \
+       ((cilen = p[1]) == CILEN_ADDR) && \
+       len >= cilen && \
+       p[0] == opt) { \
+       u32_t l; \
+       len -= cilen; \
+       INCPTR(2, p); \
+       GETLONG(l, p); \
+       cilong = lwip_htonl(l); \
+       /* Check rejected value. */ \
+       if (cilong != dnsaddr) \
+           goto bad; \
+       try_.neg = 0; \
+    }
+#endif /* LWIP_DNS */
+
+#if 0 /* UNUSED - WINS */
+#define REJCIWINS(opt, addr) \
+    if (addr && \
+       ((cilen = p[1]) == CILEN_ADDR) && \
+       len >= cilen && \
+       p[0] == opt) { \
+       u32_t l; \
+       len -= cilen; \
+       INCPTR(2, p); \
+       GETLONG(l, p); \
+       cilong = lwip_htonl(l); \
+       /* Check rejected value. */ \
+       if (cilong != addr) \
+           goto bad; \
+       try_.winsaddr[opt == CI_MS_WINS2] = 0; \
+    }
+#endif /* UNUSED - WINS */
+
+    REJCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs,
+              go->ouraddr, go->hisaddr);
+
+#if VJ_SUPPORT
+    REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj,
+           go->maxslotindex, go->cflag);
+#endif /* VJ_SUPPORT */
+
+    REJCIADDR(CI_ADDR, neg_addr, go->ouraddr);
+
+#if LWIP_DNS
+    REJCIDNS(CI_MS_DNS1, req_dns1, go->dnsaddr[0]);
+
+    REJCIDNS(CI_MS_DNS2, req_dns2, go->dnsaddr[1]);
+#endif /* LWIP_DNS */
+
+#if 0 /* UNUSED - WINS */
+    REJCIWINS(CI_MS_WINS1, go->winsaddr[0]);
+
+    REJCIWINS(CI_MS_WINS2, go->winsaddr[1]);
+#endif /* UNUSED - WINS */
+
+    /*
+     * If there are any remaining CIs, then this packet is bad.
+     */
+    if (len != 0)
+       goto bad;
+    /*
+     * Now we can update state.
+     */
+    if (f->state != PPP_FSM_OPENED)
+       *go = try_;
+    return 1;
+
+bad:
+    IPCPDEBUG(("ipcp_rejci: received bad Reject!"));
+    return 0;
+}
+
+
+/*
+ * ipcp_reqci - Check the peer's requested CIs and send appropriate response.
+ * Callback from fsm_rconfreq, Receive Configure Request
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately.  If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ *
+ * inp = Requested CIs
+ * len = Length of requested CIs
+ */
+static int ipcp_reqci(fsm *f, u_char *inp, int *len, int reject_if_disagree) {
+    ppp_pcb *pcb = f->pcb;
+    ipcp_options *wo = &pcb->ipcp_wantoptions;
+    ipcp_options *ho = &pcb->ipcp_hisoptions;
+    ipcp_options *ao = &pcb->ipcp_allowoptions;
+    u_char *cip, *next;                /* Pointer to current and next CIs */
+    u_short cilen, citype;     /* Parsed len, type */
+#if VJ_SUPPORT
+    u_short cishort;           /* Parsed short value */
+#endif /* VJ_SUPPORT */
+    u32_t tl, ciaddr1, ciaddr2;/* Parsed address values */
+    int rc = CONFACK;          /* Final packet return code */
+    int orc;                   /* Individual option return code */
+    u_char *p;                 /* Pointer to next char to parse */
+    u_char *ucp = inp;         /* Pointer to current output char */
+    int l = *len;              /* Length left */
+#if VJ_SUPPORT
+    u_char maxslotindex, cflag;
+#endif /* VJ_SUPPORT */
+#if LWIP_DNS
+    int d;
+#endif /* LWIP_DNS */
+
+    /*
+     * Reset all his options.
+     */
+    BZERO(ho, sizeof(*ho));
+    
+    /*
+     * Process all his options.
+     */
+    next = inp;
+    while (l) {
+       orc = CONFACK;                  /* Assume success */
+       cip = p = next;                 /* Remember begining of CI */
+       if (l < 2 ||                    /* Not enough data for CI header or */
+           p[1] < 2 ||                 /*  CI length too small or */
+           p[1] > l) {                 /*  CI length too big? */
+           IPCPDEBUG(("ipcp_reqci: bad CI length!"));
+           orc = CONFREJ;              /* Reject bad CI */
+           cilen = l;                  /* Reject till end of packet */
+           l = 0;                      /* Don't loop again */
+           goto endswitch;
+       }
+       GETCHAR(citype, p);             /* Parse CI type */
+       GETCHAR(cilen, p);              /* Parse CI length */
+       l -= cilen;                     /* Adjust remaining length */
+       next += cilen;                  /* Step to next CI */
+
+       switch (citype) {               /* Check CI type */
+       case CI_ADDRS:
+           if (!ao->old_addrs || ho->neg_addr ||
+               cilen != CILEN_ADDRS) { /* Check CI length */
+               orc = CONFREJ;          /* Reject CI */
+               break;
+           }
+
+           /*
+            * If he has no address, or if we both have his address but
+            * disagree about it, then NAK it with our idea.
+            * In particular, if we don't know his address, but he does,
+            * then accept it.
+            */
+           GETLONG(tl, p);             /* Parse source address (his) */
+           ciaddr1 = lwip_htonl(tl);
+           if (ciaddr1 != wo->hisaddr
+               && (ciaddr1 == 0 || !wo->accept_remote)) {
+               orc = CONFNAK;
+               if (!reject_if_disagree) {
+                   DECPTR(sizeof(u32_t), p);
+                   tl = lwip_ntohl(wo->hisaddr);
+                   PUTLONG(tl, p);
+               }
+           } else if (ciaddr1 == 0 && wo->hisaddr == 0) {
+               /*
+                * If neither we nor he knows his address, reject the option.
+                */
+               orc = CONFREJ;
+               wo->req_addr = 0;       /* don't NAK with 0.0.0.0 later */
+               break;
+           }
+
+           /*
+            * If he doesn't know our address, or if we both have our address
+            * but disagree about it, then NAK it with our idea.
+            */
+           GETLONG(tl, p);             /* Parse desination address (ours) */
+           ciaddr2 = lwip_htonl(tl);
+           if (ciaddr2 != wo->ouraddr) {
+               if (ciaddr2 == 0 || !wo->accept_local) {
+                   orc = CONFNAK;
+                   if (!reject_if_disagree) {
+                       DECPTR(sizeof(u32_t), p);
+                       tl = lwip_ntohl(wo->ouraddr);
+                       PUTLONG(tl, p);
+                   }
+               } else {
+                   wo->ouraddr = ciaddr2;      /* accept peer's idea */
+               }
+           }
+
+           ho->old_addrs = 1;
+           ho->hisaddr = ciaddr1;
+           ho->ouraddr = ciaddr2;
+           break;
+
+       case CI_ADDR:
+           if (!ao->neg_addr || ho->old_addrs ||
+               cilen != CILEN_ADDR) {  /* Check CI length */
+               orc = CONFREJ;          /* Reject CI */
+               break;
+           }
+
+           /*
+            * If he has no address, or if we both have his address but
+            * disagree about it, then NAK it with our idea.
+            * In particular, if we don't know his address, but he does,
+            * then accept it.
+            */
+           GETLONG(tl, p);     /* Parse source address (his) */
+           ciaddr1 = lwip_htonl(tl);
+           if (ciaddr1 != wo->hisaddr
+               && (ciaddr1 == 0 || !wo->accept_remote)) {
+               orc = CONFNAK;
+               if (!reject_if_disagree) {
+                   DECPTR(sizeof(u32_t), p);
+                   tl = lwip_ntohl(wo->hisaddr);
+                   PUTLONG(tl, p);
+               }
+           } else if (ciaddr1 == 0 && wo->hisaddr == 0) {
+               /*
+                * Don't ACK an address of 0.0.0.0 - reject it instead.
+                */
+               orc = CONFREJ;
+               wo->req_addr = 0;       /* don't NAK with 0.0.0.0 later */
+               break;
+           }
+       
+           ho->neg_addr = 1;
+           ho->hisaddr = ciaddr1;
+           break;
+
+#if LWIP_DNS
+       case CI_MS_DNS1:
+       case CI_MS_DNS2:
+           /* Microsoft primary or secondary DNS request */
+           d = citype == CI_MS_DNS2;
+
+           /* If we do not have a DNS address then we cannot send it */
+           if (ao->dnsaddr[d] == 0 ||
+               cilen != CILEN_ADDR) {  /* Check CI length */
+               orc = CONFREJ;          /* Reject CI */
+               break;
+           }
+           GETLONG(tl, p);
+           if (lwip_htonl(tl) != ao->dnsaddr[d]) {
+                DECPTR(sizeof(u32_t), p);
+               tl = lwip_ntohl(ao->dnsaddr[d]);
+               PUTLONG(tl, p);
+               orc = CONFNAK;
+            }
+            break;
+#endif /* LWIP_DNS */
+
+#if 0 /* UNUSED - WINS */
+       case CI_MS_WINS1:
+       case CI_MS_WINS2:
+           /* Microsoft primary or secondary WINS request */
+           d = citype == CI_MS_WINS2;
+
+           /* If we do not have a DNS address then we cannot send it */
+           if (ao->winsaddr[d] == 0 ||
+               cilen != CILEN_ADDR) {  /* Check CI length */
+               orc = CONFREJ;          /* Reject CI */
+               break;
+           }
+           GETLONG(tl, p);
+           if (lwip_htonl(tl) != ao->winsaddr[d]) {
+                DECPTR(sizeof(u32_t), p);
+               tl = lwip_ntohl(ao->winsaddr[d]);
+               PUTLONG(tl, p);
+               orc = CONFNAK;
+            }
+            break;
+#endif /* UNUSED - WINS */
+
+#if VJ_SUPPORT
+       case CI_COMPRESSTYPE:
+           if (!ao->neg_vj ||
+               (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) {
+               orc = CONFREJ;
+               break;
+           }
+           GETSHORT(cishort, p);
+
+           if (!(cishort == IPCP_VJ_COMP ||
+                 (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) {
+               orc = CONFREJ;
+               break;
+           }
+
+           ho->neg_vj = 1;
+           ho->vj_protocol = cishort;
+           if (cilen == CILEN_VJ) {
+               GETCHAR(maxslotindex, p);
+               if (maxslotindex > ao->maxslotindex) { 
+                   orc = CONFNAK;
+                   if (!reject_if_disagree){
+                       DECPTR(1, p);
+                       PUTCHAR(ao->maxslotindex, p);
+                   }
+               }
+               GETCHAR(cflag, p);
+               if (cflag && !ao->cflag) {
+                   orc = CONFNAK;
+                   if (!reject_if_disagree){
+                       DECPTR(1, p);
+                       PUTCHAR(wo->cflag, p);
+                   }
+               }
+               ho->maxslotindex = maxslotindex;
+               ho->cflag = cflag;
+           } else {
+               ho->old_vj = 1;
+               ho->maxslotindex = MAX_STATES - 1;
+               ho->cflag = 1;
+           }
+           break;
+#endif /* VJ_SUPPORT */
+
+       default:
+           orc = CONFREJ;
+           break;
+       }
+endswitch:
+       if (orc == CONFACK &&           /* Good CI */
+           rc != CONFACK)              /*  but prior CI wasnt? */
+           continue;                   /* Don't send this one */
+
+       if (orc == CONFNAK) {           /* Nak this CI? */
+           if (reject_if_disagree)     /* Getting fed up with sending NAKs? */
+               orc = CONFREJ;          /* Get tough if so */
+           else {
+               if (rc == CONFREJ)      /* Rejecting prior CI? */
+                   continue;           /* Don't send this one */
+               if (rc == CONFACK) {    /* Ack'd all prior CIs? */
+                   rc = CONFNAK;       /* Not anymore... */
+                   ucp = inp;          /* Backup */
+               }
+           }
+       }
+
+       if (orc == CONFREJ &&           /* Reject this CI */
+           rc != CONFREJ) {            /*  but no prior ones? */
+           rc = CONFREJ;
+           ucp = inp;                  /* Backup */
+       }
+
+       /* Need to move CI? */
+       if (ucp != cip)
+           MEMCPY(ucp, cip, cilen);    /* Move it */
+
+       /* Update output pointer */
+       INCPTR(cilen, ucp);
+    }
+
+    /*
+     * If we aren't rejecting this packet, and we want to negotiate
+     * their address, and they didn't send their address, then we
+     * send a NAK with a CI_ADDR option appended.  We assume the
+     * input buffer is long enough that we can append the extra
+     * option safely.
+     */
+    if (rc != CONFREJ && !ho->neg_addr && !ho->old_addrs &&
+       wo->req_addr && !reject_if_disagree && !pcb->settings.noremoteip) {
+       if (rc == CONFACK) {
+           rc = CONFNAK;
+           ucp = inp;                  /* reset pointer */
+           wo->req_addr = 0;           /* don't ask again */
+       }
+       PUTCHAR(CI_ADDR, ucp);
+       PUTCHAR(CILEN_ADDR, ucp);
+       tl = lwip_ntohl(wo->hisaddr);
+       PUTLONG(tl, ucp);
+    }
+
+    *len = ucp - inp;                  /* Compute output length */
+    IPCPDEBUG(("ipcp: returning Configure-%s", CODENAME(rc)));
+    return (rc);                       /* Return final code */
+}
+
+
+#if 0 /* UNUSED */
+/*
+ * ip_check_options - check that any IP-related options are OK,
+ * and assign appropriate defaults.
+ */
+static void
+ip_check_options()
+{
+    struct hostent *hp;
+    u32_t local;
+    ipcp_options *wo = &ipcp_wantoptions[0];
+
+    /*
+     * Default our local IP address based on our hostname.
+     * If local IP address already given, don't bother.
+     */
+    if (wo->ouraddr == 0 && !disable_defaultip) {
+       /*
+        * Look up our hostname (possibly with domain name appended)
+        * and take the first IP address as our local IP address.
+        * If there isn't an IP address for our hostname, too bad.
+        */
+       wo->accept_local = 1;   /* don't insist on this default value */
+       if ((hp = gethostbyname(hostname)) != NULL) {
+           local = *(u32_t *)hp->h_addr;
+           if (local != 0 && !bad_ip_adrs(local))
+               wo->ouraddr = local;
+       }
+    }
+    ask_for_local = wo->ouraddr != 0 || !disable_defaultip;
+}
+#endif /* UNUSED */
+
+#if DEMAND_SUPPORT
+/*
+ * ip_demand_conf - configure the interface as though
+ * IPCP were up, for use with dial-on-demand.
+ */
+static int
+ip_demand_conf(u)
+    int u;
+{
+    ppp_pcb *pcb = &ppp_pcb_list[u];
+    ipcp_options *wo = &ipcp_wantoptions[u];
+
+    if (wo->hisaddr == 0 && !pcb->settings.noremoteip) {
+       /* make up an arbitrary address for the peer */
+       wo->hisaddr = lwip_htonl(0x0a707070 + ifunit);
+       wo->accept_remote = 1;
+    }
+    if (wo->ouraddr == 0) {
+       /* make up an arbitrary address for us */
+       wo->ouraddr = lwip_htonl(0x0a404040 + ifunit);
+       wo->accept_local = 1;
+       ask_for_local = 0;      /* don't tell the peer this address */
+    }
+    if (!sifaddr(pcb, wo->ouraddr, wo->hisaddr, get_mask(wo->ouraddr)))
+       return 0;
+    if (!sifup(pcb))
+       return 0;
+    if (!sifnpmode(pcb, PPP_IP, NPMODE_QUEUE))
+       return 0;
+#if 0 /* UNUSED */
+    if (wo->default_route)
+       if (sifdefaultroute(pcb, wo->ouraddr, wo->hisaddr,
+               wo->replace_default_route))
+           default_route_set[u] = 1;
+#endif /* UNUSED */
+#if 0 /* UNUSED - PROXY ARP */
+    if (wo->proxy_arp)
+       if (sifproxyarp(pcb, wo->hisaddr))
+           proxy_arp_set[u] = 1;
+#endif /* UNUSED - PROXY ARP */
+
+    ppp_notice("local  IP address %I", wo->ouraddr);
+    if (wo->hisaddr)
+       ppp_notice("remote IP address %I", wo->hisaddr);
+
+    return 1;
+}
+#endif /* DEMAND_SUPPORT */
+
+/*
+ * ipcp_up - IPCP has come UP.
+ *
+ * Configure the IP network interface appropriately and bring it up.
+ */
+static void ipcp_up(fsm *f) {
+    ppp_pcb *pcb = f->pcb;
+    u32_t mask;
+    ipcp_options *ho = &pcb->ipcp_hisoptions;
+    ipcp_options *go = &pcb->ipcp_gotoptions;
+    ipcp_options *wo = &pcb->ipcp_wantoptions;
+
+    IPCPDEBUG(("ipcp: up"));
+
+    /*
+     * We must have a non-zero IP address for both ends of the link.
+     */
+    if (!ho->neg_addr && !ho->old_addrs)
+       ho->hisaddr = wo->hisaddr;
+
+    if (!(go->neg_addr || go->old_addrs) && (wo->neg_addr || wo->old_addrs)
+       && wo->ouraddr != 0) {
+       ppp_error("Peer refused to agree to our IP address");
+       ipcp_close(f->pcb, "Refused our IP address");
+       return;
+    }
+    if (go->ouraddr == 0) {
+       ppp_error("Could not determine local IP address");
+       ipcp_close(f->pcb, "Could not determine local IP address");
+       return;
+    }
+    if (ho->hisaddr == 0 && !pcb->settings.noremoteip) {
+       ho->hisaddr = lwip_htonl(0x0a404040);
+       ppp_warn("Could not determine remote IP address: defaulting to %I",
+            ho->hisaddr);
+    }
+#if 0 /* UNUSED */
+    script_setenv("IPLOCAL", ip_ntoa(go->ouraddr), 0);
+    if (ho->hisaddr != 0)
+       script_setenv("IPREMOTE", ip_ntoa(ho->hisaddr), 1);
+#endif /* UNUSED */
+
+#if LWIP_DNS
+    if (!go->req_dns1)
+           go->dnsaddr[0] = 0;
+    if (!go->req_dns2)
+           go->dnsaddr[1] = 0;
+#if 0 /* UNUSED */
+    if (go->dnsaddr[0])
+       script_setenv("DNS1", ip_ntoa(go->dnsaddr[0]), 0);
+    if (go->dnsaddr[1])
+       script_setenv("DNS2", ip_ntoa(go->dnsaddr[1]), 0);
+#endif /* UNUSED */
+    if (pcb->settings.usepeerdns && (go->dnsaddr[0] || go->dnsaddr[1])) {
+       sdns(pcb, go->dnsaddr[0], go->dnsaddr[1]);
+#if 0 /* UNUSED */
+       script_setenv("USEPEERDNS", "1", 0);
+       create_resolv(go->dnsaddr[0], go->dnsaddr[1]);
+#endif /* UNUSED */
+    }
+#endif /* LWIP_DNS */
+
+    /*
+     * Check that the peer is allowed to use the IP address it wants.
+     */
+    if (ho->hisaddr != 0) {
+       u32_t addr = lwip_ntohl(ho->hisaddr);
+       if ((addr >> IP_CLASSA_NSHIFT) == IP_LOOPBACKNET
+           || IP_MULTICAST(addr) || IP_BADCLASS(addr)
+           /*
+            * For now, consider that PPP in server mode with peer required
+            * to authenticate must provide the peer IP address, reject any
+            * IP address wanted by peer different than the one we wanted.
+            */
+#if PPP_SERVER && PPP_AUTH_SUPPORT
+           || (pcb->settings.auth_required && wo->hisaddr != ho->hisaddr)
+#endif /* PPP_SERVER && PPP_AUTH_SUPPORT */
+           ) {
+               ppp_error("Peer is not authorized to use remote address %I", ho->hisaddr);
+               ipcp_close(pcb, "Unauthorized remote IP address");
+               return;
+       }
+    }
+#if 0 /* Unused */
+    /* Upstream checking code */
+    if (ho->hisaddr != 0 && !auth_ip_addr(f->unit, ho->hisaddr)) {
+       ppp_error("Peer is not authorized to use remote address %I", ho->hisaddr);
+       ipcp_close(f->unit, "Unauthorized remote IP address");
+       return;
+    }
+#endif /* Unused */
+
+#if VJ_SUPPORT
+    /* set tcp compression */
+    sifvjcomp(pcb, ho->neg_vj, ho->cflag, ho->maxslotindex);
+#endif /* VJ_SUPPORT */
+
+#if DEMAND_SUPPORT
+    /*
+     * If we are doing dial-on-demand, the interface is already
+     * configured, so we put out any saved-up packets, then set the
+     * interface to pass IP packets.
+     */
+    if (demand) {
+       if (go->ouraddr != wo->ouraddr || ho->hisaddr != wo->hisaddr) {
+           ipcp_clear_addrs(f->unit, wo->ouraddr, wo->hisaddr,
+                                     wo->replace_default_route);
+           if (go->ouraddr != wo->ouraddr) {
+               ppp_warn("Local IP address changed to %I", go->ouraddr);
+               script_setenv("OLDIPLOCAL", ip_ntoa(wo->ouraddr), 0);
+               wo->ouraddr = go->ouraddr;
+           } else
+               script_unsetenv("OLDIPLOCAL");
+           if (ho->hisaddr != wo->hisaddr && wo->hisaddr != 0) {
+               ppp_warn("Remote IP address changed to %I", ho->hisaddr);
+               script_setenv("OLDIPREMOTE", ip_ntoa(wo->hisaddr), 0);
+               wo->hisaddr = ho->hisaddr;
+           } else
+               script_unsetenv("OLDIPREMOTE");
+
+           /* Set the interface to the new addresses */
+           mask = get_mask(go->ouraddr);
+           if (!sifaddr(pcb, go->ouraddr, ho->hisaddr, mask)) {
+#if PPP_DEBUG
+               ppp_warn("Interface configuration failed");
+#endif /* PPP_DEBUG */
+               ipcp_close(f->unit, "Interface configuration failed");
+               return;
+           }
+
+           /* assign a default route through the interface if required */
+           if (ipcp_wantoptions[f->unit].default_route) 
+               if (sifdefaultroute(pcb, go->ouraddr, ho->hisaddr,
+                       wo->replace_default_route))
+                   default_route_set[f->unit] = 1;
+
+#if 0 /* UNUSED - PROXY ARP */
+           /* Make a proxy ARP entry if requested. */
+           if (ho->hisaddr != 0 && ipcp_wantoptions[f->unit].proxy_arp)
+               if (sifproxyarp(pcb, ho->hisaddr))
+                   proxy_arp_set[f->unit] = 1;
+#endif /* UNUSED - PROXY ARP */
+
+       }
+       demand_rexmit(PPP_IP,go->ouraddr);
+       sifnpmode(pcb, PPP_IP, NPMODE_PASS);
+
+    } else
+#endif /* DEMAND_SUPPORT */
+    {
+       /*
+        * Set IP addresses and (if specified) netmask.
+        */
+       mask = get_mask(go->ouraddr);
+
+#if !(defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+       if (!sifaddr(pcb, go->ouraddr, ho->hisaddr, mask)) {
+#if PPP_DEBUG
+           ppp_warn("Interface configuration failed");
+#endif /* PPP_DEBUG */
+           ipcp_close(f->pcb, "Interface configuration failed");
+           return;
+       }
+#endif
+
+       /* bring the interface up for IP */
+       if (!sifup(pcb)) {
+#if PPP_DEBUG
+           ppp_warn("Interface failed to come up");
+#endif /* PPP_DEBUG */
+           ipcp_close(f->pcb, "Interface configuration failed");
+           return;
+       }
+
+#if (defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+       if (!sifaddr(pcb, go->ouraddr, ho->hisaddr, mask)) {
+#if PPP_DEBUG
+           ppp_warn("Interface configuration failed");
+#endif /* PPP_DEBUG */
+           ipcp_close(f->unit, "Interface configuration failed");
+           return;
+       }
+#endif
+#if DEMAND_SUPPORT
+       sifnpmode(pcb, PPP_IP, NPMODE_PASS);
+#endif /* DEMAND_SUPPORT */
+
+#if 0 /* UNUSED */
+       /* assign a default route through the interface if required */
+       if (wo->default_route)
+           if (sifdefaultroute(pcb, go->ouraddr, ho->hisaddr,
+                   wo->replace_default_route))
+                   pcb->default_route_set = 1;
+#endif /* UNUSED */
+
+#if 0 /* UNUSED - PROXY ARP */
+       /* Make a proxy ARP entry if requested. */
+       if (ho->hisaddr != 0 && wo->proxy_arp)
+           if (sifproxyarp(pcb, ho->hisaddr))
+               pcb->proxy_arp_set = 1;
+#endif /* UNUSED - PROXY ARP */
+
+       wo->ouraddr = go->ouraddr;
+
+       ppp_notice("local  IP address %I", go->ouraddr);
+       if (ho->hisaddr != 0)
+           ppp_notice("remote IP address %I", ho->hisaddr);
+#if LWIP_DNS
+       if (go->dnsaddr[0])
+           ppp_notice("primary   DNS address %I", go->dnsaddr[0]);
+       if (go->dnsaddr[1])
+           ppp_notice("secondary DNS address %I", go->dnsaddr[1]);
+#endif /* LWIP_DNS */
+    }
+
+#if PPP_STATS_SUPPORT
+    reset_link_stats(f->unit);
+#endif /* PPP_STATS_SUPPORT */
+
+    np_up(pcb, PPP_IP);
+    pcb->ipcp_is_up = 1;
+
+#if PPP_NOTIFY
+    notify(ip_up_notifier, 0);
+#endif /* PPP_NOTIFY */
+#if 0 /* UNUSED */
+    if (ip_up_hook)
+       ip_up_hook();
+#endif /* UNUSED */
+}
+
+
+/*
+ * ipcp_down - IPCP has gone DOWN.
+ *
+ * Take the IP network interface down, clear its addresses
+ * and delete routes through it.
+ */
+static void ipcp_down(fsm *f) {
+    ppp_pcb *pcb = f->pcb;
+    ipcp_options *ho = &pcb->ipcp_hisoptions;
+    ipcp_options *go = &pcb->ipcp_gotoptions;
+
+    IPCPDEBUG(("ipcp: down"));
+#if PPP_STATS_SUPPORT
+    /* XXX a bit IPv4-centric here, we only need to get the stats
+     * before the interface is marked down. */
+    /* XXX more correct: we must get the stats before running the notifiers,
+     * at least for the radius plugin */
+    update_link_stats(f->unit);
+#endif /* PPP_STATS_SUPPORT */
+#if PPP_NOTIFY
+    notify(ip_down_notifier, 0);
+#endif /* PPP_NOTIFY */
+#if 0 /* UNUSED */
+    if (ip_down_hook)
+       ip_down_hook();
+#endif /* UNUSED */
+    if (pcb->ipcp_is_up) {
+       pcb->ipcp_is_up = 0;
+       np_down(pcb, PPP_IP);
+    }
+#if VJ_SUPPORT
+    sifvjcomp(pcb, 0, 0, 0);
+#endif /* VJ_SUPPORT */
+
+#if PPP_STATS_SUPPORT
+    print_link_stats(); /* _after_ running the notifiers and ip_down_hook(),
+                        * because print_link_stats() sets link_stats_valid
+                        * to 0 (zero) */
+#endif /* PPP_STATS_SUPPORT */
+
+#if DEMAND_SUPPORT
+    /*
+     * If we are doing dial-on-demand, set the interface
+     * to queue up outgoing packets (for now).
+     */
+    if (demand) {
+       sifnpmode(pcb, PPP_IP, NPMODE_QUEUE);
+    } else
+#endif /* DEMAND_SUPPORT */
+    {
+#if DEMAND_SUPPORT
+       sifnpmode(pcb, PPP_IP, NPMODE_DROP);
+#endif /* DEMAND_SUPPORT */
+       sifdown(pcb);
+       ipcp_clear_addrs(pcb, go->ouraddr,
+                        ho->hisaddr, 0);
+#if LWIP_DNS
+       cdns(pcb, go->dnsaddr[0], go->dnsaddr[1]);
+#endif /* LWIP_DNS */
+    }
+}
+
+
+/*
+ * ipcp_clear_addrs() - clear the interface addresses, routes,
+ * proxy arp entries, etc.
+ */
+static void ipcp_clear_addrs(ppp_pcb *pcb, u32_t ouraddr, u32_t hisaddr, u8_t replacedefaultroute) {
+    LWIP_UNUSED_ARG(replacedefaultroute);
+
+#if 0 /* UNUSED - PROXY ARP */
+    if (pcb->proxy_arp_set) {
+       cifproxyarp(pcb, hisaddr);
+       pcb->proxy_arp_set = 0;
+    }
+#endif /* UNUSED - PROXY ARP */
+#if 0 /* UNUSED */
+    /* If replacedefaultroute, sifdefaultroute will be called soon
+     * with replacedefaultroute set and that will overwrite the current
+     * default route. This is the case only when doing demand, otherwise
+     * during demand, this cifdefaultroute would restore the old default
+     * route which is not what we want in this case. In the non-demand
+     * case, we'll delete the default route and restore the old if there
+     * is one saved by an sifdefaultroute with replacedefaultroute.
+     */
+    if (!replacedefaultroute && pcb->default_route_set) {
+       cifdefaultroute(pcb, ouraddr, hisaddr);
+       pcb->default_route_set = 0;
+    }
+#endif /* UNUSED */
+    cifaddr(pcb, ouraddr, hisaddr);
+}
+
+
+/*
+ * ipcp_finished - possibly shut down the lower layers.
+ */
+static void ipcp_finished(fsm *f) {
+       ppp_pcb *pcb = f->pcb;
+       if (pcb->ipcp_is_open) {
+               pcb->ipcp_is_open = 0;
+               np_finished(pcb, PPP_IP);
+       }
+}
+
+
+#if 0 /* UNUSED */
+/*
+ * create_resolv - create the replacement resolv.conf file
+ */
+static void
+create_resolv(peerdns1, peerdns2)
+    u32_t peerdns1, peerdns2;
+{
+
+}
+#endif /* UNUSED */
+
+#if PRINTPKT_SUPPORT
+/*
+ * ipcp_printpkt - print the contents of an IPCP packet.
+ */
+static const char* const ipcp_codenames[] = {
+    "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+    "TermReq", "TermAck", "CodeRej"
+};
+
+static int ipcp_printpkt(const u_char *p, int plen,
+               void (*printer) (void *, const char *, ...), void *arg) {
+    int code, id, len, olen;
+    const u_char *pstart, *optend;
+#if VJ_SUPPORT
+    u_short cishort;
+#endif /* VJ_SUPPORT */
+    u32_t cilong;
+
+    if (plen < HEADERLEN)
+       return 0;
+    pstart = p;
+    GETCHAR(code, p);
+    GETCHAR(id, p);
+    GETSHORT(len, p);
+    if (len < HEADERLEN || len > plen)
+       return 0;
+
+    if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(ipcp_codenames))
+       printer(arg, " %s", ipcp_codenames[code-1]);
+    else
+       printer(arg, " code=0x%x", code);
+    printer(arg, " id=0x%x", id);
+    len -= HEADERLEN;
+    switch (code) {
+    case CONFREQ:
+    case CONFACK:
+    case CONFNAK:
+    case CONFREJ:
+       /* print option list */
+       while (len >= 2) {
+           GETCHAR(code, p);
+           GETCHAR(olen, p);
+           p -= 2;
+           if (olen < 2 || olen > len) {
+               break;
+           }
+           printer(arg, " <");
+           len -= olen;
+           optend = p + olen;
+           switch (code) {
+           case CI_ADDRS:
+               if (olen == CILEN_ADDRS) {
+                   p += 2;
+                   GETLONG(cilong, p);
+                   printer(arg, "addrs %I", lwip_htonl(cilong));
+                   GETLONG(cilong, p);
+                   printer(arg, " %I", lwip_htonl(cilong));
+               }
+               break;
+#if VJ_SUPPORT
+           case CI_COMPRESSTYPE:
+               if (olen >= CILEN_COMPRESS) {
+                   p += 2;
+                   GETSHORT(cishort, p);
+                   printer(arg, "compress ");
+                   switch (cishort) {
+                   case IPCP_VJ_COMP:
+                       printer(arg, "VJ");
+                       break;
+                   case IPCP_VJ_COMP_OLD:
+                       printer(arg, "old-VJ");
+                       break;
+                   default:
+                       printer(arg, "0x%x", cishort);
+                   }
+               }
+               break;
+#endif /* VJ_SUPPORT */
+           case CI_ADDR:
+               if (olen == CILEN_ADDR) {
+                   p += 2;
+                   GETLONG(cilong, p);
+                   printer(arg, "addr %I", lwip_htonl(cilong));
+               }
+               break;
+#if LWIP_DNS
+           case CI_MS_DNS1:
+           case CI_MS_DNS2:
+               p += 2;
+               GETLONG(cilong, p);
+               printer(arg, "ms-dns%d %I", (code == CI_MS_DNS1? 1: 2),
+                       htonl(cilong));
+               break;
+#endif /* LWIP_DNS */
+#if 0 /* UNUSED - WINS */
+           case CI_MS_WINS1:
+           case CI_MS_WINS2:
+               p += 2;
+               GETLONG(cilong, p);
+               printer(arg, "ms-wins %I", lwip_htonl(cilong));
+               break;
+#endif /* UNUSED - WINS */
+           default:
+               break;
+           }
+           while (p < optend) {
+               GETCHAR(code, p);
+               printer(arg, " %.2x", code);
+           }
+           printer(arg, ">");
+       }
+       break;
+
+    case TERMACK:
+    case TERMREQ:
+       if (len > 0 && *p >= ' ' && *p < 0x7f) {
+           printer(arg, " ");
+           ppp_print_string(p, len, printer, arg);
+           p += len;
+           len = 0;
+       }
+       break;
+    default:
+       break;
+    }
+
+    /* print the rest of the bytes in the packet */
+    for (; len > 0; --len) {
+       GETCHAR(code, p);
+       printer(arg, " %.2x", code);
+    }
+
+    return p - pstart;
+}
+#endif /* PRINTPKT_SUPPORT */
+
+#if DEMAND_SUPPORT
+/*
+ * ip_active_pkt - see if this IP packet is worth bringing the link up for.
+ * We don't bring the link up for IP fragments or for TCP FIN packets
+ * with no data.
+ */
+#define IP_HDRLEN      20      /* bytes */
+#define IP_OFFMASK     0x1fff
+#ifndef IPPROTO_TCP
+#define IPPROTO_TCP    6
+#endif
+#define TCP_HDRLEN     20
+#define TH_FIN         0x01
+
+/*
+ * We use these macros because the IP header may be at an odd address,
+ * and some compilers might use word loads to get th_off or ip_hl.
+ */
+
+#define net_short(x)   (((x)[0] << 8) + (x)[1])
+#define get_iphl(x)    (((unsigned char *)(x))[0] & 0xF)
+#define get_ipoff(x)   net_short((unsigned char *)(x) + 6)
+#define get_ipproto(x) (((unsigned char *)(x))[9])
+#define get_tcpoff(x)  (((unsigned char *)(x))[12] >> 4)
+#define get_tcpflags(x)        (((unsigned char *)(x))[13])
+
+static int
+ip_active_pkt(pkt, len)
+    u_char *pkt;
+    int len;
+{
+    u_char *tcp;
+    int hlen;
+
+    len -= PPP_HDRLEN;
+    pkt += PPP_HDRLEN;
+    if (len < IP_HDRLEN)
+       return 0;
+    if ((get_ipoff(pkt) & IP_OFFMASK) != 0)
+       return 0;
+    if (get_ipproto(pkt) != IPPROTO_TCP)
+       return 1;
+    hlen = get_iphl(pkt) * 4;
+    if (len < hlen + TCP_HDRLEN)
+       return 0;
+    tcp = pkt + hlen;
+    if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == hlen + get_tcpoff(tcp) * 4)
+       return 0;
+    return 1;
+}
+#endif /* DEMAND_SUPPORT */
+
+#endif /* PPP_SUPPORT && PPP_IPV4_SUPPORT */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/ipv6cp.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/ipv6cp.c
new file mode 100644 (file)
index 0000000..11c18df
--- /dev/null
@@ -0,0 +1,1533 @@
+/*
+ * ipv6cp.c - PPP IPV6 Control Protocol.
+ *
+ * Copyright (c) 1999 Tommi Komulainen.  All rights reserved.
+ *
+ * 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.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Tommi Komulainen
+ *     <Tommi.Komulainen@iki.fi>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+/*  Original version, based on RFC2023 :
+
+    Copyright (c) 1995, 1996, 1997 Francis.Dupont@inria.fr, INRIA Rocquencourt,
+    Alain.Durand@imag.fr, IMAG,
+    Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+    Copyright (c) 1998, 1999 Francis.Dupont@inria.fr, GIE DYADE,
+    Alain.Durand@imag.fr, IMAG,
+    Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+    Ce travail a été fait au sein du GIE DYADE (Groupement d'Intérêt
+    Économique ayant pour membres BULL S.A. et l'INRIA).
+
+    Ce logiciel informatique est disponible aux conditions
+    usuelles dans la recherche, c'est-à-dire qu'il peut
+    être utilisé, copié, modifié, distribué à l'unique
+    condition que ce texte soit conservé afin que
+    l'origine de ce logiciel soit reconnue.
+
+    Le nom de l'Institut National de Recherche en Informatique
+    et en Automatique (INRIA), de l'IMAG, ou d'une personne morale
+    ou physique ayant participé à l'élaboration de ce logiciel ne peut
+    être utilisé sans son accord préalable explicite.
+
+    Ce logiciel est fourni tel quel sans aucune garantie,
+    support ou responsabilité d'aucune sorte.
+    Ce logiciel est dérivé de sources d'origine
+    "University of California at Berkeley" et
+    "Digital Equipment Corporation" couvertes par des copyrights.
+
+    L'Institut d'Informatique et de Mathématiques Appliquées de Grenoble (IMAG)
+    est une fédération d'unités mixtes de recherche du CNRS, de l'Institut National
+    Polytechnique de Grenoble et de l'Université Joseph Fourier regroupant
+    sept laboratoires dont le laboratoire Logiciels, Systèmes, Réseaux (LSR).
+
+    This work has been done in the context of GIE DYADE (joint R & D venture
+    between BULL S.A. and INRIA).
+
+    This software is available with usual "research" terms
+    with the aim of retain credits of the software. 
+    Permission to use, copy, modify and distribute this software for any
+    purpose and without fee is hereby granted, provided that the above
+    copyright notice and this permission notice appear in all copies,
+    and the name of INRIA, IMAG, or any contributor not be used in advertising
+    or publicity pertaining to this material without the prior explicit
+    permission. The software is provided "as is" without any
+    warranties, support or liabilities of any kind.
+    This software is derived from source code from
+    "University of California at Berkeley" and
+    "Digital Equipment Corporation" protected by copyrights.
+
+    Grenoble's Institute of Computer Science and Applied Mathematics (IMAG)
+    is a federation of seven research units funded by the CNRS, National
+    Polytechnic Institute of Grenoble and University Joseph Fourier.
+    The research unit in Software, Systems, Networks (LSR) is member of IMAG.
+*/
+
+/*
+ * Derived from :
+ *
+ *
+ * ipcp.c - PPP IP Control Protocol.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * 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.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ipv6cp.c,v 1.21 2005/08/25 23:59:34 paulus Exp $ 
+ */
+
+/*
+ * @todo: 
+ *
+ * Proxy Neighbour Discovery.
+ *
+ * Better defines for selecting the ordering of
+ *   interface up / set address.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPP_IPV6_SUPPORT  /* don't build if not configured for use in lwipopts.h */
+
+#if 0 /* UNUSED */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif /* UNUSED */
+
+#include "netif/ppp/ppp_impl.h"
+#include "netif/ppp/fsm.h"
+#include "netif/ppp/ipcp.h"
+#include "netif/ppp/ipv6cp.h"
+#include "netif/ppp/magic.h"
+
+/* global vars */
+#if 0 /* UNUSED */
+int no_ifaceid_neg = 0;
+#endif /* UNUSED */
+
+/*
+ * Callbacks for fsm code.  (CI = Configuration Information)
+ */
+static void ipv6cp_resetci(fsm *f); /* Reset our CI */
+static int  ipv6cp_cilen(fsm *f); /* Return length of our CI */
+static void ipv6cp_addci(fsm *f, u_char *ucp, int *lenp); /* Add our CI */
+static int  ipv6cp_ackci(fsm *f, u_char *p, int len); /* Peer ack'd our CI */
+static int  ipv6cp_nakci(fsm *f, u_char *p, int len, int treat_as_reject); /* Peer nak'd our CI */
+static int  ipv6cp_rejci(fsm *f, u_char *p, int len); /* Peer rej'd our CI */
+static int  ipv6cp_reqci(fsm *f, u_char *inp, int *len, int reject_if_disagree); /* Rcv CI */
+static void ipv6cp_up(fsm *f); /* We're UP */
+static void ipv6cp_down(fsm *f); /* We're DOWN */
+static void ipv6cp_finished(fsm *f); /* Don't need lower layer */
+
+static const fsm_callbacks ipv6cp_callbacks = { /* IPV6CP callback routines */
+    ipv6cp_resetci,            /* Reset our Configuration Information */
+    ipv6cp_cilen,              /* Length of our Configuration Information */
+    ipv6cp_addci,              /* Add our Configuration Information */
+    ipv6cp_ackci,              /* ACK our Configuration Information */
+    ipv6cp_nakci,              /* NAK our Configuration Information */
+    ipv6cp_rejci,              /* Reject our Configuration Information */
+    ipv6cp_reqci,              /* Request peer's Configuration Information */
+    ipv6cp_up,                 /* Called when fsm reaches OPENED state */
+    ipv6cp_down,               /* Called when fsm leaves OPENED state */
+    NULL,                      /* Called when we want the lower layer up */
+    ipv6cp_finished,           /* Called when we want the lower layer down */
+    NULL,                      /* Called when Protocol-Reject received */
+    NULL,                      /* Retransmission is necessary */
+    NULL,                      /* Called to handle protocol-specific codes */
+    "IPV6CP"                   /* String name of protocol */
+};
+
+#if PPP_OPTIONS
+/*
+ * Command-line options.
+ */
+static int setifaceid(char **arg));
+static void printifaceid(option_t *,
+                             void (*)(void *, char *, ...), void *));
+
+static option_t ipv6cp_option_list[] = {
+    { "ipv6", o_special, (void *)setifaceid,
+      "Set interface identifiers for IPV6",
+      OPT_A2PRINTER, (void *)printifaceid },
+
+    { "+ipv6", o_bool, &ipv6cp_protent.enabled_flag,
+      "Enable IPv6 and IPv6CP", OPT_PRIO | 1 },
+    { "noipv6", o_bool, &ipv6cp_protent.enabled_flag,
+      "Disable IPv6 and IPv6CP", OPT_PRIOSUB },
+    { "-ipv6", o_bool, &ipv6cp_protent.enabled_flag,
+      "Disable IPv6 and IPv6CP", OPT_PRIOSUB | OPT_ALIAS },
+
+    { "ipv6cp-accept-local", o_bool, &ipv6cp_allowoptions[0].accept_local,
+      "Accept peer's interface identifier for us", 1 },
+
+    { "ipv6cp-use-ipaddr", o_bool, &ipv6cp_allowoptions[0].use_ip,
+      "Use (default) IPv4 address as interface identifier", 1 },
+
+    { "ipv6cp-use-persistent", o_bool, &ipv6cp_wantoptions[0].use_persistent,
+      "Use uniquely-available persistent value for link local address", 1 },
+
+    { "ipv6cp-restart", o_int, &ipv6cp_fsm[0].timeouttime,
+      "Set timeout for IPv6CP", OPT_PRIO },
+    { "ipv6cp-max-terminate", o_int, &ipv6cp_fsm[0].maxtermtransmits,
+      "Set max #xmits for term-reqs", OPT_PRIO },
+    { "ipv6cp-max-configure", o_int, &ipv6cp_fsm[0].maxconfreqtransmits,
+      "Set max #xmits for conf-reqs", OPT_PRIO },
+    { "ipv6cp-max-failure", o_int, &ipv6cp_fsm[0].maxnakloops,
+      "Set max #conf-naks for IPv6CP", OPT_PRIO },
+
+   { NULL }
+};
+#endif /* PPP_OPTIONS */
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ipv6cp_init(ppp_pcb *pcb);
+static void ipv6cp_open(ppp_pcb *pcb);
+static void ipv6cp_close(ppp_pcb *pcb, const char *reason);
+static void ipv6cp_lowerup(ppp_pcb *pcb);
+static void ipv6cp_lowerdown(ppp_pcb *pcb);
+static void ipv6cp_input(ppp_pcb *pcb, u_char *p, int len);
+static void ipv6cp_protrej(ppp_pcb *pcb);
+#if PPP_OPTIONS
+static void ipv6_check_options(void);
+#endif /* PPP_OPTIONS */
+#if DEMAND_SUPPORT
+static int  ipv6_demand_conf(int u);
+#endif /* DEMAND_SUPPORT */
+#if PRINTPKT_SUPPORT
+static int ipv6cp_printpkt(const u_char *p, int plen,
+               void (*printer)(void *, const char *, ...), void *arg);
+#endif /* PRINTPKT_SUPPORT */
+#if DEMAND_SUPPORT
+static int ipv6_active_pkt(u_char *pkt, int len);
+#endif /* DEMAND_SUPPORT */
+
+const struct protent ipv6cp_protent = {
+    PPP_IPV6CP,
+    ipv6cp_init,
+    ipv6cp_input,
+    ipv6cp_protrej,
+    ipv6cp_lowerup,
+    ipv6cp_lowerdown,
+    ipv6cp_open,
+    ipv6cp_close,
+#if PRINTPKT_SUPPORT
+    ipv6cp_printpkt,
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_DATAINPUT
+    NULL,
+#endif /* PPP_DATAINPUT */
+#if PRINTPKT_SUPPORT
+    "IPV6CP",
+    "IPV6",
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_OPTIONS
+    ipv6cp_option_list,
+    ipv6_check_options,
+#endif /* PPP_OPTIONS */
+#if DEMAND_SUPPORT
+    ipv6_demand_conf,
+    ipv6_active_pkt
+#endif /* DEMAND_SUPPORT */
+};
+
+static void ipv6cp_clear_addrs(ppp_pcb *pcb, eui64_t ourid, eui64_t hisid);
+#if 0 /* UNUSED */
+static void ipv6cp_script(char *));
+static void ipv6cp_script_done(void *));
+#endif /* UNUSED */
+
+/*
+ * Lengths of configuration options.
+ */
+#define CILEN_VOID     2
+#define CILEN_COMPRESS 4       /* length for RFC2023 compress opt. */
+#define CILEN_IFACEID   10     /* RFC2472, interface identifier    */
+
+#define CODENAME(x)    ((x) == CONFACK ? "ACK" : \
+                        (x) == CONFNAK ? "NAK" : "REJ")
+
+#if 0 /* UNUSED */
+/*
+ * This state variable is used to ensure that we don't
+ * run an ipcp-up/down script while one is already running.
+ */
+static enum script_state {
+    s_down,
+    s_up,
+} ipv6cp_script_state;
+static pid_t ipv6cp_script_pid;
+#endif /* UNUSED */
+
+static char *llv6_ntoa(eui64_t ifaceid);
+
+#if PPP_OPTIONS
+/*
+ * setifaceid - set the interface identifiers manually
+ */
+static int
+setifaceid(argv)
+    char **argv;
+{
+    char *comma, *arg, c;
+    ipv6cp_options *wo = &ipv6cp_wantoptions[0];
+    struct in6_addr addr;
+    static int prio_local, prio_remote;
+
+#define VALIDID(a) ( (((a).s6_addr32[0] == 0) && ((a).s6_addr32[1] == 0)) && \
+                       (((a).s6_addr32[2] != 0) || ((a).s6_addr32[3] != 0)) )
+    
+    arg = *argv;
+    if ((comma = strchr(arg, ',')) == NULL)
+       comma = arg + strlen(arg);
+    
+    /* 
+     * If comma first character, then no local identifier
+     */
+    if (comma != arg) {
+       c = *comma;
+       *comma = '\0';
+
+       if (inet_pton(AF_INET6, arg, &addr) == 0 || !VALIDID(addr)) {
+           option_error("Illegal interface identifier (local): %s", arg);
+           return 0;
+       }
+
+       if (option_priority >= prio_local) {
+           eui64_copy(addr.s6_addr32[2], wo->ourid);
+           wo->opt_local = 1;
+           prio_local = option_priority;
+       }
+       *comma = c;
+    }
+    
+    /*
+     * If comma last character, the no remote identifier
+     */
+    if (*comma != 0 && *++comma != '\0') {
+       if (inet_pton(AF_INET6, comma, &addr) == 0 || !VALIDID(addr)) {
+           option_error("Illegal interface identifier (remote): %s", comma);
+           return 0;
+       }
+       if (option_priority >= prio_remote) {
+           eui64_copy(addr.s6_addr32[2], wo->hisid);
+           wo->opt_remote = 1;
+           prio_remote = option_priority;
+       }
+    }
+
+    if (override_value("+ipv6", option_priority, option_source))
+       ipv6cp_protent.enabled_flag = 1;
+    return 1;
+}
+
+static void
+printifaceid(opt, printer, arg)
+    option_t *opt;
+    void (*printer)(void *, char *, ...));
+    void *arg;
+{
+       ipv6cp_options *wo = &ipv6cp_wantoptions[0];
+
+       if (wo->opt_local)
+               printer(arg, "%s", llv6_ntoa(wo->ourid));
+       printer(arg, ",");
+       if (wo->opt_remote)
+               printer(arg, "%s", llv6_ntoa(wo->hisid));
+}
+#endif /* PPP_OPTIONS */
+
+/*
+ * Make a string representation of a network address.
+ */
+static char *
+llv6_ntoa(eui64_t ifaceid)
+{
+    static char b[26];
+
+    sprintf(b, "fe80::%02x%02x:%02x%02x:%02x%02x:%02x%02x",
+      ifaceid.e8[0], ifaceid.e8[1], ifaceid.e8[2], ifaceid.e8[3],
+      ifaceid.e8[4], ifaceid.e8[5], ifaceid.e8[6], ifaceid.e8[7]);
+
+    return b;
+}
+
+
+/*
+ * ipv6cp_init - Initialize IPV6CP.
+ */
+static void ipv6cp_init(ppp_pcb *pcb) {
+    fsm *f = &pcb->ipv6cp_fsm;
+    ipv6cp_options *wo = &pcb->ipv6cp_wantoptions;
+    ipv6cp_options *ao = &pcb->ipv6cp_allowoptions;
+
+    f->pcb = pcb;
+    f->protocol = PPP_IPV6CP;
+    f->callbacks = &ipv6cp_callbacks;
+    fsm_init(f);
+
+#if 0 /* Not necessary, everything is cleared in ppp_new() */
+    memset(wo, 0, sizeof(*wo));
+    memset(ao, 0, sizeof(*ao));
+#endif /* 0 */
+
+    wo->accept_local = 1;
+    wo->neg_ifaceid = 1;
+    ao->neg_ifaceid = 1;
+
+#ifdef IPV6CP_COMP
+    wo->neg_vj = 1;
+    ao->neg_vj = 1;
+    wo->vj_protocol = IPV6CP_COMP;
+#endif
+
+}
+
+
+/*
+ * ipv6cp_open - IPV6CP is allowed to come up.
+ */
+static void ipv6cp_open(ppp_pcb *pcb) {
+    fsm_open(&pcb->ipv6cp_fsm);
+}
+
+
+/*
+ * ipv6cp_close - Take IPV6CP down.
+ */
+static void ipv6cp_close(ppp_pcb *pcb, const char *reason) {
+    fsm_close(&pcb->ipv6cp_fsm, reason);
+}
+
+
+/*
+ * ipv6cp_lowerup - The lower layer is up.
+ */
+static void ipv6cp_lowerup(ppp_pcb *pcb) {
+    fsm_lowerup(&pcb->ipv6cp_fsm);
+}
+
+
+/*
+ * ipv6cp_lowerdown - The lower layer is down.
+ */
+static void ipv6cp_lowerdown(ppp_pcb *pcb) {
+    fsm_lowerdown(&pcb->ipv6cp_fsm);
+}
+
+
+/*
+ * ipv6cp_input - Input IPV6CP packet.
+ */
+static void ipv6cp_input(ppp_pcb *pcb, u_char *p, int len) {
+    fsm_input(&pcb->ipv6cp_fsm, p, len);
+}
+
+
+/*
+ * ipv6cp_protrej - A Protocol-Reject was received for IPV6CP.
+ *
+ * Pretend the lower layer went down, so we shut up.
+ */
+static void ipv6cp_protrej(ppp_pcb *pcb) {
+    fsm_lowerdown(&pcb->ipv6cp_fsm);
+}
+
+
+/*
+ * ipv6cp_resetci - Reset our CI.
+ */
+static void ipv6cp_resetci(fsm *f) {
+    ppp_pcb *pcb = f->pcb;
+    ipv6cp_options *wo = &pcb->ipv6cp_wantoptions;
+    ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
+    ipv6cp_options *ao = &pcb->ipv6cp_allowoptions;
+
+    wo->req_ifaceid = wo->neg_ifaceid && ao->neg_ifaceid;
+    
+    if (!wo->opt_local) {
+       eui64_magic_nz(wo->ourid);
+    }
+    
+    *go = *wo;
+    eui64_zero(go->hisid);     /* last proposed interface identifier */
+}
+
+
+/*
+ * ipv6cp_cilen - Return length of our CI.
+ */
+static int ipv6cp_cilen(fsm *f) {
+    ppp_pcb *pcb = f->pcb;
+    ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
+
+#ifdef IPV6CP_COMP
+#define LENCIVJ(neg)           (neg ? CILEN_COMPRESS : 0)
+#endif /* IPV6CP_COMP */
+#define LENCIIFACEID(neg)      (neg ? CILEN_IFACEID : 0)
+
+    return (LENCIIFACEID(go->neg_ifaceid) +
+#ifdef IPV6CP_COMP
+           LENCIVJ(go->neg_vj) +
+#endif /* IPV6CP_COMP */
+           0);
+}
+
+
+/*
+ * ipv6cp_addci - Add our desired CIs to a packet.
+ */
+static void ipv6cp_addci(fsm *f, u_char *ucp, int *lenp) {
+    ppp_pcb *pcb = f->pcb;
+    ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
+    int len = *lenp;
+
+#ifdef IPV6CP_COMP
+#define ADDCIVJ(opt, neg, val) \
+    if (neg) { \
+       int vjlen = CILEN_COMPRESS; \
+       if (len >= vjlen) { \
+           PUTCHAR(opt, ucp); \
+           PUTCHAR(vjlen, ucp); \
+           PUTSHORT(val, ucp); \
+           len -= vjlen; \
+       } else \
+           neg = 0; \
+    }
+#endif /* IPV6CP_COMP */
+
+#define ADDCIIFACEID(opt, neg, val1) \
+    if (neg) { \
+       int idlen = CILEN_IFACEID; \
+       if (len >= idlen) { \
+           PUTCHAR(opt, ucp); \
+           PUTCHAR(idlen, ucp); \
+           eui64_put(val1, ucp); \
+           len -= idlen; \
+       } else \
+           neg = 0; \
+    }
+
+    ADDCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid);
+
+#ifdef IPV6CP_COMP
+    ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol);
+#endif /* IPV6CP_COMP */
+
+    *lenp -= len;
+}
+
+
+/*
+ * ipv6cp_ackci - Ack our CIs.
+ *
+ * Returns:
+ *     0 - Ack was bad.
+ *     1 - Ack was good.
+ */
+static int ipv6cp_ackci(fsm *f, u_char *p, int len) {
+    ppp_pcb *pcb = f->pcb;
+    ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
+    u_short cilen, citype;
+#ifdef IPV6CP_COMP
+    u_short cishort;
+#endif /* IPV6CP_COMP */
+    eui64_t ifaceid;
+
+    /*
+     * CIs must be in exactly the same order that we sent...
+     * Check packet length and CI length at each step.
+     * If we find any deviations, then this packet is bad.
+     */
+
+#ifdef IPV6CP_COMP
+#define ACKCIVJ(opt, neg, val) \
+    if (neg) { \
+       int vjlen = CILEN_COMPRESS; \
+       if ((len -= vjlen) < 0) \
+           goto bad; \
+       GETCHAR(citype, p); \
+       GETCHAR(cilen, p); \
+       if (cilen != vjlen || \
+           citype != opt)  \
+           goto bad; \
+       GETSHORT(cishort, p); \
+       if (cishort != val) \
+           goto bad; \
+    }
+#endif /* IPV6CP_COMP */
+
+#define ACKCIIFACEID(opt, neg, val1) \
+    if (neg) { \
+       int idlen = CILEN_IFACEID; \
+       if ((len -= idlen) < 0) \
+           goto bad; \
+       GETCHAR(citype, p); \
+       GETCHAR(cilen, p); \
+       if (cilen != idlen || \
+           citype != opt) \
+           goto bad; \
+       eui64_get(ifaceid, p); \
+       if (! eui64_equals(val1, ifaceid)) \
+           goto bad; \
+    }
+
+    ACKCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid);
+
+#ifdef IPV6CP_COMP
+    ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol);
+#endif /* IPV6CP_COMP */
+
+    /*
+     * If there are any remaining CIs, then this packet is bad.
+     */
+    if (len != 0)
+       goto bad;
+    return (1);
+
+bad:
+    IPV6CPDEBUG(("ipv6cp_ackci: received bad Ack!"));
+    return (0);
+}
+
+/*
+ * ipv6cp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if IPV6CP is in the OPENED state.
+ *
+ * Returns:
+ *     0 - Nak was bad.
+ *     1 - Nak was good.
+ */
+static int ipv6cp_nakci(fsm *f, u_char *p, int len, int treat_as_reject) {
+    ppp_pcb *pcb = f->pcb;
+    ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
+    u_char citype, cilen, *next;
+#ifdef IPV6CP_COMP
+    u_short cishort;
+#endif /* IPV6CP_COMP */
+    eui64_t ifaceid;
+    ipv6cp_options no;         /* options we've seen Naks for */
+    ipv6cp_options try_;       /* options to request next time */
+
+    BZERO(&no, sizeof(no));
+    try_ = *go;
+
+    /*
+     * Any Nak'd CIs must be in exactly the same order that we sent.
+     * Check packet length and CI length at each step.
+     * If we find any deviations, then this packet is bad.
+     */
+#define NAKCIIFACEID(opt, neg, code) \
+    if (go->neg && \
+       len >= (cilen = CILEN_IFACEID) && \
+       p[1] == cilen && \
+       p[0] == opt) { \
+       len -= cilen; \
+       INCPTR(2, p); \
+       eui64_get(ifaceid, p); \
+       no.neg = 1; \
+       code \
+    }
+
+#ifdef IPV6CP_COMP
+#define NAKCIVJ(opt, neg, code) \
+    if (go->neg && \
+       ((cilen = p[1]) == CILEN_COMPRESS) && \
+       len >= cilen && \
+       p[0] == opt) { \
+       len -= cilen; \
+       INCPTR(2, p); \
+       GETSHORT(cishort, p); \
+       no.neg = 1; \
+        code \
+    }
+#endif /* IPV6CP_COMP */
+
+    /*
+     * Accept the peer's idea of {our,his} interface identifier, if different
+     * from our idea, only if the accept_{local,remote} flag is set.
+     */
+    NAKCIIFACEID(CI_IFACEID, neg_ifaceid,
+                if (treat_as_reject) {
+                    try_.neg_ifaceid = 0;
+                } else if (go->accept_local) {
+                    while (eui64_iszero(ifaceid) || 
+                           eui64_equals(ifaceid, go->hisid)) /* bad luck */
+                        eui64_magic(ifaceid);
+                    try_.ourid = ifaceid;
+                    IPV6CPDEBUG(("local LL address %s", llv6_ntoa(ifaceid)));
+                }
+                );
+
+#ifdef IPV6CP_COMP
+    NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
+           {
+               if (cishort == IPV6CP_COMP && !treat_as_reject) {
+                   try_.vj_protocol = cishort;
+               } else {
+                   try_.neg_vj = 0;
+               }
+           }
+           );
+#endif /* IPV6CP_COMP */
+
+    /*
+     * There may be remaining CIs, if the peer is requesting negotiation
+     * on an option that we didn't include in our request packet.
+     * If they want to negotiate about interface identifier, we comply.
+     * If they want us to ask for compression, we refuse.
+     */
+    while (len >= CILEN_VOID) {
+       GETCHAR(citype, p);
+       GETCHAR(cilen, p);
+       if ( cilen < CILEN_VOID || (len -= cilen) < 0 )
+           goto bad;
+       next = p + cilen - 2;
+
+       switch (citype) {
+#ifdef IPV6CP_COMP
+       case CI_COMPRESSTYPE:
+           if (go->neg_vj || no.neg_vj ||
+               (cilen != CILEN_COMPRESS))
+               goto bad;
+           no.neg_vj = 1;
+           break;
+#endif /* IPV6CP_COMP */
+       case CI_IFACEID:
+           if (go->neg_ifaceid || no.neg_ifaceid || cilen != CILEN_IFACEID)
+               goto bad;
+           try_.neg_ifaceid = 1;
+           eui64_get(ifaceid, p);
+           if (go->accept_local) {
+               while (eui64_iszero(ifaceid) || 
+                      eui64_equals(ifaceid, go->hisid)) /* bad luck */
+                   eui64_magic(ifaceid);
+               try_.ourid = ifaceid;
+           }
+           no.neg_ifaceid = 1;
+           break;
+       default:
+           break;
+       }
+       p = next;
+    }
+
+    /* If there is still anything left, this packet is bad. */
+    if (len != 0)
+       goto bad;
+
+    /*
+     * OK, the Nak is good.  Now we can update state.
+     */
+    if (f->state != PPP_FSM_OPENED)
+       *go = try_;
+
+    return 1;
+
+bad:
+    IPV6CPDEBUG(("ipv6cp_nakci: received bad Nak!"));
+    return 0;
+}
+
+
+/*
+ * ipv6cp_rejci - Reject some of our CIs.
+ */
+static int ipv6cp_rejci(fsm *f, u_char *p, int len) {
+    ppp_pcb *pcb = f->pcb;
+    ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
+    u_char cilen;
+#ifdef IPV6CP_COMP
+    u_short cishort;
+#endif /* IPV6CP_COMP */
+    eui64_t ifaceid;
+    ipv6cp_options try_;               /* options to request next time */
+
+    try_ = *go;
+    /*
+     * Any Rejected CIs must be in exactly the same order that we sent.
+     * Check packet length and CI length at each step.
+     * If we find any deviations, then this packet is bad.
+     */
+#define REJCIIFACEID(opt, neg, val1) \
+    if (go->neg && \
+       len >= (cilen = CILEN_IFACEID) && \
+       p[1] == cilen && \
+       p[0] == opt) { \
+       len -= cilen; \
+       INCPTR(2, p); \
+       eui64_get(ifaceid, p); \
+       /* Check rejected value. */ \
+       if (! eui64_equals(ifaceid, val1)) \
+           goto bad; \
+       try_.neg = 0; \
+    }
+
+#ifdef IPV6CP_COMP
+#define REJCIVJ(opt, neg, val) \
+    if (go->neg && \
+       p[1] == CILEN_COMPRESS && \
+       len >= p[1] && \
+       p[0] == opt) { \
+       len -= p[1]; \
+       INCPTR(2, p); \
+       GETSHORT(cishort, p); \
+       /* Check rejected value. */  \
+       if (cishort != val) \
+           goto bad; \
+       try_.neg = 0; \
+     }
+#endif /* IPV6CP_COMP */
+
+    REJCIIFACEID(CI_IFACEID, neg_ifaceid, go->ourid);
+
+#ifdef IPV6CP_COMP
+    REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol);
+#endif /* IPV6CP_COMP */
+
+    /*
+     * If there are any remaining CIs, then this packet is bad.
+     */
+    if (len != 0)
+       goto bad;
+    /*
+     * Now we can update state.
+     */
+    if (f->state != PPP_FSM_OPENED)
+       *go = try_;
+    return 1;
+
+bad:
+    IPV6CPDEBUG(("ipv6cp_rejci: received bad Reject!"));
+    return 0;
+}
+
+
+/*
+ * ipv6cp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately.  If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ *
+ * inp = Requested CIs
+ * len = Length of requested CIs
+ *
+ */
+static int ipv6cp_reqci(fsm *f, u_char *inp, int *len, int reject_if_disagree) {
+    ppp_pcb *pcb = f->pcb;
+    ipv6cp_options *wo = &pcb->ipv6cp_wantoptions;
+    ipv6cp_options *ho = &pcb->ipv6cp_hisoptions;
+    ipv6cp_options *ao = &pcb->ipv6cp_allowoptions;
+    ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
+    u_char *cip, *next;                /* Pointer to current and next CIs */
+    u_short cilen, citype;     /* Parsed len, type */
+#ifdef IPV6CP_COMP
+    u_short cishort;           /* Parsed short value */
+#endif /* IPV6CP_COMP */
+    eui64_t ifaceid;           /* Parsed interface identifier */
+    int rc = CONFACK;          /* Final packet return code */
+    int orc;                   /* Individual option return code */
+    u_char *p;                 /* Pointer to next char to parse */
+    u_char *ucp = inp;         /* Pointer to current output char */
+    int l = *len;              /* Length left */
+
+    /*
+     * Reset all his options.
+     */
+    BZERO(ho, sizeof(*ho));
+    
+    /*
+     * Process all his options.
+     */
+    next = inp;
+    while (l) {
+       orc = CONFACK;                  /* Assume success */
+       cip = p = next;                 /* Remember begining of CI */
+       if (l < 2 ||                    /* Not enough data for CI header or */
+           p[1] < 2 ||                 /*  CI length too small or */
+           p[1] > l) {                 /*  CI length too big? */
+           IPV6CPDEBUG(("ipv6cp_reqci: bad CI length!"));
+           orc = CONFREJ;              /* Reject bad CI */
+           cilen = l;                  /* Reject till end of packet */
+           l = 0;                      /* Don't loop again */
+           goto endswitch;
+       }
+       GETCHAR(citype, p);             /* Parse CI type */
+       GETCHAR(cilen, p);              /* Parse CI length */
+       l -= cilen;                     /* Adjust remaining length */
+       next += cilen;                  /* Step to next CI */
+
+       switch (citype) {               /* Check CI type */
+       case CI_IFACEID:
+           IPV6CPDEBUG(("ipv6cp: received interface identifier "));
+
+           if (!ao->neg_ifaceid ||
+               cilen != CILEN_IFACEID) {       /* Check CI length */
+               orc = CONFREJ;          /* Reject CI */
+               break;
+           }
+
+           /*
+            * If he has no interface identifier, or if we both have same 
+            * identifier then NAK it with new idea.
+            * In particular, if we don't know his identifier, but he does,
+            * then accept it.
+            */
+           eui64_get(ifaceid, p);
+           IPV6CPDEBUG(("(%s)", llv6_ntoa(ifaceid)));
+           if (eui64_iszero(ifaceid) && eui64_iszero(go->ourid)) {
+               orc = CONFREJ;          /* Reject CI */
+               break;
+           }
+           if (!eui64_iszero(wo->hisid) && 
+               !eui64_equals(ifaceid, wo->hisid) && 
+               eui64_iszero(go->hisid)) {
+                   
+               orc = CONFNAK;
+               ifaceid = wo->hisid;
+               go->hisid = ifaceid;
+               DECPTR(sizeof(ifaceid), p);
+               eui64_put(ifaceid, p);
+           } else
+           if (eui64_iszero(ifaceid) || eui64_equals(ifaceid, go->ourid)) {
+               orc = CONFNAK;
+               if (eui64_iszero(go->hisid))    /* first time, try option */
+                   ifaceid = wo->hisid;
+               while (eui64_iszero(ifaceid) || 
+                      eui64_equals(ifaceid, go->ourid)) /* bad luck */
+                   eui64_magic(ifaceid);
+               go->hisid = ifaceid;
+               DECPTR(sizeof(ifaceid), p);
+               eui64_put(ifaceid, p);
+           }
+
+           ho->neg_ifaceid = 1;
+           ho->hisid = ifaceid;
+           break;
+
+#ifdef IPV6CP_COMP
+       case CI_COMPRESSTYPE:
+           IPV6CPDEBUG(("ipv6cp: received COMPRESSTYPE "));
+           if (!ao->neg_vj ||
+               (cilen != CILEN_COMPRESS)) {
+               orc = CONFREJ;
+               break;
+           }
+           GETSHORT(cishort, p);
+           IPV6CPDEBUG(("(%d)", cishort));
+
+           if (!(cishort == IPV6CP_COMP)) {
+               orc = CONFREJ;
+               break;
+           }
+
+           ho->neg_vj = 1;
+           ho->vj_protocol = cishort;
+           break;
+#endif /* IPV6CP_COMP */
+
+       default:
+           orc = CONFREJ;
+           break;
+       }
+
+endswitch:
+       IPV6CPDEBUG((" (%s)\n", CODENAME(orc)));
+
+       if (orc == CONFACK &&           /* Good CI */
+           rc != CONFACK)              /*  but prior CI wasnt? */
+           continue;                   /* Don't send this one */
+
+       if (orc == CONFNAK) {           /* Nak this CI? */
+           if (reject_if_disagree)     /* Getting fed up with sending NAKs? */
+               orc = CONFREJ;          /* Get tough if so */
+           else {
+               if (rc == CONFREJ)      /* Rejecting prior CI? */
+                   continue;           /* Don't send this one */
+               if (rc == CONFACK) {    /* Ack'd all prior CIs? */
+                   rc = CONFNAK;       /* Not anymore... */
+                   ucp = inp;          /* Backup */
+               }
+           }
+       }
+
+       if (orc == CONFREJ &&           /* Reject this CI */
+           rc != CONFREJ) {            /*  but no prior ones? */
+           rc = CONFREJ;
+           ucp = inp;                  /* Backup */
+       }
+
+       /* Need to move CI? */
+       if (ucp != cip)
+           MEMCPY(ucp, cip, cilen);    /* Move it */
+
+       /* Update output pointer */
+       INCPTR(cilen, ucp);
+    }
+
+    /*
+     * If we aren't rejecting this packet, and we want to negotiate
+     * their identifier and they didn't send their identifier, then we
+     * send a NAK with a CI_IFACEID option appended.  We assume the
+     * input buffer is long enough that we can append the extra
+     * option safely.
+     */
+    if (rc != CONFREJ && !ho->neg_ifaceid &&
+       wo->req_ifaceid && !reject_if_disagree) {
+       if (rc == CONFACK) {
+           rc = CONFNAK;
+           ucp = inp;                          /* reset pointer */
+           wo->req_ifaceid = 0;                /* don't ask again */
+       }
+       PUTCHAR(CI_IFACEID, ucp);
+       PUTCHAR(CILEN_IFACEID, ucp);
+       eui64_put(wo->hisid, ucp);
+    }
+
+    *len = ucp - inp;                  /* Compute output length */
+    IPV6CPDEBUG(("ipv6cp: returning Configure-%s", CODENAME(rc)));
+    return (rc);                       /* Return final code */
+}
+
+#if PPP_OPTIONS
+/*
+ * ipv6_check_options - check that any IP-related options are OK,
+ * and assign appropriate defaults.
+ */
+static void ipv6_check_options() {
+    ipv6cp_options *wo = &ipv6cp_wantoptions[0];
+
+    if (!ipv6cp_protent.enabled_flag)
+       return;
+
+    /*
+     * Persistent link-local id is only used when user has not explicitly
+     * configure/hard-code the id
+     */
+    if ((wo->use_persistent) && (!wo->opt_local) && (!wo->opt_remote)) {
+
+       /* 
+        * On systems where there are no Ethernet interfaces used, there
+        * may be other ways to obtain a persistent id. Right now, it
+        * will fall back to using magic [see eui64_magic] below when
+        * an EUI-48 from MAC address can't be obtained. Other possibilities
+        * include obtaining EEPROM serial numbers, or some other unique
+        * yet persistent number. On Sparc platforms, this is possible,
+        * but too bad there's no standards yet for x86 machines.
+        */
+       if (ether_to_eui64(&wo->ourid)) {
+           wo->opt_local = 1;
+       }
+    }
+
+    if (!wo->opt_local) {      /* init interface identifier */
+       if (wo->use_ip && eui64_iszero(wo->ourid)) {
+           eui64_setlo32(wo->ourid, lwip_ntohl(ipcp_wantoptions[0].ouraddr));
+           if (!eui64_iszero(wo->ourid))
+               wo->opt_local = 1;
+       }
+       
+       while (eui64_iszero(wo->ourid))
+           eui64_magic(wo->ourid);
+    }
+
+    if (!wo->opt_remote) {
+       if (wo->use_ip && eui64_iszero(wo->hisid)) {
+           eui64_setlo32(wo->hisid, lwip_ntohl(ipcp_wantoptions[0].hisaddr));
+           if (!eui64_iszero(wo->hisid))
+               wo->opt_remote = 1;
+       }
+    }
+
+    if (demand && (eui64_iszero(wo->ourid) || eui64_iszero(wo->hisid))) {
+       option_error("local/remote LL address required for demand-dialling\n");
+       exit(1);
+    }
+}
+#endif /* PPP_OPTIONS */
+
+#if DEMAND_SUPPORT
+/*
+ * ipv6_demand_conf - configure the interface as though
+ * IPV6CP were up, for use with dial-on-demand.
+ */
+static int ipv6_demand_conf(int u) {
+    ipv6cp_options *wo = &ipv6cp_wantoptions[u];
+
+    if (!sif6up(u))
+       return 0;
+
+    if (!sif6addr(u, wo->ourid, wo->hisid))
+       return 0;
+
+    if (!sifnpmode(u, PPP_IPV6, NPMODE_QUEUE))
+       return 0;
+
+    ppp_notice("ipv6_demand_conf");
+    ppp_notice("local  LL address %s", llv6_ntoa(wo->ourid));
+    ppp_notice("remote LL address %s", llv6_ntoa(wo->hisid));
+
+    return 1;
+}
+#endif /* DEMAND_SUPPORT */
+
+
+/*
+ * ipv6cp_up - IPV6CP has come UP.
+ *
+ * Configure the IPv6 network interface appropriately and bring it up.
+ */
+static void ipv6cp_up(fsm *f) {
+    ppp_pcb *pcb = f->pcb;
+    ipv6cp_options *wo = &pcb->ipv6cp_wantoptions;
+    ipv6cp_options *ho = &pcb->ipv6cp_hisoptions;
+    ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
+
+    IPV6CPDEBUG(("ipv6cp: up"));
+
+    /*
+     * We must have a non-zero LL address for both ends of the link.
+     */
+    if (!ho->neg_ifaceid)
+       ho->hisid = wo->hisid;
+
+#if 0 /* UNUSED */
+    if(!no_ifaceid_neg) {
+#endif /* UNUSED */
+       if (eui64_iszero(ho->hisid)) {
+           ppp_error("Could not determine remote LL address");
+           ipv6cp_close(f->pcb, "Could not determine remote LL address");
+           return;
+       }
+       if (eui64_iszero(go->ourid)) {
+           ppp_error("Could not determine local LL address");
+           ipv6cp_close(f->pcb, "Could not determine local LL address");
+           return;
+       }
+       if (eui64_equals(go->ourid, ho->hisid)) {
+           ppp_error("local and remote LL addresses are equal");
+           ipv6cp_close(f->pcb, "local and remote LL addresses are equal");
+           return;
+       }
+#if 0 /* UNUSED */
+    }
+#endif /* UNUSED */
+#if 0 /* UNUSED */
+    script_setenv("LLLOCAL", llv6_ntoa(go->ourid), 0);
+    script_setenv("LLREMOTE", llv6_ntoa(ho->hisid), 0);
+#endif /* UNUSED */
+
+#ifdef IPV6CP_COMP
+    /* set tcp compression */
+    sif6comp(f->unit, ho->neg_vj);
+#endif
+
+#if DEMAND_SUPPORT
+    /*
+     * If we are doing dial-on-demand, the interface is already
+     * configured, so we put out any saved-up packets, then set the
+     * interface to pass IPv6 packets.
+     */
+    if (demand) {
+       if (! eui64_equals(go->ourid, wo->ourid) || 
+           ! eui64_equals(ho->hisid, wo->hisid)) {
+           if (! eui64_equals(go->ourid, wo->ourid))
+               warn("Local LL address changed to %s", 
+                    llv6_ntoa(go->ourid));
+           if (! eui64_equals(ho->hisid, wo->hisid))
+               warn("Remote LL address changed to %s", 
+                    llv6_ntoa(ho->hisid));
+           ipv6cp_clear_addrs(f->pcb, go->ourid, ho->hisid);
+
+           /* Set the interface to the new addresses */
+           if (!sif6addr(f->pcb, go->ourid, ho->hisid)) {
+               if (debug)
+                   warn("sif6addr failed");
+               ipv6cp_close(f->unit, "Interface configuration failed");
+               return;
+           }
+
+       }
+       demand_rexmit(PPP_IPV6);
+       sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS);
+
+    } else
+#endif /* DEMAND_SUPPORT */
+    {
+       /*
+        * Set LL addresses
+        */
+       if (!sif6addr(f->pcb, go->ourid, ho->hisid)) {
+           PPPDEBUG(LOG_DEBUG, ("sif6addr failed"));
+           ipv6cp_close(f->pcb, "Interface configuration failed");
+           return;
+       }
+
+       /* bring the interface up for IPv6 */
+       if (!sif6up(f->pcb)) {
+           PPPDEBUG(LOG_DEBUG, ("sif6up failed (IPV6)"));
+           ipv6cp_close(f->pcb, "Interface configuration failed");
+           return;
+       }
+#if DEMAND_SUPPORT
+       sifnpmode(f->pcb, PPP_IPV6, NPMODE_PASS);
+#endif /* DEMAND_SUPPORT */
+
+       ppp_notice("local  LL address %s", llv6_ntoa(go->ourid));
+       ppp_notice("remote LL address %s", llv6_ntoa(ho->hisid));
+    }
+
+    np_up(f->pcb, PPP_IPV6);
+    pcb->ipv6cp_is_up = 1;
+
+#if 0 /* UNUSED */
+    /*
+     * Execute the ipv6-up script, like this:
+     * /etc/ppp/ipv6-up interface tty speed local-LL remote-LL
+     */
+    if (ipv6cp_script_state == s_down && ipv6cp_script_pid == 0) {
+       ipv6cp_script_state = s_up;
+       ipv6cp_script(_PATH_IPV6UP);
+    }
+#endif /* UNUSED */
+}
+
+
+/*
+ * ipv6cp_down - IPV6CP has gone DOWN.
+ *
+ * Take the IPv6 network interface down, clear its addresses
+ * and delete routes through it.
+ */
+static void ipv6cp_down(fsm *f) {
+    ppp_pcb *pcb = f->pcb;
+    ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
+    ipv6cp_options *ho = &pcb->ipv6cp_hisoptions;
+
+    IPV6CPDEBUG(("ipv6cp: down"));
+#if PPP_STATS_SUPPORT
+    update_link_stats(f->unit);
+#endif /* PPP_STATS_SUPPORT */
+    if (pcb->ipv6cp_is_up) {
+       pcb->ipv6cp_is_up = 0;
+       np_down(f->pcb, PPP_IPV6);
+    }
+#ifdef IPV6CP_COMP
+    sif6comp(f->unit, 0);
+#endif
+
+#if DEMAND_SUPPORT
+    /*
+     * If we are doing dial-on-demand, set the interface
+     * to queue up outgoing packets (for now).
+     */
+    if (demand) {
+       sifnpmode(f->pcb, PPP_IPV6, NPMODE_QUEUE);
+    } else
+#endif /* DEMAND_SUPPORT */
+    {
+#if DEMAND_SUPPORT
+       sifnpmode(f->pcb, PPP_IPV6, NPMODE_DROP);
+#endif /* DEMAND_SUPPORT */
+       ipv6cp_clear_addrs(f->pcb,
+                          go->ourid,
+                          ho->hisid);
+       sif6down(f->pcb);
+    }
+
+#if 0 /* UNUSED */
+    /* Execute the ipv6-down script */
+    if (ipv6cp_script_state == s_up && ipv6cp_script_pid == 0) {
+       ipv6cp_script_state = s_down;
+       ipv6cp_script(_PATH_IPV6DOWN);
+    }
+#endif /* UNUSED */
+}
+
+
+/*
+ * ipv6cp_clear_addrs() - clear the interface addresses, routes,
+ * proxy neighbour discovery entries, etc.
+ */
+static void ipv6cp_clear_addrs(ppp_pcb *pcb, eui64_t ourid, eui64_t hisid) {
+    cif6addr(pcb, ourid, hisid);
+}
+
+
+/*
+ * ipv6cp_finished - possibly shut down the lower layers.
+ */
+static void ipv6cp_finished(fsm *f) {
+    np_finished(f->pcb, PPP_IPV6);
+}
+
+
+#if 0 /* UNUSED */
+/*
+ * ipv6cp_script_done - called when the ipv6-up or ipv6-down script
+ * has finished.
+ */
+static void
+ipv6cp_script_done(arg)
+    void *arg;
+{
+    ipv6cp_script_pid = 0;
+    switch (ipv6cp_script_state) {
+    case s_up:
+       if (ipv6cp_fsm[0].state != PPP_FSM_OPENED) {
+           ipv6cp_script_state = s_down;
+           ipv6cp_script(_PATH_IPV6DOWN);
+       }
+       break;
+    case s_down:
+       if (ipv6cp_fsm[0].state == PPP_FSM_OPENED) {
+           ipv6cp_script_state = s_up;
+           ipv6cp_script(_PATH_IPV6UP);
+       }
+       break;
+    }
+}
+
+
+/*
+ * ipv6cp_script - Execute a script with arguments
+ * interface-name tty-name speed local-LL remote-LL.
+ */
+static void
+ipv6cp_script(script)
+    char *script;
+{
+    char strspeed[32], strlocal[32], strremote[32];
+    char *argv[8];
+
+    sprintf(strspeed, "%d", baud_rate);
+    strcpy(strlocal, llv6_ntoa(ipv6cp_gotoptions[0].ourid));
+    strcpy(strremote, llv6_ntoa(ipv6cp_hisoptions[0].hisid));
+
+    argv[0] = script;
+    argv[1] = ifname;
+    argv[2] = devnam;
+    argv[3] = strspeed;
+    argv[4] = strlocal;
+    argv[5] = strremote;
+    argv[6] = ipparam;
+    argv[7] = NULL;
+
+    ipv6cp_script_pid = run_program(script, argv, 0, ipv6cp_script_done,
+                                   NULL, 0);
+}
+#endif /* UNUSED */
+
+#if PRINTPKT_SUPPORT
+/*
+ * ipv6cp_printpkt - print the contents of an IPV6CP packet.
+ */
+static const char* const ipv6cp_codenames[] = {
+    "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+    "TermReq", "TermAck", "CodeRej"
+};
+
+static int ipv6cp_printpkt(const u_char *p, int plen,
+               void (*printer)(void *, const char *, ...), void *arg) {
+    int code, id, len, olen;
+    const u_char *pstart, *optend;
+#ifdef IPV6CP_COMP
+    u_short cishort;
+#endif /* IPV6CP_COMP */
+    eui64_t ifaceid;
+
+    if (plen < HEADERLEN)
+       return 0;
+    pstart = p;
+    GETCHAR(code, p);
+    GETCHAR(id, p);
+    GETSHORT(len, p);
+    if (len < HEADERLEN || len > plen)
+       return 0;
+
+    if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(ipv6cp_codenames))
+       printer(arg, " %s", ipv6cp_codenames[code-1]);
+    else
+       printer(arg, " code=0x%x", code);
+    printer(arg, " id=0x%x", id);
+    len -= HEADERLEN;
+    switch (code) {
+    case CONFREQ:
+    case CONFACK:
+    case CONFNAK:
+    case CONFREJ:
+       /* print option list */
+       while (len >= 2) {
+           GETCHAR(code, p);
+           GETCHAR(olen, p);
+           p -= 2;
+           if (olen < 2 || olen > len) {
+               break;
+           }
+           printer(arg, " <");
+           len -= olen;
+           optend = p + olen;
+           switch (code) {
+#ifdef IPV6CP_COMP
+           case CI_COMPRESSTYPE:
+               if (olen >= CILEN_COMPRESS) {
+                   p += 2;
+                   GETSHORT(cishort, p);
+                   printer(arg, "compress ");
+                   printer(arg, "0x%x", cishort);
+               }
+               break;
+#endif /* IPV6CP_COMP */
+           case CI_IFACEID:
+               if (olen == CILEN_IFACEID) {
+                   p += 2;
+                   eui64_get(ifaceid, p);
+                   printer(arg, "addr %s", llv6_ntoa(ifaceid));
+               }
+               break;
+           default:
+               break;
+           }
+           while (p < optend) {
+               GETCHAR(code, p);
+               printer(arg, " %.2x", code);
+           }
+           printer(arg, ">");
+       }
+       break;
+
+    case TERMACK:
+    case TERMREQ:
+       if (len > 0 && *p >= ' ' && *p < 0x7f) {
+           printer(arg, " ");
+           ppp_print_string(p, len, printer, arg);
+           p += len;
+           len = 0;
+       }
+       break;
+    default:
+       break;
+    }
+
+    /* print the rest of the bytes in the packet */
+    for (; len > 0; --len) {
+       GETCHAR(code, p);
+       printer(arg, " %.2x", code);
+    }
+
+    return p - pstart;
+}
+#endif /* PRINTPKT_SUPPORT */
+
+#if DEMAND_SUPPORT
+/*
+ * ipv6_active_pkt - see if this IP packet is worth bringing the link up for.
+ * We don't bring the link up for IP fragments or for TCP FIN packets
+ * with no data.
+ */
+#define IP6_HDRLEN     40      /* bytes */
+#define IP6_NHDR_FRAG  44      /* fragment IPv6 header */
+#define TCP_HDRLEN     20
+#define TH_FIN         0x01
+
+/*
+ * We use these macros because the IP header may be at an odd address,
+ * and some compilers might use word loads to get th_off or ip_hl.
+ */
+
+#define get_ip6nh(x)   (((unsigned char *)(x))[6])
+#define get_tcpoff(x)  (((unsigned char *)(x))[12] >> 4)
+#define get_tcpflags(x)        (((unsigned char *)(x))[13])
+
+static int ipv6_active_pkt(u_char *pkt, int len) {
+    u_char *tcp;
+
+    len -= PPP_HDRLEN;
+    pkt += PPP_HDRLEN;
+    if (len < IP6_HDRLEN)
+       return 0;
+    if (get_ip6nh(pkt) == IP6_NHDR_FRAG)
+       return 0;
+    if (get_ip6nh(pkt) != IPPROTO_TCP)
+       return 1;
+    if (len < IP6_HDRLEN + TCP_HDRLEN)
+       return 0;
+    tcp = pkt + IP6_HDRLEN;
+    if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == IP6_HDRLEN + get_tcpoff(tcp) * 4)
+       return 0;
+    return 1;
+}
+#endif /* DEMAND_SUPPORT */
+
+#endif /* PPP_SUPPORT && PPP_IPV6_SUPPORT */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/lcp.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/lcp.c
new file mode 100644 (file)
index 0000000..90ed183
--- /dev/null
@@ -0,0 +1,2790 @@
+/*
+ * lcp.c - PPP Link Control Protocol.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * 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.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+/*
+ * @todo:
+ */
+
+#if 0 /* UNUSED */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#endif /* UNUSED */
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/fsm.h"
+#include "netif/ppp/lcp.h"
+#if CHAP_SUPPORT
+#include "netif/ppp/chap-new.h"
+#endif /* CHAP_SUPPORT */
+#include "netif/ppp/magic.h"
+
+/*
+ * When the link comes up we want to be able to wait for a short while,
+ * or until seeing some input from the peer, before starting to send
+ * configure-requests.  We do this by delaying the fsm_lowerup call.
+ */
+/* steal a bit in fsm flags word */
+#define DELAYED_UP     0x80
+
+static void lcp_delayed_up(void *arg);
+
+/*
+ * LCP-related command-line options.
+ */
+#if 0 /* UNUSED */
+int    lcp_echo_interval = 0;  /* Interval between LCP echo-requests */
+int    lcp_echo_fails = 0;     /* Tolerance to unanswered echo-requests */
+#endif /* UNUSED */
+
+#if 0 /* UNUSED */
+/* options */
+static u_int lcp_echo_interval      = LCP_ECHOINTERVAL; /* Interval between LCP echo-requests */
+static u_int lcp_echo_fails         = LCP_MAXECHOFAILS; /* Tolerance to unanswered echo-requests */
+#endif /* UNUSED */
+
+#if 0 /* UNUSED */
+#if PPP_LCP_ADAPTIVE
+bool   lcp_echo_adaptive = 0;  /* request echo only if the link was idle */
+#endif
+bool   lax_recv = 0;           /* accept control chars in asyncmap */
+bool   noendpoint = 0;         /* don't send/accept endpoint discriminator */
+#endif /* UNUSED */
+
+#if PPP_OPTIONS
+static int noopt (char **);
+#endif /* PPP_OPTIONS */
+
+#ifdef HAVE_MULTILINK
+static int setendpoint (char **);
+static void printendpoint (option_t *, void (*)(void *, char *, ...),
+                              void *);
+#endif /* HAVE_MULTILINK */
+
+#if PPP_OPTIONS
+static option_t lcp_option_list[] = {
+    /* LCP options */
+    { "-all", o_special_noarg, (void *)noopt,
+      "Don't request/allow any LCP options" },
+
+    { "noaccomp", o_bool, &lcp_wantoptions[0].neg_accompression,
+      "Disable address/control compression",
+      OPT_A2CLR, &lcp_allowoptions[0].neg_accompression },
+    { "-ac", o_bool, &lcp_wantoptions[0].neg_accompression,
+      "Disable address/control compression",
+      OPT_ALIAS | OPT_A2CLR, &lcp_allowoptions[0].neg_accompression },
+
+    { "asyncmap", o_uint32, &lcp_wantoptions[0].asyncmap,
+      "Set asyncmap (for received packets)",
+      OPT_OR, &lcp_wantoptions[0].neg_asyncmap },
+    { "-as", o_uint32, &lcp_wantoptions[0].asyncmap,
+      "Set asyncmap (for received packets)",
+      OPT_ALIAS | OPT_OR, &lcp_wantoptions[0].neg_asyncmap },
+    { "default-asyncmap", o_uint32, &lcp_wantoptions[0].asyncmap,
+      "Disable asyncmap negotiation",
+      OPT_OR | OPT_NOARG | OPT_VAL(~0U) | OPT_A2CLR,
+      &lcp_allowoptions[0].neg_asyncmap },
+    { "-am", o_uint32, &lcp_wantoptions[0].asyncmap,
+      "Disable asyncmap negotiation",
+      OPT_ALIAS | OPT_OR | OPT_NOARG | OPT_VAL(~0U) | OPT_A2CLR,
+      &lcp_allowoptions[0].neg_asyncmap },
+
+    { "nomagic", o_bool, &lcp_wantoptions[0].neg_magicnumber,
+      "Disable magic number negotiation (looped-back line detection)",
+      OPT_A2CLR, &lcp_allowoptions[0].neg_magicnumber },
+    { "-mn", o_bool, &lcp_wantoptions[0].neg_magicnumber,
+      "Disable magic number negotiation (looped-back line detection)",
+      OPT_ALIAS | OPT_A2CLR, &lcp_allowoptions[0].neg_magicnumber },
+
+    { "mru", o_int, &lcp_wantoptions[0].mru,
+      "Set MRU (maximum received packet size) for negotiation",
+      OPT_PRIO, &lcp_wantoptions[0].neg_mru },
+    { "default-mru", o_bool, &lcp_wantoptions[0].neg_mru,
+      "Disable MRU negotiation (use default 1500)",
+      OPT_PRIOSUB | OPT_A2CLR, &lcp_allowoptions[0].neg_mru },
+    { "-mru", o_bool, &lcp_wantoptions[0].neg_mru,
+      "Disable MRU negotiation (use default 1500)",
+      OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR, &lcp_allowoptions[0].neg_mru },
+
+    { "mtu", o_int, &lcp_allowoptions[0].mru,
+      "Set our MTU", OPT_LIMITS, NULL, MAXMRU, MINMRU },
+
+    { "nopcomp", o_bool, &lcp_wantoptions[0].neg_pcompression,
+      "Disable protocol field compression",
+      OPT_A2CLR, &lcp_allowoptions[0].neg_pcompression },
+    { "-pc", o_bool, &lcp_wantoptions[0].neg_pcompression,
+      "Disable protocol field compression",
+      OPT_ALIAS | OPT_A2CLR, &lcp_allowoptions[0].neg_pcompression },
+
+    { "passive", o_bool, &lcp_wantoptions[0].passive,
+      "Set passive mode", 1 },
+    { "-p", o_bool, &lcp_wantoptions[0].passive,
+      "Set passive mode", OPT_ALIAS | 1 },
+
+    { "silent", o_bool, &lcp_wantoptions[0].silent,
+      "Set silent mode", 1 },
+
+    { "lcp-echo-failure", o_int, &lcp_echo_fails,
+      "Set number of consecutive echo failures to indicate link failure",
+      OPT_PRIO },
+    { "lcp-echo-interval", o_int, &lcp_echo_interval,
+      "Set time in seconds between LCP echo requests", OPT_PRIO },
+#if PPP_LCP_ADAPTIVE
+    { "lcp-echo-adaptive", o_bool, &lcp_echo_adaptive,
+      "Suppress LCP echo requests if traffic was received", 1 },
+#endif
+    { "lcp-restart", o_int, &lcp_fsm[0].timeouttime,
+      "Set time in seconds between LCP retransmissions", OPT_PRIO },
+    { "lcp-max-terminate", o_int, &lcp_fsm[0].maxtermtransmits,
+      "Set maximum number of LCP terminate-request transmissions", OPT_PRIO },
+    { "lcp-max-configure", o_int, &lcp_fsm[0].maxconfreqtransmits,
+      "Set maximum number of LCP configure-request transmissions", OPT_PRIO },
+    { "lcp-max-failure", o_int, &lcp_fsm[0].maxnakloops,
+      "Set limit on number of LCP configure-naks", OPT_PRIO },
+
+    { "receive-all", o_bool, &lax_recv,
+      "Accept all received control characters", 1 },
+
+#ifdef HAVE_MULTILINK
+    { "mrru", o_int, &lcp_wantoptions[0].mrru,
+      "Maximum received packet size for multilink bundle",
+      OPT_PRIO, &lcp_wantoptions[0].neg_mrru },
+
+    { "mpshortseq", o_bool, &lcp_wantoptions[0].neg_ssnhf,
+      "Use short sequence numbers in multilink headers",
+      OPT_PRIO | 1, &lcp_allowoptions[0].neg_ssnhf },
+    { "nompshortseq", o_bool, &lcp_wantoptions[0].neg_ssnhf,
+      "Don't use short sequence numbers in multilink headers",
+      OPT_PRIOSUB | OPT_A2CLR, &lcp_allowoptions[0].neg_ssnhf },
+
+    { "endpoint", o_special, (void *) setendpoint,
+      "Endpoint discriminator for multilink",
+      OPT_PRIO | OPT_A2PRINTER, (void *) printendpoint },
+#endif /* HAVE_MULTILINK */
+
+    { "noendpoint", o_bool, &noendpoint,
+      "Don't send or accept multilink endpoint discriminator", 1 },
+
+    {NULL}
+};
+#endif /* PPP_OPTIONS */
+
+/*
+ * Callbacks for fsm code.  (CI = Configuration Information)
+ */
+static void lcp_resetci(fsm *f);       /* Reset our CI */
+static int  lcp_cilen(fsm *f);         /* Return length of our CI */
+static void lcp_addci(fsm *f, u_char *ucp, int *lenp); /* Add our CI to pkt */
+static int  lcp_ackci(fsm *f, u_char *p, int len); /* Peer ack'd our CI */
+static int  lcp_nakci(fsm *f, u_char *p, int len, int treat_as_reject); /* Peer nak'd our CI */
+static int  lcp_rejci(fsm *f, u_char *p, int len); /* Peer rej'd our CI */
+static int  lcp_reqci(fsm *f, u_char *inp, int *lenp, int reject_if_disagree); /* Rcv peer CI */
+static void lcp_up(fsm *f);            /* We're UP */
+static void lcp_down(fsm *f);          /* We're DOWN */
+static void lcp_starting (fsm *);      /* We need lower layer up */
+static void lcp_finished (fsm *);      /* We need lower layer down */
+static int  lcp_extcode(fsm *f, int code, int id, u_char *inp, int len);
+static void lcp_rprotrej(fsm *f, u_char *inp, int len);
+
+/*
+ * routines to send LCP echos to peer
+ */
+
+static void lcp_echo_lowerup(ppp_pcb *pcb);
+static void lcp_echo_lowerdown(ppp_pcb *pcb);
+static void LcpEchoTimeout(void *arg);
+static void lcp_received_echo_reply(fsm *f, int id, u_char *inp, int len);
+static void LcpSendEchoRequest(fsm *f);
+static void LcpLinkFailure(fsm *f);
+static void LcpEchoCheck(fsm *f);
+
+static const fsm_callbacks lcp_callbacks = {   /* LCP callback routines */
+    lcp_resetci,               /* Reset our Configuration Information */
+    lcp_cilen,                 /* Length of our Configuration Information */
+    lcp_addci,                 /* Add our Configuration Information */
+    lcp_ackci,                 /* ACK our Configuration Information */
+    lcp_nakci,                 /* NAK our Configuration Information */
+    lcp_rejci,                 /* Reject our Configuration Information */
+    lcp_reqci,                 /* Request peer's Configuration Information */
+    lcp_up,                    /* Called when fsm reaches OPENED state */
+    lcp_down,                  /* Called when fsm leaves OPENED state */
+    lcp_starting,              /* Called when we want the lower layer up */
+    lcp_finished,              /* Called when we want the lower layer down */
+    NULL,                      /* Called when Protocol-Reject received */
+    NULL,                      /* Retransmission is necessary */
+    lcp_extcode,               /* Called to handle LCP-specific codes */
+    "LCP"                      /* String name of protocol */
+};
+
+/*
+ * Protocol entry points.
+ * Some of these are called directly.
+ */
+
+static void lcp_init(ppp_pcb *pcb);
+static void lcp_input(ppp_pcb *pcb, u_char *p, int len);
+static void lcp_protrej(ppp_pcb *pcb);
+#if PRINTPKT_SUPPORT
+static int lcp_printpkt(const u_char *p, int plen,
+               void (*printer) (void *, const char *, ...), void *arg);
+#endif /* PRINTPKT_SUPPORT */
+
+const struct protent lcp_protent = {
+    PPP_LCP,
+    lcp_init,
+    lcp_input,
+    lcp_protrej,
+    lcp_lowerup,
+    lcp_lowerdown,
+    lcp_open,
+    lcp_close,
+#if PRINTPKT_SUPPORT
+    lcp_printpkt,
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_DATAINPUT
+    NULL,
+#endif /* PPP_DATAINPUT */
+#if PRINTPKT_SUPPORT
+    "LCP",
+    NULL,
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_OPTIONS
+    lcp_option_list,
+    NULL,
+#endif /* PPP_OPTIONS */
+#if DEMAND_SUPPORT
+    NULL,
+    NULL
+#endif /* DEMAND_SUPPORT */
+};
+
+/*
+ * Length of each type of configuration option (in octets)
+ */
+#define CILEN_VOID     2
+#define CILEN_CHAR     3
+#define CILEN_SHORT    4       /* CILEN_VOID + 2 */
+#if CHAP_SUPPORT
+#define CILEN_CHAP     5       /* CILEN_VOID + 2 + 1 */
+#endif /* CHAP_SUPPORT */
+#define CILEN_LONG     6       /* CILEN_VOID + 4 */
+#if LQR_SUPPORT
+#define CILEN_LQR      8       /* CILEN_VOID + 2 + 4 */
+#endif /* LQR_SUPPORT */
+#define CILEN_CBCP     3
+
+#define CODENAME(x)    ((x) == CONFACK ? "ACK" : \
+                        (x) == CONFNAK ? "NAK" : "REJ")
+
+#if PPP_OPTIONS
+/*
+ * noopt - Disable all options (why?).
+ */
+static int
+noopt(argv)
+    char **argv;
+{
+    BZERO((char *) &lcp_wantoptions[0], sizeof (struct lcp_options));
+    BZERO((char *) &lcp_allowoptions[0], sizeof (struct lcp_options));
+
+    return (1);
+}
+#endif /* PPP_OPTIONS */
+
+#ifdef HAVE_MULTILINK
+static int
+setendpoint(argv)
+    char **argv;
+{
+    if (str_to_epdisc(&lcp_wantoptions[0].endpoint, *argv)) {
+       lcp_wantoptions[0].neg_endpoint = 1;
+       return 1;
+    }
+    option_error("Can't parse '%s' as an endpoint discriminator", *argv);
+    return 0;
+}
+
+static void
+printendpoint(opt, printer, arg)
+    option_t *opt;
+    void (*printer) (void *, char *, ...);
+    void *arg;
+{
+       printer(arg, "%s", epdisc_to_str(&lcp_wantoptions[0].endpoint));
+}
+#endif /* HAVE_MULTILINK */
+
+/*
+ * lcp_init - Initialize LCP.
+ */
+static void lcp_init(ppp_pcb *pcb) {
+    fsm *f = &pcb->lcp_fsm;
+    lcp_options *wo = &pcb->lcp_wantoptions;
+    lcp_options *ao = &pcb->lcp_allowoptions;
+
+    f->pcb = pcb;
+    f->protocol = PPP_LCP;
+    f->callbacks = &lcp_callbacks;
+
+    fsm_init(f);
+
+    BZERO(wo, sizeof(*wo));
+    wo->neg_mru = 1;
+    wo->mru = PPP_DEFMRU;
+    wo->neg_asyncmap = 1;
+    wo->neg_magicnumber = 1;
+    wo->neg_pcompression = 1;
+    wo->neg_accompression = 1;
+
+    BZERO(ao, sizeof(*ao));
+    ao->neg_mru = 1;
+    ao->mru = PPP_MAXMRU;
+    ao->neg_asyncmap = 1;
+#if CHAP_SUPPORT
+    ao->neg_chap = 1;
+    ao->chap_mdtype = CHAP_MDTYPE_SUPPORTED;
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+    ao->neg_upap = 1;
+#endif /* PAP_SUPPORT */
+#if EAP_SUPPORT
+    ao->neg_eap = 1;
+#endif /* EAP_SUPPORT */
+    ao->neg_magicnumber = 1;
+    ao->neg_pcompression = 1;
+    ao->neg_accompression = 1;
+    ao->neg_endpoint = 1;
+}
+
+
+/*
+ * lcp_open - LCP is allowed to come up.
+ */
+void lcp_open(ppp_pcb *pcb) {
+    fsm *f = &pcb->lcp_fsm;
+    lcp_options *wo = &pcb->lcp_wantoptions;
+
+    f->flags &= ~(OPT_PASSIVE | OPT_SILENT);
+    if (wo->passive)
+       f->flags |= OPT_PASSIVE;
+    if (wo->silent)
+       f->flags |= OPT_SILENT;
+    fsm_open(f);
+}
+
+
+/*
+ * lcp_close - Take LCP down.
+ */
+void lcp_close(ppp_pcb *pcb, const char *reason) {
+    fsm *f = &pcb->lcp_fsm;
+    int oldstate;
+
+    if (pcb->phase != PPP_PHASE_DEAD
+#ifdef HAVE_MULTILINK
+    && pcb->phase != PPP_PHASE_MASTER
+#endif /* HAVE_MULTILINK */
+    )
+       new_phase(pcb, PPP_PHASE_TERMINATE);
+
+    if (f->flags & DELAYED_UP) {
+       UNTIMEOUT(lcp_delayed_up, f);
+       f->state = PPP_FSM_STOPPED;
+    }
+    oldstate = f->state;
+
+    fsm_close(f, reason);
+    if (oldstate == PPP_FSM_STOPPED && (f->flags & (OPT_PASSIVE|OPT_SILENT|DELAYED_UP))) {
+       /*
+        * This action is not strictly according to the FSM in RFC1548,
+        * but it does mean that the program terminates if you do a
+        * lcp_close() when a connection hasn't been established
+        * because we are in passive/silent mode or because we have
+        * delayed the fsm_lowerup() call and it hasn't happened yet.
+        */
+       f->flags &= ~DELAYED_UP;
+       lcp_finished(f);
+    }
+}
+
+
+/*
+ * lcp_lowerup - The lower layer is up.
+ */
+void lcp_lowerup(ppp_pcb *pcb) {
+    lcp_options *wo = &pcb->lcp_wantoptions;
+    fsm *f = &pcb->lcp_fsm;
+    /*
+     * Don't use A/C or protocol compression on transmission,
+     * but accept A/C and protocol compressed packets
+     * if we are going to ask for A/C and protocol compression.
+     */
+    if (ppp_send_config(pcb, PPP_MRU, 0xffffffff, 0, 0) < 0
+       || ppp_recv_config(pcb, PPP_MRU, (pcb->settings.lax_recv? 0: 0xffffffff),
+                          wo->neg_pcompression, wo->neg_accompression) < 0)
+           return;
+    pcb->peer_mru = PPP_MRU;
+
+    if (pcb->settings.listen_time != 0) {
+       f->flags |= DELAYED_UP;
+       TIMEOUTMS(lcp_delayed_up, f, pcb->settings.listen_time);
+    } else
+       fsm_lowerup(f);
+}
+
+
+/*
+ * lcp_lowerdown - The lower layer is down.
+ */
+void lcp_lowerdown(ppp_pcb *pcb) {
+    fsm *f = &pcb->lcp_fsm;
+
+    if (f->flags & DELAYED_UP) {
+       f->flags &= ~DELAYED_UP;
+       UNTIMEOUT(lcp_delayed_up, f);
+    } else
+       fsm_lowerdown(f);
+}
+
+
+/*
+ * lcp_delayed_up - Bring the lower layer up now.
+ */
+static void lcp_delayed_up(void *arg) {
+    fsm *f = (fsm*)arg;
+
+    if (f->flags & DELAYED_UP) {
+       f->flags &= ~DELAYED_UP;
+       fsm_lowerup(f);
+    }
+}
+
+
+/*
+ * lcp_input - Input LCP packet.
+ */
+static void lcp_input(ppp_pcb *pcb, u_char *p, int len) {
+    fsm *f = &pcb->lcp_fsm;
+
+    if (f->flags & DELAYED_UP) {
+       f->flags &= ~DELAYED_UP;
+       UNTIMEOUT(lcp_delayed_up, f);
+       fsm_lowerup(f);
+    }
+    fsm_input(f, p, len);
+}
+
+/*
+ * lcp_extcode - Handle a LCP-specific code.
+ */
+static int lcp_extcode(fsm *f, int code, int id, u_char *inp, int len) {
+    ppp_pcb *pcb = f->pcb;
+    lcp_options *go = &pcb->lcp_gotoptions;
+    u_char *magp;
+
+    switch( code ){
+    case PROTREJ:
+       lcp_rprotrej(f, inp, len);
+       break;
+    
+    case ECHOREQ:
+       if (f->state != PPP_FSM_OPENED)
+           break;
+       magp = inp;
+       PUTLONG(go->magicnumber, magp);
+       fsm_sdata(f, ECHOREP, id, inp, len);
+       break;
+    
+    case ECHOREP:
+       lcp_received_echo_reply(f, id, inp, len);
+       break;
+
+    case DISCREQ:
+    case IDENTIF:
+    case TIMEREM:
+       break;
+
+    default:
+       return 0;
+    }
+    return 1;
+}
+
+    
+/*
+ * lcp_rprotrej - Receive an Protocol-Reject.
+ *
+ * Figure out which protocol is rejected and inform it.
+ */
+static void lcp_rprotrej(fsm *f, u_char *inp, int len) {
+    int i;
+    const struct protent *protp;
+    u_short prot;
+#if PPP_PROTOCOLNAME
+    const char *pname;
+#endif /* PPP_PROTOCOLNAME */
+
+    if (len < 2) {
+       LCPDEBUG(("lcp_rprotrej: Rcvd short Protocol-Reject packet!"));
+       return;
+    }
+
+    GETSHORT(prot, inp);
+
+    /*
+     * Protocol-Reject packets received in any state other than the LCP
+     * OPENED state SHOULD be silently discarded.
+     */
+    if( f->state != PPP_FSM_OPENED ){
+       LCPDEBUG(("Protocol-Reject discarded: LCP in state %d", f->state));
+       return;
+    }
+
+#if PPP_PROTOCOLNAME
+    pname = protocol_name(prot);
+#endif /* PPP_PROTOCOLNAME */
+
+    /*
+     * Upcall the proper Protocol-Reject routine.
+     */
+    for (i = 0; (protp = protocols[i]) != NULL; ++i)
+       if (protp->protocol == prot) {
+#if PPP_PROTOCOLNAME
+           if (pname != NULL)
+               ppp_dbglog("Protocol-Reject for '%s' (0x%x) received", pname,
+                      prot);
+           else
+#endif /* PPP_PROTOCOLNAME */
+               ppp_dbglog("Protocol-Reject for 0x%x received", prot);
+           (*protp->protrej)(f->pcb);
+           return;
+       }
+
+#if PPP_PROTOCOLNAME
+    if (pname != NULL)
+       ppp_warn("Protocol-Reject for unsupported protocol '%s' (0x%x)", pname,
+            prot);
+    else
+#endif /* #if PPP_PROTOCOLNAME */
+       ppp_warn("Protocol-Reject for unsupported protocol 0x%x", prot);
+}
+
+
+/*
+ * lcp_protrej - A Protocol-Reject was received.
+ */
+/*ARGSUSED*/
+static void lcp_protrej(ppp_pcb *pcb) {
+    /*
+     * Can't reject LCP!
+     */
+    ppp_error("Received Protocol-Reject for LCP!");
+    fsm_protreject(&pcb->lcp_fsm);
+}
+
+
+/*
+ * lcp_sprotrej - Send a Protocol-Reject for some protocol.
+ */
+void lcp_sprotrej(ppp_pcb *pcb, u_char *p, int len) {
+    fsm *f = &pcb->lcp_fsm;
+    /*
+     * Send back the protocol and the information field of the
+     * rejected packet.  We only get here if LCP is in the OPENED state.
+     */
+#if 0
+    p += 2;
+    len -= 2;
+#endif
+
+    fsm_sdata(f, PROTREJ, ++f->id,
+             p, len);
+}
+
+
+/*
+ * lcp_resetci - Reset our CI.
+ */
+static void lcp_resetci(fsm *f) {
+    ppp_pcb *pcb = f->pcb;
+    lcp_options *wo = &pcb->lcp_wantoptions;
+    lcp_options *go = &pcb->lcp_gotoptions;
+    lcp_options *ao = &pcb->lcp_allowoptions;
+
+#if PPP_AUTH_SUPPORT
+
+    /* note: default value is true for allow options */
+    if (pcb->settings.user && pcb->settings.passwd) {
+#if PAP_SUPPORT
+      if (pcb->settings.refuse_pap) {
+        ao->neg_upap = 0;
+      }
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+      if (pcb->settings.refuse_chap) {
+        ao->chap_mdtype &= ~MDTYPE_MD5;
+      }
+#if MSCHAP_SUPPORT
+      if (pcb->settings.refuse_mschap) {
+        ao->chap_mdtype &= ~MDTYPE_MICROSOFT;
+      }
+      if (pcb->settings.refuse_mschap_v2) {
+        ao->chap_mdtype &= ~MDTYPE_MICROSOFT_V2;
+      }
+#endif /* MSCHAP_SUPPORT */
+      ao->neg_chap = (ao->chap_mdtype != MDTYPE_NONE);
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+      if (pcb->settings.refuse_eap) {
+        ao->neg_eap = 0;
+      }
+#endif /* EAP_SUPPORT */
+
+#if PPP_SERVER
+      /* note: default value is false for wanted options */
+      if (pcb->settings.auth_required) {
+#if PAP_SUPPORT
+        if (!pcb->settings.refuse_pap) {
+          wo->neg_upap = 1;
+        }
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+        if (!pcb->settings.refuse_chap) {
+          wo->chap_mdtype |= MDTYPE_MD5;
+        }
+#if MSCHAP_SUPPORT
+        if (!pcb->settings.refuse_mschap) {
+          wo->chap_mdtype |= MDTYPE_MICROSOFT;
+        }
+        if (!pcb->settings.refuse_mschap_v2) {
+          wo->chap_mdtype |= MDTYPE_MICROSOFT_V2;
+        }
+#endif /* MSCHAP_SUPPORT */
+        wo->neg_chap = (wo->chap_mdtype != MDTYPE_NONE);
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+        if (!pcb->settings.refuse_eap) {
+          wo->neg_eap = 1;
+        }
+#endif /* EAP_SUPPORT */
+      }
+#endif /* PPP_SERVER */
+
+    } else {
+#if PAP_SUPPORT
+      ao->neg_upap = 0;
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+      ao->neg_chap = 0;
+      ao->chap_mdtype = MDTYPE_NONE;
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+      ao->neg_eap = 0;
+#endif /* EAP_SUPPORT */
+    }
+
+    PPPDEBUG(LOG_DEBUG, ("ppp: auth protocols:"));
+#if PAP_SUPPORT
+    PPPDEBUG(LOG_DEBUG, (" PAP=%d", ao->neg_upap));
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+    PPPDEBUG(LOG_DEBUG, (" CHAP=%d CHAP_MD5=%d", ao->neg_chap, !!(ao->chap_mdtype&MDTYPE_MD5)));
+#if MSCHAP_SUPPORT
+    PPPDEBUG(LOG_DEBUG, (" CHAP_MS=%d CHAP_MS2=%d", !!(ao->chap_mdtype&MDTYPE_MICROSOFT), !!(ao->chap_mdtype&MDTYPE_MICROSOFT_V2)));
+#endif /* MSCHAP_SUPPORT */
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+    PPPDEBUG(LOG_DEBUG, (" EAP=%d", ao->neg_eap));
+#endif /* EAP_SUPPORT */
+    PPPDEBUG(LOG_DEBUG, ("\n"));
+
+#endif /* PPP_AUTH_SUPPORT */
+
+    wo->magicnumber = magic();
+    wo->numloops = 0;
+    *go = *wo;
+#ifdef HAVE_MULTILINK
+    if (!multilink) {
+       go->neg_mrru = 0;
+#endif /* HAVE_MULTILINK */
+       go->neg_ssnhf = 0;
+       go->neg_endpoint = 0;
+#ifdef HAVE_MULTILINK
+    }
+#endif /* HAVE_MULTILINK */
+    if (pcb->settings.noendpoint)
+       ao->neg_endpoint = 0;
+    pcb->peer_mru = PPP_MRU;
+#if 0 /* UNUSED */
+    auth_reset(pcb);
+#endif /* UNUSED */
+}
+
+
+/*
+ * lcp_cilen - Return length of our CI.
+ */
+static int lcp_cilen(fsm *f) {
+    ppp_pcb *pcb = f->pcb;
+    lcp_options *go = &pcb->lcp_gotoptions;
+
+#define LENCIVOID(neg) ((neg) ? CILEN_VOID : 0)
+#if CHAP_SUPPORT
+#define LENCICHAP(neg) ((neg) ? CILEN_CHAP : 0)
+#endif /* CHAP_SUPPORT */
+#define LENCISHORT(neg)        ((neg) ? CILEN_SHORT : 0)
+#define LENCILONG(neg) ((neg) ? CILEN_LONG : 0)
+#if LQR_SUPPORT
+#define LENCILQR(neg)  ((neg) ? CILEN_LQR: 0)
+#endif /* LQR_SUPPORT */
+#define LENCICBCP(neg) ((neg) ? CILEN_CBCP: 0)
+    /*
+     * NB: we only ask for one of CHAP, UPAP, or EAP, even if we will
+     * accept more than one.  We prefer EAP first, then CHAP, then
+     * PAP.
+     */
+    return (LENCISHORT(go->neg_mru && go->mru != PPP_DEFMRU) +
+           LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) +
+#if EAP_SUPPORT
+           LENCISHORT(go->neg_eap) +
+#endif /* EAP_SUPPORT */
+#if CHAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */
+#if EAP_SUPPORT
+           LENCICHAP(!go->neg_eap && go->neg_chap) +
+#endif /* EAP_SUPPORT */
+#if !EAP_SUPPORT
+           LENCICHAP(go->neg_chap) +
+#endif /* !EAP_SUPPORT */
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */
+#if EAP_SUPPORT && CHAP_SUPPORT
+           LENCISHORT(!go->neg_eap && !go->neg_chap && go->neg_upap) +
+#endif /* EAP_SUPPORT && CHAP_SUPPORT */
+#if EAP_SUPPORT && !CHAP_SUPPORT
+           LENCISHORT(!go->neg_eap && go->neg_upap) +
+#endif /* EAP_SUPPORT && !CHAP_SUPPORT */
+#if !EAP_SUPPORT && CHAP_SUPPORT
+           LENCISHORT(!go->neg_chap && go->neg_upap) +
+#endif /* !EAP_SUPPORT && CHAP_SUPPORT */
+#if !EAP_SUPPORT && !CHAP_SUPPORT
+           LENCISHORT(go->neg_upap) +
+#endif /* !EAP_SUPPORT && !CHAP_SUPPORT */
+#endif /* PAP_SUPPORT */
+#if LQR_SUPPORT
+           LENCILQR(go->neg_lqr) +
+#endif /* LQR_SUPPORT */
+           LENCICBCP(go->neg_cbcp) +
+           LENCILONG(go->neg_magicnumber) +
+           LENCIVOID(go->neg_pcompression) +
+           LENCIVOID(go->neg_accompression) +
+#ifdef HAVE_MULTILINK
+           LENCISHORT(go->neg_mrru) +
+#endif /* HAVE_MULTILINK */
+           LENCIVOID(go->neg_ssnhf) +
+           (go->neg_endpoint? CILEN_CHAR + go->endpoint.length: 0));
+}
+
+
+/*
+ * lcp_addci - Add our desired CIs to a packet.
+ */
+static void lcp_addci(fsm *f, u_char *ucp, int *lenp) {
+    ppp_pcb *pcb = f->pcb;
+    lcp_options *go = &pcb->lcp_gotoptions;
+    u_char *start_ucp = ucp;
+
+#define ADDCIVOID(opt, neg) \
+    if (neg) { \
+       PUTCHAR(opt, ucp); \
+       PUTCHAR(CILEN_VOID, ucp); \
+    }
+#define ADDCISHORT(opt, neg, val) \
+    if (neg) { \
+       PUTCHAR(opt, ucp); \
+       PUTCHAR(CILEN_SHORT, ucp); \
+       PUTSHORT(val, ucp); \
+    }
+#if CHAP_SUPPORT
+#define ADDCICHAP(opt, neg, val) \
+    if (neg) { \
+       PUTCHAR((opt), ucp); \
+       PUTCHAR(CILEN_CHAP, ucp); \
+       PUTSHORT(PPP_CHAP, ucp); \
+       PUTCHAR((CHAP_DIGEST(val)), ucp); \
+    }
+#endif /* CHAP_SUPPORT */
+#define ADDCILONG(opt, neg, val) \
+    if (neg) { \
+       PUTCHAR(opt, ucp); \
+       PUTCHAR(CILEN_LONG, ucp); \
+       PUTLONG(val, ucp); \
+    }
+#if LQR_SUPPORT
+#define ADDCILQR(opt, neg, val) \
+    if (neg) { \
+       PUTCHAR(opt, ucp); \
+       PUTCHAR(CILEN_LQR, ucp); \
+       PUTSHORT(PPP_LQR, ucp); \
+       PUTLONG(val, ucp); \
+    }
+#endif /* LQR_SUPPORT */
+#define ADDCICHAR(opt, neg, val) \
+    if (neg) { \
+       PUTCHAR(opt, ucp); \
+       PUTCHAR(CILEN_CHAR, ucp); \
+       PUTCHAR(val, ucp); \
+    }
+#define ADDCIENDP(opt, neg, class, val, len) \
+    if (neg) { \
+       int i; \
+       PUTCHAR(opt, ucp); \
+       PUTCHAR(CILEN_CHAR + len, ucp); \
+       PUTCHAR(class, ucp); \
+       for (i = 0; i < len; ++i) \
+           PUTCHAR(val[i], ucp); \
+    }
+
+    ADDCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru);
+    ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF,
+             go->asyncmap);
+#if EAP_SUPPORT
+    ADDCISHORT(CI_AUTHTYPE, go->neg_eap, PPP_EAP);
+#endif /* EAP_SUPPORT */
+#if CHAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */
+#if EAP_SUPPORT
+    ADDCICHAP(CI_AUTHTYPE, !go->neg_eap && go->neg_chap, go->chap_mdtype);
+#endif /* EAP_SUPPORT */
+#if !EAP_SUPPORT
+    ADDCICHAP(CI_AUTHTYPE, go->neg_chap, go->chap_mdtype);
+#endif /* !EAP_SUPPORT */
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */
+#if EAP_SUPPORT && CHAP_SUPPORT
+    ADDCISHORT(CI_AUTHTYPE, !go->neg_eap && !go->neg_chap && go->neg_upap, PPP_PAP);
+#endif /* EAP_SUPPORT && CHAP_SUPPORT */
+#if EAP_SUPPORT && !CHAP_SUPPORT
+    ADDCISHORT(CI_AUTHTYPE, !go->neg_eap && go->neg_upap, PPP_PAP);
+#endif /* EAP_SUPPORT && !CHAP_SUPPORT */
+#if !EAP_SUPPORT && CHAP_SUPPORT
+    ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP);
+#endif /* !EAP_SUPPORT && CHAP_SUPPORT */
+#if !EAP_SUPPORT && !CHAP_SUPPORT
+    ADDCISHORT(CI_AUTHTYPE, go->neg_upap, PPP_PAP);
+#endif /* !EAP_SUPPORT && !CHAP_SUPPORT */
+#endif /* PAP_SUPPORT */
+#if LQR_SUPPORT
+    ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
+#endif /* LQR_SUPPORT */
+    ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT);
+    ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
+    ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
+    ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
+#ifdef HAVE_MULTILINK
+    ADDCISHORT(CI_MRRU, go->neg_mrru, go->mrru);
+#endif
+    ADDCIVOID(CI_SSNHF, go->neg_ssnhf);
+    ADDCIENDP(CI_EPDISC, go->neg_endpoint, go->endpoint.class_,
+             go->endpoint.value, go->endpoint.length);
+
+    if (ucp - start_ucp != *lenp) {
+       /* this should never happen, because peer_mtu should be 1500 */
+       ppp_error("Bug in lcp_addci: wrong length");
+    }
+}
+
+
+/*
+ * lcp_ackci - Ack our CIs.
+ * This should not modify any state if the Ack is bad.
+ *
+ * Returns:
+ *     0 - Ack was bad.
+ *     1 - Ack was good.
+ */
+static int lcp_ackci(fsm *f, u_char *p, int len) {
+    ppp_pcb *pcb = f->pcb;
+    lcp_options *go = &pcb->lcp_gotoptions;
+    u_char cilen, citype, cichar;
+    u_short cishort;
+    u32_t cilong;
+
+    /*
+     * CIs must be in exactly the same order that we sent.
+     * Check packet length and CI length at each step.
+     * If we find any deviations, then this packet is bad.
+     */
+#define ACKCIVOID(opt, neg) \
+    if (neg) { \
+       if ((len -= CILEN_VOID) < 0) \
+           goto bad; \
+       GETCHAR(citype, p); \
+       GETCHAR(cilen, p); \
+       if (cilen != CILEN_VOID || \
+           citype != opt) \
+           goto bad; \
+    }
+#define ACKCISHORT(opt, neg, val) \
+    if (neg) { \
+       if ((len -= CILEN_SHORT) < 0) \
+           goto bad; \
+       GETCHAR(citype, p); \
+       GETCHAR(cilen, p); \
+       if (cilen != CILEN_SHORT || \
+           citype != opt) \
+           goto bad; \
+       GETSHORT(cishort, p); \
+       if (cishort != val) \
+           goto bad; \
+    }
+#define ACKCICHAR(opt, neg, val) \
+    if (neg) { \
+       if ((len -= CILEN_CHAR) < 0) \
+           goto bad; \
+       GETCHAR(citype, p); \
+       GETCHAR(cilen, p); \
+       if (cilen != CILEN_CHAR || \
+           citype != opt) \
+           goto bad; \
+       GETCHAR(cichar, p); \
+       if (cichar != val) \
+           goto bad; \
+    }
+#if CHAP_SUPPORT
+#define ACKCICHAP(opt, neg, val) \
+    if (neg) { \
+       if ((len -= CILEN_CHAP) < 0) \
+           goto bad; \
+       GETCHAR(citype, p); \
+       GETCHAR(cilen, p); \
+       if (cilen != CILEN_CHAP || \
+           citype != (opt)) \
+           goto bad; \
+       GETSHORT(cishort, p); \
+       if (cishort != PPP_CHAP) \
+           goto bad; \
+       GETCHAR(cichar, p); \
+       if (cichar != (CHAP_DIGEST(val))) \
+         goto bad; \
+    }
+#endif /* CHAP_SUPPORT */
+#define ACKCILONG(opt, neg, val) \
+    if (neg) { \
+       if ((len -= CILEN_LONG) < 0) \
+           goto bad; \
+       GETCHAR(citype, p); \
+       GETCHAR(cilen, p); \
+       if (cilen != CILEN_LONG || \
+           citype != opt) \
+           goto bad; \
+       GETLONG(cilong, p); \
+       if (cilong != val) \
+           goto bad; \
+    }
+#if LQR_SUPPORT
+#define ACKCILQR(opt, neg, val) \
+    if (neg) { \
+       if ((len -= CILEN_LQR) < 0) \
+           goto bad; \
+       GETCHAR(citype, p); \
+       GETCHAR(cilen, p); \
+       if (cilen != CILEN_LQR || \
+           citype != opt) \
+           goto bad; \
+       GETSHORT(cishort, p); \
+       if (cishort != PPP_LQR) \
+           goto bad; \
+       GETLONG(cilong, p); \
+       if (cilong != val) \
+         goto bad; \
+    }
+#endif /* LQR_SUPPORT */
+#define ACKCIENDP(opt, neg, class, val, vlen) \
+    if (neg) { \
+       int i; \
+       if ((len -= CILEN_CHAR + vlen) < 0) \
+           goto bad; \
+       GETCHAR(citype, p); \
+       GETCHAR(cilen, p); \
+       if (cilen != CILEN_CHAR + vlen || \
+           citype != opt) \
+           goto bad; \
+       GETCHAR(cichar, p); \
+       if (cichar != class) \
+           goto bad; \
+       for (i = 0; i < vlen; ++i) { \
+           GETCHAR(cichar, p); \
+           if (cichar != val[i]) \
+               goto bad; \
+       } \
+    }
+
+    ACKCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru);
+    ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF,
+             go->asyncmap);
+#if EAP_SUPPORT
+    ACKCISHORT(CI_AUTHTYPE, go->neg_eap, PPP_EAP);
+#endif /* EAP_SUPPORT */
+#if CHAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */
+#if EAP_SUPPORT
+    ACKCICHAP(CI_AUTHTYPE, !go->neg_eap && go->neg_chap, go->chap_mdtype);
+#endif /* EAP_SUPPORT */
+#if !EAP_SUPPORT
+    ACKCICHAP(CI_AUTHTYPE, go->neg_chap, go->chap_mdtype);
+#endif /* !EAP_SUPPORT */
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */
+#if EAP_SUPPORT && CHAP_SUPPORT
+    ACKCISHORT(CI_AUTHTYPE, !go->neg_eap && !go->neg_chap && go->neg_upap, PPP_PAP);
+#endif /* EAP_SUPPORT && CHAP_SUPPORT */
+#if EAP_SUPPORT && !CHAP_SUPPORT
+    ACKCISHORT(CI_AUTHTYPE, !go->neg_eap && go->neg_upap, PPP_PAP);
+#endif /* EAP_SUPPORT && !CHAP_SUPPORT */
+#if !EAP_SUPPORT && CHAP_SUPPORT
+    ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP);
+#endif /* !EAP_SUPPORT && CHAP_SUPPORT */
+#if !EAP_SUPPORT && !CHAP_SUPPORT
+    ACKCISHORT(CI_AUTHTYPE, go->neg_upap, PPP_PAP);
+#endif /* !EAP_SUPPORT && !CHAP_SUPPORT */
+#endif /* PAP_SUPPORT */
+#if LQR_SUPPORT
+    ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
+#endif /* LQR_SUPPORT */
+    ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT);
+    ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
+    ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
+    ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
+#ifdef HAVE_MULTILINK
+    ACKCISHORT(CI_MRRU, go->neg_mrru, go->mrru);
+#endif /* HAVE_MULTILINK */
+    ACKCIVOID(CI_SSNHF, go->neg_ssnhf);
+    ACKCIENDP(CI_EPDISC, go->neg_endpoint, go->endpoint.class_,
+             go->endpoint.value, go->endpoint.length);
+
+    /*
+     * If there are any remaining CIs, then this packet is bad.
+     */
+    if (len != 0)
+       goto bad;
+    return (1);
+bad:
+    LCPDEBUG(("lcp_acki: received bad Ack!"));
+    return (0);
+}
+
+
+/*
+ * lcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if LCP is in the OPENED state.
+ *
+ * Returns:
+ *     0 - Nak was bad.
+ *     1 - Nak was good.
+ */
+static int lcp_nakci(fsm *f, u_char *p, int len, int treat_as_reject) {
+    ppp_pcb *pcb = f->pcb;
+    lcp_options *go = &pcb->lcp_gotoptions;
+    lcp_options *wo = &pcb->lcp_wantoptions;
+    u_char citype, cichar, *next;
+    u_short cishort;
+    u32_t cilong;
+    lcp_options no;            /* options we've seen Naks for */
+    lcp_options try_;          /* options to request next time */
+    int looped_back = 0;
+    int cilen;
+
+    BZERO(&no, sizeof(no));
+    try_ = *go;
+
+    /*
+     * Any Nak'd CIs must be in exactly the same order that we sent.
+     * Check packet length and CI length at each step.
+     * If we find any deviations, then this packet is bad.
+     */
+#define NAKCIVOID(opt, neg) \
+    if (go->neg && \
+       len >= CILEN_VOID && \
+       p[1] == CILEN_VOID && \
+       p[0] == opt) { \
+       len -= CILEN_VOID; \
+       INCPTR(CILEN_VOID, p); \
+       no.neg = 1; \
+       try_.neg = 0; \
+    }
+#if CHAP_SUPPORT
+#define NAKCICHAP(opt, neg, code) \
+    if (go->neg && \
+       len >= CILEN_CHAP && \
+       p[1] == CILEN_CHAP && \
+       p[0] == opt) { \
+       len -= CILEN_CHAP; \
+       INCPTR(2, p); \
+       GETSHORT(cishort, p); \
+       GETCHAR(cichar, p); \
+       no.neg = 1; \
+       code \
+    }
+#endif /* CHAP_SUPPORT */
+#define NAKCICHAR(opt, neg, code) \
+    if (go->neg && \
+       len >= CILEN_CHAR && \
+       p[1] == CILEN_CHAR && \
+       p[0] == opt) { \
+       len -= CILEN_CHAR; \
+       INCPTR(2, p); \
+       GETCHAR(cichar, p); \
+       no.neg = 1; \
+       code \
+    }
+#define NAKCISHORT(opt, neg, code) \
+    if (go->neg && \
+       len >= CILEN_SHORT && \
+       p[1] == CILEN_SHORT && \
+       p[0] == opt) { \
+       len -= CILEN_SHORT; \
+       INCPTR(2, p); \
+       GETSHORT(cishort, p); \
+       no.neg = 1; \
+       code \
+    }
+#define NAKCILONG(opt, neg, code) \
+    if (go->neg && \
+       len >= CILEN_LONG && \
+       p[1] == CILEN_LONG && \
+       p[0] == opt) { \
+       len -= CILEN_LONG; \
+       INCPTR(2, p); \
+       GETLONG(cilong, p); \
+       no.neg = 1; \
+       code \
+    }
+#if LQR_SUPPORT
+#define NAKCILQR(opt, neg, code) \
+    if (go->neg && \
+       len >= CILEN_LQR && \
+       p[1] == CILEN_LQR && \
+       p[0] == opt) { \
+       len -= CILEN_LQR; \
+       INCPTR(2, p); \
+       GETSHORT(cishort, p); \
+       GETLONG(cilong, p); \
+       no.neg = 1; \
+       code \
+    }
+#endif /* LQR_SUPPORT */
+#define NAKCIENDP(opt, neg) \
+    if (go->neg && \
+       len >= CILEN_CHAR && \
+       p[0] == opt && \
+       p[1] >= CILEN_CHAR && \
+       p[1] <= len) { \
+       len -= p[1]; \
+       INCPTR(p[1], p); \
+       no.neg = 1; \
+       try_.neg = 0; \
+    }
+
+    /*
+     * NOTE!  There must be no assignments to individual fields of *go in
+     * the code below.  Any such assignment is a BUG!
+     */
+    /*
+     * We don't care if they want to send us smaller packets than
+     * we want.  Therefore, accept any MRU less than what we asked for,
+     * but then ignore the new value when setting the MRU in the kernel.
+     * If they send us a bigger MRU than what we asked, accept it, up to
+     * the limit of the default MRU we'd get if we didn't negotiate.
+     */
+    if (go->neg_mru && go->mru != PPP_DEFMRU) {
+       NAKCISHORT(CI_MRU, neg_mru,
+                  if (cishort <= wo->mru || cishort <= PPP_DEFMRU)
+                      try_.mru = cishort;
+                  );
+    }
+
+    /*
+     * Add any characters they want to our (receive-side) asyncmap.
+     */
+    if (go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) {
+       NAKCILONG(CI_ASYNCMAP, neg_asyncmap,
+                 try_.asyncmap = go->asyncmap | cilong;
+                 );
+    }
+
+    /*
+     * If they've nak'd our authentication-protocol, check whether
+     * they are proposing a different protocol, or a different
+     * hash algorithm for CHAP.
+     */
+    if ((0
+#if CHAP_SUPPORT
+        || go->neg_chap
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+        || go->neg_upap
+#endif /* PAP_SUPPORT */
+#if EAP_SUPPORT
+        || go->neg_eap
+#endif /* EAP_SUPPORT */
+        )
+       && len >= CILEN_SHORT
+       && p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) {
+       cilen = p[1];
+       len -= cilen;
+#if CHAP_SUPPORT
+       no.neg_chap = go->neg_chap;
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+       no.neg_upap = go->neg_upap;
+#endif /* PAP_SUPPORT */
+#if EAP_SUPPORT
+       no.neg_eap = go->neg_eap;
+#endif /* EAP_SUPPORT */
+       INCPTR(2, p);
+       GETSHORT(cishort, p);
+
+#if PAP_SUPPORT
+       if (cishort == PPP_PAP && cilen == CILEN_SHORT) {
+#if EAP_SUPPORT
+           /* If we were asking for EAP, then we need to stop that. */
+           if (go->neg_eap)
+               try_.neg_eap = 0;
+           else
+#endif /* EAP_SUPPORT */
+
+#if CHAP_SUPPORT
+           /* If we were asking for CHAP, then we need to stop that. */
+           if (go->neg_chap)
+               try_.neg_chap = 0;
+           else
+#endif /* CHAP_SUPPORT */
+
+           /*
+            * If we weren't asking for CHAP or EAP, then we were asking for
+            * PAP, in which case this Nak is bad.
+            */
+               goto bad;
+       } else
+#endif /* PAP_SUPPORT */
+
+#if CHAP_SUPPORT
+       if (cishort == PPP_CHAP && cilen == CILEN_CHAP) {
+           GETCHAR(cichar, p);
+#if EAP_SUPPORT
+           /* Stop asking for EAP, if we were. */
+           if (go->neg_eap) {
+               try_.neg_eap = 0;
+               /* Try to set up to use their suggestion, if possible */
+               if (CHAP_CANDIGEST(go->chap_mdtype, cichar))
+                   try_.chap_mdtype = CHAP_MDTYPE_D(cichar);
+           } else
+#endif /* EAP_SUPPORT */
+           if (go->neg_chap) {
+               /*
+                * We were asking for our preferred algorithm, they must
+                * want something different.
+                */
+               if (cichar != CHAP_DIGEST(go->chap_mdtype)) {
+                   if (CHAP_CANDIGEST(go->chap_mdtype, cichar)) {
+                       /* Use their suggestion if we support it ... */
+                       try_.chap_mdtype = CHAP_MDTYPE_D(cichar);
+                   } else {
+                       /* ... otherwise, try our next-preferred algorithm. */
+                       try_.chap_mdtype &= ~(CHAP_MDTYPE(try_.chap_mdtype));
+                       if (try_.chap_mdtype == MDTYPE_NONE) /* out of algos */
+                           try_.neg_chap = 0;
+                   }
+               } else {
+                   /*
+                    * Whoops, they Nak'd our algorithm of choice
+                    * but then suggested it back to us.
+                    */
+                   goto bad;
+               }
+           } else {
+               /*
+                * Stop asking for PAP if we were asking for it.
+                */
+#if PAP_SUPPORT
+               try_.neg_upap = 0;
+#endif /* PAP_SUPPORT */
+           }
+
+       } else
+#endif /* CHAP_SUPPORT */
+       {
+
+#if EAP_SUPPORT
+           /*
+            * If we were asking for EAP, and they're Conf-Naking EAP,
+            * well, that's just strange.  Nobody should do that.
+            */
+           if (cishort == PPP_EAP && cilen == CILEN_SHORT && go->neg_eap)
+               ppp_dbglog("Unexpected Conf-Nak for EAP");
+
+           /*
+            * We don't recognize what they're suggesting.
+            * Stop asking for what we were asking for.
+            */
+           if (go->neg_eap)
+               try_.neg_eap = 0;
+           else
+#endif /* EAP_SUPPORT */
+
+#if CHAP_SUPPORT
+           if (go->neg_chap)
+               try_.neg_chap = 0;
+           else
+#endif /* CHAP_SUPPORT */
+
+#if PAP_SUPPORT
+           if(1)
+               try_.neg_upap = 0;
+           else
+#endif /* PAP_SUPPORT */
+           {}
+
+           p += cilen - CILEN_SHORT;
+       }
+    }
+
+#if LQR_SUPPORT
+    /*
+     * If they can't cope with our link quality protocol, we'll have
+     * to stop asking for LQR.  We haven't got any other protocol.
+     * If they Nak the reporting period, take their value XXX ?
+     */
+    NAKCILQR(CI_QUALITY, neg_lqr,
+            if (cishort != PPP_LQR)
+                try_.neg_lqr = 0;
+            else
+                try_.lqr_period = cilong;
+            );
+#endif /* LQR_SUPPORT */
+
+    /*
+     * Only implementing CBCP...not the rest of the callback options
+     */
+    NAKCICHAR(CI_CALLBACK, neg_cbcp,
+              try_.neg_cbcp = 0;
+              (void)cichar; /* if CHAP support is not compiled, cichar is set but not used, which makes some compilers complaining */
+              );
+
+    /*
+     * Check for a looped-back line.
+     */
+    NAKCILONG(CI_MAGICNUMBER, neg_magicnumber,
+             try_.magicnumber = magic();
+             looped_back = 1;
+             );
+
+    /*
+     * Peer shouldn't send Nak for protocol compression or
+     * address/control compression requests; they should send
+     * a Reject instead.  If they send a Nak, treat it as a Reject.
+     */
+    NAKCIVOID(CI_PCOMPRESSION, neg_pcompression);
+    NAKCIVOID(CI_ACCOMPRESSION, neg_accompression);
+
+#ifdef HAVE_MULTILINK
+    /*
+     * Nak for MRRU option - accept their value if it is smaller
+     * than the one we want.
+     */
+    if (go->neg_mrru) {
+       NAKCISHORT(CI_MRRU, neg_mrru,
+                  if (treat_as_reject)
+                      try_.neg_mrru = 0;
+                  else if (cishort <= wo->mrru)
+                      try_.mrru = cishort;
+                  );
+    }
+#else /* HAVE_MULTILINK */
+    LWIP_UNUSED_ARG(treat_as_reject);
+#endif /* HAVE_MULTILINK */
+
+    /*
+     * Nak for short sequence numbers shouldn't be sent, treat it
+     * like a reject.
+     */
+    NAKCIVOID(CI_SSNHF, neg_ssnhf);
+
+    /*
+     * Nak of the endpoint discriminator option is not permitted,
+     * treat it like a reject.
+     */
+    NAKCIENDP(CI_EPDISC, neg_endpoint);
+
+    /*
+     * There may be remaining CIs, if the peer is requesting negotiation
+     * on an option that we didn't include in our request packet.
+     * If we see an option that we requested, or one we've already seen
+     * in this packet, then this packet is bad.
+     * If we wanted to respond by starting to negotiate on the requested
+     * option(s), we could, but we don't, because except for the
+     * authentication type and quality protocol, if we are not negotiating
+     * an option, it is because we were told not to.
+     * For the authentication type, the Nak from the peer means
+     * `let me authenticate myself with you' which is a bit pointless.
+     * For the quality protocol, the Nak means `ask me to send you quality
+     * reports', but if we didn't ask for them, we don't want them.
+     * An option we don't recognize represents the peer asking to
+     * negotiate some option we don't support, so ignore it.
+     */
+    while (len >= CILEN_VOID) {
+       GETCHAR(citype, p);
+       GETCHAR(cilen, p);
+       if (cilen < CILEN_VOID || (len -= cilen) < 0)
+           goto bad;
+       next = p + cilen - 2;
+
+       switch (citype) {
+       case CI_MRU:
+           if ((go->neg_mru && go->mru != PPP_DEFMRU)
+               || no.neg_mru || cilen != CILEN_SHORT)
+               goto bad;
+           GETSHORT(cishort, p);
+           if (cishort < PPP_DEFMRU) {
+               try_.neg_mru = 1;
+               try_.mru = cishort;
+           }
+           break;
+       case CI_ASYNCMAP:
+           if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF)
+               || no.neg_asyncmap || cilen != CILEN_LONG)
+               goto bad;
+           break;
+       case CI_AUTHTYPE:
+           if (0
+#if CHAP_SUPPORT
+                || go->neg_chap || no.neg_chap
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+                || go->neg_upap || no.neg_upap
+#endif /* PAP_SUPPORT */
+#if EAP_SUPPORT
+               || go->neg_eap || no.neg_eap
+#endif /* EAP_SUPPORT */
+               )
+               goto bad;
+           break;
+       case CI_MAGICNUMBER:
+           if (go->neg_magicnumber || no.neg_magicnumber ||
+               cilen != CILEN_LONG)
+               goto bad;
+           break;
+       case CI_PCOMPRESSION:
+           if (go->neg_pcompression || no.neg_pcompression
+               || cilen != CILEN_VOID)
+               goto bad;
+           break;
+       case CI_ACCOMPRESSION:
+           if (go->neg_accompression || no.neg_accompression
+               || cilen != CILEN_VOID)
+               goto bad;
+           break;
+#if LQR_SUPPORT
+       case CI_QUALITY:
+           if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR)
+               goto bad;
+           break;
+#endif /* LQR_SUPPORT */
+#ifdef HAVE_MULTILINK
+       case CI_MRRU:
+           if (go->neg_mrru || no.neg_mrru || cilen != CILEN_SHORT)
+               goto bad;
+           break;
+#endif /* HAVE_MULTILINK */
+       case CI_SSNHF:
+           if (go->neg_ssnhf || no.neg_ssnhf || cilen != CILEN_VOID)
+               goto bad;
+           try_.neg_ssnhf = 1;
+           break;
+       case CI_EPDISC:
+           if (go->neg_endpoint || no.neg_endpoint || cilen < CILEN_CHAR)
+               goto bad;
+           break;
+       default:
+           break;
+       }
+       p = next;
+    }
+
+    /*
+     * OK, the Nak is good.  Now we can update state.
+     * If there are any options left we ignore them.
+     */
+    if (f->state != PPP_FSM_OPENED) {
+       if (looped_back) {
+           if (++try_.numloops >= pcb->settings.lcp_loopbackfail) {
+               ppp_notice("Serial line is looped back.");
+               pcb->err_code = PPPERR_LOOPBACK;
+               lcp_close(f->pcb, "Loopback detected");
+           }
+       } else
+           try_.numloops = 0;
+       *go = try_;
+    }
+
+    return 1;
+
+bad:
+    LCPDEBUG(("lcp_nakci: received bad Nak!"));
+    return 0;
+}
+
+
+/*
+ * lcp_rejci - Peer has Rejected some of our CIs.
+ * This should not modify any state if the Reject is bad
+ * or if LCP is in the OPENED state.
+ *
+ * Returns:
+ *     0 - Reject was bad.
+ *     1 - Reject was good.
+ */
+static int lcp_rejci(fsm *f, u_char *p, int len) {
+    ppp_pcb *pcb = f->pcb;
+    lcp_options *go = &pcb->lcp_gotoptions;
+    u_char cichar;
+    u_short cishort;
+    u32_t cilong;
+    lcp_options try_;          /* options to request next time */
+
+    try_ = *go;
+
+    /*
+     * Any Rejected CIs must be in exactly the same order that we sent.
+     * Check packet length and CI length at each step.
+     * If we find any deviations, then this packet is bad.
+     */
+#define REJCIVOID(opt, neg) \
+    if (go->neg && \
+       len >= CILEN_VOID && \
+       p[1] == CILEN_VOID && \
+       p[0] == opt) { \
+       len -= CILEN_VOID; \
+       INCPTR(CILEN_VOID, p); \
+       try_.neg = 0; \
+    }
+#define REJCISHORT(opt, neg, val) \
+    if (go->neg && \
+       len >= CILEN_SHORT && \
+       p[1] == CILEN_SHORT && \
+       p[0] == opt) { \
+       len -= CILEN_SHORT; \
+       INCPTR(2, p); \
+       GETSHORT(cishort, p); \
+       /* Check rejected value. */ \
+       if (cishort != val) \
+           goto bad; \
+       try_.neg = 0; \
+    }
+
+#if CHAP_SUPPORT && EAP_SUPPORT && PAP_SUPPORT
+#define REJCICHAP(opt, neg, val) \
+    if (go->neg && \
+       len >= CILEN_CHAP && \
+       p[1] == CILEN_CHAP && \
+       p[0] == opt) { \
+       len -= CILEN_CHAP; \
+       INCPTR(2, p); \
+       GETSHORT(cishort, p); \
+       GETCHAR(cichar, p); \
+       /* Check rejected value. */ \
+       if ((cishort != PPP_CHAP) || (cichar != (CHAP_DIGEST(val)))) \
+           goto bad; \
+       try_.neg = 0; \
+       try_.neg_eap = try_.neg_upap = 0; \
+    }
+#endif /* CHAP_SUPPORT && EAP_SUPPORT && PAP_SUPPORT */
+
+#if CHAP_SUPPORT && !EAP_SUPPORT && PAP_SUPPORT
+#define REJCICHAP(opt, neg, val) \
+    if (go->neg && \
+       len >= CILEN_CHAP && \
+       p[1] == CILEN_CHAP && \
+       p[0] == opt) { \
+       len -= CILEN_CHAP; \
+       INCPTR(2, p); \
+       GETSHORT(cishort, p); \
+       GETCHAR(cichar, p); \
+       /* Check rejected value. */ \
+       if ((cishort != PPP_CHAP) || (cichar != (CHAP_DIGEST(val)))) \
+           goto bad; \
+       try_.neg = 0; \
+       try_.neg_upap = 0; \
+    }
+#endif /* CHAP_SUPPORT && !EAP_SUPPORT && PAP_SUPPORT */
+
+#if CHAP_SUPPORT && EAP_SUPPORT && !PAP_SUPPORT
+#define REJCICHAP(opt, neg, val) \
+    if (go->neg && \
+       len >= CILEN_CHAP && \
+       p[1] == CILEN_CHAP && \
+       p[0] == opt) { \
+       len -= CILEN_CHAP; \
+       INCPTR(2, p); \
+       GETSHORT(cishort, p); \
+       GETCHAR(cichar, p); \
+       /* Check rejected value. */ \
+       if ((cishort != PPP_CHAP) || (cichar != (CHAP_DIGEST(val)))) \
+           goto bad; \
+       try_.neg = 0; \
+       try_.neg_eap = 0; \
+    }
+#endif /* CHAP_SUPPORT && EAP_SUPPORT && !PAP_SUPPORT */
+
+#if CHAP_SUPPORT && !EAP_SUPPORT && !PAP_SUPPORT
+#define REJCICHAP(opt, neg, val) \
+    if (go->neg && \
+       len >= CILEN_CHAP && \
+       p[1] == CILEN_CHAP && \
+       p[0] == opt) { \
+       len -= CILEN_CHAP; \
+       INCPTR(2, p); \
+       GETSHORT(cishort, p); \
+       GETCHAR(cichar, p); \
+       /* Check rejected value. */ \
+       if ((cishort != PPP_CHAP) || (cichar != (CHAP_DIGEST(val)))) \
+           goto bad; \
+       try_.neg = 0; \
+    }
+#endif /* CHAP_SUPPORT && !EAP_SUPPORT && !PAP_SUPPORT */
+
+#define REJCILONG(opt, neg, val) \
+    if (go->neg && \
+       len >= CILEN_LONG && \
+       p[1] == CILEN_LONG && \
+       p[0] == opt) { \
+       len -= CILEN_LONG; \
+       INCPTR(2, p); \
+       GETLONG(cilong, p); \
+       /* Check rejected value. */ \
+       if (cilong != val) \
+           goto bad; \
+       try_.neg = 0; \
+    }
+#if LQR_SUPPORT
+#define REJCILQR(opt, neg, val) \
+    if (go->neg && \
+       len >= CILEN_LQR && \
+       p[1] == CILEN_LQR && \
+       p[0] == opt) { \
+       len -= CILEN_LQR; \
+       INCPTR(2, p); \
+       GETSHORT(cishort, p); \
+       GETLONG(cilong, p); \
+       /* Check rejected value. */ \
+       if (cishort != PPP_LQR || cilong != val) \
+           goto bad; \
+       try_.neg = 0; \
+    }
+#endif /* LQR_SUPPORT */
+#define REJCICBCP(opt, neg, val) \
+    if (go->neg && \
+       len >= CILEN_CBCP && \
+       p[1] == CILEN_CBCP && \
+       p[0] == opt) { \
+       len -= CILEN_CBCP; \
+       INCPTR(2, p); \
+       GETCHAR(cichar, p); \
+       /* Check rejected value. */ \
+       if (cichar != val) \
+           goto bad; \
+       try_.neg = 0; \
+    }
+#define REJCIENDP(opt, neg, class, val, vlen) \
+    if (go->neg && \
+       len >= CILEN_CHAR + vlen && \
+       p[0] == opt && \
+       p[1] == CILEN_CHAR + vlen) { \
+       int i; \
+       len -= CILEN_CHAR + vlen; \
+       INCPTR(2, p); \
+       GETCHAR(cichar, p); \
+       if (cichar != class) \
+           goto bad; \
+       for (i = 0; i < vlen; ++i) { \
+           GETCHAR(cichar, p); \
+           if (cichar != val[i]) \
+               goto bad; \
+       } \
+       try_.neg = 0; \
+    }
+
+    REJCISHORT(CI_MRU, neg_mru, go->mru);
+    REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap);
+#if EAP_SUPPORT
+    REJCISHORT(CI_AUTHTYPE, neg_eap, PPP_EAP);
+    if (!go->neg_eap) {
+#endif /* EAP_SUPPORT */
+#if CHAP_SUPPORT
+       REJCICHAP(CI_AUTHTYPE, neg_chap, go->chap_mdtype);
+       if (!go->neg_chap) {
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+           REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP);
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+       }
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+    }
+#endif /* EAP_SUPPORT */
+#if LQR_SUPPORT
+    REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period);
+#endif /* LQR_SUPPORT */
+    REJCICBCP(CI_CALLBACK, neg_cbcp, CBCP_OPT);
+    REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber);
+    REJCIVOID(CI_PCOMPRESSION, neg_pcompression);
+    REJCIVOID(CI_ACCOMPRESSION, neg_accompression);
+#ifdef HAVE_MULTILINK
+    REJCISHORT(CI_MRRU, neg_mrru, go->mrru);
+#endif /* HAVE_MULTILINK */
+    REJCIVOID(CI_SSNHF, neg_ssnhf);
+    REJCIENDP(CI_EPDISC, neg_endpoint, go->endpoint.class_,
+             go->endpoint.value, go->endpoint.length);
+
+    /*
+     * If there are any remaining CIs, then this packet is bad.
+     */
+    if (len != 0)
+       goto bad;
+    /*
+     * Now we can update state.
+     */
+    if (f->state != PPP_FSM_OPENED)
+       *go = try_;
+    return 1;
+
+bad:
+    LCPDEBUG(("lcp_rejci: received bad Reject!"));
+    return 0;
+}
+
+
+/*
+ * lcp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately.  If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ *
+ * inp = Requested CIs
+ * lenp = Length of requested CIs
+ */
+static int lcp_reqci(fsm *f, u_char *inp, int *lenp, int reject_if_disagree) {
+    ppp_pcb *pcb = f->pcb;
+    lcp_options *go = &pcb->lcp_gotoptions;
+    lcp_options *ho = &pcb->lcp_hisoptions;
+    lcp_options *ao = &pcb->lcp_allowoptions;
+    u_char *cip, *next;                /* Pointer to current and next CIs */
+    int cilen, citype, cichar; /* Parsed len, type, char value */
+    u_short cishort;           /* Parsed short value */
+    u32_t cilong;              /* Parse long value */
+    int rc = CONFACK;          /* Final packet return code */
+    int orc;                   /* Individual option return code */
+    u_char *p;                 /* Pointer to next char to parse */
+    u_char *rejp;              /* Pointer to next char in reject frame */
+    struct pbuf *nakp;          /* Nak buffer */
+    u_char *nakoutp;           /* Pointer to next char in Nak frame */
+    int l = *lenp;             /* Length left */
+
+    /*
+     * Reset all his options.
+     */
+    BZERO(ho, sizeof(*ho));
+
+    /*
+     * Process all his options.
+     */
+    next = inp;
+    nakp = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_CTRL_PBUF_MAX_SIZE), PPP_CTRL_PBUF_TYPE);
+    if(NULL == nakp)
+        return 0;
+    if(nakp->tot_len != nakp->len) {
+        pbuf_free(nakp);
+        return 0;
+    }
+
+    nakoutp = (u_char*)nakp->payload;
+    rejp = inp;
+    while (l) {
+       orc = CONFACK;                  /* Assume success */
+       cip = p = next;                 /* Remember begining of CI */
+       if (l < 2 ||                    /* Not enough data for CI header or */
+           p[1] < 2 ||                 /*  CI length too small or */
+           p[1] > l) {                 /*  CI length too big? */
+           LCPDEBUG(("lcp_reqci: bad CI length!"));
+           orc = CONFREJ;              /* Reject bad CI */
+           cilen = l;                  /* Reject till end of packet */
+           l = 0;                      /* Don't loop again */
+           citype = 0;
+           goto endswitch;
+       }
+       GETCHAR(citype, p);             /* Parse CI type */
+       GETCHAR(cilen, p);              /* Parse CI length */
+       l -= cilen;                     /* Adjust remaining length */
+       next += cilen;                  /* Step to next CI */
+
+       switch (citype) {               /* Check CI type */
+       case CI_MRU:
+           if (!ao->neg_mru ||         /* Allow option? */
+               cilen != CILEN_SHORT) { /* Check CI length */
+               orc = CONFREJ;          /* Reject CI */
+               break;
+           }
+           GETSHORT(cishort, p);       /* Parse MRU */
+
+           /*
+            * He must be able to receive at least our minimum.
+            * No need to check a maximum.  If he sends a large number,
+            * we'll just ignore it.
+            */
+           if (cishort < PPP_MINMRU) {
+               orc = CONFNAK;          /* Nak CI */
+               PUTCHAR(CI_MRU, nakoutp);
+               PUTCHAR(CILEN_SHORT, nakoutp);
+               PUTSHORT(PPP_MINMRU, nakoutp);  /* Give him a hint */
+               break;
+           }
+           ho->neg_mru = 1;            /* Remember he sent MRU */
+           ho->mru = cishort;          /* And remember value */
+           break;
+
+       case CI_ASYNCMAP:
+           if (!ao->neg_asyncmap ||
+               cilen != CILEN_LONG) {
+               orc = CONFREJ;
+               break;
+           }
+           GETLONG(cilong, p);
+
+           /*
+            * Asyncmap must have set at least the bits
+            * which are set in lcp_allowoptions[unit].asyncmap.
+            */
+           if ((ao->asyncmap & ~cilong) != 0) {
+               orc = CONFNAK;
+               PUTCHAR(CI_ASYNCMAP, nakoutp);
+               PUTCHAR(CILEN_LONG, nakoutp);
+               PUTLONG(ao->asyncmap | cilong, nakoutp);
+               break;
+           }
+           ho->neg_asyncmap = 1;
+           ho->asyncmap = cilong;
+           break;
+
+       case CI_AUTHTYPE:
+           if (cilen < CILEN_SHORT ||
+               !(0
+#if PAP_SUPPORT
+               || ao->neg_upap
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+               || ao->neg_chap
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+               || ao->neg_eap
+#endif /* EAP_SUPPORT */
+               )) {
+               /*
+                * Reject the option if we're not willing to authenticate.
+                */
+               ppp_dbglog("No auth is possible");
+               orc = CONFREJ;
+               break;
+           }
+           GETSHORT(cishort, p);
+
+           /*
+            * Authtype must be PAP, CHAP, or EAP.
+            *
+            * Note: if more than one of ao->neg_upap, ao->neg_chap, and
+            * ao->neg_eap are set, and the peer sends a Configure-Request
+            * with two or more authenticate-protocol requests, then we will
+            * reject the second request.
+            * Whether we end up doing CHAP, UPAP, or EAP depends then on
+            * the ordering of the CIs in the peer's Configure-Request.
+             */
+
+#if PAP_SUPPORT
+           if (cishort == PPP_PAP) {
+               /* we've already accepted CHAP or EAP */
+               if (0
+#if CHAP_SUPPORT
+                   || ho->neg_chap
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+                   || ho->neg_eap
+#endif /* EAP_SUPPORT */
+                   || cilen != CILEN_SHORT) {
+                   LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE PAP, rejecting..."));
+                   orc = CONFREJ;
+                   break;
+               }
+               if (!ao->neg_upap) {    /* we don't want to do PAP */
+                   orc = CONFNAK;      /* NAK it and suggest CHAP or EAP */
+                   PUTCHAR(CI_AUTHTYPE, nakoutp);
+#if EAP_SUPPORT
+                   if (ao->neg_eap) {
+                       PUTCHAR(CILEN_SHORT, nakoutp);
+                       PUTSHORT(PPP_EAP, nakoutp);
+                   } else {
+#endif /* EAP_SUPPORT */
+#if CHAP_SUPPORT
+                       PUTCHAR(CILEN_CHAP, nakoutp);
+                       PUTSHORT(PPP_CHAP, nakoutp);
+                       PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakoutp);
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+                   }
+#endif /* EAP_SUPPORT */
+                   break;
+               }
+               ho->neg_upap = 1;
+               break;
+           }
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+           if (cishort == PPP_CHAP) {
+               /* we've already accepted PAP or EAP */
+               if (
+#if PAP_SUPPORT
+                   ho->neg_upap ||
+#endif /* PAP_SUPPORT */
+#if EAP_SUPPORT
+                   ho->neg_eap ||
+#endif /* EAP_SUPPORT */
+                   cilen != CILEN_CHAP) {
+                   LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE CHAP, rejecting..."));
+                   orc = CONFREJ;
+                   break;
+               }
+               if (!ao->neg_chap) {    /* we don't want to do CHAP */
+                   orc = CONFNAK;      /* NAK it and suggest EAP or PAP */
+                   PUTCHAR(CI_AUTHTYPE, nakoutp);
+                   PUTCHAR(CILEN_SHORT, nakoutp);
+#if EAP_SUPPORT
+                   if (ao->neg_eap) {
+                       PUTSHORT(PPP_EAP, nakoutp);
+                   } else
+#endif /* EAP_SUPPORT */
+#if PAP_SUPPORT
+                   if(1) {
+                       PUTSHORT(PPP_PAP, nakoutp);
+                   }
+                   else
+#endif /* PAP_SUPPORT */
+                   {}
+                   break;
+               }
+               GETCHAR(cichar, p);     /* get digest type */
+               if (!(CHAP_CANDIGEST(ao->chap_mdtype, cichar))) {
+                   /*
+                    * We can't/won't do the requested type,
+                    * suggest something else.
+                    */
+                   orc = CONFNAK;
+                   PUTCHAR(CI_AUTHTYPE, nakoutp);
+                   PUTCHAR(CILEN_CHAP, nakoutp);
+                   PUTSHORT(PPP_CHAP, nakoutp);
+                   PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakoutp);
+                   break;
+               }
+               ho->chap_mdtype = CHAP_MDTYPE_D(cichar); /* save md type */
+               ho->neg_chap = 1;
+               break;
+           }
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+           if (cishort == PPP_EAP) {
+               /* we've already accepted CHAP or PAP */
+               if (
+#if CHAP_SUPPORT
+                   ho->neg_chap ||
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+                   ho->neg_upap ||
+#endif /* PAP_SUPPORT */
+                   cilen != CILEN_SHORT) {
+                   LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE EAP, rejecting..."));
+                   orc = CONFREJ;
+                   break;
+               }
+               if (!ao->neg_eap) {     /* we don't want to do EAP */
+                   orc = CONFNAK;      /* NAK it and suggest CHAP or PAP */
+                   PUTCHAR(CI_AUTHTYPE, nakoutp);
+#if CHAP_SUPPORT
+                   if (ao->neg_chap) {
+                       PUTCHAR(CILEN_CHAP, nakoutp);
+                       PUTSHORT(PPP_CHAP, nakoutp);
+                       PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakoutp);
+                   } else
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+                   if(1) {
+                       PUTCHAR(CILEN_SHORT, nakoutp);
+                       PUTSHORT(PPP_PAP, nakoutp);
+                   } else
+#endif /* PAP_SUPPORT */
+                   {}
+                   break;
+               }
+               ho->neg_eap = 1;
+               break;
+           }
+#endif /* EAP_SUPPORT */
+
+           /*
+            * We don't recognize the protocol they're asking for.
+            * Nak it with something we're willing to do.
+            * (At this point we know ao->neg_upap || ao->neg_chap ||
+            * ao->neg_eap.)
+            */
+           orc = CONFNAK;
+           PUTCHAR(CI_AUTHTYPE, nakoutp);
+
+#if EAP_SUPPORT
+           if (ao->neg_eap) {
+               PUTCHAR(CILEN_SHORT, nakoutp);
+               PUTSHORT(PPP_EAP, nakoutp);
+           } else
+#endif /* EAP_SUPPORT */
+#if CHAP_SUPPORT
+           if (ao->neg_chap) {
+               PUTCHAR(CILEN_CHAP, nakoutp);
+               PUTSHORT(PPP_CHAP, nakoutp);
+               PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakoutp);
+           } else
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+           if(1) {
+               PUTCHAR(CILEN_SHORT, nakoutp);
+               PUTSHORT(PPP_PAP, nakoutp);
+           } else
+#endif /* PAP_SUPPORT */
+           {}
+           break;
+
+#if LQR_SUPPORT
+       case CI_QUALITY:
+           if (!ao->neg_lqr ||
+               cilen != CILEN_LQR) {
+               orc = CONFREJ;
+               break;
+           }
+
+           GETSHORT(cishort, p);
+           GETLONG(cilong, p);
+
+           /*
+            * Check the protocol and the reporting period.
+            * XXX When should we Nak this, and what with?
+            */
+           if (cishort != PPP_LQR) {
+               orc = CONFNAK;
+               PUTCHAR(CI_QUALITY, nakoutp);
+               PUTCHAR(CILEN_LQR, nakoutp);
+               PUTSHORT(PPP_LQR, nakoutp);
+               PUTLONG(ao->lqr_period, nakoutp);
+               break;
+           }
+           break;
+#endif /* LQR_SUPPORT */
+
+       case CI_MAGICNUMBER:
+           if (!(ao->neg_magicnumber || go->neg_magicnumber) ||
+               cilen != CILEN_LONG) {
+               orc = CONFREJ;
+               break;
+           }
+           GETLONG(cilong, p);
+
+           /*
+            * He must have a different magic number.
+            */
+           if (go->neg_magicnumber &&
+               cilong == go->magicnumber) {
+               cilong = magic();       /* Don't put magic() inside macro! */
+               orc = CONFNAK;
+               PUTCHAR(CI_MAGICNUMBER, nakoutp);
+               PUTCHAR(CILEN_LONG, nakoutp);
+               PUTLONG(cilong, nakoutp);
+               break;
+           }
+           ho->neg_magicnumber = 1;
+           ho->magicnumber = cilong;
+           break;
+
+
+       case CI_PCOMPRESSION:
+           if (!ao->neg_pcompression ||
+               cilen != CILEN_VOID) {
+               orc = CONFREJ;
+               break;
+           }
+           ho->neg_pcompression = 1;
+           break;
+
+       case CI_ACCOMPRESSION:
+           if (!ao->neg_accompression ||
+               cilen != CILEN_VOID) {
+               orc = CONFREJ;
+               break;
+           }
+           ho->neg_accompression = 1;
+           break;
+
+#ifdef HAVE_MULTILINK
+       case CI_MRRU:
+           if (!ao->neg_mrru
+               || !multilink
+               || cilen != CILEN_SHORT) {
+               orc = CONFREJ;
+               break;
+           }
+
+           GETSHORT(cishort, p);
+           /* possibly should insist on a minimum/maximum MRRU here */
+           ho->neg_mrru = 1;
+           ho->mrru = cishort;
+           break;
+#endif /* HAVE_MULTILINK */
+
+       case CI_SSNHF:
+           if (!ao->neg_ssnhf
+#ifdef HAVE_MULTILINK
+               || !multilink
+#endif /* HAVE_MULTILINK */
+               || cilen != CILEN_VOID) {
+               orc = CONFREJ;
+               break;
+           }
+           ho->neg_ssnhf = 1;
+           break;
+
+       case CI_EPDISC:
+           if (!ao->neg_endpoint ||
+               cilen < CILEN_CHAR ||
+               cilen > CILEN_CHAR + MAX_ENDP_LEN) {
+               orc = CONFREJ;
+               break;
+           }
+           GETCHAR(cichar, p);
+           cilen -= CILEN_CHAR;
+           ho->neg_endpoint = 1;
+           ho->endpoint.class_ = cichar;
+           ho->endpoint.length = cilen;
+           MEMCPY(ho->endpoint.value, p, cilen);
+           INCPTR(cilen, p);
+           break;
+
+       default:
+           LCPDEBUG(("lcp_reqci: rcvd unknown option %d", citype));
+           orc = CONFREJ;
+           break;
+       }
+
+endswitch:
+       if (orc == CONFACK &&           /* Good CI */
+           rc != CONFACK)              /*  but prior CI wasnt? */
+           continue;                   /* Don't send this one */
+
+       if (orc == CONFNAK) {           /* Nak this CI? */
+           if (reject_if_disagree      /* Getting fed up with sending NAKs? */
+               && citype != CI_MAGICNUMBER) {
+               orc = CONFREJ;          /* Get tough if so */
+           } else {
+               if (rc == CONFREJ)      /* Rejecting prior CI? */
+                   continue;           /* Don't send this one */
+               rc = CONFNAK;
+           }
+       }
+       if (orc == CONFREJ) {           /* Reject this CI */
+           rc = CONFREJ;
+           if (cip != rejp)            /* Need to move rejected CI? */
+               MEMCPY(rejp, cip, cilen); /* Move it */
+           INCPTR(cilen, rejp);        /* Update output pointer */
+       }
+    }
+
+    /*
+     * If we wanted to send additional NAKs (for unsent CIs), the
+     * code would go here.  The extra NAKs would go at *nakoutp.
+     * At present there are no cases where we want to ask the
+     * peer to negotiate an option.
+     */
+
+    switch (rc) {
+    case CONFACK:
+       *lenp = next - inp;
+       break;
+    case CONFNAK:
+       /*
+        * Copy the Nak'd options from the nak buffer to the caller's buffer.
+        */
+       *lenp = nakoutp - (u_char*)nakp->payload;
+       MEMCPY(inp, nakp->payload, *lenp);
+       break;
+    case CONFREJ:
+       *lenp = rejp - inp;
+       break;
+    default:
+       break;
+    }
+
+    pbuf_free(nakp);
+    LCPDEBUG(("lcp_reqci: returning CONF%s.", CODENAME(rc)));
+    return (rc);                       /* Return final code */
+}
+
+
+/*
+ * lcp_up - LCP has come UP.
+ */
+static void lcp_up(fsm *f) {
+    ppp_pcb *pcb = f->pcb;
+    lcp_options *wo = &pcb->lcp_wantoptions;
+    lcp_options *ho = &pcb->lcp_hisoptions;
+    lcp_options *go = &pcb->lcp_gotoptions;
+    lcp_options *ao = &pcb->lcp_allowoptions;
+    int mtu, mru;
+
+    if (!go->neg_magicnumber)
+       go->magicnumber = 0;
+    if (!ho->neg_magicnumber)
+       ho->magicnumber = 0;
+
+    /*
+     * Set our MTU to the smaller of the MTU we wanted and
+     * the MRU our peer wanted.  If we negotiated an MRU,
+     * set our MRU to the larger of value we wanted and
+     * the value we got in the negotiation.
+     * Note on the MTU: the link MTU can be the MRU the peer wanted,
+     * the interface MTU is set to the lowest of that, the
+     * MTU we want to use, and our link MRU.
+     */
+    mtu = ho->neg_mru? ho->mru: PPP_MRU;
+    mru = go->neg_mru? LWIP_MAX(wo->mru, go->mru): PPP_MRU;
+#ifdef HAVE_MULTILINK
+    if (!(multilink && go->neg_mrru && ho->neg_mrru))
+#endif /* HAVE_MULTILINK */
+       netif_set_mtu(pcb, LWIP_MIN(LWIP_MIN(mtu, mru), ao->mru));
+    ppp_send_config(pcb, mtu,
+                   (ho->neg_asyncmap? ho->asyncmap: 0xffffffff),
+                   ho->neg_pcompression, ho->neg_accompression);
+    ppp_recv_config(pcb, mru,
+                   (pcb->settings.lax_recv? 0: go->neg_asyncmap? go->asyncmap: 0xffffffff),
+                   go->neg_pcompression, go->neg_accompression);
+
+    if (ho->neg_mru)
+       pcb->peer_mru = ho->mru;
+
+    lcp_echo_lowerup(f->pcb);  /* Enable echo messages */
+
+    link_established(pcb);
+}
+
+
+/*
+ * lcp_down - LCP has gone DOWN.
+ *
+ * Alert other protocols.
+ */
+static void lcp_down(fsm *f) {
+    ppp_pcb *pcb = f->pcb;
+    lcp_options *go = &pcb->lcp_gotoptions;
+
+    lcp_echo_lowerdown(f->pcb);
+
+    link_down(pcb);
+
+    ppp_send_config(pcb, PPP_MRU, 0xffffffff, 0, 0);
+    ppp_recv_config(pcb, PPP_MRU,
+                   (go->neg_asyncmap? go->asyncmap: 0xffffffff),
+                   go->neg_pcompression, go->neg_accompression);
+    pcb->peer_mru = PPP_MRU;
+}
+
+
+/*
+ * lcp_starting - LCP needs the lower layer up.
+ */
+static void lcp_starting(fsm *f) {
+    ppp_pcb *pcb = f->pcb;
+    link_required(pcb);
+}
+
+
+/*
+ * lcp_finished - LCP has finished with the lower layer.
+ */
+static void lcp_finished(fsm *f) {
+    ppp_pcb *pcb = f->pcb;
+    link_terminated(pcb);
+}
+
+
+#if PRINTPKT_SUPPORT
+/*
+ * lcp_printpkt - print the contents of an LCP packet.
+ */
+static const char* const lcp_codenames[] = {
+    "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+    "TermReq", "TermAck", "CodeRej", "ProtRej",
+    "EchoReq", "EchoRep", "DiscReq", "Ident",
+    "TimeRem"
+};
+
+static int lcp_printpkt(const u_char *p, int plen,
+               void (*printer) (void *, const char *, ...), void *arg) {
+    int code, id, len, olen, i;
+    const u_char *pstart, *optend;
+    u_short cishort;
+    u32_t cilong;
+
+    if (plen < HEADERLEN)
+       return 0;
+    pstart = p;
+    GETCHAR(code, p);
+    GETCHAR(id, p);
+    GETSHORT(len, p);
+    if (len < HEADERLEN || len > plen)
+       return 0;
+
+   if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(lcp_codenames))
+       printer(arg, " %s", lcp_codenames[code-1]);
+    else
+       printer(arg, " code=0x%x", code);
+    printer(arg, " id=0x%x", id);
+    len -= HEADERLEN;
+    switch (code) {
+    case CONFREQ:
+    case CONFACK:
+    case CONFNAK:
+    case CONFREJ:
+       /* print option list */
+       while (len >= 2) {
+           GETCHAR(code, p);
+           GETCHAR(olen, p);
+           p -= 2;
+           if (olen < 2 || olen > len) {
+               break;
+           }
+           printer(arg, " <");
+           len -= olen;
+           optend = p + olen;
+           switch (code) {
+           case CI_MRU:
+               if (olen == CILEN_SHORT) {
+                   p += 2;
+                   GETSHORT(cishort, p);
+                   printer(arg, "mru %d", cishort);
+               }
+               break;
+           case CI_ASYNCMAP:
+               if (olen == CILEN_LONG) {
+                   p += 2;
+                   GETLONG(cilong, p);
+                   printer(arg, "asyncmap 0x%x", cilong);
+               }
+               break;
+           case CI_AUTHTYPE:
+               if (olen >= CILEN_SHORT) {
+                   p += 2;
+                   printer(arg, "auth ");
+                   GETSHORT(cishort, p);
+                   switch (cishort) {
+#if PAP_SUPPORT
+                   case PPP_PAP:
+                       printer(arg, "pap");
+                       break;
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+                   case PPP_CHAP:
+                       printer(arg, "chap");
+                       if (p < optend) {
+                           switch (*p) {
+                           case CHAP_MD5:
+                               printer(arg, " MD5");
+                               ++p;
+                               break;
+#if MSCHAP_SUPPORT
+                           case CHAP_MICROSOFT:
+                               printer(arg, " MS");
+                               ++p;
+                               break;
+
+                           case CHAP_MICROSOFT_V2:
+                               printer(arg, " MS-v2");
+                               ++p;
+                               break;
+#endif /* MSCHAP_SUPPORT */
+                           default:
+                               break;
+                           }
+                       }
+                       break;
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+                   case PPP_EAP:
+                       printer(arg, "eap");
+                       break;
+#endif /* EAP_SUPPORT */
+                   default:
+                       printer(arg, "0x%x", cishort);
+                   }
+               }
+               break;
+#if LQR_SUPPORT
+           case CI_QUALITY:
+               if (olen >= CILEN_SHORT) {
+                   p += 2;
+                   printer(arg, "quality ");
+                   GETSHORT(cishort, p);
+                   switch (cishort) {
+                   case PPP_LQR:
+                       printer(arg, "lqr");
+                       break;
+                   default:
+                       printer(arg, "0x%x", cishort);
+                   }
+               }
+               break;
+#endif /* LQR_SUPPORT */
+           case CI_CALLBACK:
+               if (olen >= CILEN_CHAR) {
+                   p += 2;
+                   printer(arg, "callback ");
+                   GETCHAR(cishort, p);
+                   switch (cishort) {
+                   case CBCP_OPT:
+                       printer(arg, "CBCP");
+                       break;
+                   default:
+                       printer(arg, "0x%x", cishort);
+                   }
+               }
+               break;
+           case CI_MAGICNUMBER:
+               if (olen == CILEN_LONG) {
+                   p += 2;
+                   GETLONG(cilong, p);
+                   printer(arg, "magic 0x%x", cilong);
+               }
+               break;
+           case CI_PCOMPRESSION:
+               if (olen == CILEN_VOID) {
+                   p += 2;
+                   printer(arg, "pcomp");
+               }
+               break;
+           case CI_ACCOMPRESSION:
+               if (olen == CILEN_VOID) {
+                   p += 2;
+                   printer(arg, "accomp");
+               }
+               break;
+           case CI_MRRU:
+               if (olen == CILEN_SHORT) {
+                   p += 2;
+                   GETSHORT(cishort, p);
+                   printer(arg, "mrru %d", cishort);
+               }
+               break;
+           case CI_SSNHF:
+               if (olen == CILEN_VOID) {
+                   p += 2;
+                   printer(arg, "ssnhf");
+               }
+               break;
+           case CI_EPDISC:
+#ifdef HAVE_MULTILINK
+               if (olen >= CILEN_CHAR) {
+                   struct epdisc epd;
+                   p += 2;
+                   GETCHAR(epd.class, p);
+                   epd.length = olen - CILEN_CHAR;
+                   if (epd.length > MAX_ENDP_LEN)
+                       epd.length = MAX_ENDP_LEN;
+                   if (epd.length > 0) {
+                       MEMCPY(epd.value, p, epd.length);
+                       p += epd.length;
+                   }
+                   printer(arg, "endpoint [%s]", epdisc_to_str(&epd));
+               }
+#else
+               printer(arg, "endpoint");
+#endif
+               break;
+           default:
+               break;
+           }
+           while (p < optend) {
+               GETCHAR(code, p);
+               printer(arg, " %.2x", code);
+           }
+           printer(arg, ">");
+       }
+       break;
+
+    case TERMACK:
+    case TERMREQ:
+       if (len > 0 && *p >= ' ' && *p < 0x7f) {
+           printer(arg, " ");
+           ppp_print_string(p, len, printer, arg);
+           p += len;
+           len = 0;
+       }
+       break;
+
+    case ECHOREQ:
+    case ECHOREP:
+    case DISCREQ:
+       if (len >= 4) {
+           GETLONG(cilong, p);
+           printer(arg, " magic=0x%x", cilong);
+           len -= 4;
+       }
+       break;
+
+    case IDENTIF:
+    case TIMEREM:
+       if (len >= 4) {
+           GETLONG(cilong, p);
+           printer(arg, " magic=0x%x", cilong);
+           len -= 4;
+       }
+       if (code == TIMEREM) {
+           if (len < 4)
+               break;
+           GETLONG(cilong, p);
+           printer(arg, " seconds=%u", cilong);
+           len -= 4;
+       }
+       if (len > 0) {
+           printer(arg, " ");
+           ppp_print_string(p, len, printer, arg);
+           p += len;
+           len = 0;
+       }
+       break;
+    default:
+       break;
+    }
+
+    /* print the rest of the bytes in the packet */
+    for (i = 0; i < len && i < 32; ++i) {
+       GETCHAR(code, p);
+       printer(arg, " %.2x", code);
+    }
+    if (i < len) {
+       printer(arg, " ...");
+       p += len - i;
+    }
+
+    return p - pstart;
+}
+#endif /* PRINTPKT_SUPPORT */
+
+/*
+ * Time to shut down the link because there is nothing out there.
+ */
+
+static void LcpLinkFailure(fsm *f) {
+    ppp_pcb *pcb = f->pcb;
+    if (f->state == PPP_FSM_OPENED) {
+       ppp_info("No response to %d echo-requests", pcb->lcp_echos_pending);
+        ppp_notice("Serial link appears to be disconnected.");
+       pcb->err_code = PPPERR_PEERDEAD;
+       lcp_close(pcb, "Peer not responding");
+    }
+}
+
+/*
+ * Timer expired for the LCP echo requests from this process.
+ */
+
+static void LcpEchoCheck(fsm *f) {
+    ppp_pcb *pcb = f->pcb;
+
+    LcpSendEchoRequest (f);
+    if (f->state != PPP_FSM_OPENED)
+       return;
+
+    /*
+     * Start the timer for the next interval.
+     */
+    if (pcb->lcp_echo_timer_running)
+       ppp_warn("assertion lcp_echo_timer_running==0 failed");
+    TIMEOUT (LcpEchoTimeout, f, pcb->settings.lcp_echo_interval);
+    pcb->lcp_echo_timer_running = 1;
+}
+
+/*
+ * LcpEchoTimeout - Timer expired on the LCP echo
+ */
+
+static void LcpEchoTimeout(void *arg) {
+    fsm *f = (fsm*)arg;
+    ppp_pcb *pcb = f->pcb;
+    if (pcb->lcp_echo_timer_running != 0) {
+        pcb->lcp_echo_timer_running = 0;
+        LcpEchoCheck ((fsm *) arg);
+    }
+}
+
+/*
+ * LcpEchoReply - LCP has received a reply to the echo
+ */
+
+static void lcp_received_echo_reply(fsm *f, int id, u_char *inp, int len) {
+    ppp_pcb *pcb = f->pcb;
+    lcp_options *go = &pcb->lcp_gotoptions;
+    u32_t magic_val;
+    LWIP_UNUSED_ARG(id);
+
+    /* Check the magic number - don't count replies from ourselves. */
+    if (len < 4) {
+       ppp_dbglog("lcp: received short Echo-Reply, length %d", len);
+       return;
+    }
+    GETLONG(magic_val, inp);
+    if (go->neg_magicnumber
+       && magic_val == go->magicnumber) {
+       ppp_warn("appear to have received our own echo-reply!");
+       return;
+    }
+
+    /* Reset the number of outstanding echo frames */
+    pcb->lcp_echos_pending = 0;
+}
+
+/*
+ * LcpSendEchoRequest - Send an echo request frame to the peer
+ */
+
+static void LcpSendEchoRequest(fsm *f) {
+    ppp_pcb *pcb = f->pcb;
+    lcp_options *go = &pcb->lcp_gotoptions;
+    u32_t lcp_magic;
+    u_char pkt[4], *pktp;
+
+    /*
+     * Detect the failure of the peer at this point.
+     */
+    if (pcb->settings.lcp_echo_fails != 0) {
+        if (pcb->lcp_echos_pending >= pcb->settings.lcp_echo_fails) {
+            LcpLinkFailure(f);
+            pcb->lcp_echos_pending = 0;
+       }
+    }
+
+#if PPP_LCP_ADAPTIVE
+    /*
+     * If adaptive echos have been enabled, only send the echo request if
+     * no traffic was received since the last one.
+     */
+    if (pcb->settings.lcp_echo_adaptive) {
+       static unsigned int last_pkts_in = 0;
+
+#if PPP_STATS_SUPPORT
+       update_link_stats(f->unit);
+       link_stats_valid = 0;
+#endif /* PPP_STATS_SUPPORT */
+
+       if (link_stats.pkts_in != last_pkts_in) {
+           last_pkts_in = link_stats.pkts_in;
+           return;
+       }
+    }
+#endif
+
+    /*
+     * Make and send the echo request frame.
+     */
+    if (f->state == PPP_FSM_OPENED) {
+        lcp_magic = go->magicnumber;
+       pktp = pkt;
+       PUTLONG(lcp_magic, pktp);
+        fsm_sdata(f, ECHOREQ, pcb->lcp_echo_number++, pkt, pktp - pkt);
+       ++pcb->lcp_echos_pending;
+    }
+}
+
+/*
+ * lcp_echo_lowerup - Start the timer for the LCP frame
+ */
+
+static void lcp_echo_lowerup(ppp_pcb *pcb) {
+    fsm *f = &pcb->lcp_fsm;
+
+    /* Clear the parameters for generating echo frames */
+    pcb->lcp_echos_pending      = 0;
+    pcb->lcp_echo_number        = 0;
+    pcb->lcp_echo_timer_running = 0;
+  
+    /* If a timeout interval is specified then start the timer */
+    if (pcb->settings.lcp_echo_interval != 0)
+        LcpEchoCheck (f);
+}
+
+/*
+ * lcp_echo_lowerdown - Stop the timer for the LCP frame
+ */
+
+static void lcp_echo_lowerdown(ppp_pcb *pcb) {
+    fsm *f = &pcb->lcp_fsm;
+
+    if (pcb->lcp_echo_timer_running != 0) {
+        UNTIMEOUT (LcpEchoTimeout, f);
+        pcb->lcp_echo_timer_running = 0;
+    }
+}
+
+#endif /* PPP_SUPPORT */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/magic.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/magic.c
new file mode 100644 (file)
index 0000000..d0d87c5
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * magic.c - PPP Magic Number routines.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * 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.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*****************************************************************************
+* randm.c - Random number generator program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* Copyright (c) 1998 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE 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 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.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+*   Ported to lwIP.
+* 98-06-03 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+*   Extracted from avos.
+*****************************************************************************/
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "netif/ppp/ppp_impl.h"
+#include "netif/ppp/magic.h"
+
+#if PPP_MD5_RANDM /* Using MD5 for better randomness if enabled */
+
+#include "netif/ppp/pppcrypt.h"
+
+#define MD5_HASH_SIZE 16
+static char magic_randpool[MD5_HASH_SIZE];   /* Pool of randomness. */
+static long magic_randcount;      /* Pseudo-random incrementer */
+static u32_t magic_randomseed;    /* Seed used for random number generation. */
+
+/*
+ * Churn the randomness pool on a random event.  Call this early and often
+ *  on random and semi-random system events to build randomness in time for
+ *  usage.  For randomly timed events, pass a null pointer and a zero length
+ *  and this will use the system timer and other sources to add randomness.
+ *  If new random data is available, pass a pointer to that and it will be
+ *  included.
+ *
+ * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427
+ */
+static void magic_churnrand(char *rand_data, u32_t rand_len) {
+  lwip_md5_context md5_ctx;
+
+  /* LWIP_DEBUGF(LOG_INFO, ("magic_churnrand: %u@%P\n", rand_len, rand_data)); */
+  lwip_md5_init(&md5_ctx);
+  lwip_md5_starts(&md5_ctx);
+  lwip_md5_update(&md5_ctx, (u_char *)magic_randpool, sizeof(magic_randpool));
+  if (rand_data) {
+    lwip_md5_update(&md5_ctx, (u_char *)rand_data, rand_len);
+  } else {
+    struct {
+      /* INCLUDE fields for any system sources of randomness */
+      u32_t jiffies;
+#ifdef LWIP_RAND
+      u32_t rand;
+#endif /* LWIP_RAND */
+    } sys_data;
+    magic_randomseed += sys_jiffies();
+    sys_data.jiffies = magic_randomseed;
+#ifdef LWIP_RAND
+    sys_data.rand = LWIP_RAND();
+#endif /* LWIP_RAND */
+    /* Load sys_data fields here. */
+    lwip_md5_update(&md5_ctx, (u_char *)&sys_data, sizeof(sys_data));
+  }
+  lwip_md5_finish(&md5_ctx, (u_char *)magic_randpool);
+  lwip_md5_free(&md5_ctx);
+/*  LWIP_DEBUGF(LOG_INFO, ("magic_churnrand: -> 0\n")); */
+}
+
+/*
+ * Initialize the random number generator.
+ */
+void magic_init(void) {
+  magic_churnrand(NULL, 0);
+}
+
+/*
+ * Randomize our random seed value.
+ */
+void magic_randomize(void) {
+  magic_churnrand(NULL, 0);
+}
+
+/*
+ * magic_random_bytes - Fill a buffer with random bytes.
+ *
+ * Use the random pool to generate random data.  This degrades to pseudo
+ *  random when used faster than randomness is supplied using magic_churnrand().
+ * Note: It's important that there be sufficient randomness in magic_randpool
+ *  before this is called for otherwise the range of the result may be
+ *  narrow enough to make a search feasible.
+ *
+ * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427
+ *
+ * XXX Why does he not just call magic_churnrand() for each block?  Probably
+ *  so that you don't ever publish the seed which could possibly help
+ *  predict future values.
+ * XXX Why don't we preserve md5 between blocks and just update it with
+ *  magic_randcount each time?  Probably there is a weakness but I wish that
+ *  it was documented.
+ */
+void magic_random_bytes(unsigned char *buf, u32_t buf_len) {
+  lwip_md5_context md5_ctx;
+  u_char tmp[MD5_HASH_SIZE];
+  u32_t n;
+
+  while (buf_len > 0) {
+    lwip_md5_init(&md5_ctx);
+    lwip_md5_starts(&md5_ctx);
+    lwip_md5_update(&md5_ctx, (u_char *)magic_randpool, sizeof(magic_randpool));
+    lwip_md5_update(&md5_ctx, (u_char *)&magic_randcount, sizeof(magic_randcount));
+    lwip_md5_finish(&md5_ctx, tmp);
+    lwip_md5_free(&md5_ctx);
+    magic_randcount++;
+    n = LWIP_MIN(buf_len, MD5_HASH_SIZE);
+    MEMCPY(buf, tmp, n);
+    buf += n;
+    buf_len -= n;
+  }
+}
+
+/*
+ * Return a new random number.
+ */
+u32_t magic(void) {
+  u32_t new_rand;
+
+  magic_random_bytes((unsigned char *)&new_rand, sizeof(new_rand));
+
+  return new_rand;
+}
+
+#else /* PPP_MD5_RANDM */
+
+/*****************************/
+/*** LOCAL DATA STRUCTURES ***/
+/*****************************/
+#ifndef LWIP_RAND
+static int  magic_randomized;       /* Set when truely randomized. */
+#endif /* LWIP_RAND */
+static u32_t magic_randomseed;      /* Seed used for random number generation. */
+
+
+/***********************************/
+/*** PUBLIC FUNCTION DEFINITIONS ***/
+/***********************************/
+
+/*
+ * Initialize the random number generator.
+ *
+ * Here we attempt to compute a random number seed but even if
+ * it isn't random, we'll randomize it later.
+ *
+ * The current method uses the fields from the real time clock,
+ * the idle process counter, the millisecond counter, and the
+ * hardware timer tick counter.  When this is invoked
+ * in startup(), then the idle counter and timer values may
+ * repeat after each boot and the real time clock may not be
+ * operational.  Thus we call it again on the first random
+ * event.
+ */
+void magic_init(void) {
+  magic_randomseed += sys_jiffies();
+#ifndef LWIP_RAND
+  /* Initialize the Borland random number generator. */
+  srand((unsigned)magic_randomseed);
+#endif /* LWIP_RAND */
+}
+
+/*
+ * magic_init - Initialize the magic number generator.
+ *
+ * Randomize our random seed value.  Here we use the fact that
+ * this function is called at *truely random* times by the polling
+ * and network functions.  Here we only get 16 bits of new random
+ * value but we use the previous value to randomize the other 16
+ * bits.
+ */
+void magic_randomize(void) {
+#ifndef LWIP_RAND
+  if (!magic_randomized) {
+    magic_randomized = !0;
+    magic_init();
+    /* The initialization function also updates the seed. */
+  } else {
+#endif /* LWIP_RAND */
+    magic_randomseed += sys_jiffies();
+#ifndef LWIP_RAND
+  }
+#endif /* LWIP_RAND */
+}
+
+/*
+ * Return a new random number.
+ *
+ * Here we use the Borland rand() function to supply a pseudo random
+ * number which we make truely random by combining it with our own
+ * seed which is randomized by truely random events.
+ * Thus the numbers will be truely random unless there have been no
+ * operator or network events in which case it will be pseudo random
+ * seeded by the real time clock.
+ */
+u32_t magic(void) {
+#ifdef LWIP_RAND
+  return LWIP_RAND() + magic_randomseed;
+#else /* LWIP_RAND */
+  return ((u32_t)rand() << 16) + (u32_t)rand() + magic_randomseed;
+#endif /* LWIP_RAND */
+}
+
+/*
+ * magic_random_bytes - Fill a buffer with random bytes.
+ */
+void magic_random_bytes(unsigned char *buf, u32_t buf_len) {
+  u32_t new_rand, n;
+
+  while (buf_len > 0) {
+    new_rand = magic();
+    n = LWIP_MIN(buf_len, sizeof(new_rand));
+    MEMCPY(buf, &new_rand, n);
+    buf += n;
+    buf_len -= n;
+  }
+}
+#endif /* PPP_MD5_RANDM */
+
+/*
+ * Return a new random number between 0 and (2^pow)-1 included.
+ */
+u32_t magic_pow(u8_t pow) {
+  return magic() & ~(~0UL<<pow);
+}
+
+#endif /* PPP_SUPPORT */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/mppe.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/mppe.c
new file mode 100644 (file)
index 0000000..4cca89d
--- /dev/null
@@ -0,0 +1,412 @@
+/*
+ * mppe.c - interface MPPE to the PPP code.
+ *
+ * By Frank Cusack <fcusack@fcusack.com>.
+ * Copyright (c) 2002,2003,2004 Google, Inc.
+ * All rights reserved.
+ *
+ * License:
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies.  This software is provided without any
+ * warranty, express or implied.
+ *
+ * Changelog:
+ *      08/12/05 - Matt Domsch <Matt_Domsch@dell.com>
+ *                 Only need extra skb padding on transmit, not receive.
+ *      06/18/04 - Matt Domsch <Matt_Domsch@dell.com>, Oleg Makarenko <mole@quadra.ru>
+ *                 Use Linux kernel 2.6 arc4 and sha1 routines rather than
+ *                 providing our own.
+ *      2/15/04 - TS: added #include <version.h> and testing for Kernel
+ *                    version before using
+ *                    MOD_DEC_USAGE_COUNT/MOD_INC_USAGE_COUNT which are
+ *                    deprecated in 2.6
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && MPPE_SUPPORT  /* don't build if not configured for use in lwipopts.h */
+
+#include <string.h>
+
+#include "lwip/err.h"
+
+#include "netif/ppp/ppp_impl.h"
+#include "netif/ppp/ccp.h"
+#include "netif/ppp/mppe.h"
+#include "netif/ppp/pppdebug.h"
+#include "netif/ppp/pppcrypt.h"
+
+#define SHA1_SIGNATURE_SIZE 20
+
+/* ppp_mppe_state.bits definitions */
+#define MPPE_BIT_A     0x80    /* Encryption table were (re)inititalized */
+#define MPPE_BIT_B     0x40    /* MPPC only (not implemented) */
+#define MPPE_BIT_C     0x20    /* MPPC only (not implemented) */
+#define MPPE_BIT_D     0x10    /* This is an encrypted frame */
+
+#define MPPE_BIT_FLUSHED       MPPE_BIT_A
+#define MPPE_BIT_ENCRYPTED     MPPE_BIT_D
+
+#define MPPE_BITS(p) ((p)[0] & 0xf0)
+#define MPPE_CCOUNT(p) ((((p)[0] & 0x0f) << 8) + (p)[1])
+#define MPPE_CCOUNT_SPACE 0x1000       /* The size of the ccount space */
+
+#define MPPE_OVHD      2       /* MPPE overhead/packet */
+#define SANITY_MAX     1600    /* Max bogon factor we will tolerate */
+
+/*
+ * Perform the MPPE rekey algorithm, from RFC 3078, sec. 7.3.
+ * Well, not what's written there, but rather what they meant.
+ */
+static void mppe_rekey(ppp_mppe_state * state, int initial_key)
+{
+       lwip_sha1_context sha1_ctx;
+       u8_t sha1_digest[SHA1_SIGNATURE_SIZE];
+
+       /*
+        * Key Derivation, from RFC 3078, RFC 3079.
+        * Equivalent to Get_Key() for MS-CHAP as described in RFC 3079.
+        */
+       lwip_sha1_init(&sha1_ctx);
+       lwip_sha1_starts(&sha1_ctx);
+       lwip_sha1_update(&sha1_ctx, state->master_key, state->keylen);
+       lwip_sha1_update(&sha1_ctx, mppe_sha1_pad1, SHA1_PAD_SIZE);
+       lwip_sha1_update(&sha1_ctx, state->session_key, state->keylen);
+       lwip_sha1_update(&sha1_ctx, mppe_sha1_pad2, SHA1_PAD_SIZE);
+       lwip_sha1_finish(&sha1_ctx, sha1_digest);
+       lwip_sha1_free(&sha1_ctx);
+       MEMCPY(state->session_key, sha1_digest, state->keylen);
+
+       if (!initial_key) {
+               lwip_arc4_init(&state->arc4);
+               lwip_arc4_setup(&state->arc4, sha1_digest, state->keylen);
+               lwip_arc4_crypt(&state->arc4, state->session_key, state->keylen);
+               lwip_arc4_free(&state->arc4);
+       }
+       if (state->keylen == 8) {
+               /* See RFC 3078 */
+               state->session_key[0] = 0xd1;
+               state->session_key[1] = 0x26;
+               state->session_key[2] = 0x9e;
+       }
+       lwip_arc4_init(&state->arc4);
+       lwip_arc4_setup(&state->arc4, state->session_key, state->keylen);
+}
+
+/*
+ * Set key, used by MSCHAP before mppe_init() is actually called by CCP so we
+ * don't have to keep multiple copies of keys.
+ */
+void mppe_set_key(ppp_pcb *pcb, ppp_mppe_state *state, u8_t *key) {
+       LWIP_UNUSED_ARG(pcb);
+       MEMCPY(state->master_key, key, MPPE_MAX_KEY_LEN);
+}
+
+/*
+ * Initialize (de)compressor state.
+ */
+void
+mppe_init(ppp_pcb *pcb, ppp_mppe_state *state, u8_t options)
+{
+#if PPP_DEBUG
+       const u8_t *debugstr = (const u8_t*)"mppe_comp_init";
+       if (&pcb->mppe_decomp == state) {
+           debugstr = (const u8_t*)"mppe_decomp_init";
+       }
+#endif /* PPP_DEBUG */
+
+       /* Save keys. */
+       MEMCPY(state->session_key, state->master_key, sizeof(state->master_key));
+
+       if (options & MPPE_OPT_128)
+               state->keylen = 16;
+       else if (options & MPPE_OPT_40)
+               state->keylen = 8;
+       else {
+               PPPDEBUG(LOG_DEBUG, ("%s[%d]: unknown key length\n", debugstr,
+                       pcb->netif->num));
+               lcp_close(pcb, "MPPE required but peer negotiation failed");
+               return;
+       }
+       if (options & MPPE_OPT_STATEFUL)
+               state->stateful = 1;
+
+       /* Generate the initial session key. */
+       mppe_rekey(state, 1);
+
+#if PPP_DEBUG
+       {
+               int i;
+               char mkey[sizeof(state->master_key) * 2 + 1];
+               char skey[sizeof(state->session_key) * 2 + 1];
+
+               PPPDEBUG(LOG_DEBUG, ("%s[%d]: initialized with %d-bit %s mode\n",
+                      debugstr, pcb->netif->num, (state->keylen == 16) ? 128 : 40,
+                      (state->stateful) ? "stateful" : "stateless"));
+
+               for (i = 0; i < (int)sizeof(state->master_key); i++)
+                       sprintf(mkey + i * 2, "%02x", state->master_key[i]);
+               for (i = 0; i < (int)sizeof(state->session_key); i++)
+                       sprintf(skey + i * 2, "%02x", state->session_key[i]);
+               PPPDEBUG(LOG_DEBUG,
+                      ("%s[%d]: keys: master: %s initial session: %s\n",
+                      debugstr, pcb->netif->num, mkey, skey));
+       }
+#endif /* PPP_DEBUG */
+
+       /*
+        * Initialize the coherency count.  The initial value is not specified
+        * in RFC 3078, but we can make a reasonable assumption that it will
+        * start at 0.  Setting it to the max here makes the comp/decomp code
+        * do the right thing (determined through experiment).
+        */
+       state->ccount = MPPE_CCOUNT_SPACE - 1;
+
+       /*
+        * Note that even though we have initialized the key table, we don't
+        * set the FLUSHED bit.  This is contrary to RFC 3078, sec. 3.1.
+        */
+       state->bits = MPPE_BIT_ENCRYPTED;
+}
+
+/*
+ * We received a CCP Reset-Request (actually, we are sending a Reset-Ack),
+ * tell the compressor to rekey.  Note that we MUST NOT rekey for
+ * every CCP Reset-Request; we only rekey on the next xmit packet.
+ * We might get multiple CCP Reset-Requests if our CCP Reset-Ack is lost.
+ * So, rekeying for every CCP Reset-Request is broken as the peer will not
+ * know how many times we've rekeyed.  (If we rekey and THEN get another
+ * CCP Reset-Request, we must rekey again.)
+ */
+void mppe_comp_reset(ppp_pcb *pcb, ppp_mppe_state *state)
+{
+       LWIP_UNUSED_ARG(pcb);
+       state->bits |= MPPE_BIT_FLUSHED;
+}
+
+/*
+ * Compress (encrypt) a packet.
+ * It's strange to call this a compressor, since the output is always
+ * MPPE_OVHD + 2 bytes larger than the input.
+ */
+err_t
+mppe_compress(ppp_pcb *pcb, ppp_mppe_state *state, struct pbuf **pb, u16_t protocol)
+{
+       struct pbuf *n, *np;
+       u8_t *pl;
+       err_t err;
+
+       LWIP_UNUSED_ARG(pcb);
+
+       /* TCP stack requires that we don't change the packet payload, therefore we copy
+        * the whole packet before encryption.
+        */
+       np = pbuf_alloc(PBUF_RAW, MPPE_OVHD + sizeof(protocol) + (*pb)->tot_len, PBUF_RAM);
+       if (!np) {
+               return ERR_MEM;
+       }
+
+       /* Hide MPPE header + protocol */
+       pbuf_remove_header(np, MPPE_OVHD + sizeof(protocol));
+
+       if ((err = pbuf_copy(np, *pb)) != ERR_OK) {
+               pbuf_free(np);
+               return err;
+       }
+
+       /* Reveal MPPE header + protocol */
+       pbuf_add_header(np, MPPE_OVHD + sizeof(protocol));
+
+       *pb = np;
+       pl = (u8_t*)np->payload;
+
+       state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
+       PPPDEBUG(LOG_DEBUG, ("mppe_compress[%d]: ccount %d\n", pcb->netif->num, state->ccount));
+       /* FIXME: use PUT* macros */
+       pl[0] = state->ccount>>8;
+       pl[1] = state->ccount;
+
+       if (!state->stateful || /* stateless mode     */
+           ((state->ccount & 0xff) == 0xff) || /* "flag" packet      */
+           (state->bits & MPPE_BIT_FLUSHED)) { /* CCP Reset-Request  */
+               /* We must rekey */
+               if (state->stateful) {
+                       PPPDEBUG(LOG_DEBUG, ("mppe_compress[%d]: rekeying\n", pcb->netif->num));
+               }
+               mppe_rekey(state, 0);
+               state->bits |= MPPE_BIT_FLUSHED;
+       }
+       pl[0] |= state->bits;
+       state->bits &= ~MPPE_BIT_FLUSHED;       /* reset for next xmit */
+       pl += MPPE_OVHD;
+
+       /* Add protocol */
+       /* FIXME: add PFC support */
+       pl[0] = protocol >> 8;
+       pl[1] = protocol;
+
+       /* Hide MPPE header */
+       pbuf_remove_header(np, MPPE_OVHD);
+
+       /* Encrypt packet */
+       for (n = np; n != NULL; n = n->next) {
+               lwip_arc4_crypt(&state->arc4, (u8_t*)n->payload, n->len);
+               if (n->tot_len == n->len) {
+                       break;
+               }
+       }
+
+       /* Reveal MPPE header */
+       pbuf_add_header(np, MPPE_OVHD);
+
+       return ERR_OK;
+}
+
+/*
+ * We received a CCP Reset-Ack.  Just ignore it.
+ */
+void mppe_decomp_reset(ppp_pcb *pcb, ppp_mppe_state *state)
+{
+       LWIP_UNUSED_ARG(pcb);
+       LWIP_UNUSED_ARG(state);
+       return;
+}
+
+/*
+ * Decompress (decrypt) an MPPE packet.
+ */
+err_t
+mppe_decompress(ppp_pcb *pcb, ppp_mppe_state *state, struct pbuf **pb)
+{
+       struct pbuf *n0 = *pb, *n;
+       u8_t *pl;
+       u16_t ccount;
+       u8_t flushed;
+
+       /* MPPE Header */
+       if (n0->len < MPPE_OVHD) {
+               PPPDEBUG(LOG_DEBUG,
+                      ("mppe_decompress[%d]: short pkt (%d)\n",
+                      pcb->netif->num, n0->len));
+               state->sanity_errors += 100;
+               goto sanity_error;
+       }
+
+       pl = (u8_t*)n0->payload;
+       flushed = MPPE_BITS(pl) & MPPE_BIT_FLUSHED;
+       ccount = MPPE_CCOUNT(pl);
+       PPPDEBUG(LOG_DEBUG, ("mppe_decompress[%d]: ccount %d\n",
+              pcb->netif->num, ccount));
+
+       /* sanity checks -- terminate with extreme prejudice */
+       if (!(MPPE_BITS(pl) & MPPE_BIT_ENCRYPTED)) {
+               PPPDEBUG(LOG_DEBUG,
+                      ("mppe_decompress[%d]: ENCRYPTED bit not set!\n",
+                      pcb->netif->num));
+               state->sanity_errors += 100;
+               goto sanity_error;
+       }
+       if (!state->stateful && !flushed) {
+               PPPDEBUG(LOG_DEBUG, ("mppe_decompress[%d]: FLUSHED bit not set in "
+                      "stateless mode!\n", pcb->netif->num));
+               state->sanity_errors += 100;
+               goto sanity_error;
+       }
+       if (state->stateful && ((ccount & 0xff) == 0xff) && !flushed) {
+               PPPDEBUG(LOG_DEBUG, ("mppe_decompress[%d]: FLUSHED bit not set on "
+                      "flag packet!\n", pcb->netif->num));
+               state->sanity_errors += 100;
+               goto sanity_error;
+       }
+
+       /*
+        * Check the coherency count.
+        */
+
+       if (!state->stateful) {
+               /* Discard late packet */
+               if ((ccount - state->ccount) % MPPE_CCOUNT_SPACE > MPPE_CCOUNT_SPACE / 2) {
+                       state->sanity_errors++;
+                       goto sanity_error;
+               }
+
+               /* RFC 3078, sec 8.1.  Rekey for every packet. */
+               while (state->ccount != ccount) {
+                       mppe_rekey(state, 0);
+                       state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
+               }
+       } else {
+               /* RFC 3078, sec 8.2. */
+               if (!state->discard) {
+                       /* normal state */
+                       state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
+                       if (ccount != state->ccount) {
+                               /*
+                                * (ccount > state->ccount)
+                                * Packet loss detected, enter the discard state.
+                                * Signal the peer to rekey (by sending a CCP Reset-Request).
+                                */
+                               state->discard = 1;
+                               ccp_resetrequest(pcb);
+                               return ERR_BUF;
+                       }
+               } else {
+                       /* discard state */
+                       if (!flushed) {
+                               /* ccp.c will be silent (no additional CCP Reset-Requests). */
+                               return ERR_BUF;
+                       } else {
+                               /* Rekey for every missed "flag" packet. */
+                               while ((ccount & ~0xff) !=
+                                      (state->ccount & ~0xff)) {
+                                       mppe_rekey(state, 0);
+                                       state->ccount =
+                                           (state->ccount +
+                                            256) % MPPE_CCOUNT_SPACE;
+                               }
+
+                               /* reset */
+                               state->discard = 0;
+                               state->ccount = ccount;
+                               /*
+                                * Another problem with RFC 3078 here.  It implies that the
+                                * peer need not send a Reset-Ack packet.  But RFC 1962
+                                * requires it.  Hopefully, M$ does send a Reset-Ack; even
+                                * though it isn't required for MPPE synchronization, it is
+                                * required to reset CCP state.
+                                */
+                       }
+               }
+               if (flushed)
+                       mppe_rekey(state, 0);
+       }
+
+       /* Hide MPPE header */
+       pbuf_remove_header(n0, MPPE_OVHD);
+
+       /* Decrypt the packet. */
+       for (n = n0; n != NULL; n = n->next) {
+               lwip_arc4_crypt(&state->arc4, (u8_t*)n->payload, n->len);
+               if (n->tot_len == n->len) {
+                       break;
+               }
+       }
+
+       /* good packet credit */
+       state->sanity_errors >>= 1;
+
+       return ERR_OK;
+
+sanity_error:
+       if (state->sanity_errors >= SANITY_MAX) {
+               /*
+                * Take LCP down if the peer is sending too many bogons.
+                * We don't want to do this for a single or just a few
+                * instances since it could just be due to packet corruption.
+                */
+               lcp_close(pcb, "Too many MPPE errors");
+       }
+       return ERR_BUF;
+}
+
+#endif /* PPP_SUPPORT && MPPE_SUPPORT */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/multilink.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/multilink.c
new file mode 100644 (file)
index 0000000..62014e8
--- /dev/null
@@ -0,0 +1,609 @@
+/*
+ * multilink.c - support routines for multilink.
+ *
+ * Copyright (c) 2000-2002 Paul Mackerras. All rights reserved.
+ *
+ * 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. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && defined(HAVE_MULTILINK) /* don't build if not configured for use in lwipopts.h */
+
+/* Multilink support
+ *
+ * Multilink uses Samba TDB (Trivial Database Library), which
+ * we cannot port, because it needs a filesystem.
+ *
+ * We have to choose between doing a memory-shared TDB-clone,
+ * or dropping multilink support at all.
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <errno.h>
+#include <signal.h>
+#include <netinet/in.h>
+#include <unistd.h>
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/fsm.h"
+#include "netif/ppp/lcp.h"
+#include "netif/ppp/tdb.h"
+
+bool endpoint_specified;       /* user gave explicit endpoint discriminator */
+char *bundle_id;               /* identifier for our bundle */
+char *blinks_id;               /* key for the list of links */
+bool doing_multilink;          /* multilink was enabled and agreed to */
+bool multilink_master;         /* we own the multilink bundle */
+
+extern TDB_CONTEXT *pppdb;
+extern char db_key[];
+
+static void make_bundle_links (int append);
+static void remove_bundle_link (void);
+static void iterate_bundle_links (void (*func) (char *));
+
+static int get_default_epdisc (struct epdisc *);
+static int parse_num (char *str, const char *key, int *valp);
+static int owns_unit (TDB_DATA pid, int unit);
+
+#define set_ip_epdisc(ep, addr) do {   \
+       ep->length = 4;                 \
+       ep->value[0] = addr >> 24;      \
+       ep->value[1] = addr >> 16;      \
+       ep->value[2] = addr >> 8;       \
+       ep->value[3] = addr;            \
+} while (0)
+
+#define LOCAL_IP_ADDR(addr)                                              \
+       (((addr) & 0xff000000) == 0x0a000000            /* 10.x.x.x */    \
+        || ((addr) & 0xfff00000) == 0xac100000         /* 172.16.x.x */  \
+        || ((addr) & 0xffff0000) == 0xc0a80000)        /* 192.168.x.x */
+
+#define process_exists(n)      (kill((n), 0) == 0 || errno != ESRCH)
+
+void
+mp_check_options()
+{
+       lcp_options *wo = &lcp_wantoptions[0];
+       lcp_options *ao = &lcp_allowoptions[0];
+
+       doing_multilink = 0;
+       if (!multilink)
+               return;
+       /* if we're doing multilink, we have to negotiate MRRU */
+       if (!wo->neg_mrru) {
+               /* mrru not specified, default to mru */
+               wo->mrru = wo->mru;
+               wo->neg_mrru = 1;
+       }
+       ao->mrru = ao->mru;
+       ao->neg_mrru = 1;
+
+       if (!wo->neg_endpoint && !noendpoint) {
+               /* get a default endpoint value */
+               wo->neg_endpoint = get_default_epdisc(&wo->endpoint);
+       }
+}
+
+/*
+ * Make a new bundle or join us to an existing bundle
+ * if we are doing multilink.
+ */
+int
+mp_join_bundle()
+{
+       lcp_options *go = &lcp_gotoptions[0];
+       lcp_options *ho = &lcp_hisoptions[0];
+       lcp_options *ao = &lcp_allowoptions[0];
+       int unit, pppd_pid;
+       int l, mtu;
+       char *p;
+       TDB_DATA key, pid, rec;
+
+       if (doing_multilink) {
+               /* have previously joined a bundle */
+               if (!go->neg_mrru || !ho->neg_mrru) {
+                       notice("oops, didn't get multilink on renegotiation");
+                       lcp_close(pcb, "multilink required");
+                       return 0;
+               }
+               /* XXX should check the peer_authname and ho->endpoint
+                  are the same as previously */
+               return 0;
+       }
+
+       if (!go->neg_mrru || !ho->neg_mrru) {
+               /* not doing multilink */
+               if (go->neg_mrru)
+                       notice("oops, multilink negotiated only for receive");
+               mtu = ho->neg_mru? ho->mru: PPP_MRU;
+               if (mtu > ao->mru)
+                       mtu = ao->mru;
+               if (demand) {
+                       /* already have a bundle */
+                       cfg_bundle(0, 0, 0, 0);
+                       netif_set_mtu(pcb, mtu);
+                       return 0;
+               }
+               make_new_bundle(0, 0, 0, 0);
+               set_ifunit(1);
+               netif_set_mtu(pcb, mtu);
+               return 0;
+       }
+
+       doing_multilink = 1;
+
+       /*
+        * Find the appropriate bundle or join a new one.
+        * First we make up a name for the bundle.
+        * The length estimate is worst-case assuming every
+        * character has to be quoted.
+        */
+       l = 4 * strlen(peer_authname) + 10;
+       if (ho->neg_endpoint)
+               l += 3 * ho->endpoint.length + 8;
+       if (bundle_name)
+               l += 3 * strlen(bundle_name) + 2;
+       bundle_id = malloc(l);
+       if (bundle_id == 0)
+               novm("bundle identifier");
+
+       p = bundle_id;
+       p += slprintf(p, l-1, "BUNDLE=\"%q\"", peer_authname);
+       if (ho->neg_endpoint || bundle_name)
+               *p++ = '/';
+       if (ho->neg_endpoint)
+               p += slprintf(p, bundle_id+l-p, "%s",
+                             epdisc_to_str(&ho->endpoint));
+       if (bundle_name)
+               p += slprintf(p, bundle_id+l-p, "/%v", bundle_name);
+
+       /* Make the key for the list of links belonging to the bundle */
+       l = p - bundle_id;
+       blinks_id = malloc(l + 7);
+       if (blinks_id == NULL)
+               novm("bundle links key");
+       slprintf(blinks_id, l + 7, "BUNDLE_LINKS=%s", bundle_id + 7);
+
+       /*
+        * For demand mode, we only need to configure the bundle
+        * and attach the link.
+        */
+       mtu = LWIP_MIN(ho->mrru, ao->mru);
+       if (demand) {
+               cfg_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf);
+               netif_set_mtu(pcb, mtu);
+               script_setenv("BUNDLE", bundle_id + 7, 1);
+               return 0;
+       }
+
+       /*
+        * Check if the bundle ID is already in the database.
+        */
+       unit = -1;
+       lock_db();
+       key.dptr = bundle_id;
+       key.dsize = p - bundle_id;
+       pid = tdb_fetch(pppdb, key);
+       if (pid.dptr != NULL) {
+               /* bundle ID exists, see if the pppd record exists */
+               rec = tdb_fetch(pppdb, pid);
+               if (rec.dptr != NULL && rec.dsize > 0) {
+                       /* make sure the string is null-terminated */
+                       rec.dptr[rec.dsize-1] = 0;
+                       /* parse the interface number */
+                       parse_num(rec.dptr, "IFNAME=ppp", &unit);
+                       /* check the pid value */
+                       if (!parse_num(rec.dptr, "PPPD_PID=", &pppd_pid)
+                           || !process_exists(pppd_pid)
+                           || !owns_unit(pid, unit))
+                               unit = -1;
+                       free(rec.dptr);
+               }
+               free(pid.dptr);
+       }
+
+       if (unit >= 0) {
+               /* attach to existing unit */
+               if (bundle_attach(unit)) {
+                       set_ifunit(0);
+                       script_setenv("BUNDLE", bundle_id + 7, 0);
+                       make_bundle_links(1);
+                       unlock_db();
+                       info("Link attached to %s", ifname);
+                       return 1;
+               }
+               /* attach failed because bundle doesn't exist */
+       }
+
+       /* we have to make a new bundle */
+       make_new_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf);
+       set_ifunit(1);
+       netif_set_mtu(pcb, mtu);
+       script_setenv("BUNDLE", bundle_id + 7, 1);
+       make_bundle_links(pcb);
+       unlock_db();
+       info("New bundle %s created", ifname);
+       multilink_master = 1;
+       return 0;
+}
+
+void mp_exit_bundle()
+{
+       lock_db();
+       remove_bundle_link();
+       unlock_db();
+}
+
+static void sendhup(char *str)
+{
+       int pid;
+
+       if (parse_num(str, "PPPD_PID=", &pid) && pid != getpid()) {
+               if (debug)
+                       dbglog("sending SIGHUP to process %d", pid);
+               kill(pid, SIGHUP);
+       }
+}
+
+void mp_bundle_terminated()
+{
+       TDB_DATA key;
+
+       bundle_terminating = 1;
+       upper_layers_down(pcb);
+       notice("Connection terminated.");
+#if PPP_STATS_SUPPORT
+       print_link_stats();
+#endif /* PPP_STATS_SUPPORT */
+       if (!demand) {
+               remove_pidfiles();
+               script_unsetenv("IFNAME");
+       }
+
+       lock_db();
+       destroy_bundle();
+       iterate_bundle_links(sendhup);
+       key.dptr = blinks_id;
+       key.dsize = strlen(blinks_id);
+       tdb_delete(pppdb, key);
+       unlock_db();
+
+       new_phase(PPP_PHASE_DEAD);
+
+       doing_multilink = 0;
+       multilink_master = 0;
+}
+
+static void make_bundle_links(int append)
+{
+       TDB_DATA key, rec;
+       char *p;
+       char entry[32];
+       int l;
+
+       key.dptr = blinks_id;
+       key.dsize = strlen(blinks_id);
+       slprintf(entry, sizeof(entry), "%s;", db_key);
+       p = entry;
+       if (append) {
+               rec = tdb_fetch(pppdb, key);
+               if (rec.dptr != NULL && rec.dsize > 0) {
+                       rec.dptr[rec.dsize-1] = 0;
+                       if (strstr(rec.dptr, db_key) != NULL) {
+                               /* already in there? strange */
+                               warn("link entry already exists in tdb");
+                               return;
+                       }
+                       l = rec.dsize + strlen(entry);
+                       p = malloc(l);
+                       if (p == NULL)
+                               novm("bundle link list");
+                       slprintf(p, l, "%s%s", rec.dptr, entry);
+               } else {
+                       warn("bundle link list not found");
+               }
+               if (rec.dptr != NULL)
+                       free(rec.dptr);
+       }
+       rec.dptr = p;
+       rec.dsize = strlen(p) + 1;
+       if (tdb_store(pppdb, key, rec, TDB_REPLACE))
+               error("couldn't %s bundle link list",
+                     append? "update": "create");
+       if (p != entry)
+               free(p);
+}
+
+static void remove_bundle_link()
+{
+       TDB_DATA key, rec;
+       char entry[32];
+       char *p, *q;
+       int l;
+
+       key.dptr = blinks_id;
+       key.dsize = strlen(blinks_id);
+       slprintf(entry, sizeof(entry), "%s;", db_key);
+
+       rec = tdb_fetch(pppdb, key);
+       if (rec.dptr == NULL || rec.dsize <= 0) {
+               if (rec.dptr != NULL)
+                       free(rec.dptr);
+               return;
+       }
+       rec.dptr[rec.dsize-1] = 0;
+       p = strstr(rec.dptr, entry);
+       if (p != NULL) {
+               q = p + strlen(entry);
+               l = strlen(q) + 1;
+               memmove(p, q, l);
+               rec.dsize = p - rec.dptr + l;
+               if (tdb_store(pppdb, key, rec, TDB_REPLACE))
+                       error("couldn't update bundle link list (removal)");
+       }
+       free(rec.dptr);
+}
+
+static void iterate_bundle_links(void (*func)(char *))
+{
+       TDB_DATA key, rec, pp;
+       char *p, *q;
+
+       key.dptr = blinks_id;
+       key.dsize = strlen(blinks_id);
+       rec = tdb_fetch(pppdb, key);
+       if (rec.dptr == NULL || rec.dsize <= 0) {
+               error("bundle link list not found (iterating list)");
+               if (rec.dptr != NULL)
+                       free(rec.dptr);
+               return;
+       }
+       p = rec.dptr;
+       p[rec.dsize-1] = 0;
+       while ((q = strchr(p, ';')) != NULL) {
+               *q = 0;
+               key.dptr = p;
+               key.dsize = q - p;
+               pp = tdb_fetch(pppdb, key);
+               if (pp.dptr != NULL && pp.dsize > 0) {
+                       pp.dptr[pp.dsize-1] = 0;
+                       func(pp.dptr);
+               }
+               if (pp.dptr != NULL)
+                       free(pp.dptr);
+               p = q + 1;
+       }
+       free(rec.dptr);
+}
+
+static int
+parse_num(str, key, valp)
+     char *str;
+     const char *key;
+     int *valp;
+{
+       char *p, *endp;
+       int i;
+
+       p = strstr(str, key);
+       if (p != 0) {
+               p += strlen(key);
+               i = strtol(p, &endp, 10);
+               if (endp != p && (*endp == 0 || *endp == ';')) {
+                       *valp = i;
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+/*
+ * Check whether the pppd identified by `key' still owns ppp unit `unit'.
+ */
+static int
+owns_unit(key, unit)
+     TDB_DATA key;
+     int unit;
+{
+       char ifkey[32];
+       TDB_DATA kd, vd;
+       int ret = 0;
+
+       slprintf(ifkey, sizeof(ifkey), "IFNAME=ppp%d", unit);
+       kd.dptr = ifkey;
+       kd.dsize = strlen(ifkey);
+       vd = tdb_fetch(pppdb, kd);
+       if (vd.dptr != NULL) {
+               ret = vd.dsize == key.dsize
+                       && memcmp(vd.dptr, key.dptr, vd.dsize) == 0;
+               free(vd.dptr);
+       }
+       return ret;
+}
+
+static int
+get_default_epdisc(ep)
+     struct epdisc *ep;
+{
+       char *p;
+       struct hostent *hp;
+       u32_t addr;
+
+       /* First try for an ethernet MAC address */
+       p = get_first_ethernet();
+       if (p != 0 && get_if_hwaddr(ep->value, p) >= 0) {
+               ep->class = EPD_MAC;
+               ep->length = 6;
+               return 1;
+       }
+
+       /* see if our hostname corresponds to a reasonable IP address */
+       hp = gethostbyname(hostname);
+       if (hp != NULL) {
+               addr = *(u32_t *)hp->h_addr;
+               if (!bad_ip_adrs(addr)) {
+                       addr = lwip_ntohl(addr);
+                       if (!LOCAL_IP_ADDR(addr)) {
+                               ep->class = EPD_IP;
+                               set_ip_epdisc(ep, addr);
+                               return 1;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * epdisc_to_str - make a printable string from an endpoint discriminator.
+ */
+
+static char *endp_class_names[] = {
+    "null", "local", "IP", "MAC", "magic", "phone"
+};
+
+char *
+epdisc_to_str(ep)
+     struct epdisc *ep;
+{
+       static char str[MAX_ENDP_LEN*3+8];
+       u_char *p = ep->value;
+       int i, mask = 0;
+       char *q, c, c2;
+
+       if (ep->class == EPD_NULL && ep->length == 0)
+               return "null";
+       if (ep->class == EPD_IP && ep->length == 4) {
+               u32_t addr;
+
+               GETLONG(addr, p);
+               slprintf(str, sizeof(str), "IP:%I", lwip_htonl(addr));
+               return str;
+       }
+
+       c = ':';
+       c2 = '.';
+       if (ep->class == EPD_MAC && ep->length == 6)
+               c2 = ':';
+       else if (ep->class == EPD_MAGIC && (ep->length % 4) == 0)
+               mask = 3;
+       q = str;
+       if (ep->class <= EPD_PHONENUM)
+               q += slprintf(q, sizeof(str)-1, "%s",
+                             endp_class_names[ep->class]);
+       else
+               q += slprintf(q, sizeof(str)-1, "%d", ep->class);
+       c = ':';
+       for (i = 0; i < ep->length && i < MAX_ENDP_LEN; ++i) {
+               if ((i & mask) == 0) {
+                       *q++ = c;
+                       c = c2;
+               }
+               q += slprintf(q, str + sizeof(str) - q, "%.2x", ep->value[i]);
+       }
+       return str;
+}
+
+static int hexc_val(int c)
+{
+       if (c >= 'a')
+               return c - 'a' + 10;
+       if (c >= 'A')
+               return c - 'A' + 10;
+       return c - '0';
+}
+
+int
+str_to_epdisc(ep, str)
+     struct epdisc *ep;
+     char *str;
+{
+       int i, l;
+       char *p, *endp;
+
+       for (i = EPD_NULL; i <= EPD_PHONENUM; ++i) {
+               int sl = strlen(endp_class_names[i]);
+               if (strncasecmp(str, endp_class_names[i], sl) == 0) {
+                       str += sl;
+                       break;
+               }
+       }
+       if (i > EPD_PHONENUM) {
+               /* not a class name, try a decimal class number */
+               i = strtol(str, &endp, 10);
+               if (endp == str)
+                       return 0;       /* can't parse class number */
+               str = endp;
+       }
+       ep->class = i;
+       if (*str == 0) {
+               ep->length = 0;
+               return 1;
+       }
+       if (*str != ':' && *str != '.')
+               return 0;
+       ++str;
+
+       if (i == EPD_IP) {
+               u32_t addr;
+               i = parse_dotted_ip(str, &addr);
+               if (i == 0 || str[i] != 0)
+                       return 0;
+               set_ip_epdisc(ep, addr);
+               return 1;
+       }
+       if (i == EPD_MAC && get_if_hwaddr(ep->value, str) >= 0) {
+               ep->length = 6;
+               return 1;
+       }
+
+       p = str;
+       for (l = 0; l < MAX_ENDP_LEN; ++l) {
+               if (*str == 0)
+                       break;
+               if (p <= str)
+                       for (p = str; isxdigit(*p); ++p)
+                               ;
+               i = p - str;
+               if (i == 0)
+                       return 0;
+               ep->value[l] = hexc_val(*str++);
+               if ((i & 1) == 0)
+                       ep->value[l] = (ep->value[l] << 4) + hexc_val(*str++);
+               if (*str == ':' || *str == '.')
+                       ++str;
+       }
+       if (*str != 0 || (ep->class == EPD_MAC && l != 6))
+               return 0;
+       ep->length = l;
+       return 1;
+}
+
+#endif /* PPP_SUPPORT && HAVE_MULTILINK */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/polarssl/README b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/polarssl/README
new file mode 100644 (file)
index 0000000..3fdf159
--- /dev/null
@@ -0,0 +1,22 @@
+About PolarSSL files into lwIP PPP support
+------------------------------------------
+
+This folder contains some files fetched from the latest BSD release of
+the PolarSSL project (PolarSSL 0.10.1-bsd) for ciphers and encryption
+methods we need for lwIP PPP support.
+
+The PolarSSL files were cleaned to contain only the necessary struct
+fields and functions needed for lwIP.
+
+The PolarSSL API was not changed at all, so if you are already using
+PolarSSL you can choose to skip the compilation of the included PolarSSL
+library into lwIP.
+
+If you are not using the embedded copy you must include external
+libraries into your arch/cc.h port file.
+
+Beware of the stack requirements which can be a lot larger if you are not
+using our cleaned PolarSSL library.
+
+
+PolarSSL project website: http://polarssl.org/
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/polarssl/arc4.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/polarssl/arc4.c
new file mode 100644 (file)
index 0000000..6e17ec4
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ *  An implementation of the ARCFOUR algorithm
+ *
+ *  Based on XySSL: Copyright (C) 2006-2008  Christophe Devine
+ *
+ *  Copyright (C) 2009  Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *  
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * 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.
+ *    * Neither the names of PolarSSL or XySSL nor the names of its contributors
+ *      may be used to endorse or promote products derived from this software
+ *      without specific prior written permission.
+ *  
+ *  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
+ *  OWNER 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.
+ */
+/*
+ *  The ARCFOUR algorithm was publicly disclosed on 94/09.
+ *
+ *  http://groups.google.com/group/sci.crypt/msg/10a300c9d21afca0
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_ARC4
+
+#include "netif/ppp/polarssl/arc4.h"
+/*
+ * ARC4 key schedule
+ */
+void arc4_setup( arc4_context *ctx, unsigned char *key, int keylen )
+{
+    int i, j, k, a;
+    unsigned char *m;
+
+    ctx->x = 0;
+    ctx->y = 0;
+    m = ctx->m;
+
+    for( i = 0; i < 256; i++ )
+        m[i] = (unsigned char) i;
+
+    j = k = 0;
+
+    for( i = 0; i < 256; i++, k++ )
+    {
+        if( k >= keylen ) k = 0;
+
+        a = m[i];
+        j = ( j + a + key[k] ) & 0xFF;
+        m[i] = m[j];
+        m[j] = (unsigned char) a;
+    }
+}
+
+/*
+ * ARC4 cipher function
+ */
+void arc4_crypt( arc4_context *ctx, unsigned char *buf, int buflen )
+{
+    int i, x, y, a, b;
+    unsigned char *m;
+
+    x = ctx->x;
+    y = ctx->y;
+    m = ctx->m;
+
+    for( i = 0; i < buflen; i++ )
+    {
+        x = ( x + 1 ) & 0xFF; a = m[x];
+        y = ( y + a ) & 0xFF; b = m[y];
+
+        m[x] = (unsigned char) b;
+        m[y] = (unsigned char) a;
+
+        buf[i] = (unsigned char)
+            ( buf[i] ^ m[(unsigned char)( a + b )] );
+    }
+
+    ctx->x = x;
+    ctx->y = y;
+}
+
+#endif /* PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_DES */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/polarssl/des.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/polarssl/des.c
new file mode 100644 (file)
index 0000000..9a89d00
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+ *  FIPS-46-3 compliant Triple-DES implementation
+ *
+ *  Based on XySSL: Copyright (C) 2006-2008  Christophe Devine
+ *
+ *  Copyright (C) 2009  Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *  
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * 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.
+ *    * Neither the names of PolarSSL or XySSL nor the names of its contributors
+ *      may be used to endorse or promote products derived from this software
+ *      without specific prior written permission.
+ *  
+ *  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
+ *  OWNER 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.
+ */
+/*
+ *  DES, on which TDES is based, was originally designed by Horst Feistel
+ *  at IBM in 1974, and was adopted as a standard by NIST (formerly NBS).
+ *
+ *  http://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_DES
+
+#include "netif/ppp/polarssl/des.h"
+
+/*
+ * 32-bit integer manipulation macros (big endian)
+ */
+#ifndef GET_ULONG_BE
+#define GET_ULONG_BE(n,b,i)                             \
+{                                                       \
+    (n) = ( (unsigned long) (b)[(i)    ] << 24 )        \
+        | ( (unsigned long) (b)[(i) + 1] << 16 )        \
+        | ( (unsigned long) (b)[(i) + 2] <<  8 )        \
+        | ( (unsigned long) (b)[(i) + 3]       );       \
+}
+#endif
+
+#ifndef PUT_ULONG_BE
+#define PUT_ULONG_BE(n,b,i)                             \
+{                                                       \
+    (b)[(i)    ] = (unsigned char) ( (n) >> 24 );       \
+    (b)[(i) + 1] = (unsigned char) ( (n) >> 16 );       \
+    (b)[(i) + 2] = (unsigned char) ( (n) >>  8 );       \
+    (b)[(i) + 3] = (unsigned char) ( (n)       );       \
+}
+#endif
+
+/*
+ * Expanded DES S-boxes
+ */
+static const unsigned long SB1[64] =
+{
+    0x01010400, 0x00000000, 0x00010000, 0x01010404,
+    0x01010004, 0x00010404, 0x00000004, 0x00010000,
+    0x00000400, 0x01010400, 0x01010404, 0x00000400,
+    0x01000404, 0x01010004, 0x01000000, 0x00000004,
+    0x00000404, 0x01000400, 0x01000400, 0x00010400,
+    0x00010400, 0x01010000, 0x01010000, 0x01000404,
+    0x00010004, 0x01000004, 0x01000004, 0x00010004,
+    0x00000000, 0x00000404, 0x00010404, 0x01000000,
+    0x00010000, 0x01010404, 0x00000004, 0x01010000,
+    0x01010400, 0x01000000, 0x01000000, 0x00000400,
+    0x01010004, 0x00010000, 0x00010400, 0x01000004,
+    0x00000400, 0x00000004, 0x01000404, 0x00010404,
+    0x01010404, 0x00010004, 0x01010000, 0x01000404,
+    0x01000004, 0x00000404, 0x00010404, 0x01010400,
+    0x00000404, 0x01000400, 0x01000400, 0x00000000,
+    0x00010004, 0x00010400, 0x00000000, 0x01010004
+};
+
+static const unsigned long SB2[64] =
+{
+    0x80108020, 0x80008000, 0x00008000, 0x00108020,
+    0x00100000, 0x00000020, 0x80100020, 0x80008020,
+    0x80000020, 0x80108020, 0x80108000, 0x80000000,
+    0x80008000, 0x00100000, 0x00000020, 0x80100020,
+    0x00108000, 0x00100020, 0x80008020, 0x00000000,
+    0x80000000, 0x00008000, 0x00108020, 0x80100000,
+    0x00100020, 0x80000020, 0x00000000, 0x00108000,
+    0x00008020, 0x80108000, 0x80100000, 0x00008020,
+    0x00000000, 0x00108020, 0x80100020, 0x00100000,
+    0x80008020, 0x80100000, 0x80108000, 0x00008000,
+    0x80100000, 0x80008000, 0x00000020, 0x80108020,
+    0x00108020, 0x00000020, 0x00008000, 0x80000000,
+    0x00008020, 0x80108000, 0x00100000, 0x80000020,
+    0x00100020, 0x80008020, 0x80000020, 0x00100020,
+    0x00108000, 0x00000000, 0x80008000, 0x00008020,
+    0x80000000, 0x80100020, 0x80108020, 0x00108000
+};
+
+static const unsigned long SB3[64] =
+{
+    0x00000208, 0x08020200, 0x00000000, 0x08020008,
+    0x08000200, 0x00000000, 0x00020208, 0x08000200,
+    0x00020008, 0x08000008, 0x08000008, 0x00020000,
+    0x08020208, 0x00020008, 0x08020000, 0x00000208,
+    0x08000000, 0x00000008, 0x08020200, 0x00000200,
+    0x00020200, 0x08020000, 0x08020008, 0x00020208,
+    0x08000208, 0x00020200, 0x00020000, 0x08000208,
+    0x00000008, 0x08020208, 0x00000200, 0x08000000,
+    0x08020200, 0x08000000, 0x00020008, 0x00000208,
+    0x00020000, 0x08020200, 0x08000200, 0x00000000,
+    0x00000200, 0x00020008, 0x08020208, 0x08000200,
+    0x08000008, 0x00000200, 0x00000000, 0x08020008,
+    0x08000208, 0x00020000, 0x08000000, 0x08020208,
+    0x00000008, 0x00020208, 0x00020200, 0x08000008,
+    0x08020000, 0x08000208, 0x00000208, 0x08020000,
+    0x00020208, 0x00000008, 0x08020008, 0x00020200
+};
+
+static const unsigned long SB4[64] =
+{
+    0x00802001, 0x00002081, 0x00002081, 0x00000080,
+    0x00802080, 0x00800081, 0x00800001, 0x00002001,
+    0x00000000, 0x00802000, 0x00802000, 0x00802081,
+    0x00000081, 0x00000000, 0x00800080, 0x00800001,
+    0x00000001, 0x00002000, 0x00800000, 0x00802001,
+    0x00000080, 0x00800000, 0x00002001, 0x00002080,
+    0x00800081, 0x00000001, 0x00002080, 0x00800080,
+    0x00002000, 0x00802080, 0x00802081, 0x00000081,
+    0x00800080, 0x00800001, 0x00802000, 0x00802081,
+    0x00000081, 0x00000000, 0x00000000, 0x00802000,
+    0x00002080, 0x00800080, 0x00800081, 0x00000001,
+    0x00802001, 0x00002081, 0x00002081, 0x00000080,
+    0x00802081, 0x00000081, 0x00000001, 0x00002000,
+    0x00800001, 0x00002001, 0x00802080, 0x00800081,
+    0x00002001, 0x00002080, 0x00800000, 0x00802001,
+    0x00000080, 0x00800000, 0x00002000, 0x00802080
+};
+
+static const unsigned long SB5[64] =
+{
+    0x00000100, 0x02080100, 0x02080000, 0x42000100,
+    0x00080000, 0x00000100, 0x40000000, 0x02080000,
+    0x40080100, 0x00080000, 0x02000100, 0x40080100,
+    0x42000100, 0x42080000, 0x00080100, 0x40000000,
+    0x02000000, 0x40080000, 0x40080000, 0x00000000,
+    0x40000100, 0x42080100, 0x42080100, 0x02000100,
+    0x42080000, 0x40000100, 0x00000000, 0x42000000,
+    0x02080100, 0x02000000, 0x42000000, 0x00080100,
+    0x00080000, 0x42000100, 0x00000100, 0x02000000,
+    0x40000000, 0x02080000, 0x42000100, 0x40080100,
+    0x02000100, 0x40000000, 0x42080000, 0x02080100,
+    0x40080100, 0x00000100, 0x02000000, 0x42080000,
+    0x42080100, 0x00080100, 0x42000000, 0x42080100,
+    0x02080000, 0x00000000, 0x40080000, 0x42000000,
+    0x00080100, 0x02000100, 0x40000100, 0x00080000,
+    0x00000000, 0x40080000, 0x02080100, 0x40000100
+};
+
+static const unsigned long SB6[64] =
+{
+    0x20000010, 0x20400000, 0x00004000, 0x20404010,
+    0x20400000, 0x00000010, 0x20404010, 0x00400000,
+    0x20004000, 0x00404010, 0x00400000, 0x20000010,
+    0x00400010, 0x20004000, 0x20000000, 0x00004010,
+    0x00000000, 0x00400010, 0x20004010, 0x00004000,
+    0x00404000, 0x20004010, 0x00000010, 0x20400010,
+    0x20400010, 0x00000000, 0x00404010, 0x20404000,
+    0x00004010, 0x00404000, 0x20404000, 0x20000000,
+    0x20004000, 0x00000010, 0x20400010, 0x00404000,
+    0x20404010, 0x00400000, 0x00004010, 0x20000010,
+    0x00400000, 0x20004000, 0x20000000, 0x00004010,
+    0x20000010, 0x20404010, 0x00404000, 0x20400000,
+    0x00404010, 0x20404000, 0x00000000, 0x20400010,
+    0x00000010, 0x00004000, 0x20400000, 0x00404010,
+    0x00004000, 0x00400010, 0x20004010, 0x00000000,
+    0x20404000, 0x20000000, 0x00400010, 0x20004010
+};
+
+static const unsigned long SB7[64] =
+{
+    0x00200000, 0x04200002, 0x04000802, 0x00000000,
+    0x00000800, 0x04000802, 0x00200802, 0x04200800,
+    0x04200802, 0x00200000, 0x00000000, 0x04000002,
+    0x00000002, 0x04000000, 0x04200002, 0x00000802,
+    0x04000800, 0x00200802, 0x00200002, 0x04000800,
+    0x04000002, 0x04200000, 0x04200800, 0x00200002,
+    0x04200000, 0x00000800, 0x00000802, 0x04200802,
+    0x00200800, 0x00000002, 0x04000000, 0x00200800,
+    0x04000000, 0x00200800, 0x00200000, 0x04000802,
+    0x04000802, 0x04200002, 0x04200002, 0x00000002,
+    0x00200002, 0x04000000, 0x04000800, 0x00200000,
+    0x04200800, 0x00000802, 0x00200802, 0x04200800,
+    0x00000802, 0x04000002, 0x04200802, 0x04200000,
+    0x00200800, 0x00000000, 0x00000002, 0x04200802,
+    0x00000000, 0x00200802, 0x04200000, 0x00000800,
+    0x04000002, 0x04000800, 0x00000800, 0x00200002
+};
+
+static const unsigned long SB8[64] =
+{
+    0x10001040, 0x00001000, 0x00040000, 0x10041040,
+    0x10000000, 0x10001040, 0x00000040, 0x10000000,
+    0x00040040, 0x10040000, 0x10041040, 0x00041000,
+    0x10041000, 0x00041040, 0x00001000, 0x00000040,
+    0x10040000, 0x10000040, 0x10001000, 0x00001040,
+    0x00041000, 0x00040040, 0x10040040, 0x10041000,
+    0x00001040, 0x00000000, 0x00000000, 0x10040040,
+    0x10000040, 0x10001000, 0x00041040, 0x00040000,
+    0x00041040, 0x00040000, 0x10041000, 0x00001000,
+    0x00000040, 0x10040040, 0x00001000, 0x00041040,
+    0x10001000, 0x00000040, 0x10000040, 0x10040000,
+    0x10040040, 0x10000000, 0x00040000, 0x10001040,
+    0x00000000, 0x10041040, 0x00040040, 0x10000040,
+    0x10040000, 0x10001000, 0x10001040, 0x00000000,
+    0x10041040, 0x00041000, 0x00041000, 0x00001040,
+    0x00001040, 0x00040040, 0x10000000, 0x10041000
+};
+
+/*
+ * PC1: left and right halves bit-swap
+ */
+static const unsigned long LHs[16] =
+{
+    0x00000000, 0x00000001, 0x00000100, 0x00000101,
+    0x00010000, 0x00010001, 0x00010100, 0x00010101,
+    0x01000000, 0x01000001, 0x01000100, 0x01000101,
+    0x01010000, 0x01010001, 0x01010100, 0x01010101
+};
+
+static const unsigned long RHs[16] =
+{
+    0x00000000, 0x01000000, 0x00010000, 0x01010000,
+    0x00000100, 0x01000100, 0x00010100, 0x01010100,
+    0x00000001, 0x01000001, 0x00010001, 0x01010001,
+    0x00000101, 0x01000101, 0x00010101, 0x01010101,
+};
+
+/*
+ * Initial Permutation macro
+ */
+#define DES_IP(X,Y)                                             \
+{                                                               \
+    T = ((X >>  4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T <<  4);   \
+    T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16);   \
+    T = ((Y >>  2) ^ X) & 0x33333333; X ^= T; Y ^= (T <<  2);   \
+    T = ((Y >>  8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T <<  8);   \
+    Y = ((Y << 1) | (Y >> 31)) & 0xFFFFFFFF;                    \
+    T = (X ^ Y) & 0xAAAAAAAA; Y ^= T; X ^= T;                   \
+    X = ((X << 1) | (X >> 31)) & 0xFFFFFFFF;                    \
+}
+
+/*
+ * Final Permutation macro
+ */
+#define DES_FP(X,Y)                                             \
+{                                                               \
+    X = ((X << 31) | (X >> 1)) & 0xFFFFFFFF;                    \
+    T = (X ^ Y) & 0xAAAAAAAA; X ^= T; Y ^= T;                   \
+    Y = ((Y << 31) | (Y >> 1)) & 0xFFFFFFFF;                    \
+    T = ((Y >>  8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T <<  8);   \
+    T = ((Y >>  2) ^ X) & 0x33333333; X ^= T; Y ^= (T <<  2);   \
+    T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16);   \
+    T = ((X >>  4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T <<  4);   \
+}
+
+/*
+ * DES round macro
+ */
+#define DES_ROUND(X,Y)                          \
+{                                               \
+    T = *SK++ ^ X;                              \
+    Y ^= SB8[ (T      ) & 0x3F ] ^              \
+         SB6[ (T >>  8) & 0x3F ] ^              \
+         SB4[ (T >> 16) & 0x3F ] ^              \
+         SB2[ (T >> 24) & 0x3F ];               \
+                                                \
+    T = *SK++ ^ ((X << 28) | (X >> 4));         \
+    Y ^= SB7[ (T      ) & 0x3F ] ^              \
+         SB5[ (T >>  8) & 0x3F ] ^              \
+         SB3[ (T >> 16) & 0x3F ] ^              \
+         SB1[ (T >> 24) & 0x3F ];               \
+}
+
+#define SWAP(a,b) { unsigned long t = a; a = b; b = t; t = 0; }
+
+static void des_setkey( unsigned long SK[32], unsigned char key[8] )
+{
+    int i;
+    unsigned long X, Y, T;
+
+    GET_ULONG_BE( X, key, 0 );
+    GET_ULONG_BE( Y, key, 4 );
+
+    /*
+     * Permuted Choice 1
+     */
+    T =  ((Y >>  4) ^ X) & 0x0F0F0F0F;  X ^= T; Y ^= (T <<  4);
+    T =  ((Y      ) ^ X) & 0x10101010;  X ^= T; Y ^= (T      );
+
+    X =   (LHs[ (X      ) & 0xF] << 3) | (LHs[ (X >>  8) & 0xF ] << 2)
+        | (LHs[ (X >> 16) & 0xF] << 1) | (LHs[ (X >> 24) & 0xF ]     )
+        | (LHs[ (X >>  5) & 0xF] << 7) | (LHs[ (X >> 13) & 0xF ] << 6)
+        | (LHs[ (X >> 21) & 0xF] << 5) | (LHs[ (X >> 29) & 0xF ] << 4);
+
+    Y =   (RHs[ (Y >>  1) & 0xF] << 3) | (RHs[ (Y >>  9) & 0xF ] << 2)
+        | (RHs[ (Y >> 17) & 0xF] << 1) | (RHs[ (Y >> 25) & 0xF ]     )
+        | (RHs[ (Y >>  4) & 0xF] << 7) | (RHs[ (Y >> 12) & 0xF ] << 6)
+        | (RHs[ (Y >> 20) & 0xF] << 5) | (RHs[ (Y >> 28) & 0xF ] << 4);
+
+    X &= 0x0FFFFFFF;
+    Y &= 0x0FFFFFFF;
+
+    /*
+     * calculate subkeys
+     */
+    for( i = 0; i < 16; i++ )
+    {
+        if( i < 2 || i == 8 || i == 15 )
+        {
+            X = ((X <<  1) | (X >> 27)) & 0x0FFFFFFF;
+            Y = ((Y <<  1) | (Y >> 27)) & 0x0FFFFFFF;
+        }
+        else
+        {
+            X = ((X <<  2) | (X >> 26)) & 0x0FFFFFFF;
+            Y = ((Y <<  2) | (Y >> 26)) & 0x0FFFFFFF;
+        }
+
+        *SK++ =   ((X <<  4) & 0x24000000) | ((X << 28) & 0x10000000)
+                | ((X << 14) & 0x08000000) | ((X << 18) & 0x02080000)
+                | ((X <<  6) & 0x01000000) | ((X <<  9) & 0x00200000)
+                | ((X >>  1) & 0x00100000) | ((X << 10) & 0x00040000)
+                | ((X <<  2) & 0x00020000) | ((X >> 10) & 0x00010000)
+                | ((Y >> 13) & 0x00002000) | ((Y >>  4) & 0x00001000)
+                | ((Y <<  6) & 0x00000800) | ((Y >>  1) & 0x00000400)
+                | ((Y >> 14) & 0x00000200) | ((Y      ) & 0x00000100)
+                | ((Y >>  5) & 0x00000020) | ((Y >> 10) & 0x00000010)
+                | ((Y >>  3) & 0x00000008) | ((Y >> 18) & 0x00000004)
+                | ((Y >> 26) & 0x00000002) | ((Y >> 24) & 0x00000001);
+
+        *SK++ =   ((X << 15) & 0x20000000) | ((X << 17) & 0x10000000)
+                | ((X << 10) & 0x08000000) | ((X << 22) & 0x04000000)
+                | ((X >>  2) & 0x02000000) | ((X <<  1) & 0x01000000)
+                | ((X << 16) & 0x00200000) | ((X << 11) & 0x00100000)
+                | ((X <<  3) & 0x00080000) | ((X >>  6) & 0x00040000)
+                | ((X << 15) & 0x00020000) | ((X >>  4) & 0x00010000)
+                | ((Y >>  2) & 0x00002000) | ((Y <<  8) & 0x00001000)
+                | ((Y >> 14) & 0x00000808) | ((Y >>  9) & 0x00000400)
+                | ((Y      ) & 0x00000200) | ((Y <<  7) & 0x00000100)
+                | ((Y >>  7) & 0x00000020) | ((Y >>  3) & 0x00000011)
+                | ((Y <<  2) & 0x00000004) | ((Y >> 21) & 0x00000002);
+    }
+}
+
+/*
+ * DES key schedule (56-bit, encryption)
+ */
+void des_setkey_enc( des_context *ctx, unsigned char key[8] )
+{
+    des_setkey( ctx->sk, key );
+}
+
+/*
+ * DES key schedule (56-bit, decryption)
+ */
+void des_setkey_dec( des_context *ctx, unsigned char key[8] )
+{
+    int i;
+
+    des_setkey( ctx->sk, key );
+
+    for( i = 0; i < 16; i += 2 )
+    {
+        SWAP( ctx->sk[i    ], ctx->sk[30 - i] );
+        SWAP( ctx->sk[i + 1], ctx->sk[31 - i] );
+    }
+}
+
+/*
+ * DES-ECB block encryption/decryption
+ */
+void des_crypt_ecb( des_context *ctx,
+                    const unsigned char input[8],
+                    unsigned char output[8] )
+{
+    int i;
+    unsigned long X, Y, T, *SK;
+
+    SK = ctx->sk;
+
+    GET_ULONG_BE( X, input, 0 );
+    GET_ULONG_BE( Y, input, 4 );
+
+    DES_IP( X, Y );
+
+    for( i = 0; i < 8; i++ )
+    {
+        DES_ROUND( Y, X );
+        DES_ROUND( X, Y );
+    }
+
+    DES_FP( Y, X );
+
+    PUT_ULONG_BE( Y, output, 0 );
+    PUT_ULONG_BE( X, output, 4 );
+}
+
+#endif /* PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_DES */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/polarssl/md4.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/polarssl/md4.c
new file mode 100644 (file)
index 0000000..b1701a0
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ *  RFC 1186/1320 compliant MD4 implementation
+ *
+ *  Based on XySSL: Copyright (C) 2006-2008  Christophe Devine
+ *
+ *  Copyright (C) 2009  Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *  
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * 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.
+ *    * Neither the names of PolarSSL or XySSL nor the names of its contributors
+ *      may be used to endorse or promote products derived from this software
+ *      without specific prior written permission.
+ *  
+ *  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
+ *  OWNER 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.
+ */
+/*
+ *  The MD4 algorithm was designed by Ron Rivest in 1990.
+ *
+ *  http://www.ietf.org/rfc/rfc1186.txt
+ *  http://www.ietf.org/rfc/rfc1320.txt
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_MD4
+
+#include "netif/ppp/polarssl/md4.h"
+
+#include <string.h>
+
+/*
+ * 32-bit integer manipulation macros (little endian)
+ */
+#ifndef GET_ULONG_LE
+#define GET_ULONG_LE(n,b,i)                             \
+{                                                       \
+    (n) = ( (unsigned long) (b)[(i)    ]       )        \
+        | ( (unsigned long) (b)[(i) + 1] <<  8 )        \
+        | ( (unsigned long) (b)[(i) + 2] << 16 )        \
+        | ( (unsigned long) (b)[(i) + 3] << 24 );       \
+}
+#endif
+
+#ifndef PUT_ULONG_LE
+#define PUT_ULONG_LE(n,b,i)                             \
+{                                                       \
+    (b)[(i)    ] = (unsigned char) ( (n)       );       \
+    (b)[(i) + 1] = (unsigned char) ( (n) >>  8 );       \
+    (b)[(i) + 2] = (unsigned char) ( (n) >> 16 );       \
+    (b)[(i) + 3] = (unsigned char) ( (n) >> 24 );       \
+}
+#endif
+
+/*
+ * MD4 context setup
+ */
+void md4_starts( md4_context *ctx )
+{
+    ctx->total[0] = 0;
+    ctx->total[1] = 0;
+
+    ctx->state[0] = 0x67452301;
+    ctx->state[1] = 0xEFCDAB89;
+    ctx->state[2] = 0x98BADCFE;
+    ctx->state[3] = 0x10325476;
+}
+
+static void md4_process( md4_context *ctx, const unsigned char data[64] )
+{
+    unsigned long X[16], A, B, C, D;
+
+    GET_ULONG_LE( X[ 0], data,  0 );
+    GET_ULONG_LE( X[ 1], data,  4 );
+    GET_ULONG_LE( X[ 2], data,  8 );
+    GET_ULONG_LE( X[ 3], data, 12 );
+    GET_ULONG_LE( X[ 4], data, 16 );
+    GET_ULONG_LE( X[ 5], data, 20 );
+    GET_ULONG_LE( X[ 6], data, 24 );
+    GET_ULONG_LE( X[ 7], data, 28 );
+    GET_ULONG_LE( X[ 8], data, 32 );
+    GET_ULONG_LE( X[ 9], data, 36 );
+    GET_ULONG_LE( X[10], data, 40 );
+    GET_ULONG_LE( X[11], data, 44 );
+    GET_ULONG_LE( X[12], data, 48 );
+    GET_ULONG_LE( X[13], data, 52 );
+    GET_ULONG_LE( X[14], data, 56 );
+    GET_ULONG_LE( X[15], data, 60 );
+
+#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
+
+    A = ctx->state[0];
+    B = ctx->state[1];
+    C = ctx->state[2];
+    D = ctx->state[3];
+
+#define F(x, y, z) ((x & y) | ((~x) & z))
+#define P(a,b,c,d,x,s) { a += F(b,c,d) + x; a = S(a,s); }
+
+    P( A, B, C, D, X[ 0],  3 );
+    P( D, A, B, C, X[ 1],  7 );
+    P( C, D, A, B, X[ 2], 11 );
+    P( B, C, D, A, X[ 3], 19 );
+    P( A, B, C, D, X[ 4],  3 );
+    P( D, A, B, C, X[ 5],  7 );
+    P( C, D, A, B, X[ 6], 11 );
+    P( B, C, D, A, X[ 7], 19 );
+    P( A, B, C, D, X[ 8],  3 );
+    P( D, A, B, C, X[ 9],  7 );
+    P( C, D, A, B, X[10], 11 );
+    P( B, C, D, A, X[11], 19 );
+    P( A, B, C, D, X[12],  3 );
+    P( D, A, B, C, X[13],  7 );
+    P( C, D, A, B, X[14], 11 );
+    P( B, C, D, A, X[15], 19 );
+
+#undef P
+#undef F
+
+#define F(x,y,z) ((x & y) | (x & z) | (y & z))
+#define P(a,b,c,d,x,s) { a += F(b,c,d) + x + 0x5A827999; a = S(a,s); }
+
+    P( A, B, C, D, X[ 0],  3 );
+    P( D, A, B, C, X[ 4],  5 );
+    P( C, D, A, B, X[ 8],  9 );
+    P( B, C, D, A, X[12], 13 );
+    P( A, B, C, D, X[ 1],  3 );
+    P( D, A, B, C, X[ 5],  5 );
+    P( C, D, A, B, X[ 9],  9 );
+    P( B, C, D, A, X[13], 13 );
+    P( A, B, C, D, X[ 2],  3 );
+    P( D, A, B, C, X[ 6],  5 );
+    P( C, D, A, B, X[10],  9 );
+    P( B, C, D, A, X[14], 13 );
+    P( A, B, C, D, X[ 3],  3 );
+    P( D, A, B, C, X[ 7],  5 );
+    P( C, D, A, B, X[11],  9 );
+    P( B, C, D, A, X[15], 13 );
+
+#undef P
+#undef F
+
+#define F(x,y,z) (x ^ y ^ z)
+#define P(a,b,c,d,x,s) { a += F(b,c,d) + x + 0x6ED9EBA1; a = S(a,s); }
+
+    P( A, B, C, D, X[ 0],  3 );
+    P( D, A, B, C, X[ 8],  9 );
+    P( C, D, A, B, X[ 4], 11 );
+    P( B, C, D, A, X[12], 15 );
+    P( A, B, C, D, X[ 2],  3 );
+    P( D, A, B, C, X[10],  9 );
+    P( C, D, A, B, X[ 6], 11 );
+    P( B, C, D, A, X[14], 15 );
+    P( A, B, C, D, X[ 1],  3 );
+    P( D, A, B, C, X[ 9],  9 );
+    P( C, D, A, B, X[ 5], 11 );
+    P( B, C, D, A, X[13], 15 );
+    P( A, B, C, D, X[ 3],  3 );
+    P( D, A, B, C, X[11],  9 );
+    P( C, D, A, B, X[ 7], 11 );
+    P( B, C, D, A, X[15], 15 );
+
+#undef F
+#undef P
+
+    ctx->state[0] += A;
+    ctx->state[1] += B;
+    ctx->state[2] += C;
+    ctx->state[3] += D;
+}
+
+/*
+ * MD4 process buffer
+ */
+void md4_update( md4_context *ctx, const unsigned char *input, int ilen )
+{
+    int fill;
+    unsigned long left;
+
+    if( ilen <= 0 )
+        return;
+
+    left = ctx->total[0] & 0x3F;
+    fill = 64 - left;
+
+    ctx->total[0] += ilen;
+    ctx->total[0] &= 0xFFFFFFFF;
+
+    if( ctx->total[0] < (unsigned long) ilen )
+        ctx->total[1]++;
+
+    if( left && ilen >= fill )
+    {
+        MEMCPY( (void *) (ctx->buffer + left),
+                input, fill );
+        md4_process( ctx, ctx->buffer );
+        input += fill;
+        ilen  -= fill;
+        left = 0;
+    }
+
+    while( ilen >= 64 )
+    {
+        md4_process( ctx, input );
+        input += 64;
+        ilen  -= 64;
+    }
+
+    if( ilen > 0 )
+    {
+        MEMCPY( (void *) (ctx->buffer + left),
+                input, ilen );
+    }
+}
+
+static const unsigned char md4_padding[64] =
+{
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/*
+ * MD4 final digest
+ */
+void md4_finish( md4_context *ctx, unsigned char output[16] )
+{
+    unsigned long last, padn;
+    unsigned long high, low;
+    unsigned char msglen[8];
+
+    high = ( ctx->total[0] >> 29 )
+         | ( ctx->total[1] <<  3 );
+    low  = ( ctx->total[0] <<  3 );
+
+    PUT_ULONG_LE( low,  msglen, 0 );
+    PUT_ULONG_LE( high, msglen, 4 );
+
+    last = ctx->total[0] & 0x3F;
+    padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
+
+    md4_update( ctx, md4_padding, padn );
+    md4_update( ctx, msglen, 8 );
+
+    PUT_ULONG_LE( ctx->state[0], output,  0 );
+    PUT_ULONG_LE( ctx->state[1], output,  4 );
+    PUT_ULONG_LE( ctx->state[2], output,  8 );
+    PUT_ULONG_LE( ctx->state[3], output, 12 );
+}
+
+/*
+ * output = MD4( input buffer )
+ */
+void md4( unsigned char *input, int ilen, unsigned char output[16] )
+{
+    md4_context ctx;
+
+    md4_starts( &ctx );
+    md4_update( &ctx, input, ilen );
+    md4_finish( &ctx, output );
+}
+
+#endif /* PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_MD4 */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/polarssl/md5.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/polarssl/md5.c
new file mode 100644 (file)
index 0000000..1ec4d81
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ *  RFC 1321 compliant MD5 implementation
+ *
+ *  Based on XySSL: Copyright (C) 2006-2008  Christophe Devine
+ *
+ *  Copyright (C) 2009  Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *  
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * 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.
+ *    * Neither the names of PolarSSL or XySSL nor the names of its contributors
+ *      may be used to endorse or promote products derived from this software
+ *      without specific prior written permission.
+ *  
+ *  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
+ *  OWNER 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.
+ */
+/*
+ *  The MD5 algorithm was designed by Ron Rivest in 1991.
+ *
+ *  http://www.ietf.org/rfc/rfc1321.txt
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_MD5
+
+#include "netif/ppp/polarssl/md5.h"
+
+#include <string.h>
+
+/*
+ * 32-bit integer manipulation macros (little endian)
+ */
+#ifndef GET_ULONG_LE
+#define GET_ULONG_LE(n,b,i)                             \
+{                                                       \
+    (n) = ( (unsigned long) (b)[(i)    ]       )        \
+        | ( (unsigned long) (b)[(i) + 1] <<  8 )        \
+        | ( (unsigned long) (b)[(i) + 2] << 16 )        \
+        | ( (unsigned long) (b)[(i) + 3] << 24 );       \
+}
+#endif
+
+#ifndef PUT_ULONG_LE
+#define PUT_ULONG_LE(n,b,i)                             \
+{                                                       \
+    (b)[(i)    ] = (unsigned char) ( (n)       );       \
+    (b)[(i) + 1] = (unsigned char) ( (n) >>  8 );       \
+    (b)[(i) + 2] = (unsigned char) ( (n) >> 16 );       \
+    (b)[(i) + 3] = (unsigned char) ( (n) >> 24 );       \
+}
+#endif
+
+/*
+ * MD5 context setup
+ */
+void md5_starts( md5_context *ctx )
+{
+    ctx->total[0] = 0;
+    ctx->total[1] = 0;
+
+    ctx->state[0] = 0x67452301;
+    ctx->state[1] = 0xEFCDAB89;
+    ctx->state[2] = 0x98BADCFE;
+    ctx->state[3] = 0x10325476;
+}
+
+static void md5_process( md5_context *ctx, const unsigned char data[64] )
+{
+    unsigned long X[16], A, B, C, D;
+
+    GET_ULONG_LE( X[ 0], data,  0 );
+    GET_ULONG_LE( X[ 1], data,  4 );
+    GET_ULONG_LE( X[ 2], data,  8 );
+    GET_ULONG_LE( X[ 3], data, 12 );
+    GET_ULONG_LE( X[ 4], data, 16 );
+    GET_ULONG_LE( X[ 5], data, 20 );
+    GET_ULONG_LE( X[ 6], data, 24 );
+    GET_ULONG_LE( X[ 7], data, 28 );
+    GET_ULONG_LE( X[ 8], data, 32 );
+    GET_ULONG_LE( X[ 9], data, 36 );
+    GET_ULONG_LE( X[10], data, 40 );
+    GET_ULONG_LE( X[11], data, 44 );
+    GET_ULONG_LE( X[12], data, 48 );
+    GET_ULONG_LE( X[13], data, 52 );
+    GET_ULONG_LE( X[14], data, 56 );
+    GET_ULONG_LE( X[15], data, 60 );
+
+#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
+
+#define P(a,b,c,d,k,s,t)                                \
+{                                                       \
+    a += F(b,c,d) + X[k] + t; a = S(a,s) + b;           \
+}
+
+    A = ctx->state[0];
+    B = ctx->state[1];
+    C = ctx->state[2];
+    D = ctx->state[3];
+
+#define F(x,y,z) (z ^ (x & (y ^ z)))
+
+    P( A, B, C, D,  0,  7, 0xD76AA478 );
+    P( D, A, B, C,  1, 12, 0xE8C7B756 );
+    P( C, D, A, B,  2, 17, 0x242070DB );
+    P( B, C, D, A,  3, 22, 0xC1BDCEEE );
+    P( A, B, C, D,  4,  7, 0xF57C0FAF );
+    P( D, A, B, C,  5, 12, 0x4787C62A );
+    P( C, D, A, B,  6, 17, 0xA8304613 );
+    P( B, C, D, A,  7, 22, 0xFD469501 );
+    P( A, B, C, D,  8,  7, 0x698098D8 );
+    P( D, A, B, C,  9, 12, 0x8B44F7AF );
+    P( C, D, A, B, 10, 17, 0xFFFF5BB1 );
+    P( B, C, D, A, 11, 22, 0x895CD7BE );
+    P( A, B, C, D, 12,  7, 0x6B901122 );
+    P( D, A, B, C, 13, 12, 0xFD987193 );
+    P( C, D, A, B, 14, 17, 0xA679438E );
+    P( B, C, D, A, 15, 22, 0x49B40821 );
+
+#undef F
+
+#define F(x,y,z) (y ^ (z & (x ^ y)))
+
+    P( A, B, C, D,  1,  5, 0xF61E2562 );
+    P( D, A, B, C,  6,  9, 0xC040B340 );
+    P( C, D, A, B, 11, 14, 0x265E5A51 );
+    P( B, C, D, A,  0, 20, 0xE9B6C7AA );
+    P( A, B, C, D,  5,  5, 0xD62F105D );
+    P( D, A, B, C, 10,  9, 0x02441453 );
+    P( C, D, A, B, 15, 14, 0xD8A1E681 );
+    P( B, C, D, A,  4, 20, 0xE7D3FBC8 );
+    P( A, B, C, D,  9,  5, 0x21E1CDE6 );
+    P( D, A, B, C, 14,  9, 0xC33707D6 );
+    P( C, D, A, B,  3, 14, 0xF4D50D87 );
+    P( B, C, D, A,  8, 20, 0x455A14ED );
+    P( A, B, C, D, 13,  5, 0xA9E3E905 );
+    P( D, A, B, C,  2,  9, 0xFCEFA3F8 );
+    P( C, D, A, B,  7, 14, 0x676F02D9 );
+    P( B, C, D, A, 12, 20, 0x8D2A4C8A );
+
+#undef F
+    
+#define F(x,y,z) (x ^ y ^ z)
+
+    P( A, B, C, D,  5,  4, 0xFFFA3942 );
+    P( D, A, B, C,  8, 11, 0x8771F681 );
+    P( C, D, A, B, 11, 16, 0x6D9D6122 );
+    P( B, C, D, A, 14, 23, 0xFDE5380C );
+    P( A, B, C, D,  1,  4, 0xA4BEEA44 );
+    P( D, A, B, C,  4, 11, 0x4BDECFA9 );
+    P( C, D, A, B,  7, 16, 0xF6BB4B60 );
+    P( B, C, D, A, 10, 23, 0xBEBFBC70 );
+    P( A, B, C, D, 13,  4, 0x289B7EC6 );
+    P( D, A, B, C,  0, 11, 0xEAA127FA );
+    P( C, D, A, B,  3, 16, 0xD4EF3085 );
+    P( B, C, D, A,  6, 23, 0x04881D05 );
+    P( A, B, C, D,  9,  4, 0xD9D4D039 );
+    P( D, A, B, C, 12, 11, 0xE6DB99E5 );
+    P( C, D, A, B, 15, 16, 0x1FA27CF8 );
+    P( B, C, D, A,  2, 23, 0xC4AC5665 );
+
+#undef F
+
+#define F(x,y,z) (y ^ (x | ~z))
+
+    P( A, B, C, D,  0,  6, 0xF4292244 );
+    P( D, A, B, C,  7, 10, 0x432AFF97 );
+    P( C, D, A, B, 14, 15, 0xAB9423A7 );
+    P( B, C, D, A,  5, 21, 0xFC93A039 );
+    P( A, B, C, D, 12,  6, 0x655B59C3 );
+    P( D, A, B, C,  3, 10, 0x8F0CCC92 );
+    P( C, D, A, B, 10, 15, 0xFFEFF47D );
+    P( B, C, D, A,  1, 21, 0x85845DD1 );
+    P( A, B, C, D,  8,  6, 0x6FA87E4F );
+    P( D, A, B, C, 15, 10, 0xFE2CE6E0 );
+    P( C, D, A, B,  6, 15, 0xA3014314 );
+    P( B, C, D, A, 13, 21, 0x4E0811A1 );
+    P( A, B, C, D,  4,  6, 0xF7537E82 );
+    P( D, A, B, C, 11, 10, 0xBD3AF235 );
+    P( C, D, A, B,  2, 15, 0x2AD7D2BB );
+    P( B, C, D, A,  9, 21, 0xEB86D391 );
+
+#undef F
+
+    ctx->state[0] += A;
+    ctx->state[1] += B;
+    ctx->state[2] += C;
+    ctx->state[3] += D;
+}
+
+/*
+ * MD5 process buffer
+ */
+void md5_update( md5_context *ctx, const unsigned char *input, int ilen )
+{
+    int fill;
+    unsigned long left;
+
+    if( ilen <= 0 )
+        return;
+
+    left = ctx->total[0] & 0x3F;
+    fill = 64 - left;
+
+    ctx->total[0] += ilen;
+    ctx->total[0] &= 0xFFFFFFFF;
+
+    if( ctx->total[0] < (unsigned long) ilen )
+        ctx->total[1]++;
+
+    if( left && ilen >= fill )
+    {
+        MEMCPY( (void *) (ctx->buffer + left),
+                input, fill );
+        md5_process( ctx, ctx->buffer );
+        input += fill;
+        ilen  -= fill;
+        left = 0;
+    }
+
+    while( ilen >= 64 )
+    {
+        md5_process( ctx, input );
+        input += 64;
+        ilen  -= 64;
+    }
+
+    if( ilen > 0 )
+    {
+        MEMCPY( (void *) (ctx->buffer + left),
+                input, ilen );
+    }
+}
+
+static const unsigned char md5_padding[64] =
+{
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/*
+ * MD5 final digest
+ */
+void md5_finish( md5_context *ctx, unsigned char output[16] )
+{
+    unsigned long last, padn;
+    unsigned long high, low;
+    unsigned char msglen[8];
+
+    high = ( ctx->total[0] >> 29 )
+         | ( ctx->total[1] <<  3 );
+    low  = ( ctx->total[0] <<  3 );
+
+    PUT_ULONG_LE( low,  msglen, 0 );
+    PUT_ULONG_LE( high, msglen, 4 );
+
+    last = ctx->total[0] & 0x3F;
+    padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
+
+    md5_update( ctx, md5_padding, padn );
+    md5_update( ctx, msglen, 8 );
+
+    PUT_ULONG_LE( ctx->state[0], output,  0 );
+    PUT_ULONG_LE( ctx->state[1], output,  4 );
+    PUT_ULONG_LE( ctx->state[2], output,  8 );
+    PUT_ULONG_LE( ctx->state[3], output, 12 );
+}
+
+/*
+ * output = MD5( input buffer )
+ */
+void md5( unsigned char *input, int ilen, unsigned char output[16] )
+{
+    md5_context ctx;
+
+    md5_starts( &ctx );
+    md5_update( &ctx, input, ilen );
+    md5_finish( &ctx, output );
+}
+
+#endif /* PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_MD5 */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/polarssl/sha1.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/polarssl/sha1.c
new file mode 100644 (file)
index 0000000..c2192ea
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ *  FIPS-180-1 compliant SHA-1 implementation
+ *
+ *  Based on XySSL: Copyright (C) 2006-2008  Christophe Devine
+ *
+ *  Copyright (C) 2009  Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *  
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * 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.
+ *    * Neither the names of PolarSSL or XySSL nor the names of its contributors
+ *      may be used to endorse or promote products derived from this software
+ *      without specific prior written permission.
+ *  
+ *  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
+ *  OWNER 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.
+ */
+/*
+ *  The SHA-1 standard was published by NIST in 1993.
+ *
+ *  http://www.itl.nist.gov/fipspubs/fip180-1.htm
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_SHA1
+
+#include "netif/ppp/polarssl/sha1.h"
+
+#include <string.h>
+
+/*
+ * 32-bit integer manipulation macros (big endian)
+ */
+#ifndef GET_ULONG_BE
+#define GET_ULONG_BE(n,b,i)                             \
+{                                                       \
+    (n) = ( (unsigned long) (b)[(i)    ] << 24 )        \
+        | ( (unsigned long) (b)[(i) + 1] << 16 )        \
+        | ( (unsigned long) (b)[(i) + 2] <<  8 )        \
+        | ( (unsigned long) (b)[(i) + 3]       );       \
+}
+#endif
+
+#ifndef PUT_ULONG_BE
+#define PUT_ULONG_BE(n,b,i)                             \
+{                                                       \
+    (b)[(i)    ] = (unsigned char) ( (n) >> 24 );       \
+    (b)[(i) + 1] = (unsigned char) ( (n) >> 16 );       \
+    (b)[(i) + 2] = (unsigned char) ( (n) >>  8 );       \
+    (b)[(i) + 3] = (unsigned char) ( (n)       );       \
+}
+#endif
+
+/*
+ * SHA-1 context setup
+ */
+void sha1_starts( sha1_context *ctx )
+{
+    ctx->total[0] = 0;
+    ctx->total[1] = 0;
+
+    ctx->state[0] = 0x67452301;
+    ctx->state[1] = 0xEFCDAB89;
+    ctx->state[2] = 0x98BADCFE;
+    ctx->state[3] = 0x10325476;
+    ctx->state[4] = 0xC3D2E1F0;
+}
+
+static void sha1_process( sha1_context *ctx, const unsigned char data[64] )
+{
+    unsigned long temp, W[16], A, B, C, D, E;
+
+    GET_ULONG_BE( W[ 0], data,  0 );
+    GET_ULONG_BE( W[ 1], data,  4 );
+    GET_ULONG_BE( W[ 2], data,  8 );
+    GET_ULONG_BE( W[ 3], data, 12 );
+    GET_ULONG_BE( W[ 4], data, 16 );
+    GET_ULONG_BE( W[ 5], data, 20 );
+    GET_ULONG_BE( W[ 6], data, 24 );
+    GET_ULONG_BE( W[ 7], data, 28 );
+    GET_ULONG_BE( W[ 8], data, 32 );
+    GET_ULONG_BE( W[ 9], data, 36 );
+    GET_ULONG_BE( W[10], data, 40 );
+    GET_ULONG_BE( W[11], data, 44 );
+    GET_ULONG_BE( W[12], data, 48 );
+    GET_ULONG_BE( W[13], data, 52 );
+    GET_ULONG_BE( W[14], data, 56 );
+    GET_ULONG_BE( W[15], data, 60 );
+
+#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
+
+#define R(t)                                            \
+(                                                       \
+    temp = W[(t -  3) & 0x0F] ^ W[(t - 8) & 0x0F] ^     \
+           W[(t - 14) & 0x0F] ^ W[ t      & 0x0F],      \
+    ( W[t & 0x0F] = S(temp,1) )                         \
+)
+
+#define P(a,b,c,d,e,x)                                  \
+{                                                       \
+    e += S(a,5) + F(b,c,d) + K + x; b = S(b,30);        \
+}
+
+    A = ctx->state[0];
+    B = ctx->state[1];
+    C = ctx->state[2];
+    D = ctx->state[3];
+    E = ctx->state[4];
+
+#define F(x,y,z) (z ^ (x & (y ^ z)))
+#define K 0x5A827999
+
+    P( A, B, C, D, E, W[0]  );
+    P( E, A, B, C, D, W[1]  );
+    P( D, E, A, B, C, W[2]  );
+    P( C, D, E, A, B, W[3]  );
+    P( B, C, D, E, A, W[4]  );
+    P( A, B, C, D, E, W[5]  );
+    P( E, A, B, C, D, W[6]  );
+    P( D, E, A, B, C, W[7]  );
+    P( C, D, E, A, B, W[8]  );
+    P( B, C, D, E, A, W[9]  );
+    P( A, B, C, D, E, W[10] );
+    P( E, A, B, C, D, W[11] );
+    P( D, E, A, B, C, W[12] );
+    P( C, D, E, A, B, W[13] );
+    P( B, C, D, E, A, W[14] );
+    P( A, B, C, D, E, W[15] );
+    P( E, A, B, C, D, R(16) );
+    P( D, E, A, B, C, R(17) );
+    P( C, D, E, A, B, R(18) );
+    P( B, C, D, E, A, R(19) );
+
+#undef K
+#undef F
+
+#define F(x,y,z) (x ^ y ^ z)
+#define K 0x6ED9EBA1
+
+    P( A, B, C, D, E, R(20) );
+    P( E, A, B, C, D, R(21) );
+    P( D, E, A, B, C, R(22) );
+    P( C, D, E, A, B, R(23) );
+    P( B, C, D, E, A, R(24) );
+    P( A, B, C, D, E, R(25) );
+    P( E, A, B, C, D, R(26) );
+    P( D, E, A, B, C, R(27) );
+    P( C, D, E, A, B, R(28) );
+    P( B, C, D, E, A, R(29) );
+    P( A, B, C, D, E, R(30) );
+    P( E, A, B, C, D, R(31) );
+    P( D, E, A, B, C, R(32) );
+    P( C, D, E, A, B, R(33) );
+    P( B, C, D, E, A, R(34) );
+    P( A, B, C, D, E, R(35) );
+    P( E, A, B, C, D, R(36) );
+    P( D, E, A, B, C, R(37) );
+    P( C, D, E, A, B, R(38) );
+    P( B, C, D, E, A, R(39) );
+
+#undef K
+#undef F
+
+#define F(x,y,z) ((x & y) | (z & (x | y)))
+#define K 0x8F1BBCDC
+
+    P( A, B, C, D, E, R(40) );
+    P( E, A, B, C, D, R(41) );
+    P( D, E, A, B, C, R(42) );
+    P( C, D, E, A, B, R(43) );
+    P( B, C, D, E, A, R(44) );
+    P( A, B, C, D, E, R(45) );
+    P( E, A, B, C, D, R(46) );
+    P( D, E, A, B, C, R(47) );
+    P( C, D, E, A, B, R(48) );
+    P( B, C, D, E, A, R(49) );
+    P( A, B, C, D, E, R(50) );
+    P( E, A, B, C, D, R(51) );
+    P( D, E, A, B, C, R(52) );
+    P( C, D, E, A, B, R(53) );
+    P( B, C, D, E, A, R(54) );
+    P( A, B, C, D, E, R(55) );
+    P( E, A, B, C, D, R(56) );
+    P( D, E, A, B, C, R(57) );
+    P( C, D, E, A, B, R(58) );
+    P( B, C, D, E, A, R(59) );
+
+#undef K
+#undef F
+
+#define F(x,y,z) (x ^ y ^ z)
+#define K 0xCA62C1D6
+
+    P( A, B, C, D, E, R(60) );
+    P( E, A, B, C, D, R(61) );
+    P( D, E, A, B, C, R(62) );
+    P( C, D, E, A, B, R(63) );
+    P( B, C, D, E, A, R(64) );
+    P( A, B, C, D, E, R(65) );
+    P( E, A, B, C, D, R(66) );
+    P( D, E, A, B, C, R(67) );
+    P( C, D, E, A, B, R(68) );
+    P( B, C, D, E, A, R(69) );
+    P( A, B, C, D, E, R(70) );
+    P( E, A, B, C, D, R(71) );
+    P( D, E, A, B, C, R(72) );
+    P( C, D, E, A, B, R(73) );
+    P( B, C, D, E, A, R(74) );
+    P( A, B, C, D, E, R(75) );
+    P( E, A, B, C, D, R(76) );
+    P( D, E, A, B, C, R(77) );
+    P( C, D, E, A, B, R(78) );
+    P( B, C, D, E, A, R(79) );
+
+#undef K
+#undef F
+
+    ctx->state[0] += A;
+    ctx->state[1] += B;
+    ctx->state[2] += C;
+    ctx->state[3] += D;
+    ctx->state[4] += E;
+}
+
+/*
+ * SHA-1 process buffer
+ */
+void sha1_update( sha1_context *ctx, const unsigned char *input, int ilen )
+{
+    int fill;
+    unsigned long left;
+
+    if( ilen <= 0 )
+        return;
+
+    left = ctx->total[0] & 0x3F;
+    fill = 64 - left;
+
+    ctx->total[0] += ilen;
+    ctx->total[0] &= 0xFFFFFFFF;
+
+    if( ctx->total[0] < (unsigned long) ilen )
+        ctx->total[1]++;
+
+    if( left && ilen >= fill )
+    {
+        MEMCPY( (void *) (ctx->buffer + left),
+                input, fill );
+        sha1_process( ctx, ctx->buffer );
+        input += fill;
+        ilen  -= fill;
+        left = 0;
+    }
+
+    while( ilen >= 64 )
+    {
+        sha1_process( ctx, input );
+        input += 64;
+        ilen  -= 64;
+    }
+
+    if( ilen > 0 )
+    {
+        MEMCPY( (void *) (ctx->buffer + left),
+                input, ilen );
+    }
+}
+
+static const unsigned char sha1_padding[64] =
+{
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/*
+ * SHA-1 final digest
+ */
+void sha1_finish( sha1_context *ctx, unsigned char output[20] )
+{
+    unsigned long last, padn;
+    unsigned long high, low;
+    unsigned char msglen[8];
+
+    high = ( ctx->total[0] >> 29 )
+         | ( ctx->total[1] <<  3 );
+    low  = ( ctx->total[0] <<  3 );
+
+    PUT_ULONG_BE( high, msglen, 0 );
+    PUT_ULONG_BE( low,  msglen, 4 );
+
+    last = ctx->total[0] & 0x3F;
+    padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
+
+    sha1_update( ctx, sha1_padding, padn );
+    sha1_update( ctx, msglen, 8 );
+
+    PUT_ULONG_BE( ctx->state[0], output,  0 );
+    PUT_ULONG_BE( ctx->state[1], output,  4 );
+    PUT_ULONG_BE( ctx->state[2], output,  8 );
+    PUT_ULONG_BE( ctx->state[3], output, 12 );
+    PUT_ULONG_BE( ctx->state[4], output, 16 );
+}
+
+/*
+ * output = SHA-1( input buffer )
+ */
+void sha1( unsigned char *input, int ilen, unsigned char output[20] )
+{
+    sha1_context ctx;
+
+    sha1_starts( &ctx );
+    sha1_update( &ctx, input, ilen );
+    sha1_finish( &ctx, output );
+}
+
+#endif /* PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_SHA1 */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/ppp.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/ppp.c
new file mode 100644 (file)
index 0000000..be58553
--- /dev/null
@@ -0,0 +1,1631 @@
+/*****************************************************************************
+* ppp.c - Network Point to Point Protocol program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE 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 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.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+*   Ported to lwIP.
+* 97-11-05 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+*   Original.
+*****************************************************************************/
+
+/*
+ * ppp_defs.h - PPP definitions.
+ *
+ * if_pppvar.h - private structures and declarations for PPP.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies.  This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ */
+
+/*
+ * if_ppp.h - Point-to-Point Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/**
+ * @defgroup ppp PPP
+ * @ingroup netifs
+ * @verbinclude "ppp.txt"
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/stats.h"
+#include "lwip/sys.h"
+#include "lwip/tcpip.h"
+#include "lwip/api.h"
+#include "lwip/snmp.h"
+#include "lwip/ip4.h" /* for ip4_input() */
+#if PPP_IPV6_SUPPORT
+#include "lwip/ip6.h" /* for ip6_input() */
+#endif /* PPP_IPV6_SUPPORT */
+#include "lwip/dns.h"
+
+#include "netif/ppp/ppp_impl.h"
+#include "netif/ppp/pppos.h"
+
+#include "netif/ppp/fsm.h"
+#include "netif/ppp/lcp.h"
+#include "netif/ppp/magic.h"
+
+#if PAP_SUPPORT
+#include "netif/ppp/upap.h"
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+#include "netif/ppp/chap-new.h"
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+#include "netif/ppp/eap.h"
+#endif /* EAP_SUPPORT */
+#if CCP_SUPPORT
+#include "netif/ppp/ccp.h"
+#endif /* CCP_SUPPORT */
+#if MPPE_SUPPORT
+#include "netif/ppp/mppe.h"
+#endif /* MPPE_SUPPORT */
+#if ECP_SUPPORT
+#include "netif/ppp/ecp.h"
+#endif /* EAP_SUPPORT */
+#if VJ_SUPPORT
+#include "netif/ppp/vj.h"
+#endif /* VJ_SUPPORT */
+#if PPP_IPV4_SUPPORT
+#include "netif/ppp/ipcp.h"
+#endif /* PPP_IPV4_SUPPORT */
+#if PPP_IPV6_SUPPORT
+#include "netif/ppp/ipv6cp.h"
+#endif /* PPP_IPV6_SUPPORT */
+
+/*************************/
+/*** LOCAL DEFINITIONS ***/
+/*************************/
+
+/* Memory pools */
+#if PPPOS_SUPPORT
+LWIP_MEMPOOL_PROTOTYPE(PPPOS_PCB);
+#endif
+#if PPPOE_SUPPORT
+LWIP_MEMPOOL_PROTOTYPE(PPPOE_IF);
+#endif
+#if PPPOL2TP_SUPPORT
+LWIP_MEMPOOL_PROTOTYPE(PPPOL2TP_PCB);
+#endif
+#if LWIP_PPP_API && LWIP_MPU_COMPATIBLE
+LWIP_MEMPOOL_PROTOTYPE(PPPAPI_MSG);
+#endif
+LWIP_MEMPOOL_DECLARE(PPP_PCB, MEMP_NUM_PPP_PCB, sizeof(ppp_pcb), "PPP_PCB")
+
+/* FIXME: add stats per PPP session */
+#if PPP_STATS_SUPPORT
+static struct timeval start_time; /* Time when link was started. */
+static struct pppd_stats old_link_stats;
+struct pppd_stats link_stats;
+unsigned link_connect_time;
+int link_stats_valid;
+#endif /* PPP_STATS_SUPPORT */
+
+/*
+ * PPP Data Link Layer "protocol" table.
+ * One entry per supported protocol.
+ * The last entry must be NULL.
+ */
+const struct protent* const protocols[] = {
+    &lcp_protent,
+#if PAP_SUPPORT
+    &pap_protent,
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+    &chap_protent,
+#endif /* CHAP_SUPPORT */
+#if CBCP_SUPPORT
+    &cbcp_protent,
+#endif /* CBCP_SUPPORT */
+#if PPP_IPV4_SUPPORT
+    &ipcp_protent,
+#endif /* PPP_IPV4_SUPPORT */
+#if PPP_IPV6_SUPPORT
+    &ipv6cp_protent,
+#endif /* PPP_IPV6_SUPPORT */
+#if CCP_SUPPORT
+    &ccp_protent,
+#endif /* CCP_SUPPORT */
+#if ECP_SUPPORT
+    &ecp_protent,
+#endif /* ECP_SUPPORT */
+#ifdef AT_CHANGE
+    &atcp_protent,
+#endif /* AT_CHANGE */
+#if EAP_SUPPORT
+    &eap_protent,
+#endif /* EAP_SUPPORT */
+    NULL
+};
+
+/* Prototypes for procedures local to this file. */
+static void ppp_do_connect(void *arg);
+static err_t ppp_netif_init_cb(struct netif *netif);
+#if PPP_IPV4_SUPPORT
+static err_t ppp_netif_output_ip4(struct netif *netif, struct pbuf *pb, const ip4_addr_t *ipaddr);
+#endif /* PPP_IPV4_SUPPORT */
+#if PPP_IPV6_SUPPORT
+static err_t ppp_netif_output_ip6(struct netif *netif, struct pbuf *pb, const ip6_addr_t *ipaddr);
+#endif /* PPP_IPV6_SUPPORT */
+static err_t ppp_netif_output(struct netif *netif, struct pbuf *pb, u16_t protocol);
+
+/***********************************/
+/*** PUBLIC FUNCTION DEFINITIONS ***/
+/***********************************/
+#if PPP_AUTH_SUPPORT
+void ppp_set_auth(ppp_pcb *pcb, u8_t authtype, const char *user, const char *passwd) {
+  LWIP_ASSERT("pcb->phase == PPP_PHASE_DEAD", pcb->phase == PPP_PHASE_DEAD);
+
+#if PAP_SUPPORT
+  pcb->settings.refuse_pap = !(authtype & PPPAUTHTYPE_PAP);
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+  pcb->settings.refuse_chap = !(authtype & PPPAUTHTYPE_CHAP);
+#if MSCHAP_SUPPORT
+  pcb->settings.refuse_mschap = !(authtype & PPPAUTHTYPE_MSCHAP);
+  pcb->settings.refuse_mschap_v2 = !(authtype & PPPAUTHTYPE_MSCHAP_V2);
+#endif /* MSCHAP_SUPPORT */
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+  pcb->settings.refuse_eap = !(authtype & PPPAUTHTYPE_EAP);
+#endif /* EAP_SUPPORT */
+  pcb->settings.user = user;
+  pcb->settings.passwd = passwd;
+}
+#endif /* PPP_AUTH_SUPPORT */
+
+#if MPPE_SUPPORT
+/* Set MPPE configuration */
+void ppp_set_mppe(ppp_pcb *pcb, u8_t flags) {
+  LWIP_ASSERT("pcb->phase == PPP_PHASE_DEAD", pcb->phase == PPP_PHASE_DEAD);
+
+  if (flags == PPP_MPPE_DISABLE) {
+    pcb->settings.require_mppe = 0;
+    return;
+  }
+
+  pcb->settings.require_mppe = 1;
+  pcb->settings.refuse_mppe_stateful = !(flags & PPP_MPPE_ALLOW_STATEFUL);
+  pcb->settings.refuse_mppe_40 = !!(flags & PPP_MPPE_REFUSE_40);
+  pcb->settings.refuse_mppe_128 = !!(flags & PPP_MPPE_REFUSE_128);
+}
+#endif /* MPPE_SUPPORT */
+
+#if PPP_NOTIFY_PHASE
+void ppp_set_notify_phase_callback(ppp_pcb *pcb, ppp_notify_phase_cb_fn notify_phase_cb) {
+  pcb->notify_phase_cb = notify_phase_cb;
+  notify_phase_cb(pcb, pcb->phase, pcb->ctx_cb);
+}
+#endif /* PPP_NOTIFY_PHASE */
+
+/*
+ * Initiate a PPP connection.
+ *
+ * This can only be called if PPP is in the dead phase.
+ *
+ * Holdoff is the time to wait (in seconds) before initiating
+ * the connection.
+ *
+ * If this port connects to a modem, the modem connection must be
+ * established before calling this.
+ */
+err_t ppp_connect(ppp_pcb *pcb, u16_t holdoff) {
+  LWIP_ASSERT_CORE_LOCKED();
+  if (pcb->phase != PPP_PHASE_DEAD) {
+    return ERR_ALREADY;
+  }
+
+  PPPDEBUG(LOG_DEBUG, ("ppp_connect[%d]: holdoff=%d\n", pcb->netif->num, holdoff));
+
+  magic_randomize();
+
+  if (holdoff == 0) {
+    ppp_do_connect(pcb);
+    return ERR_OK;
+  }
+
+  new_phase(pcb, PPP_PHASE_HOLDOFF);
+  sys_timeout((u32_t)(holdoff*1000), ppp_do_connect, pcb);
+  return ERR_OK;
+}
+
+#if PPP_SERVER
+/*
+ * Listen for an incoming PPP connection.
+ *
+ * This can only be called if PPP is in the dead phase.
+ *
+ * If this port connects to a modem, the modem connection must be
+ * established before calling this.
+ */
+err_t ppp_listen(ppp_pcb *pcb) {
+  LWIP_ASSERT_CORE_LOCKED();
+  if (pcb->phase != PPP_PHASE_DEAD) {
+    return ERR_ALREADY;
+  }
+
+  PPPDEBUG(LOG_DEBUG, ("ppp_listen[%d]\n", pcb->netif->num));
+
+  magic_randomize();
+
+  if (pcb->link_cb->listen) {
+    new_phase(pcb, PPP_PHASE_INITIALIZE);
+    pcb->link_cb->listen(pcb, pcb->link_ctx_cb);
+    return ERR_OK;
+  }
+  return ERR_IF;
+}
+#endif /* PPP_SERVER */
+
+/*
+ * Initiate the end of a PPP connection.
+ * Any outstanding packets in the queues are dropped.
+ *
+ * Setting nocarrier to 1 close the PPP connection without initiating the
+ * shutdown procedure. Always using nocarrier = 0 is still recommended,
+ * this is going to take a little longer time if your link is down, but
+ * is a safer choice for the PPP state machine.
+ *
+ * Return 0 on success, an error code on failure.
+ */
+err_t
+ppp_close(ppp_pcb *pcb, u8_t nocarrier)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+
+  pcb->err_code = PPPERR_USER;
+
+  /* holdoff phase, cancel the reconnection */
+  if (pcb->phase == PPP_PHASE_HOLDOFF) {
+    sys_untimeout(ppp_do_connect, pcb);
+    new_phase(pcb, PPP_PHASE_DEAD);
+  }
+
+  /* dead phase, nothing to do, call the status callback to be consistent */
+  if (pcb->phase == PPP_PHASE_DEAD) {
+    pcb->link_status_cb(pcb, pcb->err_code, pcb->ctx_cb);
+    return ERR_OK;
+  }
+
+  /* Already terminating, nothing to do */
+  if (pcb->phase >= PPP_PHASE_TERMINATE) {
+    return ERR_INPROGRESS;
+  }
+
+  /* LCP not open, close link protocol */
+  if (pcb->phase < PPP_PHASE_ESTABLISH) {
+    new_phase(pcb, PPP_PHASE_DISCONNECT);
+    ppp_link_terminated(pcb);
+    return ERR_OK;
+  }
+
+  /*
+   * Only accept carrier lost signal on the stable running phase in order
+   * to prevent changing the PPP phase FSM in transition phases.
+   *
+   * Always using nocarrier = 0 is still recommended, this is going to
+   * take a little longer time, but is a safer choice from FSM point of view.
+   */
+  if (nocarrier && pcb->phase == PPP_PHASE_RUNNING) {
+    PPPDEBUG(LOG_DEBUG, ("ppp_close[%d]: carrier lost -> lcp_lowerdown\n", pcb->netif->num));
+    lcp_lowerdown(pcb);
+    /* forced link termination, this will force link protocol to disconnect. */
+    link_terminated(pcb);
+    return ERR_OK;
+  }
+
+  /* Disconnect */
+  PPPDEBUG(LOG_DEBUG, ("ppp_close[%d]: kill_link -> lcp_close\n", pcb->netif->num));
+  /* LCP soft close request. */
+  lcp_close(pcb, "User request");
+  return ERR_OK;
+}
+
+/*
+ * Release the control block.
+ *
+ * This can only be called if PPP is in the dead phase.
+ *
+ * You must use ppp_close() before if you wish to terminate
+ * an established PPP session.
+ *
+ * Return 0 on success, an error code on failure.
+ */
+err_t ppp_free(ppp_pcb *pcb) {
+  err_t err;
+  LWIP_ASSERT_CORE_LOCKED();
+  if (pcb->phase != PPP_PHASE_DEAD) {
+    return ERR_CONN;
+  }
+
+  PPPDEBUG(LOG_DEBUG, ("ppp_free[%d]\n", pcb->netif->num));
+
+  netif_remove(pcb->netif);
+
+  err = pcb->link_cb->free(pcb, pcb->link_ctx_cb);
+
+  LWIP_MEMPOOL_FREE(PPP_PCB, pcb);
+  return err;
+}
+
+/* Get and set parameters for the given connection.
+ * Return 0 on success, an error code on failure. */
+err_t
+ppp_ioctl(ppp_pcb *pcb, u8_t cmd, void *arg)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  if (pcb == NULL) {
+    return ERR_VAL;
+  }
+
+  switch(cmd) {
+    case PPPCTLG_UPSTATUS:      /* Get the PPP up status. */
+      if (!arg) {
+        goto fail;
+      }
+      *(int *)arg = (int)(0
+#if PPP_IPV4_SUPPORT
+           || pcb->if4_up
+#endif /* PPP_IPV4_SUPPORT */
+#if PPP_IPV6_SUPPORT
+           || pcb->if6_up
+#endif /* PPP_IPV6_SUPPORT */
+           );
+      return ERR_OK;
+
+    case PPPCTLG_ERRCODE:       /* Get the PPP error code. */
+      if (!arg) {
+        goto fail;
+      }
+      *(int *)arg = (int)(pcb->err_code);
+      return ERR_OK;
+
+    default:
+      goto fail;
+  }
+
+fail:
+  return ERR_VAL;
+}
+
+
+/**********************************/
+/*** LOCAL FUNCTION DEFINITIONS ***/
+/**********************************/
+
+static void ppp_do_connect(void *arg) {
+  ppp_pcb *pcb = (ppp_pcb*)arg;
+
+  LWIP_ASSERT("pcb->phase == PPP_PHASE_DEAD || pcb->phase == PPP_PHASE_HOLDOFF", pcb->phase == PPP_PHASE_DEAD || pcb->phase == PPP_PHASE_HOLDOFF);
+
+  new_phase(pcb, PPP_PHASE_INITIALIZE);
+  pcb->link_cb->connect(pcb, pcb->link_ctx_cb);
+}
+
+/*
+ * ppp_netif_init_cb - netif init callback
+ */
+static err_t ppp_netif_init_cb(struct netif *netif) {
+  netif->name[0] = 'p';
+  netif->name[1] = 'p';
+#if PPP_IPV4_SUPPORT
+  netif->output = ppp_netif_output_ip4;
+#endif /* PPP_IPV4_SUPPORT */
+#if PPP_IPV6_SUPPORT
+  netif->output_ip6 = ppp_netif_output_ip6;
+#endif /* PPP_IPV6_SUPPORT */
+  netif->flags = NETIF_FLAG_UP;
+#if LWIP_NETIF_HOSTNAME
+  /* @todo: Initialize interface hostname */
+  /* netif_set_hostname(netif, "lwip"); */
+#endif /* LWIP_NETIF_HOSTNAME */
+  return ERR_OK;
+}
+
+#if PPP_IPV4_SUPPORT
+/*
+ * Send an IPv4 packet on the given connection.
+ */
+static err_t ppp_netif_output_ip4(struct netif *netif, struct pbuf *pb, const ip4_addr_t *ipaddr) {
+  LWIP_UNUSED_ARG(ipaddr);
+  return ppp_netif_output(netif, pb, PPP_IP);
+}
+#endif /* PPP_IPV4_SUPPORT */
+
+#if PPP_IPV6_SUPPORT
+/*
+ * Send an IPv6 packet on the given connection.
+ */
+static err_t ppp_netif_output_ip6(struct netif *netif, struct pbuf *pb, const ip6_addr_t *ipaddr) {
+  LWIP_UNUSED_ARG(ipaddr);
+  return ppp_netif_output(netif, pb, PPP_IPV6);
+}
+#endif /* PPP_IPV6_SUPPORT */
+
+static err_t ppp_netif_output(struct netif *netif, struct pbuf *pb, u16_t protocol) {
+  ppp_pcb *pcb = (ppp_pcb*)netif->state;
+  err_t err;
+  struct pbuf *fpb = NULL;
+
+  /* Check that the link is up. */
+  if (0
+#if PPP_IPV4_SUPPORT
+      || (protocol == PPP_IP && !pcb->if4_up)
+#endif /* PPP_IPV4_SUPPORT */
+#if PPP_IPV6_SUPPORT
+      || (protocol == PPP_IPV6 && !pcb->if6_up)
+#endif /* PPP_IPV6_SUPPORT */
+      ) {
+    PPPDEBUG(LOG_ERR, ("ppp_netif_output[%d]: link not up\n", pcb->netif->num));
+    goto err_rte_drop;
+  }
+
+#if MPPE_SUPPORT
+  /* If MPPE is required, refuse any IP packet until we are able to crypt them. */
+  if (pcb->settings.require_mppe && pcb->ccp_transmit_method != CI_MPPE) {
+    PPPDEBUG(LOG_ERR, ("ppp_netif_output[%d]: MPPE required, not up\n", pcb->netif->num));
+    goto err_rte_drop;
+  }
+#endif /* MPPE_SUPPORT */
+
+#if VJ_SUPPORT
+  /*
+   * Attempt Van Jacobson header compression if VJ is configured and
+   * this is an IP packet.
+   */
+  if (protocol == PPP_IP && pcb->vj_enabled) {
+    switch (vj_compress_tcp(&pcb->vj_comp, &pb)) {
+      case TYPE_IP:
+        /* No change...
+           protocol = PPP_IP; */
+        break;
+      case TYPE_COMPRESSED_TCP:
+        /* vj_compress_tcp() returns a new allocated pbuf, indicate we should free
+         * our duplicated pbuf later */
+        fpb = pb;
+        protocol = PPP_VJC_COMP;
+        break;
+      case TYPE_UNCOMPRESSED_TCP:
+        /* vj_compress_tcp() returns a new allocated pbuf, indicate we should free
+         * our duplicated pbuf later */
+        fpb = pb;
+        protocol = PPP_VJC_UNCOMP;
+        break;
+      default:
+        PPPDEBUG(LOG_WARNING, ("ppp_netif_output[%d]: bad IP packet\n", pcb->netif->num));
+        LINK_STATS_INC(link.proterr);
+        LINK_STATS_INC(link.drop);
+        MIB2_STATS_NETIF_INC(pcb->netif, ifoutdiscards);
+        return ERR_VAL;
+    }
+  }
+#endif /* VJ_SUPPORT */
+
+#if CCP_SUPPORT
+  switch (pcb->ccp_transmit_method) {
+  case 0:
+    break; /* Don't compress */
+#if MPPE_SUPPORT
+  case CI_MPPE:
+    if ((err = mppe_compress(pcb, &pcb->mppe_comp, &pb, protocol)) != ERR_OK) {
+      LINK_STATS_INC(link.memerr);
+      LINK_STATS_INC(link.drop);
+      MIB2_STATS_NETIF_INC(netif, ifoutdiscards);
+      goto err;
+    }
+    /* if VJ compressor returned a new allocated pbuf, free it */
+    if (fpb) {
+      pbuf_free(fpb);
+    }
+    /* mppe_compress() returns a new allocated pbuf, indicate we should free
+     * our duplicated pbuf later */
+    fpb = pb;
+    protocol = PPP_COMP;
+    break;
+#endif /* MPPE_SUPPORT */
+  default:
+    PPPDEBUG(LOG_ERR, ("ppp_netif_output[%d]: bad CCP transmit method\n", pcb->netif->num));
+    goto err_rte_drop; /* Cannot really happen, we only negotiate what we are able to do */
+  }
+#endif /* CCP_SUPPORT */
+
+  err = pcb->link_cb->netif_output(pcb, pcb->link_ctx_cb, pb, protocol);
+  goto err;
+
+err_rte_drop:
+  err = ERR_RTE;
+  LINK_STATS_INC(link.rterr);
+  LINK_STATS_INC(link.drop);
+  MIB2_STATS_NETIF_INC(netif, ifoutdiscards);
+err:
+  if (fpb) {
+    pbuf_free(fpb);
+  }
+  return err;
+}
+
+/************************************/
+/*** PRIVATE FUNCTION DEFINITIONS ***/
+/************************************/
+
+/* Initialize the PPP subsystem. */
+int ppp_init(void)
+{
+#if PPPOS_SUPPORT
+  LWIP_MEMPOOL_INIT(PPPOS_PCB);
+#endif
+#if PPPOE_SUPPORT
+  LWIP_MEMPOOL_INIT(PPPOE_IF);
+#endif
+#if PPPOL2TP_SUPPORT
+  LWIP_MEMPOOL_INIT(PPPOL2TP_PCB);
+#endif
+#if LWIP_PPP_API && LWIP_MPU_COMPATIBLE
+  LWIP_MEMPOOL_INIT(PPPAPI_MSG);
+#endif
+
+  LWIP_MEMPOOL_INIT(PPP_PCB);
+
+  /*
+   * Initialize magic number generator now so that protocols may
+   * use magic numbers in initialization.
+   */
+  magic_init();
+
+  return 0;
+}
+ 
+/*
+ * Create a new PPP control block.
+ *
+ * This initializes the PPP control block but does not
+ * attempt to negotiate the LCP session.
+ *
+ * Return a new PPP connection control block pointer
+ * on success or a null pointer on failure.
+ */
+ppp_pcb *ppp_new(struct netif *pppif, const struct link_callbacks *callbacks, void *link_ctx_cb, ppp_link_status_cb_fn link_status_cb, void *ctx_cb) {
+  ppp_pcb *pcb;
+  const struct protent *protp;
+  int i;
+
+  /* PPP is single-threaded: without a callback,
+   * there is no way to know when the link is up. */
+  if (link_status_cb == NULL) {
+    return NULL;
+  }
+
+  pcb = (ppp_pcb*)LWIP_MEMPOOL_ALLOC(PPP_PCB);
+  if (pcb == NULL) {
+    return NULL;
+  }
+
+  memset(pcb, 0, sizeof(ppp_pcb));
+
+  /* default configuration */
+#if PAP_SUPPORT
+  pcb->settings.pap_timeout_time = UPAP_DEFTIMEOUT;
+  pcb->settings.pap_max_transmits = UPAP_DEFTRANSMITS;
+#if PPP_SERVER
+  pcb->settings.pap_req_timeout = UPAP_DEFREQTIME;
+#endif /* PPP_SERVER */
+#endif /* PAP_SUPPORT */
+
+#if CHAP_SUPPORT
+  pcb->settings.chap_timeout_time = CHAP_DEFTIMEOUT;
+  pcb->settings.chap_max_transmits = CHAP_DEFTRANSMITS;
+#if PPP_SERVER
+  pcb->settings.chap_rechallenge_time = CHAP_DEFRECHALLENGETIME;
+#endif /* PPP_SERVER */
+#endif /* CHAP_SUPPPORT */
+
+#if EAP_SUPPORT
+  pcb->settings.eap_req_time = EAP_DEFREQTIME;
+  pcb->settings.eap_allow_req = EAP_DEFALLOWREQ;
+#if PPP_SERVER
+  pcb->settings.eap_timeout_time = EAP_DEFTIMEOUT;
+  pcb->settings.eap_max_transmits = EAP_DEFTRANSMITS;
+#endif /* PPP_SERVER */
+#endif /* EAP_SUPPORT */
+
+  pcb->settings.lcp_loopbackfail = LCP_DEFLOOPBACKFAIL;
+  pcb->settings.lcp_echo_interval = LCP_ECHOINTERVAL;
+  pcb->settings.lcp_echo_fails = LCP_MAXECHOFAILS;
+
+  pcb->settings.fsm_timeout_time = FSM_DEFTIMEOUT;
+  pcb->settings.fsm_max_conf_req_transmits = FSM_DEFMAXCONFREQS;
+  pcb->settings.fsm_max_term_transmits = FSM_DEFMAXTERMREQS;
+  pcb->settings.fsm_max_nak_loops = FSM_DEFMAXNAKLOOPS;
+
+  pcb->netif = pppif;
+  MIB2_INIT_NETIF(pppif, snmp_ifType_ppp, 0);
+  if (!netif_add(pcb->netif,
+#if LWIP_IPV4
+                 IP4_ADDR_ANY4, IP4_ADDR_BROADCAST, IP4_ADDR_ANY4,
+#endif /* LWIP_IPV4 */
+                 (void *)pcb, ppp_netif_init_cb, NULL)) {
+    LWIP_MEMPOOL_FREE(PPP_PCB, pcb);
+    PPPDEBUG(LOG_ERR, ("ppp_new: netif_add failed\n"));
+    return NULL;
+  }
+
+  pcb->link_cb = callbacks;
+  pcb->link_ctx_cb = link_ctx_cb;
+  pcb->link_status_cb = link_status_cb;
+  pcb->ctx_cb = ctx_cb;
+
+  /*
+   * Initialize each protocol.
+   */
+  for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+      (*protp->init)(pcb);
+  }
+
+  new_phase(pcb, PPP_PHASE_DEAD);
+  return pcb;
+}
+
+/** Initiate LCP open request */
+void ppp_start(ppp_pcb *pcb) {
+  PPPDEBUG(LOG_DEBUG, ("ppp_start[%d]\n", pcb->netif->num));
+
+  /* Clean data not taken care by anything else, mostly shared data. */
+#if PPP_STATS_SUPPORT
+  link_stats_valid = 0;
+#endif /* PPP_STATS_SUPPORT */
+#if MPPE_SUPPORT
+  pcb->mppe_keys_set = 0;
+  memset(&pcb->mppe_comp, 0, sizeof(pcb->mppe_comp));
+  memset(&pcb->mppe_decomp, 0, sizeof(pcb->mppe_decomp));
+#endif /* MPPE_SUPPORT */
+#if VJ_SUPPORT
+  vj_compress_init(&pcb->vj_comp);
+#endif /* VJ_SUPPORT */
+
+  /* Start protocol */
+  new_phase(pcb, PPP_PHASE_ESTABLISH);
+  lcp_open(pcb);
+  lcp_lowerup(pcb);
+  PPPDEBUG(LOG_DEBUG, ("ppp_start[%d]: finished\n", pcb->netif->num));
+}
+
+/** Called when link failed to setup */
+void ppp_link_failed(ppp_pcb *pcb) {
+  PPPDEBUG(LOG_DEBUG, ("ppp_link_failed[%d]\n", pcb->netif->num));
+  new_phase(pcb, PPP_PHASE_DEAD);
+  pcb->err_code = PPPERR_OPEN;
+  pcb->link_status_cb(pcb, pcb->err_code, pcb->ctx_cb);
+}
+
+/** Called when link is normally down (i.e. it was asked to end) */
+void ppp_link_end(ppp_pcb *pcb) {
+  PPPDEBUG(LOG_DEBUG, ("ppp_link_end[%d]\n", pcb->netif->num));
+  new_phase(pcb, PPP_PHASE_DEAD);
+  if (pcb->err_code == PPPERR_NONE) {
+    pcb->err_code = PPPERR_CONNECT;
+  }
+  pcb->link_status_cb(pcb, pcb->err_code, pcb->ctx_cb);
+}
+
+/*
+ * Pass the processed input packet to the appropriate handler.
+ * This function and all handlers run in the context of the tcpip_thread
+ */
+void ppp_input(ppp_pcb *pcb, struct pbuf *pb) {
+  u16_t protocol;
+#if PPP_DEBUG && PPP_PROTOCOLNAME
+    const char *pname;
+#endif /* PPP_DEBUG && PPP_PROTOCOLNAME */
+
+  magic_randomize();
+
+  if (pb->len < 2) {
+    PPPDEBUG(LOG_ERR, ("ppp_input[%d]: packet too short\n", pcb->netif->num));
+    goto drop;
+  }
+  protocol = (((u8_t *)pb->payload)[0] << 8) | ((u8_t*)pb->payload)[1];
+
+#if PRINTPKT_SUPPORT
+  ppp_dump_packet(pcb, "rcvd", (unsigned char *)pb->payload, pb->len);
+#endif /* PRINTPKT_SUPPORT */
+
+  pbuf_remove_header(pb, sizeof(protocol));
+
+  LINK_STATS_INC(link.recv);
+  MIB2_STATS_NETIF_INC(pcb->netif, ifinucastpkts);
+  MIB2_STATS_NETIF_ADD(pcb->netif, ifinoctets, pb->tot_len);
+
+  /*
+   * Toss all non-LCP packets unless LCP is OPEN.
+   */
+  if (protocol != PPP_LCP && pcb->lcp_fsm.state != PPP_FSM_OPENED) {
+    ppp_dbglog("Discarded non-LCP packet when LCP not open");
+    goto drop;
+  }
+
+  /*
+   * Until we get past the authentication phase, toss all packets
+   * except LCP, LQR and authentication packets.
+   */
+  if (pcb->phase <= PPP_PHASE_AUTHENTICATE
+   && !(protocol == PPP_LCP
+#if LQR_SUPPORT
+   || protocol == PPP_LQR
+#endif /* LQR_SUPPORT */
+#if PAP_SUPPORT
+   || protocol == PPP_PAP
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+   || protocol == PPP_CHAP
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+   || protocol == PPP_EAP
+#endif /* EAP_SUPPORT */
+   )) {
+    ppp_dbglog("discarding proto 0x%x in phase %d", protocol, pcb->phase);
+    goto drop;
+  }
+
+#if CCP_SUPPORT
+#if MPPE_SUPPORT
+  /*
+   * MPPE is required and unencrypted data has arrived (this
+   * should never happen!). We should probably drop the link if
+   * the protocol is in the range of what should be encrypted.
+   * At the least, we drop this packet.
+   */
+  if (pcb->settings.require_mppe && protocol != PPP_COMP && protocol < 0x8000) {
+    PPPDEBUG(LOG_ERR, ("ppp_input[%d]: MPPE required, received unencrypted data!\n", pcb->netif->num));
+    goto drop;
+  }
+#endif /* MPPE_SUPPORT */
+
+  if (protocol == PPP_COMP) {
+    u8_t *pl;
+
+    switch (pcb->ccp_receive_method) {
+#if MPPE_SUPPORT
+    case CI_MPPE:
+      if (mppe_decompress(pcb, &pcb->mppe_decomp, &pb) != ERR_OK) {
+        goto drop;
+      }
+      break;
+#endif /* MPPE_SUPPORT */
+    default:
+      PPPDEBUG(LOG_ERR, ("ppp_input[%d]: bad CCP receive method\n", pcb->netif->num));
+      goto drop; /* Cannot really happen, we only negotiate what we are able to do */
+    }
+
+    /* Assume no PFC */
+    if (pb->len < 2) {
+      goto drop;
+    }
+
+    /* Extract and hide protocol (do PFC decompression if necessary) */
+    pl = (u8_t*)pb->payload;
+    if (pl[0] & 0x01) {
+      protocol = pl[0];
+      pbuf_remove_header(pb, 1);
+    } else {
+      protocol = (pl[0] << 8) | pl[1];
+      pbuf_remove_header(pb, 2);
+    }
+  }
+#endif /* CCP_SUPPORT */
+
+  switch(protocol) {
+
+#if PPP_IPV4_SUPPORT
+    case PPP_IP:            /* Internet Protocol */
+      PPPDEBUG(LOG_INFO, ("ppp_input[%d]: ip in pbuf len=%d\n", pcb->netif->num, pb->tot_len));
+      ip4_input(pb, pcb->netif);
+      return;
+#endif /* PPP_IPV4_SUPPORT */
+
+#if PPP_IPV6_SUPPORT
+    case PPP_IPV6:          /* Internet Protocol Version 6 */
+      PPPDEBUG(LOG_INFO, ("ppp_input[%d]: ip6 in pbuf len=%d\n", pcb->netif->num, pb->tot_len));
+      ip6_input(pb, pcb->netif);
+      return;
+#endif /* PPP_IPV6_SUPPORT */
+
+#if VJ_SUPPORT
+    case PPP_VJC_COMP:      /* VJ compressed TCP */
+      /*
+       * Clip off the VJ header and prepend the rebuilt TCP/IP header and
+       * pass the result to IP.
+       */
+      PPPDEBUG(LOG_INFO, ("ppp_input[%d]: vj_comp in pbuf len=%d\n", pcb->netif->num, pb->tot_len));
+      if (pcb->vj_enabled && vj_uncompress_tcp(&pb, &pcb->vj_comp) >= 0) {
+        ip4_input(pb, pcb->netif);
+        return;
+      }
+      /* Something's wrong so drop it. */
+      PPPDEBUG(LOG_WARNING, ("ppp_input[%d]: Dropping VJ compressed\n", pcb->netif->num));
+      break;
+
+    case PPP_VJC_UNCOMP:    /* VJ uncompressed TCP */
+      /*
+       * Process the TCP/IP header for VJ header compression and then pass
+       * the packet to IP.
+       */
+      PPPDEBUG(LOG_INFO, ("ppp_input[%d]: vj_un in pbuf len=%d\n", pcb->netif->num, pb->tot_len));
+      if (pcb->vj_enabled && vj_uncompress_uncomp(pb, &pcb->vj_comp) >= 0) {
+        ip4_input(pb, pcb->netif);
+        return;
+      }
+      /* Something's wrong so drop it. */
+      PPPDEBUG(LOG_WARNING, ("ppp_input[%d]: Dropping VJ uncompressed\n", pcb->netif->num));
+      break;
+#endif /* VJ_SUPPORT */
+
+    default: {
+      int i;
+      const struct protent *protp;
+
+      /*
+       * Upcall the proper protocol input routine.
+       */
+      for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+        if (protp->protocol == protocol) {
+          pb = pbuf_coalesce(pb, PBUF_RAW);
+          (*protp->input)(pcb, (u8_t*)pb->payload, pb->len);
+          goto out;
+        }
+#if 0   /* UNUSED
+         *
+         * This is actually a (hacked?) way for the Linux kernel to pass a data
+         * packet to pppd. pppd in normal condition only do signaling
+         * (LCP, PAP, CHAP, IPCP, ...) and does not handle any data packet at all.
+         *
+         * We don't even need this interface, which is only there because of PPP
+         * interface limitation between Linux kernel and pppd. For MPPE, which uses
+         * CCP to negotiate although it is not really a (de)compressor, we added
+         * ccp_resetrequest() in CCP and MPPE input data flow is calling either
+         * ccp_resetrequest() or lcp_close() if the issue is, respectively, non-fatal
+         * or fatal, this is what ccp_datainput() really do.
+         */
+        if (protocol == (protp->protocol & ~0x8000)
+          && protp->datainput != NULL) {
+          (*protp->datainput)(pcb, pb->payload, pb->len);
+          goto out;
+        }
+#endif /* UNUSED */
+      }
+
+#if PPP_DEBUG
+#if PPP_PROTOCOLNAME
+      pname = protocol_name(protocol);
+      if (pname != NULL) {
+        ppp_warn("Unsupported protocol '%s' (0x%x) received", pname, protocol);
+      } else
+#endif /* PPP_PROTOCOLNAME */
+        ppp_warn("Unsupported protocol 0x%x received", protocol);
+#endif /* PPP_DEBUG */
+        if (pbuf_add_header(pb, sizeof(protocol))) {
+          PPPDEBUG(LOG_WARNING, ("ppp_input[%d]: Dropping (pbuf_add_header failed)\n", pcb->netif->num));
+          goto drop;
+        }
+        lcp_sprotrej(pcb, (u8_t*)pb->payload, pb->len);
+      }
+      break;
+  }
+
+drop:
+  LINK_STATS_INC(link.drop);
+  MIB2_STATS_NETIF_INC(pcb->netif, ifindiscards);
+
+out:
+  pbuf_free(pb);
+}
+
+/*
+ * Write a pbuf to a ppp link, only used from PPP functions
+ * to send PPP packets.
+ *
+ * IPv4 and IPv6 packets from lwIP are sent, respectively,
+ * with ppp_netif_output_ip4() and ppp_netif_output_ip6()
+ * functions (which are callbacks of the netif PPP interface).
+ */
+err_t ppp_write(ppp_pcb *pcb, struct pbuf *p) {
+#if PRINTPKT_SUPPORT
+  ppp_dump_packet(pcb, "sent", (unsigned char *)p->payload+2, p->len-2);
+#endif /* PRINTPKT_SUPPORT */
+  return pcb->link_cb->write(pcb, pcb->link_ctx_cb, p);
+}
+
+void ppp_link_terminated(ppp_pcb *pcb) {
+  PPPDEBUG(LOG_DEBUG, ("ppp_link_terminated[%d]\n", pcb->netif->num));
+  pcb->link_cb->disconnect(pcb, pcb->link_ctx_cb);
+  PPPDEBUG(LOG_DEBUG, ("ppp_link_terminated[%d]: finished.\n", pcb->netif->num));
+}
+
+
+/************************************************************************
+ * Functions called by various PPP subsystems to configure
+ * the PPP interface or change the PPP phase.
+ */
+
+/*
+ * new_phase - signal the start of a new phase of pppd's operation.
+ */
+void new_phase(ppp_pcb *pcb, int p) {
+  pcb->phase = p;
+  PPPDEBUG(LOG_DEBUG, ("ppp phase changed[%d]: phase=%d\n", pcb->netif->num, pcb->phase));
+#if PPP_NOTIFY_PHASE
+  if (pcb->notify_phase_cb != NULL) {
+    pcb->notify_phase_cb(pcb, p, pcb->ctx_cb);
+  }
+#endif /* PPP_NOTIFY_PHASE */
+}
+
+/*
+ * ppp_send_config - configure the transmit-side characteristics of
+ * the ppp interface.
+ */
+int ppp_send_config(ppp_pcb *pcb, int mtu, u32_t accm, int pcomp, int accomp) {
+  LWIP_UNUSED_ARG(mtu);
+  /* pcb->mtu = mtu; -- set correctly with netif_set_mtu */
+
+  if (pcb->link_cb->send_config) {
+    pcb->link_cb->send_config(pcb, pcb->link_ctx_cb, accm, pcomp, accomp);
+  }
+
+  PPPDEBUG(LOG_INFO, ("ppp_send_config[%d]\n", pcb->netif->num) );
+  return 0;
+}
+
+/*
+ * ppp_recv_config - configure the receive-side characteristics of
+ * the ppp interface.
+ */
+int ppp_recv_config(ppp_pcb *pcb, int mru, u32_t accm, int pcomp, int accomp) {
+  LWIP_UNUSED_ARG(mru);
+
+  if (pcb->link_cb->recv_config) {
+    pcb->link_cb->recv_config(pcb, pcb->link_ctx_cb, accm, pcomp, accomp);
+  }
+
+  PPPDEBUG(LOG_INFO, ("ppp_recv_config[%d]\n", pcb->netif->num));
+  return 0;
+}
+
+#if PPP_IPV4_SUPPORT
+/*
+ * sifaddr - Config the interface IP addresses and netmask.
+ */
+int sifaddr(ppp_pcb *pcb, u32_t our_adr, u32_t his_adr, u32_t netmask) {
+  ip4_addr_t ip, nm, gw;
+
+  ip4_addr_set_u32(&ip, our_adr);
+  ip4_addr_set_u32(&nm, netmask);
+  ip4_addr_set_u32(&gw, his_adr);
+  netif_set_addr(pcb->netif, &ip, &nm, &gw);
+  return 1;
+}
+
+/********************************************************************
+ *
+ * cifaddr - Clear the interface IP addresses, and delete routes
+ * through the interface if possible.
+ */
+int cifaddr(ppp_pcb *pcb, u32_t our_adr, u32_t his_adr) {
+  LWIP_UNUSED_ARG(our_adr);
+  LWIP_UNUSED_ARG(his_adr);
+
+  netif_set_addr(pcb->netif, IP4_ADDR_ANY4, IP4_ADDR_BROADCAST, IP4_ADDR_ANY4);
+  return 1;
+}
+
+#if 0 /* UNUSED - PROXY ARP */
+/********************************************************************
+ *
+ * sifproxyarp - Make a proxy ARP entry for the peer.
+ */
+
+int sifproxyarp(ppp_pcb *pcb, u32_t his_adr) {
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_UNUSED_ARG(his_adr);
+  return 0;
+}
+
+/********************************************************************
+ *
+ * cifproxyarp - Delete the proxy ARP entry for the peer.
+ */
+
+int cifproxyarp(ppp_pcb *pcb, u32_t his_adr) {
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_UNUSED_ARG(his_adr);
+  return 0;
+}
+#endif /* UNUSED - PROXY ARP */
+
+#if LWIP_DNS
+/*
+ * sdns - Config the DNS servers
+ */
+int sdns(ppp_pcb *pcb, u32_t ns1, u32_t ns2) {
+  ip_addr_t ns;
+  LWIP_UNUSED_ARG(pcb);
+
+  ip_addr_set_ip4_u32_val(ns, ns1);
+  dns_setserver(0, &ns);
+  ip_addr_set_ip4_u32_val(ns, ns2);
+  dns_setserver(1, &ns);
+  return 1;
+}
+
+/********************************************************************
+ *
+ * cdns - Clear the DNS servers
+ */
+int cdns(ppp_pcb *pcb, u32_t ns1, u32_t ns2) {
+  const ip_addr_t *nsa;
+  ip_addr_t nsb;
+  LWIP_UNUSED_ARG(pcb);
+
+  nsa = dns_getserver(0);
+  ip_addr_set_ip4_u32_val(nsb, ns1);
+  if (ip_addr_cmp(nsa, &nsb)) {
+    dns_setserver(0, IP_ADDR_ANY);
+  }
+  nsa = dns_getserver(1);
+  ip_addr_set_ip4_u32_val(nsb, ns2);
+  if (ip_addr_cmp(nsa, &nsb)) {
+    dns_setserver(1, IP_ADDR_ANY);
+  }
+  return 1;
+}
+#endif /* LWIP_DNS */
+
+#if VJ_SUPPORT
+/********************************************************************
+ *
+ * sifvjcomp - config tcp header compression
+ */
+int sifvjcomp(ppp_pcb *pcb, int vjcomp, int cidcomp, int maxcid) {
+  pcb->vj_enabled = vjcomp;
+  pcb->vj_comp.compressSlot = cidcomp;
+  pcb->vj_comp.maxSlotIndex = maxcid;
+  PPPDEBUG(LOG_INFO, ("sifvjcomp[%d]: VJ compress enable=%d slot=%d max slot=%d\n",
+            pcb->netif->num, vjcomp, cidcomp, maxcid));
+  return 0;
+}
+#endif /* VJ_SUPPORT */
+
+/*
+ * sifup - Config the interface up and enable IP packets to pass.
+ */
+int sifup(ppp_pcb *pcb) {
+  pcb->if4_up = 1;
+  pcb->err_code = PPPERR_NONE;
+  netif_set_link_up(pcb->netif);
+
+  PPPDEBUG(LOG_DEBUG, ("sifup[%d]: err_code=%d\n", pcb->netif->num, pcb->err_code));
+  pcb->link_status_cb(pcb, pcb->err_code, pcb->ctx_cb);
+  return 1;
+}
+
+/********************************************************************
+ *
+ * sifdown - Disable the indicated protocol and config the interface
+ *           down if there are no remaining protocols.
+ */
+int sifdown(ppp_pcb *pcb) {
+
+  pcb->if4_up = 0;
+
+  if (1
+#if PPP_IPV6_SUPPORT
+   /* set the interface down if IPv6 is down as well */
+   && !pcb->if6_up
+#endif /* PPP_IPV6_SUPPORT */
+  ) {
+    /* make sure the netif link callback is called */
+    netif_set_link_down(pcb->netif);
+  }
+  PPPDEBUG(LOG_DEBUG, ("sifdown[%d]: err_code=%d\n", pcb->netif->num, pcb->err_code));
+  return 1;
+}
+
+/********************************************************************
+ *
+ * Return user specified netmask, modified by any mask we might determine
+ * for address `addr' (in network byte order).
+ * Here we scan through the system's list of interfaces, looking for
+ * any non-point-to-point interfaces which might appear to be on the same
+ * network as `addr'.  If we find any, we OR in their netmask to the
+ * user-specified netmask.
+ */
+u32_t get_mask(u32_t addr) {
+#if 0
+  u32_t mask, nmask;
+
+  addr = lwip_htonl(addr);
+  if (IP_CLASSA(addr)) { /* determine network mask for address class */
+    nmask = IP_CLASSA_NET;
+  } else if (IP_CLASSB(addr)) {
+    nmask = IP_CLASSB_NET;
+  } else {
+    nmask = IP_CLASSC_NET;
+  }
+
+  /* class D nets are disallowed by bad_ip_adrs */
+  mask = PP_HTONL(0xffffff00UL) | lwip_htonl(nmask);
+
+  /* XXX
+   * Scan through the system's network interfaces.
+   * Get each netmask and OR them into our mask.
+   */
+  /* return mask; */
+  return mask;
+#endif /* 0 */
+  LWIP_UNUSED_ARG(addr);
+  return IPADDR_BROADCAST;
+}
+#endif /* PPP_IPV4_SUPPORT */
+
+#if PPP_IPV6_SUPPORT
+#define IN6_LLADDR_FROM_EUI64(ip6, eui64) do {    \
+  ip6.addr[0] = PP_HTONL(0xfe800000);             \
+  ip6.addr[1] = 0;                                \
+  eui64_copy(eui64, ip6.addr[2]);                 \
+  } while (0)
+
+/********************************************************************
+ *
+ * sif6addr - Config the interface with an IPv6 link-local address
+ */
+int sif6addr(ppp_pcb *pcb, eui64_t our_eui64, eui64_t his_eui64) {
+  ip6_addr_t ip6;
+  LWIP_UNUSED_ARG(his_eui64);
+
+  IN6_LLADDR_FROM_EUI64(ip6, our_eui64);
+  netif_ip6_addr_set(pcb->netif, 0, &ip6);
+  netif_ip6_addr_set_state(pcb->netif, 0, IP6_ADDR_PREFERRED);
+  /* FIXME: should we add an IPv6 static neighbor using his_eui64 ? */
+  return 1;
+}
+
+/********************************************************************
+ *
+ * cif6addr - Remove IPv6 address from interface
+ */
+int cif6addr(ppp_pcb *pcb, eui64_t our_eui64, eui64_t his_eui64) {
+  LWIP_UNUSED_ARG(our_eui64);
+  LWIP_UNUSED_ARG(his_eui64);
+
+  netif_ip6_addr_set_state(pcb->netif, 0, IP6_ADDR_INVALID);
+  netif_ip6_addr_set(pcb->netif, 0, IP6_ADDR_ANY6);
+  return 1;
+}
+
+/*
+ * sif6up - Config the interface up and enable IPv6 packets to pass.
+ */
+int sif6up(ppp_pcb *pcb) {
+
+  pcb->if6_up = 1;
+  pcb->err_code = PPPERR_NONE;
+  netif_set_link_up(pcb->netif);
+
+  PPPDEBUG(LOG_DEBUG, ("sif6up[%d]: err_code=%d\n", pcb->netif->num, pcb->err_code));
+  pcb->link_status_cb(pcb, pcb->err_code, pcb->ctx_cb);
+  return 1;
+}
+
+/********************************************************************
+ *
+ * sif6down - Disable the indicated protocol and config the interface
+ *            down if there are no remaining protocols.
+ */
+int sif6down(ppp_pcb *pcb) {
+
+  pcb->if6_up = 0;
+
+  if (1
+#if PPP_IPV4_SUPPORT
+   /* set the interface down if IPv4 is down as well */
+   && !pcb->if4_up
+#endif /* PPP_IPV4_SUPPORT */
+  ) {
+    /* make sure the netif link callback is called */
+    netif_set_link_down(pcb->netif);
+  }
+  PPPDEBUG(LOG_DEBUG, ("sif6down[%d]: err_code=%d\n", pcb->netif->num, pcb->err_code));
+  return 1;
+}
+#endif /* PPP_IPV6_SUPPORT */
+
+#if DEMAND_SUPPORT
+/*
+ * sifnpmode - Set the mode for handling packets for a given NP.
+ */
+int sifnpmode(ppp_pcb *pcb, int proto, enum NPmode mode) {
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_UNUSED_ARG(proto);
+  LWIP_UNUSED_ARG(mode);
+  return 0;
+}
+#endif /* DEMAND_SUPPORT */
+
+/*
+ * netif_set_mtu - set the MTU on the PPP network interface.
+ */
+void netif_set_mtu(ppp_pcb *pcb, int mtu) {
+
+  pcb->netif->mtu = mtu;
+  PPPDEBUG(LOG_INFO, ("netif_set_mtu[%d]: mtu=%d\n", pcb->netif->num, mtu));
+}
+
+/*
+ * netif_get_mtu - get PPP interface MTU
+ */
+int netif_get_mtu(ppp_pcb *pcb) {
+
+  return pcb->netif->mtu;
+}
+
+#if CCP_SUPPORT
+#if 0 /* unused */
+/*
+ * ccp_test - whether a given compression method is acceptable for use.
+ */
+int
+ccp_test(ppp_pcb *pcb, u_char *opt_ptr, int opt_len, int for_transmit)
+{
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_UNUSED_ARG(opt_ptr);
+  LWIP_UNUSED_ARG(opt_len);
+  LWIP_UNUSED_ARG(for_transmit);
+  return -1;
+}
+#endif /* unused */
+
+/*
+ * ccp_set - inform about the current state of CCP.
+ */
+void
+ccp_set(ppp_pcb *pcb, u8_t isopen, u8_t isup, u8_t receive_method, u8_t transmit_method)
+{
+  LWIP_UNUSED_ARG(isopen);
+  LWIP_UNUSED_ARG(isup);
+  pcb->ccp_receive_method = receive_method;
+  pcb->ccp_transmit_method = transmit_method;
+  PPPDEBUG(LOG_DEBUG, ("ccp_set[%d]: is_open=%d, is_up=%d, receive_method=%u, transmit_method=%u\n",
+           pcb->netif->num, isopen, isup, receive_method, transmit_method));
+}
+
+void
+ccp_reset_comp(ppp_pcb *pcb)
+{
+  switch (pcb->ccp_transmit_method) {
+#if MPPE_SUPPORT
+  case CI_MPPE:
+    mppe_comp_reset(pcb, &pcb->mppe_comp);
+    break;
+#endif /* MPPE_SUPPORT */
+  default:
+    break;
+  }
+}
+
+void
+ccp_reset_decomp(ppp_pcb *pcb)
+{
+  switch (pcb->ccp_receive_method) {
+#if MPPE_SUPPORT
+  case CI_MPPE:
+    mppe_decomp_reset(pcb, &pcb->mppe_decomp);
+    break;
+#endif /* MPPE_SUPPORT */
+  default:
+    break;
+  }
+}
+
+#if 0 /* unused */
+/*
+ * ccp_fatal_error - returns 1 if decompression was disabled as a
+ * result of an error detected after decompression of a packet,
+ * 0 otherwise.  This is necessary because of patent nonsense.
+ */
+int
+ccp_fatal_error(ppp_pcb *pcb)
+{
+  LWIP_UNUSED_ARG(pcb);
+  return 1;
+}
+#endif /* unused */
+#endif /* CCP_SUPPORT */
+
+#if PPP_IDLETIMELIMIT
+/********************************************************************
+ *
+ * get_idle_time - return how long the link has been idle.
+ */
+int get_idle_time(ppp_pcb *pcb, struct ppp_idle *ip) {
+  /* FIXME: add idle time support and make it optional */
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_UNUSED_ARG(ip);
+  return 1;
+}
+#endif /* PPP_IDLETIMELIMIT */
+
+#if DEMAND_SUPPORT
+/********************************************************************
+ *
+ * get_loop_output - get outgoing packets from the ppp device,
+ * and detect when we want to bring the real link up.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ */
+int get_loop_output(void) {
+  return 0;
+}
+#endif /* DEMAND_SUPPORT */
+
+#if PPP_PROTOCOLNAME
+/* List of protocol names, to make our messages a little more informative. */
+struct protocol_list {
+  u_short proto;
+  const char *name;
+} const protocol_list[] = {
+  { 0x21, "IP" },
+  { 0x23, "OSI Network Layer" },
+  { 0x25, "Xerox NS IDP" },
+  { 0x27, "DECnet Phase IV" },
+  { 0x29, "Appletalk" },
+  { 0x2b, "Novell IPX" },
+  { 0x2d, "VJ compressed TCP/IP" },
+  { 0x2f, "VJ uncompressed TCP/IP" },
+  { 0x31, "Bridging PDU" },
+  { 0x33, "Stream Protocol ST-II" },
+  { 0x35, "Banyan Vines" },
+  { 0x39, "AppleTalk EDDP" },
+  { 0x3b, "AppleTalk SmartBuffered" },
+  { 0x3d, "Multi-Link" },
+  { 0x3f, "NETBIOS Framing" },
+  { 0x41, "Cisco Systems" },
+  { 0x43, "Ascom Timeplex" },
+  { 0x45, "Fujitsu Link Backup and Load Balancing (LBLB)" },
+  { 0x47, "DCA Remote Lan" },
+  { 0x49, "Serial Data Transport Protocol (PPP-SDTP)" },
+  { 0x4b, "SNA over 802.2" },
+  { 0x4d, "SNA" },
+  { 0x4f, "IP6 Header Compression" },
+  { 0x51, "KNX Bridging Data" },
+  { 0x53, "Encryption" },
+  { 0x55, "Individual Link Encryption" },
+  { 0x57, "IPv6" },
+  { 0x59, "PPP Muxing" },
+  { 0x5b, "Vendor-Specific Network Protocol" },
+  { 0x61, "RTP IPHC Full Header" },
+  { 0x63, "RTP IPHC Compressed TCP" },
+  { 0x65, "RTP IPHC Compressed non-TCP" },
+  { 0x67, "RTP IPHC Compressed UDP 8" },
+  { 0x69, "RTP IPHC Compressed RTP 8" },
+  { 0x6f, "Stampede Bridging" },
+  { 0x73, "MP+" },
+  { 0xc1, "NTCITS IPI" },
+  { 0xfb, "single-link compression" },
+  { 0xfd, "Compressed Datagram" },
+  { 0x0201, "802.1d Hello Packets" },
+  { 0x0203, "IBM Source Routing BPDU" },
+  { 0x0205, "DEC LANBridge100 Spanning Tree" },
+  { 0x0207, "Cisco Discovery Protocol" },
+  { 0x0209, "Netcs Twin Routing" },
+  { 0x020b, "STP - Scheduled Transfer Protocol" },
+  { 0x020d, "EDP - Extreme Discovery Protocol" },
+  { 0x0211, "Optical Supervisory Channel Protocol" },
+  { 0x0213, "Optical Supervisory Channel Protocol" },
+  { 0x0231, "Luxcom" },
+  { 0x0233, "Sigma Network Systems" },
+  { 0x0235, "Apple Client Server Protocol" },
+  { 0x0281, "MPLS Unicast" },
+  { 0x0283, "MPLS Multicast" },
+  { 0x0285, "IEEE p1284.4 standard - data packets" },
+  { 0x0287, "ETSI TETRA Network Protocol Type 1" },
+  { 0x0289, "Multichannel Flow Treatment Protocol" },
+  { 0x2063, "RTP IPHC Compressed TCP No Delta" },
+  { 0x2065, "RTP IPHC Context State" },
+  { 0x2067, "RTP IPHC Compressed UDP 16" },
+  { 0x2069, "RTP IPHC Compressed RTP 16" },
+  { 0x4001, "Cray Communications Control Protocol" },
+  { 0x4003, "CDPD Mobile Network Registration Protocol" },
+  { 0x4005, "Expand accelerator protocol" },
+  { 0x4007, "ODSICP NCP" },
+  { 0x4009, "DOCSIS DLL" },
+  { 0x400B, "Cetacean Network Detection Protocol" },
+  { 0x4021, "Stacker LZS" },
+  { 0x4023, "RefTek Protocol" },
+  { 0x4025, "Fibre Channel" },
+  { 0x4027, "EMIT Protocols" },
+  { 0x405b, "Vendor-Specific Protocol (VSP)" },
+  { 0x8021, "Internet Protocol Control Protocol" },
+  { 0x8023, "OSI Network Layer Control Protocol" },
+  { 0x8025, "Xerox NS IDP Control Protocol" },
+  { 0x8027, "DECnet Phase IV Control Protocol" },
+  { 0x8029, "Appletalk Control Protocol" },
+  { 0x802b, "Novell IPX Control Protocol" },
+  { 0x8031, "Bridging NCP" },
+  { 0x8033, "Stream Protocol Control Protocol" },
+  { 0x8035, "Banyan Vines Control Protocol" },
+  { 0x803d, "Multi-Link Control Protocol" },
+  { 0x803f, "NETBIOS Framing Control Protocol" },
+  { 0x8041, "Cisco Systems Control Protocol" },
+  { 0x8043, "Ascom Timeplex" },
+  { 0x8045, "Fujitsu LBLB Control Protocol" },
+  { 0x8047, "DCA Remote Lan Network Control Protocol (RLNCP)" },
+  { 0x8049, "Serial Data Control Protocol (PPP-SDCP)" },
+  { 0x804b, "SNA over 802.2 Control Protocol" },
+  { 0x804d, "SNA Control Protocol" },
+  { 0x804f, "IP6 Header Compression Control Protocol" },
+  { 0x8051, "KNX Bridging Control Protocol" },
+  { 0x8053, "Encryption Control Protocol" },
+  { 0x8055, "Individual Link Encryption Control Protocol" },
+  { 0x8057, "IPv6 Control Protocol" },
+  { 0x8059, "PPP Muxing Control Protocol" },
+  { 0x805b, "Vendor-Specific Network Control Protocol (VSNCP)" },
+  { 0x806f, "Stampede Bridging Control Protocol" },
+  { 0x8073, "MP+ Control Protocol" },
+  { 0x80c1, "NTCITS IPI Control Protocol" },
+  { 0x80fb, "Single Link Compression Control Protocol" },
+  { 0x80fd, "Compression Control Protocol" },
+  { 0x8207, "Cisco Discovery Protocol Control" },
+  { 0x8209, "Netcs Twin Routing" },
+  { 0x820b, "STP - Control Protocol" },
+  { 0x820d, "EDPCP - Extreme Discovery Protocol Ctrl Prtcl" },
+  { 0x8235, "Apple Client Server Protocol Control" },
+  { 0x8281, "MPLSCP" },
+  { 0x8285, "IEEE p1284.4 standard - Protocol Control" },
+  { 0x8287, "ETSI TETRA TNP1 Control Protocol" },
+  { 0x8289, "Multichannel Flow Treatment Protocol" },
+  { 0xc021, "Link Control Protocol" },
+  { 0xc023, "Password Authentication Protocol" },
+  { 0xc025, "Link Quality Report" },
+  { 0xc027, "Shiva Password Authentication Protocol" },
+  { 0xc029, "CallBack Control Protocol (CBCP)" },
+  { 0xc02b, "BACP Bandwidth Allocation Control Protocol" },
+  { 0xc02d, "BAP" },
+  { 0xc05b, "Vendor-Specific Authentication Protocol (VSAP)" },
+  { 0xc081, "Container Control Protocol" },
+  { 0xc223, "Challenge Handshake Authentication Protocol" },
+  { 0xc225, "RSA Authentication Protocol" },
+  { 0xc227, "Extensible Authentication Protocol" },
+  { 0xc229, "Mitsubishi Security Info Exch Ptcl (SIEP)" },
+  { 0xc26f, "Stampede Bridging Authorization Protocol" },
+  { 0xc281, "Proprietary Authentication Protocol" },
+  { 0xc283, "Proprietary Authentication Protocol" },
+  { 0xc481, "Proprietary Node ID Authentication Protocol" },
+  { 0, NULL },
+};
+
+/*
+ * protocol_name - find a name for a PPP protocol.
+ */
+const char * protocol_name(int proto) {
+  const struct protocol_list *lp;
+
+  for (lp = protocol_list; lp->proto != 0; ++lp) {
+    if (proto == lp->proto) {
+      return lp->name;
+    }
+  }
+  return NULL;
+}
+#endif /* PPP_PROTOCOLNAME */
+
+#if PPP_STATS_SUPPORT
+
+/* ---- Note on PPP Stats support ----
+ *
+ * The one willing link stats support should add the get_ppp_stats()
+ * to fetch statistics from lwIP.
+ */
+
+/*
+ * reset_link_stats - "reset" stats when link goes up.
+ */
+void reset_link_stats(int u) {
+  if (!get_ppp_stats(u, &old_link_stats)) {
+    return;
+  }
+  gettimeofday(&start_time, NULL);
+}
+
+/*
+ * update_link_stats - get stats at link termination.
+ */
+void update_link_stats(int u) {
+  struct timeval now;
+  char numbuf[32];
+
+  if (!get_ppp_stats(u, &link_stats) || gettimeofday(&now, NULL) < 0) {
+    return;
+  }
+  link_connect_time = now.tv_sec - start_time.tv_sec;
+  link_stats_valid = 1;
+
+  link_stats.bytes_in  -= old_link_stats.bytes_in;
+  link_stats.bytes_out -= old_link_stats.bytes_out;
+  link_stats.pkts_in   -= old_link_stats.pkts_in;
+  link_stats.pkts_out  -= old_link_stats.pkts_out;
+}
+
+void print_link_stats() {
+  /*
+   * Print connect time and statistics.
+   */
+  if (link_stats_valid) {
+    int t = (link_connect_time + 5) / 6;    /* 1/10ths of minutes */
+    info("Connect time %d.%d minutes.", t/10, t%10);
+    info("Sent %u bytes, received %u bytes.", link_stats.bytes_out, link_stats.bytes_in);
+    link_stats_valid = 0;
+  }
+}
+#endif /* PPP_STATS_SUPPORT */
+
+#endif /* PPP_SUPPORT */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/pppapi.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/pppapi.c
new file mode 100644 (file)
index 0000000..947f7ba
--- /dev/null
@@ -0,0 +1,427 @@
+/**
+ * @file
+ * Point To Point Protocol Sequential API module
+ *
+ */
+
+/*
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "netif/ppp/ppp_opts.h"
+
+#if LWIP_PPP_API /* don't build if not configured for use in lwipopts.h */
+
+#include "netif/ppp/pppapi.h"
+#include "lwip/priv/tcpip_priv.h"
+#include "netif/ppp/pppoe.h"
+#include "netif/ppp/pppol2tp.h"
+#include "netif/ppp/pppos.h"
+
+#if LWIP_MPU_COMPATIBLE
+LWIP_MEMPOOL_DECLARE(PPPAPI_MSG, MEMP_NUM_PPP_API_MSG, sizeof(struct pppapi_msg), "PPPAPI_MSG")
+#endif
+
+#define PPPAPI_VAR_REF(name)               API_VAR_REF(name)
+#define PPPAPI_VAR_DECLARE(name)           API_VAR_DECLARE(struct pppapi_msg, name)
+#define PPPAPI_VAR_ALLOC(name)             API_VAR_ALLOC_POOL(struct pppapi_msg, PPPAPI_MSG, name, ERR_MEM)
+#define PPPAPI_VAR_ALLOC_RETURN_NULL(name) API_VAR_ALLOC_POOL(struct pppapi_msg, PPPAPI_MSG, name, NULL)
+#define PPPAPI_VAR_FREE(name)              API_VAR_FREE_POOL(PPPAPI_MSG, name)
+
+/**
+ * Call ppp_set_default() inside the tcpip_thread context.
+ */
+static err_t
+pppapi_do_ppp_set_default(struct tcpip_api_call_data *m)
+{
+  /* cast through void* to silence alignment warnings. 
+   * We know it works because the structs have been instantiated as struct pppapi_msg */
+  struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
+  
+  ppp_set_default(msg->msg.ppp);
+  return ERR_OK;
+}
+
+/**
+ * Call ppp_set_default() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+err_t
+pppapi_set_default(ppp_pcb *pcb)
+{
+  err_t err;
+  PPPAPI_VAR_DECLARE(msg);
+  PPPAPI_VAR_ALLOC(msg);
+
+  PPPAPI_VAR_REF(msg).msg.ppp = pcb;
+  err = tcpip_api_call(pppapi_do_ppp_set_default, &PPPAPI_VAR_REF(msg).call);
+  PPPAPI_VAR_FREE(msg);
+  return err;
+}
+
+
+#if PPP_NOTIFY_PHASE
+/**
+ * Call ppp_set_notify_phase_callback() inside the tcpip_thread context.
+ */
+static err_t
+pppapi_do_ppp_set_notify_phase_callback(struct tcpip_api_call_data *m)
+{
+  /* cast through void* to silence alignment warnings. 
+   * We know it works because the structs have been instantiated as struct pppapi_msg */
+   struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
+
+  ppp_set_notify_phase_callback(msg->msg.ppp, msg->msg.msg.setnotifyphasecb.notify_phase_cb);
+  return ERR_OK;
+}
+
+/**
+ * Call ppp_set_notify_phase_callback() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+err_t
+pppapi_set_notify_phase_callback(ppp_pcb *pcb, ppp_notify_phase_cb_fn notify_phase_cb)
+{
+  err_t err;
+  PPPAPI_VAR_DECLARE(msg);
+  PPPAPI_VAR_ALLOC(msg);
+
+  PPPAPI_VAR_REF(msg).msg.ppp = pcb;
+  PPPAPI_VAR_REF(msg).msg.msg.setnotifyphasecb.notify_phase_cb = notify_phase_cb;
+  err = tcpip_api_call(pppapi_do_ppp_set_notify_phase_callback, &PPPAPI_VAR_REF(msg).call);
+  PPPAPI_VAR_FREE(msg);
+  return err;
+}
+#endif /* PPP_NOTIFY_PHASE */
+
+
+#if PPPOS_SUPPORT
+/**
+ * Call pppos_create() inside the tcpip_thread context.
+ */
+static err_t
+pppapi_do_pppos_create(struct tcpip_api_call_data *m)
+{
+  /* cast through void* to silence alignment warnings. 
+   * We know it works because the structs have been instantiated as struct pppapi_msg */
+  struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
+
+  msg->msg.ppp = pppos_create(msg->msg.msg.serialcreate.pppif, msg->msg.msg.serialcreate.output_cb,
+    msg->msg.msg.serialcreate.link_status_cb, msg->msg.msg.serialcreate.ctx_cb);
+  return ERR_OK;
+}
+
+/**
+ * Call pppos_create() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+ppp_pcb*
+pppapi_pppos_create(struct netif *pppif, pppos_output_cb_fn output_cb,
+               ppp_link_status_cb_fn link_status_cb, void *ctx_cb)
+{
+  ppp_pcb* result;
+  PPPAPI_VAR_DECLARE(msg);
+  PPPAPI_VAR_ALLOC_RETURN_NULL(msg);
+
+  PPPAPI_VAR_REF(msg).msg.ppp = NULL;
+  PPPAPI_VAR_REF(msg).msg.msg.serialcreate.pppif = pppif;
+  PPPAPI_VAR_REF(msg).msg.msg.serialcreate.output_cb = output_cb;
+  PPPAPI_VAR_REF(msg).msg.msg.serialcreate.link_status_cb = link_status_cb;
+  PPPAPI_VAR_REF(msg).msg.msg.serialcreate.ctx_cb = ctx_cb;
+  tcpip_api_call(pppapi_do_pppos_create, &PPPAPI_VAR_REF(msg).call);
+  result = PPPAPI_VAR_REF(msg).msg.ppp;
+  PPPAPI_VAR_FREE(msg);
+  return result;
+}
+#endif /* PPPOS_SUPPORT */
+
+
+#if PPPOE_SUPPORT
+/**
+ * Call pppoe_create() inside the tcpip_thread context.
+ */
+static err_t
+pppapi_do_pppoe_create(struct tcpip_api_call_data *m)
+{
+  /* cast through void* to silence alignment warnings. 
+   * We know it works because the structs have been instantiated as struct pppapi_msg */
+  struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
+
+  msg->msg.ppp = pppoe_create(msg->msg.msg.ethernetcreate.pppif, msg->msg.msg.ethernetcreate.ethif,
+    msg->msg.msg.ethernetcreate.service_name, msg->msg.msg.ethernetcreate.concentrator_name,
+    msg->msg.msg.ethernetcreate.link_status_cb, msg->msg.msg.ethernetcreate.ctx_cb);
+  return ERR_OK;
+}
+
+/**
+ * Call pppoe_create() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+ppp_pcb*
+pppapi_pppoe_create(struct netif *pppif, struct netif *ethif, const char *service_name,
+                            const char *concentrator_name, ppp_link_status_cb_fn link_status_cb,
+                            void *ctx_cb)
+{
+  ppp_pcb* result;
+  PPPAPI_VAR_DECLARE(msg);
+  PPPAPI_VAR_ALLOC_RETURN_NULL(msg);
+
+  PPPAPI_VAR_REF(msg).msg.ppp = NULL;
+  PPPAPI_VAR_REF(msg).msg.msg.ethernetcreate.pppif = pppif;
+  PPPAPI_VAR_REF(msg).msg.msg.ethernetcreate.ethif = ethif;
+  PPPAPI_VAR_REF(msg).msg.msg.ethernetcreate.service_name = service_name;
+  PPPAPI_VAR_REF(msg).msg.msg.ethernetcreate.concentrator_name = concentrator_name;
+  PPPAPI_VAR_REF(msg).msg.msg.ethernetcreate.link_status_cb = link_status_cb;
+  PPPAPI_VAR_REF(msg).msg.msg.ethernetcreate.ctx_cb = ctx_cb;
+  tcpip_api_call(pppapi_do_pppoe_create, &PPPAPI_VAR_REF(msg).call);
+  result = PPPAPI_VAR_REF(msg).msg.ppp;
+  PPPAPI_VAR_FREE(msg);
+  return result;
+}
+#endif /* PPPOE_SUPPORT */
+
+
+#if PPPOL2TP_SUPPORT
+/**
+ * Call pppol2tp_create() inside the tcpip_thread context.
+ */
+static err_t
+pppapi_do_pppol2tp_create(struct tcpip_api_call_data *m)
+{
+  /* cast through void* to silence alignment warnings. 
+   * We know it works because the structs have been instantiated as struct pppapi_msg */
+  struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
+
+  msg->msg.ppp = pppol2tp_create(msg->msg.msg.l2tpcreate.pppif,
+    msg->msg.msg.l2tpcreate.netif, API_EXPR_REF(msg->msg.msg.l2tpcreate.ipaddr), msg->msg.msg.l2tpcreate.port,
+#if PPPOL2TP_AUTH_SUPPORT
+    msg->msg.msg.l2tpcreate.secret,
+    msg->msg.msg.l2tpcreate.secret_len,
+#else /* PPPOL2TP_AUTH_SUPPORT */
+    NULL,
+    0,
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+    msg->msg.msg.l2tpcreate.link_status_cb, msg->msg.msg.l2tpcreate.ctx_cb);
+  return ERR_OK;
+}
+
+/**
+ * Call pppol2tp_create() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+ppp_pcb*
+pppapi_pppol2tp_create(struct netif *pppif, struct netif *netif, ip_addr_t *ipaddr, u16_t port,
+                        const u8_t *secret, u8_t secret_len,
+                        ppp_link_status_cb_fn link_status_cb, void *ctx_cb)
+{
+  ppp_pcb* result;
+  PPPAPI_VAR_DECLARE(msg);
+  PPPAPI_VAR_ALLOC_RETURN_NULL(msg);
+#if !PPPOL2TP_AUTH_SUPPORT
+  LWIP_UNUSED_ARG(secret);
+  LWIP_UNUSED_ARG(secret_len);
+#endif /* !PPPOL2TP_AUTH_SUPPORT */
+
+  PPPAPI_VAR_REF(msg).msg.ppp = NULL;
+  PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.pppif = pppif;
+  PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.netif = netif;
+  PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.ipaddr = PPPAPI_VAR_REF(ipaddr);
+  PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.port = port;
+#if PPPOL2TP_AUTH_SUPPORT
+  PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.secret = secret;
+  PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.secret_len = secret_len;
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+  PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.link_status_cb = link_status_cb;
+  PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.ctx_cb = ctx_cb;
+  tcpip_api_call(pppapi_do_pppol2tp_create, &PPPAPI_VAR_REF(msg).call);
+  result = PPPAPI_VAR_REF(msg).msg.ppp;
+  PPPAPI_VAR_FREE(msg);
+  return result;
+}
+#endif /* PPPOL2TP_SUPPORT */
+
+
+/**
+ * Call ppp_connect() inside the tcpip_thread context.
+ */
+static err_t
+pppapi_do_ppp_connect(struct tcpip_api_call_data *m)
+{
+  /* cast through void* to silence alignment warnings. 
+   * We know it works because the structs have been instantiated as struct pppapi_msg */
+  struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
+
+  return ppp_connect(msg->msg.ppp, msg->msg.msg.connect.holdoff);
+}
+
+/**
+ * Call ppp_connect() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+err_t
+pppapi_connect(ppp_pcb *pcb, u16_t holdoff)
+{
+  err_t err;
+  PPPAPI_VAR_DECLARE(msg);
+  PPPAPI_VAR_ALLOC(msg);
+
+  PPPAPI_VAR_REF(msg).msg.ppp = pcb;
+  PPPAPI_VAR_REF(msg).msg.msg.connect.holdoff = holdoff;
+  err = tcpip_api_call(pppapi_do_ppp_connect, &PPPAPI_VAR_REF(msg).call);
+  PPPAPI_VAR_FREE(msg);
+  return err;
+}
+
+
+#if PPP_SERVER
+/**
+ * Call ppp_listen() inside the tcpip_thread context.
+ */
+static err_t
+pppapi_do_ppp_listen(struct tcpip_api_call_data *m)
+{
+  /* cast through void* to silence alignment warnings. 
+   * We know it works because the structs have been instantiated as struct pppapi_msg */
+  struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
+
+  return ppp_listen(msg->msg.ppp);
+}
+
+/**
+ * Call ppp_listen() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+err_t
+pppapi_listen(ppp_pcb *pcb)
+{
+  err_t err;
+  PPPAPI_VAR_DECLARE(msg);
+  PPPAPI_VAR_ALLOC(msg);
+
+  PPPAPI_VAR_REF(msg).msg.ppp = pcb;
+  err = tcpip_api_call(pppapi_do_ppp_listen, &PPPAPI_VAR_REF(msg).call);
+  PPPAPI_VAR_FREE(msg);
+  return err;
+}
+#endif /* PPP_SERVER */
+
+
+/**
+ * Call ppp_close() inside the tcpip_thread context.
+ */
+static err_t
+pppapi_do_ppp_close(struct tcpip_api_call_data *m)
+{
+  /* cast through void* to silence alignment warnings. 
+   * We know it works because the structs have been instantiated as struct pppapi_msg */
+  struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
+
+  return ppp_close(msg->msg.ppp, msg->msg.msg.close.nocarrier);
+}
+
+/**
+ * Call ppp_close() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+err_t
+pppapi_close(ppp_pcb *pcb, u8_t nocarrier)
+{
+  err_t err;
+  PPPAPI_VAR_DECLARE(msg);
+  PPPAPI_VAR_ALLOC(msg);
+
+  PPPAPI_VAR_REF(msg).msg.ppp = pcb;
+  PPPAPI_VAR_REF(msg).msg.msg.close.nocarrier = nocarrier;
+  err = tcpip_api_call(pppapi_do_ppp_close, &PPPAPI_VAR_REF(msg).call);
+  PPPAPI_VAR_FREE(msg);
+  return err;
+}
+
+
+/**
+ * Call ppp_free() inside the tcpip_thread context.
+ */
+static err_t
+pppapi_do_ppp_free(struct tcpip_api_call_data *m)
+{
+  /* cast through void* to silence alignment warnings. 
+   * We know it works because the structs have been instantiated as struct pppapi_msg */
+  struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
+
+  return ppp_free(msg->msg.ppp);
+}
+
+/**
+ * Call ppp_free() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+err_t
+pppapi_free(ppp_pcb *pcb)
+{
+  err_t err;
+  PPPAPI_VAR_DECLARE(msg);
+  PPPAPI_VAR_ALLOC(msg);
+
+  PPPAPI_VAR_REF(msg).msg.ppp = pcb;
+  err = tcpip_api_call(pppapi_do_ppp_free, &PPPAPI_VAR_REF(msg).call);
+  PPPAPI_VAR_FREE(msg);
+  return err;
+}
+
+
+/**
+ * Call ppp_ioctl() inside the tcpip_thread context.
+ */
+static err_t
+pppapi_do_ppp_ioctl(struct tcpip_api_call_data *m)
+{
+  /* cast through void* to silence alignment warnings. 
+   * We know it works because the structs have been instantiated as struct pppapi_msg */
+  struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
+
+  return ppp_ioctl(msg->msg.ppp, msg->msg.msg.ioctl.cmd, msg->msg.msg.ioctl.arg);
+}
+
+/**
+ * Call ppp_ioctl() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+err_t
+pppapi_ioctl(ppp_pcb *pcb, u8_t cmd, void *arg)
+{
+  err_t err;
+  PPPAPI_VAR_DECLARE(msg);
+  PPPAPI_VAR_ALLOC(msg);
+
+  PPPAPI_VAR_REF(msg).msg.ppp = pcb;
+  PPPAPI_VAR_REF(msg).msg.msg.ioctl.cmd = cmd;
+  PPPAPI_VAR_REF(msg).msg.msg.ioctl.arg = arg;
+  err = tcpip_api_call(pppapi_do_ppp_ioctl, &PPPAPI_VAR_REF(msg).call);
+  PPPAPI_VAR_FREE(msg);
+  return err;
+}
+
+#endif /* LWIP_PPP_API */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/pppcrypt.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/pppcrypt.c
new file mode 100644 (file)
index 0000000..82d78c1
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * pppcrypt.c - PPP/DES linkage for MS-CHAP and EAP SRP-SHA1
+ *
+ * Extracted from chap_ms.c by James Carlson.
+ *
+ * Copyright (c) 1995 Eric Rosenquist.  All rights reserved.
+ *
+ * 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.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && MSCHAP_SUPPORT /* don't build if not necessary */
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/pppcrypt.h"
+
+
+static u_char pppcrypt_get_7bits(u_char *input, int startBit) {
+       unsigned int word;
+
+       word  = (unsigned)input[startBit / 8] << 8;
+       word |= (unsigned)input[startBit / 8 + 1];
+
+       word >>= 15 - (startBit % 8 + 7);
+
+       return word & 0xFE;
+}
+
+/* IN  56 bit DES key missing parity bits
+ * OUT 64 bit DES key with parity bits added
+ */
+void pppcrypt_56_to_64_bit_key(u_char *key, u_char * des_key) {
+       des_key[0] = pppcrypt_get_7bits(key,  0);
+       des_key[1] = pppcrypt_get_7bits(key,  7);
+       des_key[2] = pppcrypt_get_7bits(key, 14);
+       des_key[3] = pppcrypt_get_7bits(key, 21);
+       des_key[4] = pppcrypt_get_7bits(key, 28);
+       des_key[5] = pppcrypt_get_7bits(key, 35);
+       des_key[6] = pppcrypt_get_7bits(key, 42);
+       des_key[7] = pppcrypt_get_7bits(key, 49);
+}
+
+#endif /* PPP_SUPPORT && MSCHAP_SUPPORT */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/pppoe.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/pppoe.c
new file mode 100644 (file)
index 0000000..971b36b
--- /dev/null
@@ -0,0 +1,1198 @@
+/*****************************************************************************
+* pppoe.c - PPP Over Ethernet implementation for lwIP.
+*
+* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any 
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE 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 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.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 06-01-01 Marc Boucher <marc@mbsi.ca>
+*   Ported to lwIP.
+*****************************************************************************/
+
+
+
+/* based on NetBSD: if_pppoe.c,v 1.64 2006/01/31 23:50:15 martin Exp */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Martin Husemann <martin@NetBSD.org>.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *        This product includes software developed by the NetBSD
+ *        Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPPOE_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#if 0 /* UNUSED */
+#include <string.h>
+#include <stdio.h>
+#endif /* UNUSED */
+
+#include "lwip/timeouts.h"
+#include "lwip/memp.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+
+#include "netif/ethernet.h"
+#include "netif/ppp/ppp_impl.h"
+#include "netif/ppp/lcp.h"
+#include "netif/ppp/ipcp.h"
+#include "netif/ppp/pppoe.h"
+
+/* Memory pool */
+LWIP_MEMPOOL_DECLARE(PPPOE_IF, MEMP_NUM_PPPOE_INTERFACES, sizeof(struct pppoe_softc), "PPPOE_IF")
+
+/* Add a 16 bit unsigned value to a buffer pointed to by PTR */
+#define PPPOE_ADD_16(PTR, VAL) \
+    *(PTR)++ = (u8_t)((VAL) / 256);    \
+    *(PTR)++ = (u8_t)((VAL) % 256)
+
+/* Add a complete PPPoE header to the buffer pointed to by PTR */
+#define PPPOE_ADD_HEADER(PTR, CODE, SESS, LEN)  \
+    *(PTR)++ = PPPOE_VERTYPE;  \
+    *(PTR)++ = (CODE);         \
+    PPPOE_ADD_16(PTR, SESS);   \
+    PPPOE_ADD_16(PTR, LEN)
+
+#define PPPOE_DISC_TIMEOUT (5*1000)  /* base for quick timeout calculation */
+#define PPPOE_SLOW_RETRY   (60*1000) /* persistent retry interval */
+#define PPPOE_DISC_MAXPADI  4        /* retry PADI four times (quickly) */
+#define PPPOE_DISC_MAXPADR  2        /* retry PADR twice */
+
+#ifdef PPPOE_SERVER
+#error "PPPOE_SERVER is not yet supported under lwIP!"
+/* from if_spppsubr.c */
+#define IFF_PASSIVE IFF_LINK0 /* wait passively for connection */
+#endif
+
+#define PPPOE_ERRORSTRING_LEN     64
+
+
+/* callbacks called from PPP core */
+static err_t pppoe_write(ppp_pcb *ppp, void *ctx, struct pbuf *p);
+static err_t pppoe_netif_output(ppp_pcb *ppp, void *ctx, struct pbuf *p, u_short protocol);
+static void pppoe_connect(ppp_pcb *ppp, void *ctx);
+static void pppoe_disconnect(ppp_pcb *ppp, void *ctx);
+static err_t pppoe_destroy(ppp_pcb *ppp, void *ctx);
+
+/* management routines */
+static void pppoe_abort_connect(struct pppoe_softc *);
+#if 0 /* UNUSED */
+static void pppoe_clear_softc(struct pppoe_softc *, const char *);
+#endif /* UNUSED */
+
+/* internal timeout handling */
+static void pppoe_timeout(void *);
+
+/* sending actual protocol controll packets */
+static err_t pppoe_send_padi(struct pppoe_softc *);
+static err_t pppoe_send_padr(struct pppoe_softc *);
+#ifdef PPPOE_SERVER
+static err_t pppoe_send_pado(struct pppoe_softc *);
+static err_t pppoe_send_pads(struct pppoe_softc *);
+#endif
+static err_t pppoe_send_padt(struct netif *, u_int, const u8_t *);
+
+/* internal helper functions */
+static err_t pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb);
+static struct pppoe_softc* pppoe_find_softc_by_session(u_int session, struct netif *rcvif);
+static struct pppoe_softc* pppoe_find_softc_by_hunique(u8_t *token, size_t len, struct netif *rcvif);
+
+/** linked list of created pppoe interfaces */
+static struct pppoe_softc *pppoe_softc_list;
+
+/* Callbacks structure for PPP core */
+static const struct link_callbacks pppoe_callbacks = {
+  pppoe_connect,
+#if PPP_SERVER
+  NULL,
+#endif /* PPP_SERVER */
+  pppoe_disconnect,
+  pppoe_destroy,
+  pppoe_write,
+  pppoe_netif_output,
+  NULL,
+  NULL
+};
+
+/*
+ * Create a new PPP Over Ethernet (PPPoE) connection.
+ *
+ * Return 0 on success, an error code on failure.
+ */
+ppp_pcb *pppoe_create(struct netif *pppif,
+       struct netif *ethif,
+       const char *service_name, const char *concentrator_name,
+       ppp_link_status_cb_fn link_status_cb, void *ctx_cb)
+{
+  ppp_pcb *ppp;
+  struct pppoe_softc *sc;
+#if !PPPOE_SCNAME_SUPPORT
+  LWIP_UNUSED_ARG(service_name);
+  LWIP_UNUSED_ARG(concentrator_name);
+#endif /* !PPPOE_SCNAME_SUPPORT */
+  LWIP_ASSERT_CORE_LOCKED();
+
+  sc = (struct pppoe_softc *)LWIP_MEMPOOL_ALLOC(PPPOE_IF);
+  if (sc == NULL) {
+    return NULL;
+  }
+
+  ppp = ppp_new(pppif, &pppoe_callbacks, sc, link_status_cb, ctx_cb);
+  if (ppp == NULL) {
+    LWIP_MEMPOOL_FREE(PPPOE_IF, sc);
+    return NULL;
+  }
+
+  memset(sc, 0, sizeof(struct pppoe_softc));
+  sc->pcb = ppp;
+  sc->sc_ethif = ethif;
+#if PPPOE_SCNAME_SUPPORT
+  sc->sc_service_name = service_name;
+  sc->sc_concentrator_name = concentrator_name;
+#endif /* PPPOE_SCNAME_SUPPORT */
+  /* put the new interface at the head of the list */
+  sc->next = pppoe_softc_list;
+  pppoe_softc_list = sc;
+  return ppp;
+}
+
+/* Called by PPP core */
+static err_t pppoe_write(ppp_pcb *ppp, void *ctx, struct pbuf *p) {
+  struct pppoe_softc *sc = (struct pppoe_softc *)ctx;
+  struct pbuf *ph; /* Ethernet + PPPoE header */
+  err_t ret;
+#if MIB2_STATS
+  u16_t tot_len;
+#else /* MIB2_STATS */
+  LWIP_UNUSED_ARG(ppp);
+#endif /* MIB2_STATS */
+
+  /* skip address & flags */
+  pbuf_remove_header(p, 2);
+
+  ph = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN), PBUF_RAM);
+  if(!ph) {
+    LINK_STATS_INC(link.memerr);
+    LINK_STATS_INC(link.proterr);
+    MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
+    pbuf_free(p);
+    return ERR_MEM;
+  }
+
+  pbuf_remove_header(ph, PPPOE_HEADERLEN); /* hide PPPoE header */
+  pbuf_cat(ph, p);
+#if MIB2_STATS
+  tot_len = ph->tot_len;
+#endif /* MIB2_STATS */
+
+  ret = pppoe_xmit(sc, ph);
+  if (ret != ERR_OK) {
+    LINK_STATS_INC(link.err);
+    MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
+    return ret;
+  }
+
+  MIB2_STATS_NETIF_ADD(ppp->netif, ifoutoctets, (u16_t)tot_len);
+  MIB2_STATS_NETIF_INC(ppp->netif, ifoutucastpkts);
+  LINK_STATS_INC(link.xmit);
+  return ERR_OK;
+}
+
+/* Called by PPP core */
+static err_t pppoe_netif_output(ppp_pcb *ppp, void *ctx, struct pbuf *p, u_short protocol) {
+  struct pppoe_softc *sc = (struct pppoe_softc *)ctx;
+  struct pbuf *pb;
+  u8_t *pl;
+  err_t err;
+#if MIB2_STATS
+  u16_t tot_len;
+#else /* MIB2_STATS */
+  LWIP_UNUSED_ARG(ppp);
+#endif /* MIB2_STATS */
+
+  /* @todo: try to use pbuf_header() here! */
+  pb = pbuf_alloc(PBUF_LINK, PPPOE_HEADERLEN + sizeof(protocol), PBUF_RAM);
+  if(!pb) {
+    LINK_STATS_INC(link.memerr);
+    LINK_STATS_INC(link.proterr);
+    MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
+    return ERR_MEM;
+  }
+
+  pbuf_remove_header(pb, PPPOE_HEADERLEN);
+
+  pl = (u8_t*)pb->payload;
+  PUTSHORT(protocol, pl);
+
+  pbuf_chain(pb, p);
+#if MIB2_STATS
+  tot_len = pb->tot_len;
+#endif /* MIB2_STATS */
+
+  if( (err = pppoe_xmit(sc, pb)) != ERR_OK) {
+    LINK_STATS_INC(link.err);
+    MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
+    return err;
+  }
+
+  MIB2_STATS_NETIF_ADD(ppp->netif, ifoutoctets, tot_len);
+  MIB2_STATS_NETIF_INC(ppp->netif, ifoutucastpkts);
+  LINK_STATS_INC(link.xmit);
+  return ERR_OK;
+}
+
+static err_t
+pppoe_destroy(ppp_pcb *ppp, void *ctx)
+{
+  struct pppoe_softc *sc = (struct pppoe_softc *)ctx;
+  struct pppoe_softc **copp, *freep;
+  LWIP_UNUSED_ARG(ppp);
+
+  sys_untimeout(pppoe_timeout, sc);
+
+  /* remove interface from list */
+  for (copp = &pppoe_softc_list; (freep = *copp); copp = &freep->next) {
+    if (freep == sc) {
+       *copp = freep->next;
+       break;
+    }
+  }
+  LWIP_MEMPOOL_FREE(PPPOE_IF, sc);
+
+  return ERR_OK;
+}
+
+/*
+ * Find the interface handling the specified session.
+ * Note: O(number of sessions open), this is a client-side only, mean
+ * and lean implementation, so number of open sessions typically should
+ * be 1.
+ */
+static struct pppoe_softc* pppoe_find_softc_by_session(u_int session, struct netif *rcvif) {
+  struct pppoe_softc *sc;
+
+  for (sc = pppoe_softc_list; sc != NULL; sc = sc->next) {
+    if (sc->sc_state == PPPOE_STATE_SESSION
+        && sc->sc_session == session
+         && sc->sc_ethif == rcvif) {
+           return sc;
+      }
+  }
+  return NULL;
+}
+
+/* Check host unique token passed and return appropriate softc pointer,
+ * or NULL if token is bogus. */
+static struct pppoe_softc* pppoe_find_softc_by_hunique(u8_t *token, size_t len, struct netif *rcvif) {
+  struct pppoe_softc *sc, *t;
+
+  if (len != sizeof sc) {
+    return NULL;
+  }
+  MEMCPY(&t, token, len);
+
+  for (sc = pppoe_softc_list; sc != NULL; sc = sc->next) {
+    if (sc == t) {
+      break;
+    }
+  }
+
+  if (sc == NULL) {
+    PPPDEBUG(LOG_DEBUG, ("pppoe: alien host unique tag, no session found\n"));
+    return NULL;
+  }
+
+  /* should be safe to access *sc now */
+  if (sc->sc_state < PPPOE_STATE_PADI_SENT || sc->sc_state >= PPPOE_STATE_SESSION) {
+    PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": host unique tag found, but it belongs to a connection in state %d\n",
+      sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_state));
+    return NULL;
+  }
+  if (sc->sc_ethif != rcvif) {
+    PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": wrong interface, not accepting host unique\n",
+      sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
+    return NULL;
+  }
+  return sc;
+}
+
+/* analyze and handle a single received packet while not in session state */
+void
+pppoe_disc_input(struct netif *netif, struct pbuf *pb)
+{
+  u16_t tag, len, off;
+  u16_t session, plen;
+  struct pppoe_softc *sc;
+#if PPP_DEBUG
+  const char *err_msg = NULL;
+#endif /* PPP_DEBUG */
+  u8_t *ac_cookie;
+  u16_t ac_cookie_len;
+#ifdef PPPOE_SERVER
+  u8_t *hunique;
+  size_t hunique_len;
+#endif
+  struct pppoehdr *ph;
+  struct pppoetag pt;
+  int err;
+  struct eth_hdr *ethhdr;
+
+  /* don't do anything if there is not a single PPPoE instance */
+  if (pppoe_softc_list == NULL) {
+    pbuf_free(pb);
+    return;
+  }
+
+  pb = pbuf_coalesce(pb, PBUF_RAW);
+
+  ethhdr = (struct eth_hdr *)pb->payload;
+
+  ac_cookie = NULL;
+  ac_cookie_len = 0;
+#ifdef PPPOE_SERVER
+  hunique = NULL;
+  hunique_len = 0;
+#endif
+  session = 0;
+  off = sizeof(struct eth_hdr) + sizeof(struct pppoehdr);
+  if (pb->len < off) {
+    PPPDEBUG(LOG_DEBUG, ("pppoe: packet too short: %d\n", pb->len));
+    goto done;
+  }
+
+  ph = (struct pppoehdr *) (ethhdr + 1);
+  if (ph->vertype != PPPOE_VERTYPE) {
+    PPPDEBUG(LOG_DEBUG, ("pppoe: unknown version/type packet: 0x%x\n", ph->vertype));
+    goto done;
+  }
+  session = lwip_ntohs(ph->session);
+  plen = lwip_ntohs(ph->plen);
+
+  if (plen > (pb->len - off)) {
+    PPPDEBUG(LOG_DEBUG, ("pppoe: packet content does not fit: data available = %d, packet size = %u\n",
+        pb->len - off, plen));
+    goto done;
+  }
+  if(pb->tot_len == pb->len) {
+    u16_t framelen = off + plen;
+    if (framelen < pb->len) {
+      /* ignore trailing garbage */
+      pb->tot_len = pb->len = framelen;
+    }
+  }
+  tag = 0;
+  len = 0;
+  sc = NULL;
+  while (off + sizeof(pt) <= pb->len) {
+    MEMCPY(&pt, (u8_t*)pb->payload + off, sizeof(pt));
+    tag = lwip_ntohs(pt.tag);
+    len = lwip_ntohs(pt.len);
+    if (off + sizeof(pt) + len > pb->len) {
+      PPPDEBUG(LOG_DEBUG, ("pppoe: tag 0x%x len 0x%x is too long\n", tag, len));
+      goto done;
+    }
+    switch (tag) {
+      case PPPOE_TAG_EOL:
+        goto breakbreak;
+      case PPPOE_TAG_SNAME:
+        break;  /* ignored */
+      case PPPOE_TAG_ACNAME:
+        break;  /* ignored */
+      case PPPOE_TAG_HUNIQUE:
+        if (sc != NULL) {
+          break;
+        }
+#ifdef PPPOE_SERVER
+        hunique = (u8_t*)pb->payload + off + sizeof(pt);
+        hunique_len = len;
+#endif
+        sc = pppoe_find_softc_by_hunique((u8_t*)pb->payload + off + sizeof(pt), len, netif);
+        break;
+      case PPPOE_TAG_ACCOOKIE:
+        if (ac_cookie == NULL) {
+          if (len > PPPOE_MAX_AC_COOKIE_LEN) {
+            PPPDEBUG(LOG_DEBUG, ("pppoe: AC cookie is too long: len = %d, max = %d\n", len, PPPOE_MAX_AC_COOKIE_LEN));
+            goto done;
+          }
+          ac_cookie = (u8_t*)pb->payload + off + sizeof(pt);
+          ac_cookie_len = len;
+        }
+        break;
+#if PPP_DEBUG
+      case PPPOE_TAG_SNAME_ERR:
+        err_msg = "SERVICE NAME ERROR";
+        break;
+      case PPPOE_TAG_ACSYS_ERR:
+        err_msg = "AC SYSTEM ERROR";
+        break;
+      case PPPOE_TAG_GENERIC_ERR:
+        err_msg = "GENERIC ERROR";
+        break;
+#endif /* PPP_DEBUG */
+      default:
+        break;
+    }
+#if PPP_DEBUG
+    if (err_msg != NULL) {
+      char error_tmp[PPPOE_ERRORSTRING_LEN];
+      u16_t error_len = LWIP_MIN(len, sizeof(error_tmp)-1);
+      strncpy(error_tmp, (char*)pb->payload + off + sizeof(pt), error_len);
+      error_tmp[error_len] = '\0';
+      if (sc) {
+        PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": %s: %s\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err_msg, error_tmp));
+      } else {
+        PPPDEBUG(LOG_DEBUG, ("pppoe: %s: %s\n", err_msg, error_tmp));
+      }
+    }
+#endif /* PPP_DEBUG */
+    off += sizeof(pt) + len;
+  }
+
+breakbreak:;
+  switch (ph->code) {
+    case PPPOE_CODE_PADI:
+#ifdef PPPOE_SERVER
+      /*
+       * got service name, concentrator name, and/or host unique.
+       * ignore if we have no interfaces with IFF_PASSIVE|IFF_UP.
+       */
+      if (LIST_EMPTY(&pppoe_softc_list)) {
+        goto done;
+      }
+      LIST_FOREACH(sc, &pppoe_softc_list, sc_list) {
+        if (!(sc->sc_sppp.pp_if.if_flags & IFF_UP)) {
+          continue;
+        }
+        if (!(sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) {
+          continue;
+        }
+        if (sc->sc_state == PPPOE_STATE_INITIAL) {
+          break;
+        }
+      }
+      if (sc == NULL) {
+        /* PPPDEBUG(LOG_DEBUG, ("pppoe: free passive interface is not found\n")); */
+        goto done;
+      }
+      if (hunique) {
+        if (sc->sc_hunique) {
+          mem_free(sc->sc_hunique);
+        }
+        sc->sc_hunique = mem_malloc(hunique_len);
+        if (sc->sc_hunique == NULL) {
+          goto done;
+        }
+        sc->sc_hunique_len = hunique_len;
+        MEMCPY(sc->sc_hunique, hunique, hunique_len);
+      }
+      MEMCPY(&sc->sc_dest, eh->ether_shost, sizeof sc->sc_dest);
+      sc->sc_state = PPPOE_STATE_PADO_SENT;
+      pppoe_send_pado(sc);
+      break;
+#endif /* PPPOE_SERVER */
+    case PPPOE_CODE_PADR:
+#ifdef PPPOE_SERVER
+      /*
+       * get sc from ac_cookie if IFF_PASSIVE
+       */
+      if (ac_cookie == NULL) {
+        /* be quiet if there is not a single pppoe instance */
+        PPPDEBUG(LOG_DEBUG, ("pppoe: received PADR but not includes ac_cookie\n"));
+        goto done;
+      }
+      sc = pppoe_find_softc_by_hunique(ac_cookie, ac_cookie_len, netif);
+      if (sc == NULL) {
+        /* be quiet if there is not a single pppoe instance */
+        if (!LIST_EMPTY(&pppoe_softc_list)) {
+          PPPDEBUG(LOG_DEBUG, ("pppoe: received PADR but could not find request for it\n"));
+        }
+        goto done;
+      }
+      if (sc->sc_state != PPPOE_STATE_PADO_SENT) {
+        PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": received unexpected PADR\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
+        goto done;
+      }
+      if (hunique) {
+        if (sc->sc_hunique) {
+          mem_free(sc->sc_hunique);
+        }
+        sc->sc_hunique = mem_malloc(hunique_len);
+        if (sc->sc_hunique == NULL) {
+          goto done;
+        }
+        sc->sc_hunique_len = hunique_len;
+        MEMCPY(sc->sc_hunique, hunique, hunique_len);
+      }
+      pppoe_send_pads(sc);
+      sc->sc_state = PPPOE_STATE_SESSION;
+      ppp_start(sc->pcb); /* notify upper layers */
+      break;
+#else
+      /* ignore, we are no access concentrator */
+      goto done;
+#endif /* PPPOE_SERVER */
+    case PPPOE_CODE_PADO:
+      if (sc == NULL) {
+        /* be quiet if there is not a single pppoe instance */
+        if (pppoe_softc_list != NULL) {
+          PPPDEBUG(LOG_DEBUG, ("pppoe: received PADO but could not find request for it\n"));
+        }
+        goto done;
+      }
+      if (sc->sc_state != PPPOE_STATE_PADI_SENT) {
+        PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": received unexpected PADO\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
+        goto done;
+      }
+      if (ac_cookie) {
+        sc->sc_ac_cookie_len = ac_cookie_len;
+        MEMCPY(sc->sc_ac_cookie, ac_cookie, ac_cookie_len);
+      }
+      MEMCPY(&sc->sc_dest, ethhdr->src.addr, sizeof(sc->sc_dest.addr));
+      sys_untimeout(pppoe_timeout, sc);
+      sc->sc_padr_retried = 0;
+      sc->sc_state = PPPOE_STATE_PADR_SENT;
+      if ((err = pppoe_send_padr(sc)) != 0) {
+        PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADR, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err));
+        LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
+      }
+      sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc);
+      break;
+    case PPPOE_CODE_PADS:
+      if (sc == NULL) {
+        goto done;
+      }
+      sc->sc_session = session;
+      sys_untimeout(pppoe_timeout, sc);
+      PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": session 0x%x connected\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, session));
+      sc->sc_state = PPPOE_STATE_SESSION;
+      ppp_start(sc->pcb); /* notify upper layers */
+      break;
+    case PPPOE_CODE_PADT:
+      /* Don't disconnect here, we let the LCP Echo/Reply find the fact
+       * that PPP session is down. Asking the PPP stack to end the session
+       * require strict checking about the PPP phase to prevent endless
+       * disconnection loops.
+       */
+#if 0 /* UNUSED */
+      if (sc == NULL) { /* PADT frames are rarely sent with a hunique tag, this is actually almost always true */
+        goto done;
+      }
+      pppoe_clear_softc(sc, "received PADT");
+#endif /* UNUSED */
+      break;
+    default:
+      if(sc) {
+        PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": unknown code (0x%"X16_F") session = 0x%"X16_F"\n",
+            sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num,
+            (u16_t)ph->code, session));
+      } else {
+        PPPDEBUG(LOG_DEBUG, ("pppoe: unknown code (0x%"X16_F") session = 0x%"X16_F"\n", (u16_t)ph->code, session));
+      }
+      break;
+  }
+
+done:
+  pbuf_free(pb);
+  return;
+}
+
+void
+pppoe_data_input(struct netif *netif, struct pbuf *pb)
+{
+  u16_t session, plen;
+  struct pppoe_softc *sc;
+  struct pppoehdr *ph;
+#ifdef PPPOE_TERM_UNKNOWN_SESSIONS
+  u8_t shost[ETHER_ADDR_LEN];
+#endif
+
+#ifdef PPPOE_TERM_UNKNOWN_SESSIONS
+  MEMCPY(shost, ((struct eth_hdr *)pb->payload)->src.addr, sizeof(shost));
+#endif
+  if (pbuf_remove_header(pb, sizeof(struct eth_hdr)) != 0) {
+    /* bail out */
+    PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_remove_header failed\n"));
+    LINK_STATS_INC(link.lenerr);
+    goto drop;
+  } 
+
+  if (pb->len < sizeof(*ph)) {
+    PPPDEBUG(LOG_DEBUG, ("pppoe_data_input: could not get PPPoE header\n"));
+    goto drop;
+  }
+  ph = (struct pppoehdr *)pb->payload;
+
+  if (ph->vertype != PPPOE_VERTYPE) {
+    PPPDEBUG(LOG_DEBUG, ("pppoe (data): unknown version/type packet: 0x%x\n", ph->vertype));
+    goto drop;
+  }
+  if (ph->code != 0) {
+    goto drop;
+  }
+
+  session = lwip_ntohs(ph->session);
+  sc = pppoe_find_softc_by_session(session, netif);
+  if (sc == NULL) {
+#ifdef PPPOE_TERM_UNKNOWN_SESSIONS
+    PPPDEBUG(LOG_DEBUG, ("pppoe: input for unknown session 0x%x, sending PADT\n", session));
+    pppoe_send_padt(netif, session, shost);
+#endif
+    goto drop;
+  }
+
+  plen = lwip_ntohs(ph->plen);
+
+  if (pbuf_remove_header(pb, PPPOE_HEADERLEN) != 0) {
+    /* bail out */
+    PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_remove_header PPPOE_HEADERLEN failed\n"));
+    LINK_STATS_INC(link.lenerr);
+    goto drop;
+  } 
+
+  PPPDEBUG(LOG_DEBUG, ("pppoe_data_input: %c%c%"U16_F": pkthdr.len=%d, pppoe.len=%d\n",
+        sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num,
+        pb->len, plen));
+
+  if (pb->tot_len < plen) {
+    goto drop;
+  }
+
+  /* Dispatch the packet thereby consuming it. */
+  ppp_input(sc->pcb, pb);
+  return;
+
+drop:
+  pbuf_free(pb);
+}
+
+static err_t
+pppoe_output(struct pppoe_softc *sc, struct pbuf *pb)
+{
+  struct eth_hdr *ethhdr;
+  u16_t etype;
+  err_t res;
+
+  /* make room for Ethernet header - should not fail */
+  if (pbuf_add_header(pb, sizeof(struct eth_hdr)) != 0) {
+    /* bail out */
+    PPPDEBUG(LOG_ERR, ("pppoe: %c%c%"U16_F": pppoe_output: could not allocate room for Ethernet header\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
+    LINK_STATS_INC(link.lenerr);
+    pbuf_free(pb);
+    return ERR_BUF;
+  }
+  ethhdr = (struct eth_hdr *)pb->payload;
+  etype = sc->sc_state == PPPOE_STATE_SESSION ? ETHTYPE_PPPOE : ETHTYPE_PPPOEDISC;
+  ethhdr->type = lwip_htons(etype);
+  MEMCPY(&ethhdr->dest.addr, &sc->sc_dest.addr, sizeof(ethhdr->dest.addr));
+  MEMCPY(&ethhdr->src.addr, &sc->sc_ethif->hwaddr, sizeof(ethhdr->src.addr));
+
+  PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F" (%x) state=%d, session=0x%x output -> %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F", len=%d\n",
+      sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, etype,
+      sc->sc_state, sc->sc_session,
+      sc->sc_dest.addr[0], sc->sc_dest.addr[1], sc->sc_dest.addr[2], sc->sc_dest.addr[3], sc->sc_dest.addr[4], sc->sc_dest.addr[5],
+      pb->tot_len));
+
+  res = sc->sc_ethif->linkoutput(sc->sc_ethif, pb);
+
+  pbuf_free(pb);
+
+  return res;
+}
+
+static err_t
+pppoe_send_padi(struct pppoe_softc *sc)
+{
+  struct pbuf *pb;
+  u8_t *p;
+  int len;
+#if PPPOE_SCNAME_SUPPORT
+  int l1 = 0, l2 = 0; /* XXX: gcc */
+#endif /* PPPOE_SCNAME_SUPPORT */
+
+  /* calculate length of frame (excluding ethernet header + pppoe header) */
+  len = 2 + 2 + 2 + 2 + sizeof sc;  /* service name tag is required, host unique is send too */
+#if PPPOE_SCNAME_SUPPORT
+  if (sc->sc_service_name != NULL) {
+    l1 = (int)strlen(sc->sc_service_name);
+    len += l1;
+  }
+  if (sc->sc_concentrator_name != NULL) {
+    l2 = (int)strlen(sc->sc_concentrator_name);
+    len += 2 + 2 + l2;
+  }
+#endif /* PPPOE_SCNAME_SUPPORT */
+  LWIP_ASSERT("sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff",
+    sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff);
+
+  /* allocate a buffer */
+  pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN + len), PBUF_RAM);
+  if (!pb) {
+    return ERR_MEM;
+  }
+  LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+
+  p = (u8_t*)pb->payload;
+  /* fill in pkt */
+  PPPOE_ADD_HEADER(p, PPPOE_CODE_PADI, 0, (u16_t)len);
+  PPPOE_ADD_16(p, PPPOE_TAG_SNAME);
+#if PPPOE_SCNAME_SUPPORT
+  if (sc->sc_service_name != NULL) {
+    PPPOE_ADD_16(p, l1);
+    MEMCPY(p, sc->sc_service_name, l1);
+    p += l1;
+  } else
+#endif /* PPPOE_SCNAME_SUPPORT */
+  {
+    PPPOE_ADD_16(p, 0);
+  }
+#if PPPOE_SCNAME_SUPPORT
+  if (sc->sc_concentrator_name != NULL) {
+    PPPOE_ADD_16(p, PPPOE_TAG_ACNAME);
+    PPPOE_ADD_16(p, l2);
+    MEMCPY(p, sc->sc_concentrator_name, l2);
+    p += l2;
+  }
+#endif /* PPPOE_SCNAME_SUPPORT */
+  PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE);
+  PPPOE_ADD_16(p, sizeof(sc));
+  MEMCPY(p, &sc, sizeof sc);
+
+  /* send pkt */
+  return pppoe_output(sc, pb);
+}
+
+static void
+pppoe_timeout(void *arg)
+{
+  u32_t retry_wait;
+  int err;
+  struct pppoe_softc *sc = (struct pppoe_softc*)arg;
+
+  PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": timeout\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
+
+  switch (sc->sc_state) {
+    case PPPOE_STATE_PADI_SENT:
+      /*
+       * We have two basic ways of retrying:
+       *  - Quick retry mode: try a few times in short sequence
+       *  - Slow retry mode: we already had a connection successfully
+       *    established and will try infinitely (without user
+       *    intervention)
+       * We only enter slow retry mode if IFF_LINK1 (aka autodial)
+       * is not set.
+       */
+      if (sc->sc_padi_retried < 0xff) {
+        sc->sc_padi_retried++;
+      }
+      if (!sc->pcb->settings.persist && sc->sc_padi_retried >= PPPOE_DISC_MAXPADI) {
+#if 0
+        if ((sc->sc_sppp.pp_if.if_flags & IFF_LINK1) == 0) {
+          /* slow retry mode */
+          retry_wait = PPPOE_SLOW_RETRY;
+        } else
+#endif
+        {
+          pppoe_abort_connect(sc);
+          return;
+        }
+      }
+      /* initialize for quick retry mode */
+      retry_wait = LWIP_MIN(PPPOE_DISC_TIMEOUT * sc->sc_padi_retried, PPPOE_SLOW_RETRY);
+      if ((err = pppoe_send_padi(sc)) != 0) {
+        sc->sc_padi_retried--;
+        PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to transmit PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err));
+        LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
+      }
+      sys_timeout(retry_wait, pppoe_timeout, sc);
+      break;
+
+    case PPPOE_STATE_PADR_SENT:
+      sc->sc_padr_retried++;
+      if (sc->sc_padr_retried >= PPPOE_DISC_MAXPADR) {
+        MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest));
+        sc->sc_state = PPPOE_STATE_PADI_SENT;
+        sc->sc_padr_retried = 0;
+        if ((err = pppoe_send_padi(sc)) != 0) {
+          PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err));
+          LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
+        }
+        sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried), pppoe_timeout, sc);
+        return;
+      }
+      if ((err = pppoe_send_padr(sc)) != 0) {
+        sc->sc_padr_retried--;
+        PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADR, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err));
+        LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
+      }
+      sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc);
+      break;
+    default:
+      return;  /* all done, work in peace */
+  }
+}
+
+/* Start a connection (i.e. initiate discovery phase) */
+static void
+pppoe_connect(ppp_pcb *ppp, void *ctx)
+{
+  err_t err;
+  struct pppoe_softc *sc = (struct pppoe_softc *)ctx;
+  lcp_options *lcp_wo;
+  lcp_options *lcp_ao;
+#if PPP_IPV4_SUPPORT && VJ_SUPPORT
+  ipcp_options *ipcp_wo;
+  ipcp_options *ipcp_ao;
+#endif /* PPP_IPV4_SUPPORT && VJ_SUPPORT */
+
+  sc->sc_session = 0;
+  sc->sc_ac_cookie_len = 0;
+  sc->sc_padi_retried = 0;
+  sc->sc_padr_retried = 0;
+  /* changed to real address later */
+  MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest));
+#ifdef PPPOE_SERVER
+  /* wait PADI if IFF_PASSIVE */
+  if ((sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) {
+    return 0;
+  }
+#endif
+
+  lcp_wo = &ppp->lcp_wantoptions;
+  lcp_wo->mru = sc->sc_ethif->mtu-PPPOE_HEADERLEN-2; /* two byte PPP protocol discriminator, then IP data */
+  lcp_wo->neg_asyncmap = 0;
+  lcp_wo->neg_pcompression = 0;
+  lcp_wo->neg_accompression = 0;
+  lcp_wo->passive = 0;
+  lcp_wo->silent = 0;
+
+  lcp_ao = &ppp->lcp_allowoptions;
+  lcp_ao->mru = sc->sc_ethif->mtu-PPPOE_HEADERLEN-2; /* two byte PPP protocol discriminator, then IP data */
+  lcp_ao->neg_asyncmap = 0;
+  lcp_ao->neg_pcompression = 0;
+  lcp_ao->neg_accompression = 0;
+
+#if PPP_IPV4_SUPPORT && VJ_SUPPORT
+  ipcp_wo = &ppp->ipcp_wantoptions;
+  ipcp_wo->neg_vj = 0;
+  ipcp_wo->old_vj = 0;
+
+  ipcp_ao = &ppp->ipcp_allowoptions;
+  ipcp_ao->neg_vj = 0;
+  ipcp_ao->old_vj = 0;
+#endif /* PPP_IPV4_SUPPORT && VJ_SUPPORT */
+
+  /* save state, in case we fail to send PADI */
+  sc->sc_state = PPPOE_STATE_PADI_SENT;
+  if ((err = pppoe_send_padi(sc)) != 0) {
+    PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err));
+  }
+  sys_timeout(PPPOE_DISC_TIMEOUT, pppoe_timeout, sc);
+}
+
+/* disconnect */
+static void
+pppoe_disconnect(ppp_pcb *ppp, void *ctx)
+{
+  struct pppoe_softc *sc = (struct pppoe_softc *)ctx;
+
+  PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": disconnecting\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
+  if (sc->sc_state == PPPOE_STATE_SESSION) {
+    pppoe_send_padt(sc->sc_ethif, sc->sc_session, (const u8_t *)&sc->sc_dest);
+  }
+
+  /* stop any timer, disconnect can be called while initiating is in progress */
+  sys_untimeout(pppoe_timeout, sc);
+  sc->sc_state = PPPOE_STATE_INITIAL;
+#ifdef PPPOE_SERVER
+  if (sc->sc_hunique) {
+    mem_free(sc->sc_hunique);
+    sc->sc_hunique = NULL; /* probably not necessary, if state is initial we shouldn't have to access hunique anyway  */
+  }
+  sc->sc_hunique_len = 0; /* probably not necessary, if state is initial we shouldn't have to access hunique anyway  */
+#endif
+  ppp_link_end(ppp); /* notify upper layers */
+  return;
+}
+
+/* Connection attempt aborted */
+static void
+pppoe_abort_connect(struct pppoe_softc *sc)
+{
+  PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": could not establish connection\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
+  sc->sc_state = PPPOE_STATE_INITIAL;
+  ppp_link_failed(sc->pcb); /* notify upper layers */
+}
+
+/* Send a PADR packet */
+static err_t
+pppoe_send_padr(struct pppoe_softc *sc)
+{
+  struct pbuf *pb;
+  u8_t *p;
+  size_t len;
+#if PPPOE_SCNAME_SUPPORT
+  size_t l1 = 0; /* XXX: gcc */
+#endif /* PPPOE_SCNAME_SUPPORT */
+
+  len = 2 + 2 + 2 + 2 + sizeof(sc);    /* service name, host unique */
+#if PPPOE_SCNAME_SUPPORT
+  if (sc->sc_service_name != NULL) {    /* service name tag maybe empty */
+    l1 = strlen(sc->sc_service_name);
+    len += l1;
+  }
+#endif /* PPPOE_SCNAME_SUPPORT */
+  if (sc->sc_ac_cookie_len > 0) {
+    len += 2 + 2 + sc->sc_ac_cookie_len;  /* AC cookie */
+  }
+  LWIP_ASSERT("sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff",
+    sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff);
+  pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN + len), PBUF_RAM);
+  if (!pb) {
+    return ERR_MEM;
+  }
+  LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+  p = (u8_t*)pb->payload;
+  PPPOE_ADD_HEADER(p, PPPOE_CODE_PADR, 0, len);
+  PPPOE_ADD_16(p, PPPOE_TAG_SNAME);
+#if PPPOE_SCNAME_SUPPORT
+  if (sc->sc_service_name != NULL) {
+    PPPOE_ADD_16(p, l1);
+    MEMCPY(p, sc->sc_service_name, l1);
+    p += l1;
+  } else
+#endif /* PPPOE_SCNAME_SUPPORT */
+  {
+    PPPOE_ADD_16(p, 0);
+  }
+  if (sc->sc_ac_cookie_len > 0) {
+    PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE);
+    PPPOE_ADD_16(p, sc->sc_ac_cookie_len);
+    MEMCPY(p, sc->sc_ac_cookie, sc->sc_ac_cookie_len);
+    p += sc->sc_ac_cookie_len;
+  }
+  PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE);
+  PPPOE_ADD_16(p, sizeof(sc));
+  MEMCPY(p, &sc, sizeof sc);
+
+  return pppoe_output(sc, pb);
+}
+
+/* send a PADT packet */
+static err_t
+pppoe_send_padt(struct netif *outgoing_if, u_int session, const u8_t *dest)
+{
+  struct pbuf *pb;
+  struct eth_hdr *ethhdr;
+  err_t res;
+  u8_t *p;
+
+  pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN), PBUF_RAM);
+  if (!pb) {
+    return ERR_MEM;
+  }
+  LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+
+  if (pbuf_add_header(pb, sizeof(struct eth_hdr))) {
+    PPPDEBUG(LOG_ERR, ("pppoe: pppoe_send_padt: could not allocate room for PPPoE header\n"));
+    LINK_STATS_INC(link.lenerr);
+    pbuf_free(pb);
+    return ERR_BUF;
+  }
+  ethhdr = (struct eth_hdr *)pb->payload;
+  ethhdr->type = PP_HTONS(ETHTYPE_PPPOEDISC);
+  MEMCPY(&ethhdr->dest.addr, dest, sizeof(ethhdr->dest.addr));
+  MEMCPY(&ethhdr->src.addr, &outgoing_if->hwaddr, sizeof(ethhdr->src.addr));
+
+  p = (u8_t*)(ethhdr + 1);
+  PPPOE_ADD_HEADER(p, PPPOE_CODE_PADT, session, 0);
+
+  res = outgoing_if->linkoutput(outgoing_if, pb);
+
+  pbuf_free(pb);
+
+  return res;
+}
+
+#ifdef PPPOE_SERVER
+static err_t
+pppoe_send_pado(struct pppoe_softc *sc)
+{
+  struct pbuf *pb;
+  u8_t *p;
+  size_t len;
+
+  /* calc length */
+  len = 0;
+  /* include ac_cookie */
+  len += 2 + 2 + sizeof(sc);
+  /* include hunique */
+  len += 2 + 2 + sc->sc_hunique_len;
+  pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN + len), PBUF_RAM);
+  if (!pb) {
+    return ERR_MEM;
+  }
+  LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+  p = (u8_t*)pb->payload;
+  PPPOE_ADD_HEADER(p, PPPOE_CODE_PADO, 0, len);
+  PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE);
+  PPPOE_ADD_16(p, sizeof(sc));
+  MEMCPY(p, &sc, sizeof(sc));
+  p += sizeof(sc);
+  PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE);
+  PPPOE_ADD_16(p, sc->sc_hunique_len);
+  MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len);
+  return pppoe_output(sc, pb);
+}
+
+static err_t
+pppoe_send_pads(struct pppoe_softc *sc)
+{
+  struct pbuf *pb;
+  u8_t *p;
+  size_t len, l1 = 0;  /* XXX: gcc */
+
+  sc->sc_session = mono_time.tv_sec % 0xff + 1;
+  /* calc length */
+  len = 0;
+  /* include hunique */
+  len += 2 + 2 + 2 + 2 + sc->sc_hunique_len;  /* service name, host unique*/
+  if (sc->sc_service_name != NULL) {    /* service name tag maybe empty */
+    l1 = strlen(sc->sc_service_name);
+    len += l1;
+  }
+  pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN + len), PBUF_RAM);
+  if (!pb) {
+    return ERR_MEM;
+  }
+  LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+  p = (u8_t*)pb->payload;
+  PPPOE_ADD_HEADER(p, PPPOE_CODE_PADS, sc->sc_session, len);
+  PPPOE_ADD_16(p, PPPOE_TAG_SNAME);
+  if (sc->sc_service_name != NULL) {
+    PPPOE_ADD_16(p, l1);
+    MEMCPY(p, sc->sc_service_name, l1);
+    p += l1;
+  } else {
+    PPPOE_ADD_16(p, 0);
+  }
+  PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE);
+  PPPOE_ADD_16(p, sc->sc_hunique_len);
+  MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len);
+  return pppoe_output(sc, pb);
+}
+#endif
+
+static err_t
+pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb)
+{
+  u8_t *p;
+  size_t len;
+
+  len = pb->tot_len;
+
+  /* make room for PPPoE header - should not fail */
+  if (pbuf_add_header(pb, PPPOE_HEADERLEN) != 0) {
+    /* bail out */
+    PPPDEBUG(LOG_ERR, ("pppoe: %c%c%"U16_F": pppoe_xmit: could not allocate room for PPPoE header\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
+    LINK_STATS_INC(link.lenerr);
+    pbuf_free(pb);
+    return ERR_BUF;
+  }
+
+  p = (u8_t*)pb->payload;
+  PPPOE_ADD_HEADER(p, 0, sc->sc_session, len);
+
+  return pppoe_output(sc, pb);
+}
+
+#if 0 /*def PFIL_HOOKS*/
+static int
+pppoe_ifattach_hook(void *arg, struct pbuf **mp, struct netif *ifp, int dir)
+{
+  struct pppoe_softc *sc;
+  int s;
+
+  if (mp != (struct pbuf **)PFIL_IFNET_DETACH) {
+    return 0;
+  }
+
+  LIST_FOREACH(sc, &pppoe_softc_list, sc_list) {
+    if (sc->sc_ethif != ifp) {
+      continue;
+    }
+    if (sc->sc_sppp.pp_if.if_flags & IFF_UP) {
+      sc->sc_sppp.pp_if.if_flags &= ~(IFF_UP|IFF_RUNNING);
+      PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": ethernet interface detached, going down\n",
+          sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
+    }
+    sc->sc_ethif = NULL;
+    pppoe_clear_softc(sc, "ethernet interface detached");
+  }
+
+  return 0;
+}
+#endif
+
+#if 0 /* UNUSED */
+static void
+pppoe_clear_softc(struct pppoe_softc *sc, const char *message)
+{
+  LWIP_UNUSED_ARG(message);
+
+  /* stop timer */
+  sys_untimeout(pppoe_timeout, sc);
+  PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": session 0x%x terminated, %s\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_session, message));
+  sc->sc_state = PPPOE_STATE_INITIAL;
+  ppp_link_end(sc->pcb);  /* notify upper layers - /!\ dangerous /!\ - see pppoe_disc_input() */
+}
+#endif /* UNUSED */
+#endif /* PPP_SUPPORT && PPPOE_SUPPORT */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/pppol2tp.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/pppol2tp.c
new file mode 100644 (file)
index 0000000..4c4557f
--- /dev/null
@@ -0,0 +1,1159 @@
+/**
+ * @file
+ * Network Point to Point Protocol over Layer 2 Tunneling Protocol program file.
+ *
+ */
+
+/*
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+/*
+ * L2TP Support status:
+ *
+ * Supported:
+ * - L2TPv2 (PPP over L2TP, a.k.a. UDP tunnels)
+ * - LAC
+ *
+ * Not supported:
+ * - LNS (require PPP server support)
+ * - L2TPv3 ethernet pseudowires
+ * - L2TPv3 VLAN pseudowire
+ * - L2TPv3 PPP pseudowires
+ * - L2TPv3 IP encapsulation
+ * - L2TPv3 IP pseudowire
+ * - L2TP tunnel switching - http://tools.ietf.org/html/draft-ietf-l2tpext-tunnel-switching-08
+ * - Multiple tunnels per UDP socket, as well as multiple sessions per tunnel
+ * - Hidden AVPs
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPPOL2TP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/err.h"
+#include "lwip/memp.h"
+#include "lwip/netif.h"
+#include "lwip/udp.h"
+#include "lwip/snmp.h"
+
+#include "netif/ppp/ppp_impl.h"
+#include "netif/ppp/lcp.h"
+#include "netif/ppp/ipcp.h"
+#include "netif/ppp/pppol2tp.h"
+#include "netif/ppp/pppcrypt.h"
+#include "netif/ppp/magic.h"
+
+/* Memory pool */
+LWIP_MEMPOOL_DECLARE(PPPOL2TP_PCB, MEMP_NUM_PPPOL2TP_INTERFACES, sizeof(pppol2tp_pcb), "PPPOL2TP_PCB")
+
+/* callbacks called from PPP core */
+static err_t pppol2tp_write(ppp_pcb *ppp, void *ctx, struct pbuf *p);
+static err_t pppol2tp_netif_output(ppp_pcb *ppp, void *ctx, struct pbuf *p, u_short protocol);
+static err_t pppol2tp_destroy(ppp_pcb *ppp, void *ctx);    /* Destroy a L2TP control block */
+static void pppol2tp_connect(ppp_pcb *ppp, void *ctx);    /* Be a LAC, connect to a LNS. */
+static void pppol2tp_disconnect(ppp_pcb *ppp, void *ctx);  /* Disconnect */
+
+ /* Prototypes for procedures local to this file. */
+static void pppol2tp_input(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
+static void pppol2tp_dispatch_control_packet(pppol2tp_pcb *l2tp, u16_t port, struct pbuf *p, u16_t ns, u16_t nr);
+static void pppol2tp_timeout(void *arg);
+static void pppol2tp_abort_connect(pppol2tp_pcb *l2tp);
+static err_t pppol2tp_send_sccrq(pppol2tp_pcb *l2tp);
+static err_t pppol2tp_send_scccn(pppol2tp_pcb *l2tp, u16_t ns);
+static err_t pppol2tp_send_icrq(pppol2tp_pcb *l2tp, u16_t ns);
+static err_t pppol2tp_send_iccn(pppol2tp_pcb *l2tp, u16_t ns);
+static err_t pppol2tp_send_zlb(pppol2tp_pcb *l2tp, u16_t ns, u16_t nr);
+static err_t pppol2tp_send_stopccn(pppol2tp_pcb *l2tp, u16_t ns);
+static err_t pppol2tp_xmit(pppol2tp_pcb *l2tp, struct pbuf *pb);
+static err_t pppol2tp_udp_send(pppol2tp_pcb *l2tp, struct pbuf *pb);
+
+/* Callbacks structure for PPP core */
+static const struct link_callbacks pppol2tp_callbacks = {
+  pppol2tp_connect,
+#if PPP_SERVER
+  NULL,
+#endif /* PPP_SERVER */
+  pppol2tp_disconnect,
+  pppol2tp_destroy,
+  pppol2tp_write,
+  pppol2tp_netif_output,
+  NULL,
+  NULL
+};
+
+
+/* Create a new L2TP session. */
+ppp_pcb *pppol2tp_create(struct netif *pppif,
+       struct netif *netif, const ip_addr_t *ipaddr, u16_t port,
+       const u8_t *secret, u8_t secret_len,
+       ppp_link_status_cb_fn link_status_cb, void *ctx_cb) {
+  ppp_pcb *ppp;
+  pppol2tp_pcb *l2tp;
+  struct udp_pcb *udp;
+#if !PPPOL2TP_AUTH_SUPPORT
+  LWIP_UNUSED_ARG(secret);
+  LWIP_UNUSED_ARG(secret_len);
+#endif /* !PPPOL2TP_AUTH_SUPPORT */
+
+  if (ipaddr == NULL) {
+    goto ipaddr_check_failed;
+  }
+
+  l2tp = (pppol2tp_pcb *)LWIP_MEMPOOL_ALLOC(PPPOL2TP_PCB);
+  if (l2tp == NULL) {
+    goto memp_malloc_l2tp_failed;
+  }
+
+  udp = udp_new_ip_type(IP_GET_TYPE(ipaddr));
+  if (udp == NULL) {
+    goto udp_new_failed;
+  }
+  udp_recv(udp, pppol2tp_input, l2tp);
+
+  ppp = ppp_new(pppif, &pppol2tp_callbacks, l2tp, link_status_cb, ctx_cb);
+  if (ppp == NULL) {
+    goto ppp_new_failed;
+  }
+
+  memset(l2tp, 0, sizeof(pppol2tp_pcb));
+  l2tp->phase = PPPOL2TP_STATE_INITIAL;
+  l2tp->ppp = ppp;
+  l2tp->udp = udp;
+  l2tp->netif = netif;
+  ip_addr_copy(l2tp->remote_ip, *ipaddr);
+  l2tp->remote_port = port;
+#if PPPOL2TP_AUTH_SUPPORT
+  l2tp->secret = secret;
+  l2tp->secret_len = secret_len;
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+
+  return ppp;
+
+ppp_new_failed:
+  udp_remove(udp);
+udp_new_failed:
+  LWIP_MEMPOOL_FREE(PPPOL2TP_PCB, l2tp);
+memp_malloc_l2tp_failed:
+ipaddr_check_failed:
+  return NULL;
+}
+
+/* Called by PPP core */
+static err_t pppol2tp_write(ppp_pcb *ppp, void *ctx, struct pbuf *p) {
+  pppol2tp_pcb *l2tp = (pppol2tp_pcb *)ctx;
+  struct pbuf *ph; /* UDP + L2TP header */
+  err_t ret;
+#if MIB2_STATS
+  u16_t tot_len;
+#else /* MIB2_STATS */
+  LWIP_UNUSED_ARG(ppp);
+#endif /* MIB2_STATS */
+
+  ph = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(PPPOL2TP_OUTPUT_DATA_HEADER_LEN), PBUF_RAM);
+  if(!ph) {
+    LINK_STATS_INC(link.memerr);
+    LINK_STATS_INC(link.proterr);
+    MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
+    pbuf_free(p);
+    return ERR_MEM;
+  }
+
+  pbuf_remove_header(ph, PPPOL2TP_OUTPUT_DATA_HEADER_LEN); /* hide L2TP header */
+  pbuf_cat(ph, p);
+#if MIB2_STATS
+  tot_len = ph->tot_len;
+#endif /* MIB2_STATS */
+
+  ret = pppol2tp_xmit(l2tp, ph);
+  if (ret != ERR_OK) {
+    LINK_STATS_INC(link.err);
+    MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
+    return ret;
+  }
+
+  MIB2_STATS_NETIF_ADD(ppp->netif, ifoutoctets, (u16_t)tot_len);
+  MIB2_STATS_NETIF_INC(ppp->netif, ifoutucastpkts);
+  LINK_STATS_INC(link.xmit);
+  return ERR_OK;
+}
+
+/* Called by PPP core */
+static err_t pppol2tp_netif_output(ppp_pcb *ppp, void *ctx, struct pbuf *p, u_short protocol) {
+  pppol2tp_pcb *l2tp = (pppol2tp_pcb *)ctx;
+  struct pbuf *pb;
+  u8_t *pl;
+  err_t err;
+#if MIB2_STATS
+  u16_t tot_len;
+#else /* MIB2_STATS */
+  LWIP_UNUSED_ARG(ppp);
+#endif /* MIB2_STATS */
+
+  /* @todo: try to use pbuf_header() here! */
+  pb = pbuf_alloc(PBUF_TRANSPORT, PPPOL2TP_OUTPUT_DATA_HEADER_LEN + sizeof(protocol), PBUF_RAM);
+  if(!pb) {
+    LINK_STATS_INC(link.memerr);
+    LINK_STATS_INC(link.proterr);
+    MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
+    return ERR_MEM;
+  }
+
+  pbuf_remove_header(pb, PPPOL2TP_OUTPUT_DATA_HEADER_LEN);
+
+  pl = (u8_t*)pb->payload;
+  PUTSHORT(protocol, pl);
+
+  pbuf_chain(pb, p);
+#if MIB2_STATS
+  tot_len = pb->tot_len;
+#endif /* MIB2_STATS */
+
+  if( (err = pppol2tp_xmit(l2tp, pb)) != ERR_OK) {
+    LINK_STATS_INC(link.err);
+    MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
+    return err;
+  }
+
+  MIB2_STATS_NETIF_ADD(ppp->netif, ifoutoctets, tot_len);
+  MIB2_STATS_NETIF_INC(ppp->netif, ifoutucastpkts);
+  LINK_STATS_INC(link.xmit);
+  return ERR_OK;
+}
+
+/* Destroy a L2TP control block */
+static err_t pppol2tp_destroy(ppp_pcb *ppp, void *ctx) {
+  pppol2tp_pcb *l2tp = (pppol2tp_pcb *)ctx;
+  LWIP_UNUSED_ARG(ppp);
+
+  sys_untimeout(pppol2tp_timeout, l2tp);
+  udp_remove(l2tp->udp);
+  LWIP_MEMPOOL_FREE(PPPOL2TP_PCB, l2tp);
+  return ERR_OK;
+}
+
+/* Be a LAC, connect to a LNS. */
+static void pppol2tp_connect(ppp_pcb *ppp, void *ctx) {
+  err_t err;
+  pppol2tp_pcb *l2tp = (pppol2tp_pcb *)ctx;
+  lcp_options *lcp_wo;
+  lcp_options *lcp_ao;
+#if PPP_IPV4_SUPPORT && VJ_SUPPORT
+  ipcp_options *ipcp_wo;
+  ipcp_options *ipcp_ao;
+#endif /* PPP_IPV4_SUPPORT && VJ_SUPPORT */
+
+  l2tp->tunnel_port = l2tp->remote_port;
+  l2tp->our_ns = 0;
+  l2tp->peer_nr = 0;
+  l2tp->peer_ns = 0;
+  l2tp->source_tunnel_id = 0;
+  l2tp->remote_tunnel_id = 0;
+  l2tp->source_session_id = 0;
+  l2tp->remote_session_id = 0;
+  /* l2tp->*_retried are cleared when used */
+
+  lcp_wo = &ppp->lcp_wantoptions;
+  lcp_wo->mru = PPPOL2TP_DEFMRU;
+  lcp_wo->neg_asyncmap = 0;
+  lcp_wo->neg_pcompression = 0;
+  lcp_wo->neg_accompression = 0;
+  lcp_wo->passive = 0;
+  lcp_wo->silent = 0;
+
+  lcp_ao = &ppp->lcp_allowoptions;
+  lcp_ao->mru = PPPOL2TP_DEFMRU;
+  lcp_ao->neg_asyncmap = 0;
+  lcp_ao->neg_pcompression = 0;
+  lcp_ao->neg_accompression = 0;
+
+#if PPP_IPV4_SUPPORT && VJ_SUPPORT
+  ipcp_wo = &ppp->ipcp_wantoptions;
+  ipcp_wo->neg_vj = 0;
+  ipcp_wo->old_vj = 0;
+
+  ipcp_ao = &ppp->ipcp_allowoptions;
+  ipcp_ao->neg_vj = 0;
+  ipcp_ao->old_vj = 0;
+#endif /* PPP_IPV4_SUPPORT && VJ_SUPPORT */
+
+  /* Listen to a random source port, we need to do that instead of using udp_connect()
+   * because the L2TP LNS might answer with its own random source port (!= 1701)
+   */
+#if LWIP_IPV6
+  if (IP_IS_V6_VAL(l2tp->udp->local_ip)) {
+    udp_bind(l2tp->udp, IP6_ADDR_ANY, 0);
+  } else
+#endif /* LWIP_IPV6 */
+  udp_bind(l2tp->udp, IP_ADDR_ANY, 0);
+
+#if PPPOL2TP_AUTH_SUPPORT
+  /* Generate random vector */
+  if (l2tp->secret != NULL) {
+    magic_random_bytes(l2tp->secret_rv, sizeof(l2tp->secret_rv));
+  }
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+
+  do {
+    l2tp->remote_tunnel_id = magic();
+  } while(l2tp->remote_tunnel_id == 0);
+  /* save state, in case we fail to send SCCRQ */
+  l2tp->sccrq_retried = 0;
+  l2tp->phase = PPPOL2TP_STATE_SCCRQ_SENT;
+  if ((err = pppol2tp_send_sccrq(l2tp)) != 0) {
+    PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send SCCRQ, error=%d\n", err));
+  }
+  sys_timeout(PPPOL2TP_CONTROL_TIMEOUT, pppol2tp_timeout, l2tp);
+}
+
+/* Disconnect */
+static void pppol2tp_disconnect(ppp_pcb *ppp, void *ctx) {
+  pppol2tp_pcb *l2tp = (pppol2tp_pcb *)ctx;
+
+  l2tp->our_ns++;
+  pppol2tp_send_stopccn(l2tp, l2tp->our_ns);
+
+  /* stop any timer, disconnect can be called while initiating is in progress */
+  sys_untimeout(pppol2tp_timeout, l2tp);
+  l2tp->phase = PPPOL2TP_STATE_INITIAL;
+  ppp_link_end(ppp); /* notify upper layers */
+}
+
+/* UDP Callback for incoming IPv4 L2TP frames */
+static void pppol2tp_input(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) {
+  pppol2tp_pcb *l2tp = (pppol2tp_pcb*)arg;
+  u16_t hflags, hlen, len=0, tunnel_id=0, session_id=0, ns=0, nr=0, offset=0;
+  u8_t *inp;
+  LWIP_UNUSED_ARG(pcb);
+
+  /* we can't unbound a UDP pcb, thus we can still receive UDP frames after the link is closed */
+  if (l2tp->phase < PPPOL2TP_STATE_SCCRQ_SENT) {
+    goto free_and_return;
+  }
+
+  if (!ip_addr_cmp(&l2tp->remote_ip, addr)) {
+    goto free_and_return;
+  }
+
+  /* discard packet if port mismatch, but only if we received a SCCRP */
+  if (l2tp->phase > PPPOL2TP_STATE_SCCRQ_SENT && l2tp->tunnel_port != port) {
+    goto free_and_return;
+  }
+
+  /* printf("-----------\nL2TP INPUT, %d\n", p->len); */
+
+  /* L2TP header */
+  if (p->len < sizeof(hflags) + sizeof(tunnel_id) + sizeof(session_id) ) {
+    goto packet_too_short;
+  }
+
+  inp = (u8_t*)p->payload;
+  GETSHORT(hflags, inp);
+
+  if (hflags & PPPOL2TP_HEADERFLAG_CONTROL) {
+    /* check mandatory flags for a control packet */
+    if ( (hflags & PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY) != PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY ) {
+      PPPDEBUG(LOG_DEBUG, ("pppol2tp: mandatory header flags for control packet not set\n"));
+      goto free_and_return;
+    }
+    /* check forbidden flags for a control packet */
+    if (hflags & PPPOL2TP_HEADERFLAG_CONTROL_FORBIDDEN) {
+      PPPDEBUG(LOG_DEBUG, ("pppol2tp: forbidden header flags for control packet found\n"));
+      goto free_and_return;
+    }
+  } else {
+    /* check mandatory flags for a data packet */
+    if ( (hflags & PPPOL2TP_HEADERFLAG_DATA_MANDATORY) != PPPOL2TP_HEADERFLAG_DATA_MANDATORY) {
+      PPPDEBUG(LOG_DEBUG, ("pppol2tp: mandatory header flags for data packet not set\n"));
+      goto free_and_return;
+    }
+  }
+
+  /* Expected header size  */
+  hlen = sizeof(hflags) + sizeof(tunnel_id) + sizeof(session_id);
+  if (hflags & PPPOL2TP_HEADERFLAG_LENGTH) {
+    hlen += sizeof(len);
+  }
+  if (hflags & PPPOL2TP_HEADERFLAG_SEQUENCE) {
+    hlen += sizeof(ns) + sizeof(nr);
+  }
+  if (hflags & PPPOL2TP_HEADERFLAG_OFFSET) {
+    hlen += sizeof(offset);
+  }
+  if (p->len < hlen) {
+    goto packet_too_short;
+  }
+
+  if (hflags & PPPOL2TP_HEADERFLAG_LENGTH) {
+    GETSHORT(len, inp);
+    if (p->len < len || len < hlen) {
+      goto packet_too_short;
+    }
+  }
+  GETSHORT(tunnel_id, inp);
+  GETSHORT(session_id, inp);
+  if (hflags & PPPOL2TP_HEADERFLAG_SEQUENCE) {
+    GETSHORT(ns, inp);
+    GETSHORT(nr, inp);
+  }
+  if (hflags & PPPOL2TP_HEADERFLAG_OFFSET) {
+    GETSHORT(offset, inp)
+    if (offset > 4096) { /* don't be fooled with large offset which might overflow hlen */
+      PPPDEBUG(LOG_DEBUG, ("pppol2tp: strange packet received, offset=%d\n", offset));
+      goto free_and_return;
+    }
+    hlen += offset;
+    if (p->len < hlen) {
+      goto packet_too_short;
+    }
+    INCPTR(offset, inp);
+  }
+
+  /* printf("HLEN = %d\n", hlen); */
+
+  /* skip L2TP header */
+  if (pbuf_remove_header(p, hlen) != 0) {
+    goto free_and_return;
+  }
+
+  /* printf("LEN=%d, TUNNEL_ID=%d, SESSION_ID=%d, NS=%d, NR=%d, OFFSET=%d\n", len, tunnel_id, session_id, ns, nr, offset); */
+  PPPDEBUG(LOG_DEBUG, ("pppol2tp: input packet, len=%"U16_F", tunnel=%"U16_F", session=%"U16_F", ns=%"U16_F", nr=%"U16_F"\n",
+    len, tunnel_id, session_id, ns, nr));
+
+  /* Control packet */
+  if (hflags & PPPOL2TP_HEADERFLAG_CONTROL) {
+    pppol2tp_dispatch_control_packet(l2tp, port, p, ns, nr);
+    goto free_and_return;
+  }
+
+  /* Data packet */
+  if(l2tp->phase != PPPOL2TP_STATE_DATA) {
+    goto free_and_return;
+  }
+  if(tunnel_id != l2tp->remote_tunnel_id) {
+     PPPDEBUG(LOG_DEBUG, ("pppol2tp: tunnel ID mismatch, assigned=%d, received=%d\n", l2tp->remote_tunnel_id, tunnel_id));
+     goto free_and_return;
+  }
+  if(session_id != l2tp->remote_session_id) {
+     PPPDEBUG(LOG_DEBUG, ("pppol2tp: session ID mismatch, assigned=%d, received=%d\n", l2tp->remote_session_id, session_id));
+     goto free_and_return;
+  }
+  /*
+   * skip address & flags if necessary
+   *
+   * RFC 2661 does not specify whether the PPP frame in the L2TP payload should
+   * have a HDLC header or not. We handle both cases for compatibility.
+   */
+  if (p->len >= 2) {
+    GETSHORT(hflags, inp);
+    if (hflags == 0xff03) {
+      pbuf_remove_header(p, 2);
+    }
+  }
+  /* Dispatch the packet thereby consuming it. */
+  ppp_input(l2tp->ppp, p);
+  return;
+
+packet_too_short:
+  PPPDEBUG(LOG_DEBUG, ("pppol2tp: packet too short: %d\n", p->len));
+free_and_return:
+  pbuf_free(p);
+}
+
+/* L2TP Control packet entry point */
+static void pppol2tp_dispatch_control_packet(pppol2tp_pcb *l2tp, u16_t port, struct pbuf *p, u16_t ns, u16_t nr) {
+  u8_t *inp;
+  u16_t avplen, avpflags, vendorid, attributetype, messagetype=0;
+  err_t err;
+#if PPPOL2TP_AUTH_SUPPORT
+  lwip_md5_context md5_ctx;
+  u8_t md5_hash[16];
+  u8_t challenge_id = 0;
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+
+  /* printf("L2TP CTRL INPUT, ns=%d, nr=%d, len=%d\n", ns, nr, p->len); */
+
+  /* Drop unexpected packet */
+  if (ns != l2tp->peer_ns) {
+    PPPDEBUG(LOG_DEBUG, ("pppol2tp: drop unexpected packet: received NS=%d, expected NS=%d\n", ns, l2tp->peer_ns));
+    /*
+     * In order to ensure that all messages are acknowledged properly
+     * (particularly in the case of a lost ZLB ACK message), receipt
+     * of duplicate messages MUST be acknowledged.
+     *
+     * In this very special case we Ack a packet we previously received.
+     * Therefore our NS is the NR we just received. And our NR is the
+     * NS we just received plus one.
+     */
+    if ((s16_t)(ns - l2tp->peer_ns) < 0) {
+      pppol2tp_send_zlb(l2tp, nr, ns+1);
+    }
+    return;
+  }
+
+  l2tp->peer_nr = nr;
+
+  /* Handle the special case of the ICCN acknowledge */
+  if (l2tp->phase == PPPOL2TP_STATE_ICCN_SENT && (s16_t)(l2tp->peer_nr - l2tp->our_ns) > 0) {
+    l2tp->phase = PPPOL2TP_STATE_DATA;
+    sys_untimeout(pppol2tp_timeout, l2tp);
+    ppp_start(l2tp->ppp); /* notify upper layers */
+  }
+
+  /* ZLB packets */
+  if (p->tot_len == 0) {
+    return;
+  }
+  /* A ZLB packet does not consume a NS slot thus we don't record the NS value for ZLB packets */
+  l2tp->peer_ns = ns+1;
+
+  p = pbuf_coalesce(p, PBUF_RAW);
+  inp = (u8_t*)p->payload;
+  /* Decode AVPs */
+  while (p->len > 0) {
+    if (p->len < sizeof(avpflags) + sizeof(vendorid) + sizeof(attributetype) ) {
+      goto packet_too_short;
+    }
+    GETSHORT(avpflags, inp);
+    avplen = avpflags & PPPOL2TP_AVPHEADERFLAG_LENGTHMASK;
+    /* printf("AVPLEN = %d\n", avplen); */
+    if (p->len < avplen || avplen < sizeof(avpflags) + sizeof(vendorid) + sizeof(attributetype)) {
+      goto packet_too_short;
+    }
+    GETSHORT(vendorid, inp);
+    GETSHORT(attributetype, inp);
+    avplen -= sizeof(avpflags) + sizeof(vendorid) + sizeof(attributetype);
+
+    /* Message type must be the first AVP */
+    if (messagetype == 0) {
+      if (attributetype != 0 || vendorid != 0 || avplen != sizeof(messagetype) ) {
+        PPPDEBUG(LOG_DEBUG, ("pppol2tp: message type must be the first AVP\n"));
+        return;
+      }
+      GETSHORT(messagetype, inp);
+      /* printf("Message type = %d\n", messagetype); */
+      switch(messagetype) {
+        /* Start Control Connection Reply */
+        case PPPOL2TP_MESSAGETYPE_SCCRP:
+          /* Only accept SCCRP packet if we sent a SCCRQ */
+          if (l2tp->phase != PPPOL2TP_STATE_SCCRQ_SENT) {
+            goto send_zlb;
+          }
+          break;
+        /* Incoming Call Reply */
+        case PPPOL2TP_MESSAGETYPE_ICRP:
+          /* Only accept ICRP packet if we sent a IRCQ */
+          if (l2tp->phase != PPPOL2TP_STATE_ICRQ_SENT) {
+            goto send_zlb;
+          }
+          break;
+        /* Stop Control Connection Notification */
+        case PPPOL2TP_MESSAGETYPE_STOPCCN:
+          pppol2tp_send_zlb(l2tp, l2tp->our_ns+1, l2tp->peer_ns); /* Ack the StopCCN before we switch to down state */
+          if (l2tp->phase < PPPOL2TP_STATE_DATA) {
+            pppol2tp_abort_connect(l2tp);
+          } else if (l2tp->phase == PPPOL2TP_STATE_DATA) {
+            /* Don't disconnect here, we let the LCP Echo/Reply find the fact
+             * that PPP session is down. Asking the PPP stack to end the session
+             * require strict checking about the PPP phase to prevent endless
+             * disconnection loops.
+             */
+          }
+          return;
+        default:
+          break;
+      }
+      goto nextavp;
+    }
+
+    /* Skip proprietary L2TP extensions */
+    if (vendorid != 0) {
+      goto skipavp;
+    }
+
+    switch (messagetype) {
+      /* Start Control Connection Reply */
+      case PPPOL2TP_MESSAGETYPE_SCCRP:
+       switch (attributetype) {
+          case PPPOL2TP_AVPTYPE_TUNNELID:
+            if (avplen != sizeof(l2tp->source_tunnel_id) ) {
+               PPPDEBUG(LOG_DEBUG, ("pppol2tp: AVP Assign tunnel ID length check failed\n"));
+               return;
+            }
+            GETSHORT(l2tp->source_tunnel_id, inp);
+            PPPDEBUG(LOG_DEBUG, ("pppol2tp: Assigned tunnel ID %"U16_F"\n", l2tp->source_tunnel_id));
+            goto nextavp;
+#if PPPOL2TP_AUTH_SUPPORT
+          case PPPOL2TP_AVPTYPE_CHALLENGE:
+            if (avplen == 0) {
+               PPPDEBUG(LOG_DEBUG, ("pppol2tp: Challenge length check failed\n"));
+               return;
+            }
+            if (l2tp->secret == NULL) {
+              PPPDEBUG(LOG_DEBUG, ("pppol2tp: Received challenge from peer and no secret key available\n"));
+              pppol2tp_abort_connect(l2tp);
+              return;
+            }
+            /* Generate hash of ID, secret, challenge */
+            lwip_md5_init(&md5_ctx);
+            lwip_md5_starts(&md5_ctx);
+            challenge_id = PPPOL2TP_MESSAGETYPE_SCCCN;
+            lwip_md5_update(&md5_ctx, &challenge_id, 1);
+            lwip_md5_update(&md5_ctx, l2tp->secret, l2tp->secret_len);
+            lwip_md5_update(&md5_ctx, inp, avplen);
+            lwip_md5_finish(&md5_ctx, l2tp->challenge_hash);
+            lwip_md5_free(&md5_ctx);
+            l2tp->send_challenge = 1;
+            goto skipavp;
+          case PPPOL2TP_AVPTYPE_CHALLENGERESPONSE:
+            if (avplen != PPPOL2TP_AVPTYPE_CHALLENGERESPONSE_SIZE) {
+               PPPDEBUG(LOG_DEBUG, ("pppol2tp: AVP Challenge Response length check failed\n"));
+               return;
+            }
+            /* Generate hash of ID, secret, challenge */
+            lwip_md5_init(&md5_ctx);
+            lwip_md5_starts(&md5_ctx);
+            challenge_id = PPPOL2TP_MESSAGETYPE_SCCRP;
+            lwip_md5_update(&md5_ctx, &challenge_id, 1);
+            lwip_md5_update(&md5_ctx, l2tp->secret, l2tp->secret_len);
+            lwip_md5_update(&md5_ctx, l2tp->secret_rv, sizeof(l2tp->secret_rv));
+            lwip_md5_finish(&md5_ctx, md5_hash);
+            lwip_md5_free(&md5_ctx);
+            if ( memcmp(inp, md5_hash, sizeof(md5_hash)) ) {
+              PPPDEBUG(LOG_DEBUG, ("pppol2tp: Received challenge response from peer and secret key do not match\n"));
+              pppol2tp_abort_connect(l2tp);
+              return;
+            }
+            goto skipavp;
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+          default:
+            break;
+        }
+        break;
+      /* Incoming Call Reply */
+      case PPPOL2TP_MESSAGETYPE_ICRP:
+        switch (attributetype) {
+         case PPPOL2TP_AVPTYPE_SESSIONID:
+            if (avplen != sizeof(l2tp->source_session_id) ) {
+               PPPDEBUG(LOG_DEBUG, ("pppol2tp: AVP Assign session ID length check failed\n"));
+               return;
+            }
+            GETSHORT(l2tp->source_session_id, inp);
+            PPPDEBUG(LOG_DEBUG, ("pppol2tp: Assigned session ID %"U16_F"\n", l2tp->source_session_id));
+            goto nextavp;
+          default:
+            break;
+        }
+        break;
+      default:
+        break;
+    }
+
+skipavp:
+    INCPTR(avplen, inp);
+nextavp:
+    /* printf("AVP Found, vendor=%d, attribute=%d, len=%d\n", vendorid, attributetype, avplen); */
+    /* next AVP */
+    if (pbuf_remove_header(p, avplen + sizeof(avpflags) + sizeof(vendorid) + sizeof(attributetype)) != 0) {
+      return;
+    }
+  }
+
+  switch(messagetype) {
+    /* Start Control Connection Reply */
+    case PPPOL2TP_MESSAGETYPE_SCCRP:
+      do {
+        l2tp->remote_session_id = magic();
+      } while(l2tp->remote_session_id == 0);
+      l2tp->tunnel_port = port; /* LNS server might have chosen its own local port */
+      l2tp->icrq_retried = 0;
+      l2tp->phase = PPPOL2TP_STATE_ICRQ_SENT;
+      l2tp->our_ns++;
+      if ((err = pppol2tp_send_scccn(l2tp, l2tp->our_ns)) != 0) {
+        PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send SCCCN, error=%d\n", err));
+        LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
+      }
+      l2tp->our_ns++;
+      if ((err = pppol2tp_send_icrq(l2tp, l2tp->our_ns)) != 0) {
+        PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send ICRQ, error=%d\n", err));
+        LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
+      }
+      sys_untimeout(pppol2tp_timeout, l2tp);
+      sys_timeout(PPPOL2TP_CONTROL_TIMEOUT, pppol2tp_timeout, l2tp);
+      break;
+    /* Incoming Call Reply */
+    case PPPOL2TP_MESSAGETYPE_ICRP:
+      l2tp->iccn_retried = 0;
+      l2tp->phase = PPPOL2TP_STATE_ICCN_SENT;
+      l2tp->our_ns++;
+      if ((err = pppol2tp_send_iccn(l2tp, l2tp->our_ns)) != 0) {
+        PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send ICCN, error=%d\n", err));
+        LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
+      }
+      sys_untimeout(pppol2tp_timeout, l2tp);
+      sys_timeout(PPPOL2TP_CONTROL_TIMEOUT, pppol2tp_timeout, l2tp);
+      break;
+    /* Unhandled packet, send ZLB ACK */
+    default:
+      goto send_zlb;
+  }
+  return;
+
+send_zlb:
+  pppol2tp_send_zlb(l2tp, l2tp->our_ns+1, l2tp->peer_ns);
+  return;
+packet_too_short:
+  PPPDEBUG(LOG_DEBUG, ("pppol2tp: packet too short: %d\n", p->len));
+}
+
+/* L2TP Timeout handler */
+static void pppol2tp_timeout(void *arg) {
+  pppol2tp_pcb *l2tp = (pppol2tp_pcb*)arg;
+  err_t err;
+  u32_t retry_wait;
+
+  PPPDEBUG(LOG_DEBUG, ("pppol2tp: timeout\n"));
+
+  switch (l2tp->phase) {
+    case PPPOL2TP_STATE_SCCRQ_SENT:
+      /* backoff wait */
+      if (l2tp->sccrq_retried < 0xff) {
+        l2tp->sccrq_retried++;
+      }
+      if (!l2tp->ppp->settings.persist && l2tp->sccrq_retried >= PPPOL2TP_MAXSCCRQ) {
+        pppol2tp_abort_connect(l2tp);
+        return;
+      }
+      retry_wait = LWIP_MIN(PPPOL2TP_CONTROL_TIMEOUT * l2tp->sccrq_retried, PPPOL2TP_SLOW_RETRY);
+      PPPDEBUG(LOG_DEBUG, ("pppol2tp: sccrq_retried=%d\n", l2tp->sccrq_retried));
+      if ((err = pppol2tp_send_sccrq(l2tp)) != 0) {
+        l2tp->sccrq_retried--;
+        PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send SCCRQ, error=%d\n", err));
+        LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
+      }
+      sys_timeout(retry_wait, pppol2tp_timeout, l2tp);
+      break;
+
+    case PPPOL2TP_STATE_ICRQ_SENT:
+      l2tp->icrq_retried++;
+      if (l2tp->icrq_retried >= PPPOL2TP_MAXICRQ) {
+        pppol2tp_abort_connect(l2tp);
+        return;
+      }
+      PPPDEBUG(LOG_DEBUG, ("pppol2tp: icrq_retried=%d\n", l2tp->icrq_retried));
+      if ((s16_t)(l2tp->peer_nr - l2tp->our_ns) < 0) { /* the SCCCN was not acknowledged */
+        if ((err = pppol2tp_send_scccn(l2tp, l2tp->our_ns -1)) != 0) {
+          l2tp->icrq_retried--;
+          PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send SCCCN, error=%d\n", err));
+          LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
+          sys_timeout(PPPOL2TP_CONTROL_TIMEOUT, pppol2tp_timeout, l2tp);
+          break;
+        }
+      }
+      if ((err = pppol2tp_send_icrq(l2tp, l2tp->our_ns)) != 0) {
+        l2tp->icrq_retried--;
+        PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send ICRQ, error=%d\n", err));
+        LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
+      }
+      sys_timeout(PPPOL2TP_CONTROL_TIMEOUT, pppol2tp_timeout, l2tp);
+      break;
+
+    case PPPOL2TP_STATE_ICCN_SENT:
+      l2tp->iccn_retried++;
+      if (l2tp->iccn_retried >= PPPOL2TP_MAXICCN) {
+        pppol2tp_abort_connect(l2tp);
+        return;
+      }
+      PPPDEBUG(LOG_DEBUG, ("pppol2tp: iccn_retried=%d\n", l2tp->iccn_retried));
+      if ((err = pppol2tp_send_iccn(l2tp, l2tp->our_ns)) != 0) {
+        l2tp->iccn_retried--;
+        PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send ICCN, error=%d\n", err));
+        LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
+      }
+      sys_timeout(PPPOL2TP_CONTROL_TIMEOUT, pppol2tp_timeout, l2tp);
+      break;
+
+    default:
+      return;  /* all done, work in peace */
+  }
+}
+
+/* Connection attempt aborted */
+static void pppol2tp_abort_connect(pppol2tp_pcb *l2tp) {
+  PPPDEBUG(LOG_DEBUG, ("pppol2tp: could not establish connection\n"));
+  l2tp->phase = PPPOL2TP_STATE_INITIAL;
+  ppp_link_failed(l2tp->ppp); /* notify upper layers */
+}
+
+/* Initiate a new tunnel */
+static err_t pppol2tp_send_sccrq(pppol2tp_pcb *l2tp) {
+  struct pbuf *pb;
+  u8_t *p;
+  u16_t len;
+
+  /* calculate UDP packet length */
+  len = 12 +8 +8 +10 +10 +6+sizeof(PPPOL2TP_HOSTNAME)-1 +6+sizeof(PPPOL2TP_VENDORNAME)-1 +8 +8;
+#if PPPOL2TP_AUTH_SUPPORT
+  if (l2tp->secret != NULL) {
+    len += 6 + sizeof(l2tp->secret_rv);
+  }
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+
+  /* allocate a buffer */
+  pb = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
+  if (pb == NULL) {
+    return ERR_MEM;
+  }
+  LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+
+  p = (u8_t*)pb->payload;
+  /* fill in pkt */
+  /* L2TP control header */
+  PUTSHORT(PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY, p);
+  PUTSHORT(len, p); /* Length */
+  PUTSHORT(0, p); /* Tunnel Id */
+  PUTSHORT(0, p); /* Session Id */
+  PUTSHORT(0, p); /* NS Sequence number - to peer */
+  PUTSHORT(0, p); /* NR Sequence number - expected for peer */
+
+  /* AVP - Message type */
+  PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */
+  PUTSHORT(0, p); /* Vendor ID */
+  PUTSHORT(PPPOL2TP_AVPTYPE_MESSAGE, p); /* Attribute type: Message Type */
+  PUTSHORT(PPPOL2TP_MESSAGETYPE_SCCRQ, p); /* Attribute value: Message type: SCCRQ */
+
+  /* AVP - L2TP Version */
+  PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */
+  PUTSHORT(0, p); /* Vendor ID */
+  PUTSHORT(PPPOL2TP_AVPTYPE_VERSION, p); /* Attribute type: Version */
+  PUTSHORT(PPPOL2TP_VERSION, p); /* Attribute value: L2TP Version */
+
+  /* AVP - Framing capabilities */
+  PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 10, p); /* Mandatory flag + len field */
+  PUTSHORT(0, p); /* Vendor ID */
+  PUTSHORT(PPPOL2TP_AVPTYPE_FRAMINGCAPABILITIES, p); /* Attribute type: Framing capabilities */
+  PUTLONG(PPPOL2TP_FRAMINGCAPABILITIES, p); /* Attribute value: Framing capabilities */
+
+  /* AVP - Bearer capabilities */
+  PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 10, p); /* Mandatory flag + len field */
+  PUTSHORT(0, p); /* Vendor ID */
+  PUTSHORT(PPPOL2TP_AVPTYPE_BEARERCAPABILITIES, p); /* Attribute type: Bearer capabilities */
+  PUTLONG(PPPOL2TP_BEARERCAPABILITIES, p); /* Attribute value: Bearer capabilities */
+
+  /* AVP - Host name */
+  PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 6+sizeof(PPPOL2TP_HOSTNAME)-1, p); /* Mandatory flag + len field */
+  PUTSHORT(0, p); /* Vendor ID */
+  PUTSHORT(PPPOL2TP_AVPTYPE_HOSTNAME, p); /* Attribute type: Hostname */
+  MEMCPY(p, PPPOL2TP_HOSTNAME, sizeof(PPPOL2TP_HOSTNAME)-1); /* Attribute value: Hostname */
+  INCPTR(sizeof(PPPOL2TP_HOSTNAME)-1, p);
+
+  /* AVP - Vendor name */
+  PUTSHORT(6+sizeof(PPPOL2TP_VENDORNAME)-1, p); /* len field */
+  PUTSHORT(0, p); /* Vendor ID */
+  PUTSHORT(PPPOL2TP_AVPTYPE_VENDORNAME, p); /* Attribute type: Vendor name */
+  MEMCPY(p, PPPOL2TP_VENDORNAME, sizeof(PPPOL2TP_VENDORNAME)-1); /* Attribute value: Vendor name */
+  INCPTR(sizeof(PPPOL2TP_VENDORNAME)-1, p);
+
+  /* AVP - Assign tunnel ID */
+  PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */
+  PUTSHORT(0, p); /* Vendor ID */
+  PUTSHORT(PPPOL2TP_AVPTYPE_TUNNELID, p); /* Attribute type: Tunnel ID */
+  PUTSHORT(l2tp->remote_tunnel_id, p); /* Attribute value: Tunnel ID */
+
+  /* AVP - Receive window size */
+  PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */
+  PUTSHORT(0, p); /* Vendor ID */
+  PUTSHORT(PPPOL2TP_AVPTYPE_RECEIVEWINDOWSIZE, p); /* Attribute type: Receive window size */
+  PUTSHORT(PPPOL2TP_RECEIVEWINDOWSIZE, p); /* Attribute value: Receive window size */
+
+#if PPPOL2TP_AUTH_SUPPORT
+  /* AVP - Challenge */
+  if (l2tp->secret != NULL) {
+    PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 6 + sizeof(l2tp->secret_rv), p); /* Mandatory flag + len field */
+    PUTSHORT(0, p); /* Vendor ID */
+    PUTSHORT(PPPOL2TP_AVPTYPE_CHALLENGE, p); /* Attribute type: Challenge */
+    MEMCPY(p, l2tp->secret_rv, sizeof(l2tp->secret_rv)); /* Attribute value: Random vector */
+    INCPTR(sizeof(l2tp->secret_rv), p);
+  }
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+
+  return pppol2tp_udp_send(l2tp, pb);
+}
+
+/* Complete tunnel establishment */
+static err_t pppol2tp_send_scccn(pppol2tp_pcb *l2tp, u16_t ns) {
+  struct pbuf *pb;
+  u8_t *p;
+  u16_t len;
+
+  /* calculate UDP packet length */
+  len = 12 +8;
+#if PPPOL2TP_AUTH_SUPPORT
+  if (l2tp->send_challenge) {
+    len += 6 + sizeof(l2tp->challenge_hash);
+  }
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+
+  /* allocate a buffer */
+  pb = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
+  if (pb == NULL) {
+    return ERR_MEM;
+  }
+  LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+
+  p = (u8_t*)pb->payload;
+  /* fill in pkt */
+  /* L2TP control header */
+  PUTSHORT(PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY, p);
+  PUTSHORT(len, p); /* Length */
+  PUTSHORT(l2tp->source_tunnel_id, p); /* Tunnel Id */
+  PUTSHORT(0, p); /* Session Id */
+  PUTSHORT(ns, p); /* NS Sequence number - to peer */
+  PUTSHORT(l2tp->peer_ns, p); /* NR Sequence number - expected for peer */
+
+  /* AVP - Message type */
+  PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */
+  PUTSHORT(0, p); /* Vendor ID */
+  PUTSHORT(PPPOL2TP_AVPTYPE_MESSAGE, p); /* Attribute type: Message Type */
+  PUTSHORT(PPPOL2TP_MESSAGETYPE_SCCCN, p); /* Attribute value: Message type: SCCCN */
+
+#if PPPOL2TP_AUTH_SUPPORT
+  /* AVP - Challenge response */
+  if (l2tp->send_challenge) {
+    PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 6 + sizeof(l2tp->challenge_hash), p); /* Mandatory flag + len field */
+    PUTSHORT(0, p); /* Vendor ID */
+    PUTSHORT(PPPOL2TP_AVPTYPE_CHALLENGERESPONSE, p); /* Attribute type: Challenge response */
+    MEMCPY(p, l2tp->challenge_hash, sizeof(l2tp->challenge_hash)); /* Attribute value: Computed challenge */
+    INCPTR(sizeof(l2tp->challenge_hash), p);
+  }
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+
+  return pppol2tp_udp_send(l2tp, pb);
+}
+
+/* Initiate a new session */
+static err_t pppol2tp_send_icrq(pppol2tp_pcb *l2tp, u16_t ns) {
+  struct pbuf *pb;
+  u8_t *p;
+  u16_t len;
+  u32_t serialnumber;
+
+  /* calculate UDP packet length */
+  len = 12 +8 +8 +10;
+
+  /* allocate a buffer */
+  pb = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
+  if (pb == NULL) {
+    return ERR_MEM;
+  }
+  LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+
+  p = (u8_t*)pb->payload;
+  /* fill in pkt */
+  /* L2TP control header */
+  PUTSHORT(PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY, p);
+  PUTSHORT(len, p); /* Length */
+  PUTSHORT(l2tp->source_tunnel_id, p); /* Tunnel Id */
+  PUTSHORT(0, p); /* Session Id */
+  PUTSHORT(ns, p); /* NS Sequence number - to peer */
+  PUTSHORT(l2tp->peer_ns, p); /* NR Sequence number - expected for peer */
+
+  /* AVP - Message type */
+  PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */
+  PUTSHORT(0, p); /* Vendor ID */
+  PUTSHORT(PPPOL2TP_AVPTYPE_MESSAGE, p); /* Attribute type: Message Type */
+  PUTSHORT(PPPOL2TP_MESSAGETYPE_ICRQ, p); /* Attribute value: Message type: ICRQ */
+
+  /* AVP - Assign session ID */
+  PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */
+  PUTSHORT(0, p); /* Vendor ID */
+  PUTSHORT(PPPOL2TP_AVPTYPE_SESSIONID, p); /* Attribute type: Session ID */
+  PUTSHORT(l2tp->remote_session_id, p); /* Attribute value: Session ID */
+
+  /* AVP - Call Serial Number */
+  PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 10, p); /* Mandatory flag + len field */
+  PUTSHORT(0, p); /* Vendor ID */
+  PUTSHORT(PPPOL2TP_AVPTYPE_CALLSERIALNUMBER, p); /* Attribute type: Serial number */
+  serialnumber = magic();
+  PUTLONG(serialnumber, p); /* Attribute value: Serial number */
+
+  return pppol2tp_udp_send(l2tp, pb);
+}
+
+/* Complete tunnel establishment */
+static err_t pppol2tp_send_iccn(pppol2tp_pcb *l2tp, u16_t ns) {
+  struct pbuf *pb;
+  u8_t *p;
+  u16_t len;
+
+  /* calculate UDP packet length */
+  len = 12 +8 +10 +10;
+
+  /* allocate a buffer */
+  pb = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
+  if (pb == NULL) {
+    return ERR_MEM;
+  }
+  LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+
+  p = (u8_t*)pb->payload;
+  /* fill in pkt */
+  /* L2TP control header */
+  PUTSHORT(PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY, p);
+  PUTSHORT(len, p); /* Length */
+  PUTSHORT(l2tp->source_tunnel_id, p); /* Tunnel Id */
+  PUTSHORT(l2tp->source_session_id, p); /* Session Id */
+  PUTSHORT(ns, p); /* NS Sequence number - to peer */
+  PUTSHORT(l2tp->peer_ns, p); /* NR Sequence number - expected for peer */
+
+  /* AVP - Message type */
+  PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */
+  PUTSHORT(0, p); /* Vendor ID */
+  PUTSHORT(PPPOL2TP_AVPTYPE_MESSAGE, p); /* Attribute type: Message Type */
+  PUTSHORT(PPPOL2TP_MESSAGETYPE_ICCN, p); /* Attribute value: Message type: ICCN */
+
+  /* AVP - Framing type */
+  PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 10, p); /* Mandatory flag + len field */
+  PUTSHORT(0, p); /* Vendor ID */
+  PUTSHORT(PPPOL2TP_AVPTYPE_FRAMINGTYPE, p); /* Attribute type: Framing type */
+  PUTLONG(PPPOL2TP_FRAMINGTYPE, p); /* Attribute value: Framing type */
+
+  /* AVP - TX Connect speed */
+  PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 10, p); /* Mandatory flag + len field */
+  PUTSHORT(0, p); /* Vendor ID */
+  PUTSHORT(PPPOL2TP_AVPTYPE_TXCONNECTSPEED, p); /* Attribute type: TX Connect speed */
+  PUTLONG(PPPOL2TP_TXCONNECTSPEED, p); /* Attribute value: TX Connect speed */
+
+  return pppol2tp_udp_send(l2tp, pb);
+}
+
+/* Send a ZLB ACK packet */
+static err_t pppol2tp_send_zlb(pppol2tp_pcb *l2tp, u16_t ns, u16_t nr) {
+  struct pbuf *pb;
+  u8_t *p;
+  u16_t len;
+
+  /* calculate UDP packet length */
+  len = 12;
+
+  /* allocate a buffer */
+  pb = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
+  if (pb == NULL) {
+    return ERR_MEM;
+  }
+  LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+
+  p = (u8_t*)pb->payload;
+  /* fill in pkt */
+  /* L2TP control header */
+  PUTSHORT(PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY, p);
+  PUTSHORT(len, p); /* Length */
+  PUTSHORT(l2tp->source_tunnel_id, p); /* Tunnel Id */
+  PUTSHORT(0, p); /* Session Id */
+  PUTSHORT(ns, p); /* NS Sequence number - to peer */
+  PUTSHORT(nr, p); /* NR Sequence number - expected for peer */
+
+  return pppol2tp_udp_send(l2tp, pb);
+}
+
+/* Send a StopCCN packet */
+static err_t pppol2tp_send_stopccn(pppol2tp_pcb *l2tp, u16_t ns) {
+  struct pbuf *pb;
+  u8_t *p;
+  u16_t len;
+
+  /* calculate UDP packet length */
+  len = 12 +8 +8 +8;
+
+  /* allocate a buffer */
+  pb = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
+  if (pb == NULL) {
+    return ERR_MEM;
+  }
+  LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+
+  p = (u8_t*)pb->payload;
+  /* fill in pkt */
+  /* L2TP control header */
+  PUTSHORT(PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY, p);
+  PUTSHORT(len, p); /* Length */
+  PUTSHORT(l2tp->source_tunnel_id, p); /* Tunnel Id */
+  PUTSHORT(0, p); /* Session Id */
+  PUTSHORT(ns, p); /* NS Sequence number - to peer */
+  PUTSHORT(l2tp->peer_ns, p); /* NR Sequence number - expected for peer */
+
+  /* AVP - Message type */
+  PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */
+  PUTSHORT(0, p); /* Vendor ID */
+  PUTSHORT(PPPOL2TP_AVPTYPE_MESSAGE, p); /* Attribute type: Message Type */
+  PUTSHORT(PPPOL2TP_MESSAGETYPE_STOPCCN, p); /* Attribute value: Message type: StopCCN */
+
+  /* AVP - Assign tunnel ID */
+  PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */
+  PUTSHORT(0, p); /* Vendor ID */
+  PUTSHORT(PPPOL2TP_AVPTYPE_TUNNELID, p); /* Attribute type: Tunnel ID */
+  PUTSHORT(l2tp->remote_tunnel_id, p); /* Attribute value: Tunnel ID */
+
+  /* AVP - Result code */
+  PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */
+  PUTSHORT(0, p); /* Vendor ID */
+  PUTSHORT(PPPOL2TP_AVPTYPE_RESULTCODE, p); /* Attribute type: Result code */
+  PUTSHORT(PPPOL2TP_RESULTCODE, p); /* Attribute value: Result code */
+
+  return pppol2tp_udp_send(l2tp, pb);
+}
+
+static err_t pppol2tp_xmit(pppol2tp_pcb *l2tp, struct pbuf *pb) {
+  u8_t *p;
+
+  /* make room for L2TP header - should not fail */
+  if (pbuf_add_header(pb, PPPOL2TP_OUTPUT_DATA_HEADER_LEN) != 0) {
+    /* bail out */
+    PPPDEBUG(LOG_ERR, ("pppol2tp: pppol2tp_pcb: could not allocate room for L2TP header\n"));
+    LINK_STATS_INC(link.lenerr);
+    pbuf_free(pb);
+    return ERR_BUF;
+  }
+
+  p = (u8_t*)pb->payload;
+  PUTSHORT(PPPOL2TP_HEADERFLAG_DATA_MANDATORY, p);
+  PUTSHORT(l2tp->source_tunnel_id, p); /* Tunnel Id */
+  PUTSHORT(l2tp->source_session_id, p); /* Session Id */
+
+  return pppol2tp_udp_send(l2tp, pb);
+}
+
+static err_t pppol2tp_udp_send(pppol2tp_pcb *l2tp, struct pbuf *pb) {
+  err_t err;
+  if (l2tp->netif) {
+    err = udp_sendto_if(l2tp->udp, pb, &l2tp->remote_ip, l2tp->tunnel_port, l2tp->netif);
+  } else {
+    err = udp_sendto(l2tp->udp, pb, &l2tp->remote_ip, l2tp->tunnel_port);
+  }
+  pbuf_free(pb);
+  return err;
+}
+
+#endif /* PPP_SUPPORT && PPPOL2TP_SUPPORT */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/pppos.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/pppos.c
new file mode 100644 (file)
index 0000000..dff0255
--- /dev/null
@@ -0,0 +1,895 @@
+/**
+ * @file
+ * Network Point to Point Protocol over Serial file.
+ *
+ */
+
+/*
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPPOS_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include <string.h>
+
+#include "lwip/arch.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+#include "lwip/sys.h"
+#include "lwip/memp.h"
+#include "lwip/netif.h"
+#include "lwip/snmp.h"
+#include "lwip/priv/tcpip_priv.h"
+#include "lwip/api.h"
+#include "lwip/ip4.h" /* for ip4_input() */
+
+#include "netif/ppp/ppp_impl.h"
+#include "netif/ppp/pppos.h"
+#include "netif/ppp/vj.h"
+
+/* Memory pool */
+LWIP_MEMPOOL_DECLARE(PPPOS_PCB, MEMP_NUM_PPPOS_INTERFACES, sizeof(pppos_pcb), "PPPOS_PCB")
+
+/* callbacks called from PPP core */
+static err_t pppos_write(ppp_pcb *ppp, void *ctx, struct pbuf *p);
+static err_t pppos_netif_output(ppp_pcb *ppp, void *ctx, struct pbuf *pb, u16_t protocol);
+static void pppos_connect(ppp_pcb *ppp, void *ctx);
+#if PPP_SERVER
+static void pppos_listen(ppp_pcb *ppp, void *ctx);
+#endif /* PPP_SERVER */
+static void pppos_disconnect(ppp_pcb *ppp, void *ctx);
+static err_t pppos_destroy(ppp_pcb *ppp, void *ctx);
+static void pppos_send_config(ppp_pcb *ppp, void *ctx, u32_t accm, int pcomp, int accomp);
+static void pppos_recv_config(ppp_pcb *ppp, void *ctx, u32_t accm, int pcomp, int accomp);
+
+/* Prototypes for procedures local to this file. */
+#if PPP_INPROC_IRQ_SAFE
+static void pppos_input_callback(void *arg);
+#endif /* PPP_INPROC_IRQ_SAFE */
+static void pppos_input_free_current_packet(pppos_pcb *pppos);
+static void pppos_input_drop(pppos_pcb *pppos);
+static err_t pppos_output_append(pppos_pcb *pppos, err_t err, struct pbuf *nb, u8_t c, u8_t accm, u16_t *fcs);
+static err_t pppos_output_last(pppos_pcb *pppos, err_t err, struct pbuf *nb, u16_t *fcs);
+
+/* Callbacks structure for PPP core */
+static const struct link_callbacks pppos_callbacks = {
+  pppos_connect,
+#if PPP_SERVER
+  pppos_listen,
+#endif /* PPP_SERVER */
+  pppos_disconnect,
+  pppos_destroy,
+  pppos_write,
+  pppos_netif_output,
+  pppos_send_config,
+  pppos_recv_config
+};
+
+/* PPP's Asynchronous-Control-Character-Map.  The mask array is used
+ * to select the specific bit for a character. */
+#define ESCAPE_P(accm, c) ((accm)[(c) >> 3] & 1 << (c & 0x07))
+
+#if PPP_FCS_TABLE
+/*
+ * FCS lookup table as calculated by genfcstab.
+ */
+static const u16_t fcstab[256] = {
+  0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+  0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+  0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+  0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+  0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+  0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+  0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+  0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+  0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+  0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+  0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+  0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+  0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+  0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+  0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+  0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+  0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+  0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+  0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+  0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+  0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+  0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+  0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+  0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+  0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+  0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+  0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+  0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+  0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+  0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+  0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+  0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff])
+#else /* PPP_FCS_TABLE */
+/* The HDLC polynomial: X**0 + X**5 + X**12 + X**16 (0x8408) */
+#define PPP_FCS_POLYNOMIAL 0x8408
+static u16_t
+ppp_get_fcs(u8_t byte)
+{
+  unsigned int octet;
+  int bit;
+  octet = byte;
+  for (bit = 8; bit-- > 0; ) {
+    octet = (octet & 0x01) ? ((octet >> 1) ^ PPP_FCS_POLYNOMIAL) : (octet >> 1);
+  }
+  return octet & 0xffff;
+}
+#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ ppp_get_fcs(((fcs) ^ (c)) & 0xff))
+#endif /* PPP_FCS_TABLE */
+
+/*
+ * Values for FCS calculations.
+ */
+#define PPP_INITFCS     0xffff  /* Initial FCS value */
+#define PPP_GOODFCS     0xf0b8  /* Good final FCS value */
+
+#if PPP_INPROC_IRQ_SAFE
+#define PPPOS_DECL_PROTECT(lev) SYS_ARCH_DECL_PROTECT(lev)
+#define PPPOS_PROTECT(lev) SYS_ARCH_PROTECT(lev)
+#define PPPOS_UNPROTECT(lev) SYS_ARCH_UNPROTECT(lev)
+#else
+#define PPPOS_DECL_PROTECT(lev)
+#define PPPOS_PROTECT(lev)
+#define PPPOS_UNPROTECT(lev)
+#endif /* PPP_INPROC_IRQ_SAFE */
+
+
+/*
+ * Create a new PPP connection using the given serial I/O device.
+ *
+ * Return 0 on success, an error code on failure.
+ */
+ppp_pcb *pppos_create(struct netif *pppif, pppos_output_cb_fn output_cb,
+       ppp_link_status_cb_fn link_status_cb, void *ctx_cb)
+{
+  pppos_pcb *pppos;
+  ppp_pcb *ppp;
+  LWIP_ASSERT_CORE_LOCKED();
+
+  pppos = (pppos_pcb *)LWIP_MEMPOOL_ALLOC(PPPOS_PCB);
+  if (pppos == NULL) {
+    return NULL;
+  }
+
+  ppp = ppp_new(pppif, &pppos_callbacks, pppos, link_status_cb, ctx_cb);
+  if (ppp == NULL) {
+    LWIP_MEMPOOL_FREE(PPPOS_PCB, pppos);
+    return NULL;
+  }
+
+  memset(pppos, 0, sizeof(pppos_pcb));
+  pppos->ppp = ppp;
+  pppos->output_cb = output_cb;
+  return ppp;
+}
+
+/* Called by PPP core */
+static err_t
+pppos_write(ppp_pcb *ppp, void *ctx, struct pbuf *p)
+{
+  pppos_pcb *pppos = (pppos_pcb *)ctx;
+  u8_t *s;
+  struct pbuf *nb;
+  u16_t n;
+  u16_t fcs_out;
+  err_t err;
+  LWIP_UNUSED_ARG(ppp);
+
+  /* Grab an output buffer. Using PBUF_POOL here for tx is ok since the pbuf
+     gets freed by 'pppos_output_last' before this function returns and thus
+     cannot starve rx. */
+  nb = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL);
+  if (nb == NULL) {
+    PPPDEBUG(LOG_WARNING, ("pppos_write[%d]: alloc fail\n", ppp->netif->num));
+    LINK_STATS_INC(link.memerr);
+    LINK_STATS_INC(link.drop);
+    MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
+    pbuf_free(p);
+    return ERR_MEM;
+  }
+
+  /* Set nb->tot_len to actual payload length */
+  nb->tot_len = p->len;
+
+  /* If the link has been idle, we'll send a fresh flag character to
+   * flush any noise. */
+  err = ERR_OK;
+  if ((sys_now() - pppos->last_xmit) >= PPP_MAXIDLEFLAG) {
+    err = pppos_output_append(pppos, err,  nb, PPP_FLAG, 0, NULL);
+  }
+
+  /* Load output buffer. */
+  fcs_out = PPP_INITFCS;
+  s = (u8_t*)p->payload;
+  n = p->len;
+  while (n-- > 0) {
+    err = pppos_output_append(pppos, err,  nb, *s++, 1, &fcs_out);
+  }
+
+  err = pppos_output_last(pppos, err, nb, &fcs_out);
+  if (err == ERR_OK) {
+    PPPDEBUG(LOG_INFO, ("pppos_write[%d]: len=%d\n", ppp->netif->num, p->len));
+  } else {
+    PPPDEBUG(LOG_WARNING, ("pppos_write[%d]: output failed len=%d\n", ppp->netif->num, p->len));
+  }
+  pbuf_free(p);
+  return err;
+}
+
+/* Called by PPP core */
+static err_t
+pppos_netif_output(ppp_pcb *ppp, void *ctx, struct pbuf *pb, u16_t protocol)
+{
+  pppos_pcb *pppos = (pppos_pcb *)ctx;
+  struct pbuf *nb, *p;
+  u16_t fcs_out;
+  err_t err;
+  LWIP_UNUSED_ARG(ppp);
+
+  /* Grab an output buffer. Using PBUF_POOL here for tx is ok since the pbuf
+     gets freed by 'pppos_output_last' before this function returns and thus
+     cannot starve rx. */
+  nb = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL);
+  if (nb == NULL) {
+    PPPDEBUG(LOG_WARNING, ("pppos_netif_output[%d]: alloc fail\n", ppp->netif->num));
+    LINK_STATS_INC(link.memerr);
+    LINK_STATS_INC(link.drop);
+    MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
+    return ERR_MEM;
+  }
+
+  /* Set nb->tot_len to actual payload length */
+  nb->tot_len = pb->tot_len;
+
+  /* If the link has been idle, we'll send a fresh flag character to
+   * flush any noise. */
+  err = ERR_OK;
+  if ((sys_now() - pppos->last_xmit) >= PPP_MAXIDLEFLAG) {
+    err = pppos_output_append(pppos, err,  nb, PPP_FLAG, 0, NULL);
+  }
+
+  fcs_out = PPP_INITFCS;
+  if (!pppos->accomp) {
+    err = pppos_output_append(pppos, err,  nb, PPP_ALLSTATIONS, 1, &fcs_out);
+    err = pppos_output_append(pppos, err,  nb, PPP_UI, 1, &fcs_out);
+  }
+  if (!pppos->pcomp || protocol > 0xFF) {
+    err = pppos_output_append(pppos, err,  nb, (protocol >> 8) & 0xFF, 1, &fcs_out);
+  }
+  err = pppos_output_append(pppos, err,  nb, protocol & 0xFF, 1, &fcs_out);
+
+  /* Load packet. */
+  for(p = pb; p; p = p->next) {
+    u16_t n = p->len;
+    u8_t *s = (u8_t*)p->payload;
+
+    while (n-- > 0) {
+      err = pppos_output_append(pppos, err,  nb, *s++, 1, &fcs_out);
+    }
+  }
+
+  err = pppos_output_last(pppos, err, nb, &fcs_out);
+  if (err == ERR_OK) {
+    PPPDEBUG(LOG_INFO, ("pppos_netif_output[%d]: proto=0x%"X16_F", len = %d\n", ppp->netif->num, protocol, pb->tot_len));
+  } else {
+    PPPDEBUG(LOG_WARNING, ("pppos_netif_output[%d]: output failed proto=0x%"X16_F", len = %d\n", ppp->netif->num, protocol, pb->tot_len));
+  }
+  return err;
+}
+
+static void
+pppos_connect(ppp_pcb *ppp, void *ctx)
+{
+  pppos_pcb *pppos = (pppos_pcb *)ctx;
+  PPPOS_DECL_PROTECT(lev);
+
+#if PPP_INPROC_IRQ_SAFE
+  /* input pbuf left over from last session? */
+  pppos_input_free_current_packet(pppos);
+#endif /* PPP_INPROC_IRQ_SAFE */
+
+  /* reset PPPoS control block to its initial state */
+  memset(&pppos->last_xmit, 0, sizeof(pppos_pcb) - offsetof(pppos_pcb, last_xmit));
+
+  /*
+   * Default the in and out accm so that escape and flag characters
+   * are always escaped.
+   */
+  pppos->in_accm[15] = 0x60; /* no need to protect since RX is not running */
+  pppos->out_accm[15] = 0x60;
+  PPPOS_PROTECT(lev);
+  pppos->open = 1;
+  PPPOS_UNPROTECT(lev);
+
+  /*
+   * Start the connection and handle incoming events (packet or timeout).
+   */
+  PPPDEBUG(LOG_INFO, ("pppos_connect: unit %d: connecting\n", ppp->netif->num));
+  ppp_start(ppp); /* notify upper layers */
+}
+
+#if PPP_SERVER
+static void
+pppos_listen(ppp_pcb *ppp, void *ctx)
+{
+  pppos_pcb *pppos = (pppos_pcb *)ctx;
+  PPPOS_DECL_PROTECT(lev);
+
+#if PPP_INPROC_IRQ_SAFE
+  /* input pbuf left over from last session? */
+  pppos_input_free_current_packet(pppos);
+#endif /* PPP_INPROC_IRQ_SAFE */
+
+  /* reset PPPoS control block to its initial state */
+  memset(&pppos->last_xmit, 0, sizeof(pppos_pcb) - offsetof(pppos_pcb, last_xmit));
+
+  /*
+   * Default the in and out accm so that escape and flag characters
+   * are always escaped.
+   */
+  pppos->in_accm[15] = 0x60; /* no need to protect since RX is not running */
+  pppos->out_accm[15] = 0x60;
+  PPPOS_PROTECT(lev);
+  pppos->open = 1;
+  PPPOS_UNPROTECT(lev);
+
+  /*
+   * Wait for something to happen.
+   */
+  PPPDEBUG(LOG_INFO, ("pppos_listen: unit %d: listening\n", ppp->netif->num));
+  ppp_start(ppp); /* notify upper layers */
+}
+#endif /* PPP_SERVER */
+
+static void
+pppos_disconnect(ppp_pcb *ppp, void *ctx)
+{
+  pppos_pcb *pppos = (pppos_pcb *)ctx;
+  PPPOS_DECL_PROTECT(lev);
+
+  PPPOS_PROTECT(lev);
+  pppos->open = 0;
+  PPPOS_UNPROTECT(lev);
+
+  /* If PPP_INPROC_IRQ_SAFE is used we cannot call
+   * pppos_input_free_current_packet() here because
+   * rx IRQ might still call pppos_input().
+   */
+#if !PPP_INPROC_IRQ_SAFE
+  /* input pbuf left ? */
+  pppos_input_free_current_packet(pppos);
+#endif /* !PPP_INPROC_IRQ_SAFE */
+
+  ppp_link_end(ppp); /* notify upper layers */
+}
+
+static err_t
+pppos_destroy(ppp_pcb *ppp, void *ctx)
+{
+  pppos_pcb *pppos = (pppos_pcb *)ctx;
+  LWIP_UNUSED_ARG(ppp);
+
+#if PPP_INPROC_IRQ_SAFE
+  /* input pbuf left ? */
+  pppos_input_free_current_packet(pppos);
+#endif /* PPP_INPROC_IRQ_SAFE */
+
+  LWIP_MEMPOOL_FREE(PPPOS_PCB, pppos);
+  return ERR_OK;
+}
+
+#if !NO_SYS && !PPP_INPROC_IRQ_SAFE
+/** Pass received raw characters to PPPoS to be decoded through lwIP TCPIP thread.
+ *
+ * This is one of the only functions that may be called outside of the TCPIP thread!
+ *
+ * @param ppp PPP descriptor index, returned by pppos_create()
+ * @param s received data
+ * @param l length of received data
+ */
+err_t
+pppos_input_tcpip(ppp_pcb *ppp, u8_t *s, int l)
+{
+  struct pbuf *p;
+  err_t err;
+
+  p = pbuf_alloc(PBUF_RAW, l, PBUF_POOL);
+  if (!p) {
+    return ERR_MEM;
+  }
+  pbuf_take(p, s, l);
+
+  err = tcpip_inpkt(p, ppp_netif(ppp), pppos_input_sys);
+  if (err != ERR_OK) {
+     pbuf_free(p);
+  }
+  return err;
+}
+
+/* called from TCPIP thread */
+err_t pppos_input_sys(struct pbuf *p, struct netif *inp) {
+  ppp_pcb *ppp = (ppp_pcb*)inp->state;
+  struct pbuf *n;
+  LWIP_ASSERT_CORE_LOCKED();
+
+  for (n = p; n; n = n->next) {
+    pppos_input(ppp, (u8_t*)n->payload, n->len);
+  }
+  pbuf_free(p);
+  return ERR_OK;
+}
+#endif /* !NO_SYS && !PPP_INPROC_IRQ_SAFE */
+
+/** PPPoS input helper struct, must be packed since it is stored
+ * to pbuf->payload, which might be unaligned. */
+#if PPP_INPROC_IRQ_SAFE
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct pppos_input_header {
+  PACK_STRUCT_FIELD(ppp_pcb *ppp);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+#endif /* PPP_INPROC_IRQ_SAFE */
+
+/** Pass received raw characters to PPPoS to be decoded.
+ *
+ * @param ppp PPP descriptor index, returned by pppos_create()
+ * @param s received data
+ * @param l length of received data
+ */
+void
+pppos_input(ppp_pcb *ppp, u8_t *s, int l)
+{
+  pppos_pcb *pppos = (pppos_pcb *)ppp->link_ctx_cb;
+  struct pbuf *next_pbuf;
+  u8_t cur_char;
+  u8_t escaped;
+  PPPOS_DECL_PROTECT(lev);
+#if !PPP_INPROC_IRQ_SAFE
+  LWIP_ASSERT_CORE_LOCKED();
+#endif
+
+  PPPDEBUG(LOG_DEBUG, ("pppos_input[%d]: got %d bytes\n", ppp->netif->num, l));
+  while (l-- > 0) {
+    cur_char = *s++;
+
+    PPPOS_PROTECT(lev);
+    /* ppp_input can disconnect the interface, we need to abort to prevent a memory
+     * leak if there are remaining bytes because pppos_connect and pppos_listen
+     * functions expect input buffer to be free. Furthermore there are no real
+     * reason to continue reading bytes if we are disconnected.
+     */
+    if (!pppos->open) {
+      PPPOS_UNPROTECT(lev);
+      return;
+    }
+    escaped = ESCAPE_P(pppos->in_accm, cur_char);
+    PPPOS_UNPROTECT(lev);
+    /* Handle special characters. */
+    if (escaped) {
+      /* Check for escape sequences. */
+      /* XXX Note that this does not handle an escaped 0x5d character which
+       * would appear as an escape character.  Since this is an ASCII ']'
+       * and there is no reason that I know of to escape it, I won't complicate
+       * the code to handle this case. GLL */
+      if (cur_char == PPP_ESCAPE) {
+        pppos->in_escaped = 1;
+      /* Check for the flag character. */
+      } else if (cur_char == PPP_FLAG) {
+        /* If this is just an extra flag character, ignore it. */
+        if (pppos->in_state <= PDADDRESS) {
+          /* ignore it */;
+        /* If we haven't received the packet header, drop what has come in. */
+        } else if (pppos->in_state < PDDATA) {
+          PPPDEBUG(LOG_WARNING,
+                   ("pppos_input[%d]: Dropping incomplete packet %d\n",
+                    ppp->netif->num, pppos->in_state));
+          LINK_STATS_INC(link.lenerr);
+          pppos_input_drop(pppos);
+        /* If the fcs is invalid, drop the packet. */
+        } else if (pppos->in_fcs != PPP_GOODFCS) {
+          PPPDEBUG(LOG_INFO,
+                   ("pppos_input[%d]: Dropping bad fcs 0x%"X16_F" proto=0x%"X16_F"\n",
+                    ppp->netif->num, pppos->in_fcs, pppos->in_protocol));
+          /* Note: If you get lots of these, check for UART frame errors or try different baud rate */
+          LINK_STATS_INC(link.chkerr);
+          pppos_input_drop(pppos);
+        /* Otherwise it's a good packet so pass it on. */
+        } else {
+          struct pbuf *inp;
+          /* Trim off the checksum. */
+          if(pppos->in_tail->len > 2) {
+            pppos->in_tail->len -= 2;
+
+            pppos->in_tail->tot_len = pppos->in_tail->len;
+            if (pppos->in_tail != pppos->in_head) {
+              pbuf_cat(pppos->in_head, pppos->in_tail);
+            }
+          } else {
+            pppos->in_tail->tot_len = pppos->in_tail->len;
+            if (pppos->in_tail != pppos->in_head) {
+              pbuf_cat(pppos->in_head, pppos->in_tail);
+            }
+
+            pbuf_realloc(pppos->in_head, pppos->in_head->tot_len - 2);
+          }
+
+          /* Dispatch the packet thereby consuming it. */
+          inp = pppos->in_head;
+          /* Packet consumed, release our references. */
+          pppos->in_head = NULL;
+          pppos->in_tail = NULL;
+#if IP_FORWARD || LWIP_IPV6_FORWARD
+          /* hide the room for Ethernet forwarding header */
+          pbuf_remove_header(inp, PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN);
+#endif /* IP_FORWARD || LWIP_IPV6_FORWARD */
+#if PPP_INPROC_IRQ_SAFE
+          if(tcpip_try_callback(pppos_input_callback, inp) != ERR_OK) {
+            PPPDEBUG(LOG_ERR, ("pppos_input[%d]: tcpip_callback() failed, dropping packet\n", ppp->netif->num));
+            pbuf_free(inp);
+            LINK_STATS_INC(link.drop);
+            MIB2_STATS_NETIF_INC(ppp->netif, ifindiscards);
+          }
+#else /* PPP_INPROC_IRQ_SAFE */
+          ppp_input(ppp, inp);
+#endif /* PPP_INPROC_IRQ_SAFE */
+        }
+
+        /* Prepare for a new packet. */
+        pppos->in_fcs = PPP_INITFCS;
+        pppos->in_state = PDADDRESS;
+        pppos->in_escaped = 0;
+      /* Other characters are usually control characters that may have
+       * been inserted by the physical layer so here we just drop them. */
+      } else {
+        PPPDEBUG(LOG_WARNING,
+                 ("pppos_input[%d]: Dropping ACCM char <%d>\n", ppp->netif->num, cur_char));
+      }
+    /* Process other characters. */
+    } else {
+      /* Unencode escaped characters. */
+      if (pppos->in_escaped) {
+        pppos->in_escaped = 0;
+        cur_char ^= PPP_TRANS;
+      }
+
+      /* Process character relative to current state. */
+      switch(pppos->in_state) {
+        case PDIDLE:                    /* Idle state - waiting. */
+          /* Drop the character if it's not 0xff
+           * we would have processed a flag character above. */
+          if (cur_char != PPP_ALLSTATIONS) {
+            break;
+          }
+          /* no break */
+          /* Fall through */
+
+        case PDSTART:                   /* Process start flag. */
+          /* Prepare for a new packet. */
+          pppos->in_fcs = PPP_INITFCS;
+          /* no break */
+          /* Fall through */
+
+        case PDADDRESS:                 /* Process address field. */
+          if (cur_char == PPP_ALLSTATIONS) {
+            pppos->in_state = PDCONTROL;
+            break;
+          }
+          /* no break */
+
+          /* Else assume compressed address and control fields so
+           * fall through to get the protocol... */
+          /* Fall through */
+        case PDCONTROL:                 /* Process control field. */
+          /* If we don't get a valid control code, restart. */
+          if (cur_char == PPP_UI) {
+            pppos->in_state = PDPROTOCOL1;
+            break;
+          }
+          /* no break */
+
+#if 0
+          else {
+            PPPDEBUG(LOG_WARNING,
+                     ("pppos_input[%d]: Invalid control <%d>\n", ppp->netif->num, cur_char));
+            pppos->in_state = PDSTART;
+          }
+#endif
+          /* Fall through */
+
+      case PDPROTOCOL1:               /* Process protocol field 1. */
+          /* If the lower bit is set, this is the end of the protocol
+           * field. */
+          if (cur_char & 1) {
+            pppos->in_protocol = cur_char;
+            pppos->in_state = PDDATA;
+          } else {
+            pppos->in_protocol = (u16_t)cur_char << 8;
+            pppos->in_state = PDPROTOCOL2;
+          }
+          break;
+        case PDPROTOCOL2:               /* Process protocol field 2. */
+          pppos->in_protocol |= cur_char;
+          pppos->in_state = PDDATA;
+          break;
+        case PDDATA:                    /* Process data byte. */
+          /* Make space to receive processed data. */
+          if (pppos->in_tail == NULL || pppos->in_tail->len == PBUF_POOL_BUFSIZE) {
+            u16_t pbuf_alloc_len;
+            if (pppos->in_tail != NULL) {
+              pppos->in_tail->tot_len = pppos->in_tail->len;
+              if (pppos->in_tail != pppos->in_head) {
+                pbuf_cat(pppos->in_head, pppos->in_tail);
+                /* give up the in_tail reference now */
+                pppos->in_tail = NULL;
+              }
+            }
+            /* If we haven't started a packet, we need a packet header. */
+            pbuf_alloc_len = 0;
+#if IP_FORWARD || LWIP_IPV6_FORWARD
+            /* If IP forwarding is enabled we are reserving PBUF_LINK_ENCAPSULATION_HLEN
+             * + PBUF_LINK_HLEN bytes so the packet is being allocated with enough header
+             * space to be forwarded (to Ethernet for example).
+             */
+            if (pppos->in_head == NULL) {
+              pbuf_alloc_len = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN;
+            }
+#endif /* IP_FORWARD || LWIP_IPV6_FORWARD */
+            next_pbuf = pbuf_alloc(PBUF_RAW, pbuf_alloc_len, PBUF_POOL);
+            if (next_pbuf == NULL) {
+              /* No free buffers.  Drop the input packet and let the
+               * higher layers deal with it.  Continue processing
+               * the received pbuf chain in case a new packet starts. */
+              PPPDEBUG(LOG_ERR, ("pppos_input[%d]: NO FREE PBUFS!\n", ppp->netif->num));
+              LINK_STATS_INC(link.memerr);
+              pppos_input_drop(pppos);
+              pppos->in_state = PDSTART;  /* Wait for flag sequence. */
+              break;
+            }
+            if (pppos->in_head == NULL) {
+              u8_t *payload = ((u8_t*)next_pbuf->payload) + pbuf_alloc_len;
+#if PPP_INPROC_IRQ_SAFE
+              ((struct pppos_input_header*)payload)->ppp = ppp;
+              payload += sizeof(struct pppos_input_header);
+              next_pbuf->len += sizeof(struct pppos_input_header);
+#endif /* PPP_INPROC_IRQ_SAFE */
+              next_pbuf->len += sizeof(pppos->in_protocol);
+              *(payload++) = pppos->in_protocol >> 8;
+              *(payload) = pppos->in_protocol & 0xFF;
+              pppos->in_head = next_pbuf;
+            }
+            pppos->in_tail = next_pbuf;
+          }
+          /* Load character into buffer. */
+          ((u8_t*)pppos->in_tail->payload)[pppos->in_tail->len++] = cur_char;
+          break;
+        default:
+          break;
+      }
+
+      /* update the frame check sequence number. */
+      pppos->in_fcs = PPP_FCS(pppos->in_fcs, cur_char);
+    }
+  } /* while (l-- > 0), all bytes processed */
+}
+
+#if PPP_INPROC_IRQ_SAFE
+/* PPPoS input callback using one input pointer
+ */
+static void pppos_input_callback(void *arg) {
+  struct pbuf *pb = (struct pbuf*)arg;
+  ppp_pcb *ppp;
+
+  ppp = ((struct pppos_input_header*)pb->payload)->ppp;
+  if(pbuf_remove_header(pb, sizeof(struct pppos_input_header))) {
+    LWIP_ASSERT("pbuf_remove_header failed\n", 0);
+    goto drop;
+  }
+
+  /* Dispatch the packet thereby consuming it. */
+  ppp_input(ppp, pb);
+  return;
+
+drop:
+  LINK_STATS_INC(link.drop);
+  MIB2_STATS_NETIF_INC(ppp->netif, ifindiscards);
+  pbuf_free(pb);
+}
+#endif /* PPP_INPROC_IRQ_SAFE */
+
+static void
+pppos_send_config(ppp_pcb *ppp, void *ctx, u32_t accm, int pcomp, int accomp)
+{
+  int i;
+  pppos_pcb *pppos = (pppos_pcb *)ctx;
+  LWIP_UNUSED_ARG(ppp);
+
+  pppos->pcomp = pcomp;
+  pppos->accomp = accomp;
+
+  /* Load the ACCM bits for the 32 control codes. */
+  for (i = 0; i < 32/8; i++) {
+    pppos->out_accm[i] = (u8_t)((accm >> (8 * i)) & 0xFF);
+  }
+
+  PPPDEBUG(LOG_INFO, ("pppos_send_config[%d]: out_accm=%X %X %X %X\n",
+            pppos->ppp->netif->num,
+            pppos->out_accm[0], pppos->out_accm[1], pppos->out_accm[2], pppos->out_accm[3]));
+}
+
+static void
+pppos_recv_config(ppp_pcb *ppp, void *ctx, u32_t accm, int pcomp, int accomp)
+{
+  int i;
+  pppos_pcb *pppos = (pppos_pcb *)ctx;
+  PPPOS_DECL_PROTECT(lev);
+  LWIP_UNUSED_ARG(ppp);
+  LWIP_UNUSED_ARG(pcomp);
+  LWIP_UNUSED_ARG(accomp);
+
+  /* Load the ACCM bits for the 32 control codes. */
+  PPPOS_PROTECT(lev);
+  for (i = 0; i < 32 / 8; i++) {
+    pppos->in_accm[i] = (u8_t)(accm >> (i * 8));
+  }
+  PPPOS_UNPROTECT(lev);
+
+  PPPDEBUG(LOG_INFO, ("pppos_recv_config[%d]: in_accm=%X %X %X %X\n",
+            pppos->ppp->netif->num,
+            pppos->in_accm[0], pppos->in_accm[1], pppos->in_accm[2], pppos->in_accm[3]));
+}
+
+/*
+ * Drop the input packet.
+ */
+static void
+pppos_input_free_current_packet(pppos_pcb *pppos)
+{
+  if (pppos->in_head != NULL) {
+    if (pppos->in_tail && (pppos->in_tail != pppos->in_head)) {
+      pbuf_free(pppos->in_tail);
+    }
+    pbuf_free(pppos->in_head);
+    pppos->in_head = NULL;
+  }
+  pppos->in_tail = NULL;
+}
+
+/*
+ * Drop the input packet and increase error counters.
+ */
+static void
+pppos_input_drop(pppos_pcb *pppos)
+{
+  if (pppos->in_head != NULL) {
+#if 0
+    PPPDEBUG(LOG_INFO, ("pppos_input_drop: %d:%.*H\n", pppos->in_head->len, min(60, pppos->in_head->len * 2), pppos->in_head->payload));
+#endif
+    PPPDEBUG(LOG_INFO, ("pppos_input_drop: pbuf len=%d, addr %p\n", pppos->in_head->len, (void*)pppos->in_head));
+  }
+  pppos_input_free_current_packet(pppos);
+#if VJ_SUPPORT
+  vj_uncompress_err(&pppos->ppp->vj_comp);
+#endif /* VJ_SUPPORT */
+
+  LINK_STATS_INC(link.drop);
+  MIB2_STATS_NETIF_INC(pppos->ppp->netif, ifindiscards);
+}
+
+/*
+ * pppos_output_append - append given character to end of given pbuf.
+ * If out_accm is not 0 and the character needs to be escaped, do so.
+ * If pbuf is full, send the pbuf and reuse it.
+ * Return the current pbuf.
+ */
+static err_t
+pppos_output_append(pppos_pcb *pppos, err_t err, struct pbuf *nb, u8_t c, u8_t accm, u16_t *fcs)
+{
+  if (err != ERR_OK) {
+    return err;
+  }
+
+  /* Make sure there is room for the character and an escape code.
+   * Sure we don't quite fill the buffer if the character doesn't
+   * get escaped but is one character worth complicating this? */
+  if ((PBUF_POOL_BUFSIZE - nb->len) < 2) {
+    u32_t l = pppos->output_cb(pppos->ppp, (u8_t*)nb->payload, nb->len, pppos->ppp->ctx_cb);
+    if (l != nb->len) {
+      return ERR_IF;
+    }
+    nb->len = 0;
+  }
+
+  /* Update FCS before checking for special characters. */
+  if (fcs) {
+    *fcs = PPP_FCS(*fcs, c);
+  }
+
+  /* Copy to output buffer escaping special characters. */
+  if (accm && ESCAPE_P(pppos->out_accm, c)) {
+    *((u8_t*)nb->payload + nb->len++) = PPP_ESCAPE;
+    *((u8_t*)nb->payload + nb->len++) = c ^ PPP_TRANS;
+  } else {
+    *((u8_t*)nb->payload + nb->len++) = c;
+  }
+
+  return ERR_OK;
+}
+
+static err_t
+pppos_output_last(pppos_pcb *pppos, err_t err, struct pbuf *nb, u16_t *fcs)
+{
+  ppp_pcb *ppp = pppos->ppp;
+
+  /* Add FCS and trailing flag. */
+  err = pppos_output_append(pppos, err,  nb, ~(*fcs) & 0xFF, 1, NULL);
+  err = pppos_output_append(pppos, err,  nb, (~(*fcs) >> 8) & 0xFF, 1, NULL);
+  err = pppos_output_append(pppos, err,  nb, PPP_FLAG, 0, NULL);
+
+  if (err != ERR_OK) {
+    goto failed;
+  }
+
+  /* Send remaining buffer if not empty */
+  if (nb->len > 0) {
+    u32_t l = pppos->output_cb(ppp, (u8_t*)nb->payload, nb->len, ppp->ctx_cb);
+    if (l != nb->len) {
+      err = ERR_IF;
+      goto failed;
+    }
+  }
+
+  pppos->last_xmit = sys_now();
+  MIB2_STATS_NETIF_ADD(ppp->netif, ifoutoctets, nb->tot_len);
+  MIB2_STATS_NETIF_INC(ppp->netif, ifoutucastpkts);
+  LINK_STATS_INC(link.xmit);
+  pbuf_free(nb);
+  return ERR_OK;
+
+failed:
+  pppos->last_xmit = 0; /* prepend PPP_FLAG to next packet */
+  LINK_STATS_INC(link.err);
+  LINK_STATS_INC(link.drop);
+  MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
+  pbuf_free(nb);
+  return err;
+}
+
+#endif /* PPP_SUPPORT && PPPOS_SUPPORT */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/upap.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/upap.c
new file mode 100644 (file)
index 0000000..3b2399d
--- /dev/null
@@ -0,0 +1,677 @@
+/*
+ * upap.c - User/Password Authentication Protocol.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * 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.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PAP_SUPPORT  /* don't build if not configured for use in lwipopts.h */
+
+/*
+ * @todo:
+ */
+
+#if 0 /* UNUSED */
+#include <stdio.h>
+#include <string.h>
+#endif /* UNUSED */
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/upap.h"
+
+#if PPP_OPTIONS
+/*
+ * Command-line options.
+ */
+static option_t pap_option_list[] = {
+    { "hide-password", o_bool, &hide_password,
+      "Don't output passwords to log", OPT_PRIO | 1 },
+    { "show-password", o_bool, &hide_password,
+      "Show password string in debug log messages", OPT_PRIOSUB | 0 },
+
+    { "pap-restart", o_int, &upap[0].us_timeouttime,
+      "Set retransmit timeout for PAP", OPT_PRIO },
+    { "pap-max-authreq", o_int, &upap[0].us_maxtransmits,
+      "Set max number of transmissions for auth-reqs", OPT_PRIO },
+    { "pap-timeout", o_int, &upap[0].us_reqtimeout,
+      "Set time limit for peer PAP authentication", OPT_PRIO },
+
+    { NULL }
+};
+#endif /* PPP_OPTIONS */
+
+/*
+ * Protocol entry points.
+ */
+static void upap_init(ppp_pcb *pcb);
+static void upap_lowerup(ppp_pcb *pcb);
+static void upap_lowerdown(ppp_pcb *pcb);
+static void upap_input(ppp_pcb *pcb, u_char *inpacket, int l);
+static void upap_protrej(ppp_pcb *pcb);
+#if PRINTPKT_SUPPORT
+static int upap_printpkt(const u_char *p, int plen, void (*printer) (void *, const char *, ...), void *arg);
+#endif /* PRINTPKT_SUPPORT */
+
+const struct protent pap_protent = {
+    PPP_PAP,
+    upap_init,
+    upap_input,
+    upap_protrej,
+    upap_lowerup,
+    upap_lowerdown,
+    NULL,
+    NULL,
+#if PRINTPKT_SUPPORT
+    upap_printpkt,
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_DATAINPUT
+    NULL,
+#endif /* PPP_DATAINPUT */
+#if PRINTPKT_SUPPORT
+    "PAP",
+    NULL,
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_OPTIONS
+    pap_option_list,
+    NULL,
+#endif /* PPP_OPTIONS */
+#if DEMAND_SUPPORT
+    NULL,
+    NULL
+#endif /* DEMAND_SUPPORT */
+};
+
+static void upap_timeout(void *arg);
+#if PPP_SERVER
+static void upap_reqtimeout(void *arg);
+static void upap_rauthreq(ppp_pcb *pcb, u_char *inp, int id, int len);
+#endif /* PPP_SERVER */
+static void upap_rauthack(ppp_pcb *pcb, u_char *inp, int id, int len);
+static void upap_rauthnak(ppp_pcb *pcb, u_char *inp, int id, int len);
+static void upap_sauthreq(ppp_pcb *pcb);
+#if PPP_SERVER
+static void upap_sresp(ppp_pcb *pcb, u_char code, u_char id, const char *msg, int msglen);
+#endif /* PPP_SERVER */
+
+
+/*
+ * upap_init - Initialize a UPAP unit.
+ */
+static void upap_init(ppp_pcb *pcb) {
+    pcb->upap.us_user = NULL;
+    pcb->upap.us_userlen = 0;
+    pcb->upap.us_passwd = NULL;
+    pcb->upap.us_passwdlen = 0;
+    pcb->upap.us_clientstate = UPAPCS_INITIAL;
+#if PPP_SERVER
+    pcb->upap.us_serverstate = UPAPSS_INITIAL;
+#endif /* PPP_SERVER */
+    pcb->upap.us_id = 0;
+}
+
+
+/*
+ * upap_authwithpeer - Authenticate us with our peer (start client).
+ *
+ * Set new state and send authenticate's.
+ */
+void upap_authwithpeer(ppp_pcb *pcb, const char *user, const char *password) {
+
+    if(!user || !password)
+        return;
+
+    /* Save the username and password we're given */
+    pcb->upap.us_user = user;
+    pcb->upap.us_userlen = (u8_t)LWIP_MIN(strlen(user), 0xff);
+    pcb->upap.us_passwd = password;
+    pcb->upap.us_passwdlen = (u8_t)LWIP_MIN(strlen(password), 0xff);
+    pcb->upap.us_transmits = 0;
+
+    /* Lower layer up yet? */
+    if (pcb->upap.us_clientstate == UPAPCS_INITIAL ||
+       pcb->upap.us_clientstate == UPAPCS_PENDING) {
+       pcb->upap.us_clientstate = UPAPCS_PENDING;
+       return;
+    }
+
+    upap_sauthreq(pcb);                /* Start protocol */
+}
+
+#if PPP_SERVER
+/*
+ * upap_authpeer - Authenticate our peer (start server).
+ *
+ * Set new state.
+ */
+void upap_authpeer(ppp_pcb *pcb) {
+
+    /* Lower layer up yet? */
+    if (pcb->upap.us_serverstate == UPAPSS_INITIAL ||
+       pcb->upap.us_serverstate == UPAPSS_PENDING) {
+       pcb->upap.us_serverstate = UPAPSS_PENDING;
+       return;
+    }
+
+    pcb->upap.us_serverstate = UPAPSS_LISTEN;
+    if (pcb->settings.pap_req_timeout > 0)
+       TIMEOUT(upap_reqtimeout, pcb, pcb->settings.pap_req_timeout);
+}
+#endif /* PPP_SERVER */
+
+/*
+ * upap_timeout - Retransmission timer for sending auth-reqs expired.
+ */
+static void upap_timeout(void *arg) {
+    ppp_pcb *pcb = (ppp_pcb*)arg;
+
+    if (pcb->upap.us_clientstate != UPAPCS_AUTHREQ)
+       return;
+
+    if (pcb->upap.us_transmits >= pcb->settings.pap_max_transmits) {
+       /* give up in disgust */
+       ppp_error("No response to PAP authenticate-requests");
+       pcb->upap.us_clientstate = UPAPCS_BADAUTH;
+       auth_withpeer_fail(pcb, PPP_PAP);
+       return;
+    }
+
+    upap_sauthreq(pcb);                /* Send Authenticate-Request */
+}
+
+
+#if PPP_SERVER
+/*
+ * upap_reqtimeout - Give up waiting for the peer to send an auth-req.
+ */
+static void upap_reqtimeout(void *arg) {
+    ppp_pcb *pcb = (ppp_pcb*)arg;
+
+    if (pcb->upap.us_serverstate != UPAPSS_LISTEN)
+       return;                 /* huh?? */
+
+    auth_peer_fail(pcb, PPP_PAP);
+    pcb->upap.us_serverstate = UPAPSS_BADAUTH;
+}
+#endif /* PPP_SERVER */
+
+
+/*
+ * upap_lowerup - The lower layer is up.
+ *
+ * Start authenticating if pending.
+ */
+static void upap_lowerup(ppp_pcb *pcb) {
+
+    if (pcb->upap.us_clientstate == UPAPCS_INITIAL)
+       pcb->upap.us_clientstate = UPAPCS_CLOSED;
+    else if (pcb->upap.us_clientstate == UPAPCS_PENDING) {
+       upap_sauthreq(pcb);     /* send an auth-request */
+    }
+
+#if PPP_SERVER
+    if (pcb->upap.us_serverstate == UPAPSS_INITIAL)
+       pcb->upap.us_serverstate = UPAPSS_CLOSED;
+    else if (pcb->upap.us_serverstate == UPAPSS_PENDING) {
+       pcb->upap.us_serverstate = UPAPSS_LISTEN;
+       if (pcb->settings.pap_req_timeout > 0)
+           TIMEOUT(upap_reqtimeout, pcb, pcb->settings.pap_req_timeout);
+    }
+#endif /* PPP_SERVER */
+}
+
+
+/*
+ * upap_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts.
+ */
+static void upap_lowerdown(ppp_pcb *pcb) {
+
+    if (pcb->upap.us_clientstate == UPAPCS_AUTHREQ)    /* Timeout pending? */
+       UNTIMEOUT(upap_timeout, pcb);           /* Cancel timeout */
+#if PPP_SERVER
+    if (pcb->upap.us_serverstate == UPAPSS_LISTEN && pcb->settings.pap_req_timeout > 0)
+       UNTIMEOUT(upap_reqtimeout, pcb);
+#endif /* PPP_SERVER */
+
+    pcb->upap.us_clientstate = UPAPCS_INITIAL;
+#if PPP_SERVER
+    pcb->upap.us_serverstate = UPAPSS_INITIAL;
+#endif /* PPP_SERVER */
+}
+
+
+/*
+ * upap_protrej - Peer doesn't speak this protocol.
+ *
+ * This shouldn't happen.  In any case, pretend lower layer went down.
+ */
+static void upap_protrej(ppp_pcb *pcb) {
+
+    if (pcb->upap.us_clientstate == UPAPCS_AUTHREQ) {
+       ppp_error("PAP authentication failed due to protocol-reject");
+       auth_withpeer_fail(pcb, PPP_PAP);
+    }
+#if PPP_SERVER
+    if (pcb->upap.us_serverstate == UPAPSS_LISTEN) {
+       ppp_error("PAP authentication of peer failed (protocol-reject)");
+       auth_peer_fail(pcb, PPP_PAP);
+    }
+#endif /* PPP_SERVER */
+    upap_lowerdown(pcb);
+}
+
+
+/*
+ * upap_input - Input UPAP packet.
+ */
+static void upap_input(ppp_pcb *pcb, u_char *inpacket, int l) {
+    u_char *inp;
+    u_char code, id;
+    int len;
+
+    /*
+     * Parse header (code, id and length).
+     * If packet too short, drop it.
+     */
+    inp = inpacket;
+    if (l < UPAP_HEADERLEN) {
+       UPAPDEBUG(("pap_input: rcvd short header."));
+       return;
+    }
+    GETCHAR(code, inp);
+    GETCHAR(id, inp);
+    GETSHORT(len, inp);
+    if (len < UPAP_HEADERLEN) {
+       UPAPDEBUG(("pap_input: rcvd illegal length."));
+       return;
+    }
+    if (len > l) {
+       UPAPDEBUG(("pap_input: rcvd short packet."));
+       return;
+    }
+    len -= UPAP_HEADERLEN;
+
+    /*
+     * Action depends on code.
+     */
+    switch (code) {
+    case UPAP_AUTHREQ:
+#if PPP_SERVER
+       upap_rauthreq(pcb, inp, id, len);
+#endif /* PPP_SERVER */
+       break;
+
+    case UPAP_AUTHACK:
+       upap_rauthack(pcb, inp, id, len);
+       break;
+
+    case UPAP_AUTHNAK:
+       upap_rauthnak(pcb, inp, id, len);
+       break;
+
+    default:                           /* XXX Need code reject */
+       break;
+    }
+}
+
+#if PPP_SERVER
+/*
+ * upap_rauth - Receive Authenticate.
+ */
+static void upap_rauthreq(ppp_pcb *pcb, u_char *inp, int id, int len) {
+    u_char ruserlen, rpasswdlen;
+    char *ruser;
+    char *rpasswd;
+    char rhostname[256];
+    int retcode;
+    const char *msg;
+    int msglen;
+
+    if (pcb->upap.us_serverstate < UPAPSS_LISTEN)
+       return;
+
+    /*
+     * If we receive a duplicate authenticate-request, we are
+     * supposed to return the same status as for the first request.
+     */
+    if (pcb->upap.us_serverstate == UPAPSS_OPEN) {
+       upap_sresp(pcb, UPAP_AUTHACK, id, "", 0);       /* return auth-ack */
+       return;
+    }
+    if (pcb->upap.us_serverstate == UPAPSS_BADAUTH) {
+       upap_sresp(pcb, UPAP_AUTHNAK, id, "", 0);       /* return auth-nak */
+       return;
+    }
+
+    /*
+     * Parse user/passwd.
+     */
+    if (len < 1) {
+       UPAPDEBUG(("pap_rauth: rcvd short packet."));
+       return;
+    }
+    GETCHAR(ruserlen, inp);
+    len -= sizeof (u_char) + ruserlen + sizeof (u_char);
+    if (len < 0) {
+       UPAPDEBUG(("pap_rauth: rcvd short packet."));
+       return;
+    }
+    ruser = (char *) inp;
+    INCPTR(ruserlen, inp);
+    GETCHAR(rpasswdlen, inp);
+    if (len < rpasswdlen) {
+       UPAPDEBUG(("pap_rauth: rcvd short packet."));
+       return;
+    }
+
+    rpasswd = (char *) inp;
+
+    /*
+     * Check the username and password given.
+     */
+    retcode = UPAP_AUTHNAK;
+    if (auth_check_passwd(pcb, ruser, ruserlen, rpasswd, rpasswdlen, &msg, &msglen)) {
+      retcode = UPAP_AUTHACK;
+    }
+    BZERO(rpasswd, rpasswdlen);
+
+#if 0 /* UNUSED */
+    /*
+     * Check remote number authorization.  A plugin may have filled in
+     * the remote number or added an allowed number, and rather than
+     * return an authenticate failure, is leaving it for us to verify.
+     */
+    if (retcode == UPAP_AUTHACK) {
+       if (!auth_number()) {
+           /* We do not want to leak info about the pap result. */
+           retcode = UPAP_AUTHNAK; /* XXX exit value will be "wrong" */
+           warn("calling number %q is not authorized", remote_number);
+       }
+    }
+
+    msglen = strlen(msg);
+    if (msglen > 255)
+       msglen = 255;
+#endif /* UNUSED */
+
+    upap_sresp(pcb, retcode, id, msg, msglen);
+
+    /* Null terminate and clean remote name. */
+    ppp_slprintf(rhostname, sizeof(rhostname), "%.*v", ruserlen, ruser);
+
+    if (retcode == UPAP_AUTHACK) {
+       pcb->upap.us_serverstate = UPAPSS_OPEN;
+       ppp_notice("PAP peer authentication succeeded for %q", rhostname);
+       auth_peer_success(pcb, PPP_PAP, 0, ruser, ruserlen);
+    } else {
+       pcb->upap.us_serverstate = UPAPSS_BADAUTH;
+       ppp_warn("PAP peer authentication failed for %q", rhostname);
+       auth_peer_fail(pcb, PPP_PAP);
+    }
+
+    if (pcb->settings.pap_req_timeout > 0)
+       UNTIMEOUT(upap_reqtimeout, pcb);
+}
+#endif /* PPP_SERVER */
+
+/*
+ * upap_rauthack - Receive Authenticate-Ack.
+ */
+static void upap_rauthack(ppp_pcb *pcb, u_char *inp, int id, int len) {
+    u_char msglen;
+    char *msg;
+    LWIP_UNUSED_ARG(id);
+
+    if (pcb->upap.us_clientstate != UPAPCS_AUTHREQ) /* XXX */
+       return;
+
+    /*
+     * Parse message.
+     */
+    if (len < 1) {
+       UPAPDEBUG(("pap_rauthack: ignoring missing msg-length."));
+    } else {
+       GETCHAR(msglen, inp);
+       if (msglen > 0) {
+           len -= sizeof (u_char);
+           if (len < msglen) {
+               UPAPDEBUG(("pap_rauthack: rcvd short packet."));
+               return;
+           }
+           msg = (char *) inp;
+           PRINTMSG(msg, msglen);
+       }
+    }
+
+    pcb->upap.us_clientstate = UPAPCS_OPEN;
+
+    auth_withpeer_success(pcb, PPP_PAP, 0);
+}
+
+
+/*
+ * upap_rauthnak - Receive Authenticate-Nak.
+ */
+static void upap_rauthnak(ppp_pcb *pcb, u_char *inp, int id, int len) {
+    u_char msglen;
+    char *msg;
+    LWIP_UNUSED_ARG(id);
+
+    if (pcb->upap.us_clientstate != UPAPCS_AUTHREQ) /* XXX */
+       return;
+
+    /*
+     * Parse message.
+     */
+    if (len < 1) {
+       UPAPDEBUG(("pap_rauthnak: ignoring missing msg-length."));
+    } else {
+       GETCHAR(msglen, inp);
+       if (msglen > 0) {
+           len -= sizeof (u_char);
+           if (len < msglen) {
+               UPAPDEBUG(("pap_rauthnak: rcvd short packet."));
+               return;
+           }
+           msg = (char *) inp;
+           PRINTMSG(msg, msglen);
+       }
+    }
+
+    pcb->upap.us_clientstate = UPAPCS_BADAUTH;
+
+    ppp_error("PAP authentication failed");
+    auth_withpeer_fail(pcb, PPP_PAP);
+}
+
+
+/*
+ * upap_sauthreq - Send an Authenticate-Request.
+ */
+static void upap_sauthreq(ppp_pcb *pcb) {
+    struct pbuf *p;
+    u_char *outp;
+    int outlen;
+
+    outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) +
+       pcb->upap.us_userlen + pcb->upap.us_passwdlen;
+    p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN +outlen), PPP_CTRL_PBUF_TYPE);
+    if(NULL == p)
+        return;
+    if(p->tot_len != p->len) {
+        pbuf_free(p);
+        return;
+    }
+
+    outp = (u_char*)p->payload;
+    MAKEHEADER(outp, PPP_PAP);
+
+    PUTCHAR(UPAP_AUTHREQ, outp);
+    PUTCHAR(++pcb->upap.us_id, outp);
+    PUTSHORT(outlen, outp);
+    PUTCHAR(pcb->upap.us_userlen, outp);
+    MEMCPY(outp, pcb->upap.us_user, pcb->upap.us_userlen);
+    INCPTR(pcb->upap.us_userlen, outp);
+    PUTCHAR(pcb->upap.us_passwdlen, outp);
+    MEMCPY(outp, pcb->upap.us_passwd, pcb->upap.us_passwdlen);
+
+    ppp_write(pcb, p);
+
+    TIMEOUT(upap_timeout, pcb, pcb->settings.pap_timeout_time);
+    ++pcb->upap.us_transmits;
+    pcb->upap.us_clientstate = UPAPCS_AUTHREQ;
+}
+
+#if PPP_SERVER
+/*
+ * upap_sresp - Send a response (ack or nak).
+ */
+static void upap_sresp(ppp_pcb *pcb, u_char code, u_char id, const char *msg, int msglen) {
+    struct pbuf *p;
+    u_char *outp;
+    int outlen;
+
+    outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen;
+    p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN +outlen), PPP_CTRL_PBUF_TYPE);
+    if(NULL == p)
+        return;
+    if(p->tot_len != p->len) {
+        pbuf_free(p);
+        return;
+    }
+
+    outp = (u_char*)p->payload;
+    MAKEHEADER(outp, PPP_PAP);
+
+    PUTCHAR(code, outp);
+    PUTCHAR(id, outp);
+    PUTSHORT(outlen, outp);
+    PUTCHAR(msglen, outp);
+    MEMCPY(outp, msg, msglen);
+
+    ppp_write(pcb, p);
+}
+#endif /* PPP_SERVER */
+
+#if PRINTPKT_SUPPORT
+/*
+ * upap_printpkt - print the contents of a PAP packet.
+ */
+static const char* const upap_codenames[] = {
+    "AuthReq", "AuthAck", "AuthNak"
+};
+
+static int upap_printpkt(const u_char *p, int plen, void (*printer) (void *, const char *, ...), void *arg) {
+    int code, id, len;
+    int mlen, ulen, wlen;
+    const u_char *user, *pwd, *msg;
+    const u_char *pstart;
+
+    if (plen < UPAP_HEADERLEN)
+       return 0;
+    pstart = p;
+    GETCHAR(code, p);
+    GETCHAR(id, p);
+    GETSHORT(len, p);
+    if (len < UPAP_HEADERLEN || len > plen)
+       return 0;
+
+    if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(upap_codenames))
+       printer(arg, " %s", upap_codenames[code-1]);
+    else
+       printer(arg, " code=0x%x", code);
+    printer(arg, " id=0x%x", id);
+    len -= UPAP_HEADERLEN;
+    switch (code) {
+    case UPAP_AUTHREQ:
+       if (len < 1)
+           break;
+       ulen = p[0];
+       if (len < ulen + 2)
+           break;
+       wlen = p[ulen + 1];
+       if (len < ulen + wlen + 2)
+           break;
+       user = (const u_char *) (p + 1);
+       pwd = (const u_char *) (p + ulen + 2);
+       p += ulen + wlen + 2;
+       len -= ulen + wlen + 2;
+       printer(arg, " user=");
+       ppp_print_string(user, ulen, printer, arg);
+       printer(arg, " password=");
+/* FIXME: require ppp_pcb struct as printpkt() argument */
+#if 0
+       if (!pcb->settings.hide_password)
+#endif
+           ppp_print_string(pwd, wlen, printer, arg);
+#if 0
+       else
+           printer(arg, "<hidden>");
+#endif
+       break;
+    case UPAP_AUTHACK:
+    case UPAP_AUTHNAK:
+       if (len < 1)
+           break;
+       mlen = p[0];
+       if (len < mlen + 1)
+           break;
+       msg = (const u_char *) (p + 1);
+       p += mlen + 1;
+       len -= mlen + 1;
+       printer(arg, " ");
+       ppp_print_string(msg, mlen, printer, arg);
+       break;
+    default:
+       break;
+    }
+
+    /* print the rest of the bytes in the packet */
+    for (; len > 0; --len) {
+       GETCHAR(code, p);
+       printer(arg, " %.2x", code);
+    }
+
+    return p - pstart;
+}
+#endif /* PRINTPKT_SUPPORT */
+
+#endif /* PPP_SUPPORT && PAP_SUPPORT */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/utils.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/utils.c
new file mode 100644 (file)
index 0000000..f1366da
--- /dev/null
@@ -0,0 +1,957 @@
+/*
+ * utils.c - various utility functions used in pppd.
+ *
+ * Copyright (c) 1999-2002 Paul Mackerras. All rights reserved.
+ *
+ * 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. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#if 0 /* UNUSED */
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <time.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#ifdef SVR4
+#include <sys/mkdev.h>
+#endif
+#endif /* UNUSED */
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/fsm.h"
+#include "netif/ppp/lcp.h"
+
+#if defined(SUNOS4)
+extern char *strerror();
+#endif
+
+static void ppp_logit(int level, const char *fmt, va_list args);
+static void ppp_log_write(int level, char *buf);
+#if PRINTPKT_SUPPORT
+static void ppp_vslp_printer(void *arg, const char *fmt, ...);
+static void ppp_format_packet(const u_char *p, int len,
+               void (*printer) (void *, const char *, ...), void *arg);
+
+struct buffer_info {
+    char *ptr;
+    int len;
+};
+#endif /* PRINTPKT_SUPPORT */
+
+/*
+ * ppp_strlcpy - like strcpy/strncpy, doesn't overflow destination buffer,
+ * always leaves destination null-terminated (for len > 0).
+ */
+size_t ppp_strlcpy(char *dest, const char *src, size_t len) {
+    size_t ret = strlen(src);
+
+    if (len != 0) {
+       if (ret < len)
+           strcpy(dest, src);
+       else {
+           strncpy(dest, src, len - 1);
+           dest[len-1] = 0;
+       }
+    }
+    return ret;
+}
+
+/*
+ * ppp_strlcat - like strcat/strncat, doesn't overflow destination buffer,
+ * always leaves destination null-terminated (for len > 0).
+ */
+size_t ppp_strlcat(char *dest, const char *src, size_t len) {
+    size_t dlen = strlen(dest);
+
+    return dlen + ppp_strlcpy(dest + dlen, src, (len > dlen? len - dlen: 0));
+}
+
+
+/*
+ * ppp_slprintf - format a message into a buffer.  Like sprintf except we
+ * also specify the length of the output buffer, and we handle
+ * %m (error message), %v (visible string),
+ * %q (quoted string), %t (current time) and %I (IP address) formats.
+ * Doesn't do floating-point formats.
+ * Returns the number of chars put into buf.
+ */
+int ppp_slprintf(char *buf, int buflen, const char *fmt, ...) {
+    va_list args;
+    int n;
+
+    va_start(args, fmt);
+    n = ppp_vslprintf(buf, buflen, fmt, args);
+    va_end(args);
+    return n;
+}
+
+/*
+ * ppp_vslprintf - like ppp_slprintf, takes a va_list instead of a list of args.
+ */
+#define OUTCHAR(c)     (buflen > 0? (--buflen, *buf++ = (c)): 0)
+
+int ppp_vslprintf(char *buf, int buflen, const char *fmt, va_list args) {
+    int c, i, n;
+    int width, prec, fillch;
+    int base, len, neg, quoted;
+    unsigned long val = 0;
+    const char *f;
+    char *str, *buf0;
+    const unsigned char *p;
+    char num[32];
+#if 0 /* need port */
+    time_t t;
+#endif /* need port */
+    u32_t ip;
+    static char hexchars[] = "0123456789abcdef";
+#if PRINTPKT_SUPPORT
+    struct buffer_info bufinfo;
+#endif /* PRINTPKT_SUPPORT */
+
+    buf0 = buf;
+    --buflen;
+    while (buflen > 0) {
+       for (f = fmt; *f != '%' && *f != 0; ++f)
+           ;
+       if (f > fmt) {
+           len = f - fmt;
+           if (len > buflen)
+               len = buflen;
+           memcpy(buf, fmt, len);
+           buf += len;
+           buflen -= len;
+           fmt = f;
+       }
+       if (*fmt == 0)
+           break;
+       c = *++fmt;
+       width = 0;
+       prec = -1;
+       fillch = ' ';
+       if (c == '0') {
+           fillch = '0';
+           c = *++fmt;
+       }
+       if (c == '*') {
+           width = va_arg(args, int);
+           c = *++fmt;
+       } else {
+           while (lwip_isdigit(c)) {
+               width = width * 10 + c - '0';
+               c = *++fmt;
+           }
+       }
+       if (c == '.') {
+           c = *++fmt;
+           if (c == '*') {
+               prec = va_arg(args, int);
+               c = *++fmt;
+           } else {
+               prec = 0;
+               while (lwip_isdigit(c)) {
+                   prec = prec * 10 + c - '0';
+                   c = *++fmt;
+               }
+           }
+       }
+       str = 0;
+       base = 0;
+       neg = 0;
+       ++fmt;
+       switch (c) {
+       case 'l':
+           c = *fmt++;
+           switch (c) {
+           case 'd':
+               val = va_arg(args, long);
+               if ((long)val < 0) {
+                   neg = 1;
+                   val = (unsigned long)-(long)val;
+               }
+               base = 10;
+               break;
+           case 'u':
+               val = va_arg(args, unsigned long);
+               base = 10;
+               break;
+           default:
+               OUTCHAR('%');
+               OUTCHAR('l');
+               --fmt;          /* so %lz outputs %lz etc. */
+               continue;
+           }
+           break;
+       case 'd':
+           i = va_arg(args, int);
+           if (i < 0) {
+               neg = 1;
+               val = -i;
+           } else
+               val = i;
+           base = 10;
+           break;
+       case 'u':
+           val = va_arg(args, unsigned int);
+           base = 10;
+           break;
+       case 'o':
+           val = va_arg(args, unsigned int);
+           base = 8;
+           break;
+       case 'x':
+       case 'X':
+           val = va_arg(args, unsigned int);
+           base = 16;
+           break;
+#if 0 /* unused (and wrong on LLP64 systems) */
+       case 'p':
+           val = (unsigned long) va_arg(args, void *);
+           base = 16;
+           neg = 2;
+           break;
+#endif /* unused (and wrong on LLP64 systems) */
+       case 's':
+           str = va_arg(args, char *);
+           break;
+       case 'c':
+           num[0] = va_arg(args, int);
+           num[1] = 0;
+           str = num;
+           break;
+#if 0 /* do we always have strerror() in embedded ? */
+       case 'm':
+           str = strerror(errno);
+           break;
+#endif /* do we always have strerror() in embedded ? */
+       case 'I':
+           ip = va_arg(args, u32_t);
+           ip = lwip_ntohl(ip);
+           ppp_slprintf(num, sizeof(num), "%d.%d.%d.%d", (ip >> 24) & 0xff,
+                    (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
+           str = num;
+           break;
+#if 0 /* need port */
+       case 't':
+           time(&t);
+           str = ctime(&t);
+           str += 4;           /* chop off the day name */
+           str[15] = 0;        /* chop off year and newline */
+           break;
+#endif /* need port */
+       case 'v':               /* "visible" string */
+       case 'q':               /* quoted string */
+           quoted = c == 'q';
+           p = va_arg(args, unsigned char *);
+           if (p == NULL)
+               p = (const unsigned char *)"<NULL>";
+           if (fillch == '0' && prec >= 0) {
+               n = prec;
+           } else {
+               n = strlen((const char *)p);
+               if (prec >= 0 && n > prec)
+                   n = prec;
+           }
+           while (n > 0 && buflen > 0) {
+               c = *p++;
+               --n;
+               if (!quoted && c >= 0x80) {
+                   OUTCHAR('M');
+                   OUTCHAR('-');
+                   c -= 0x80;
+               }
+               if (quoted && (c == '"' || c == '\\'))
+                   OUTCHAR('\\');
+               if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
+                   if (quoted) {
+                       OUTCHAR('\\');
+                       switch (c) {
+                       case '\t':      OUTCHAR('t');   break;
+                       case '\n':      OUTCHAR('n');   break;
+                       case '\b':      OUTCHAR('b');   break;
+                       case '\f':      OUTCHAR('f');   break;
+                       default:
+                           OUTCHAR('x');
+                           OUTCHAR(hexchars[c >> 4]);
+                           OUTCHAR(hexchars[c & 0xf]);
+                       }
+                   } else {
+                       if (c == '\t')
+                           OUTCHAR(c);
+                       else {
+                           OUTCHAR('^');
+                           OUTCHAR(c ^ 0x40);
+                       }
+                   }
+               } else
+                   OUTCHAR(c);
+           }
+           continue;
+#if PRINTPKT_SUPPORT
+       case 'P':               /* print PPP packet */
+           bufinfo.ptr = buf;
+           bufinfo.len = buflen + 1;
+           p = va_arg(args, unsigned char *);
+           n = va_arg(args, int);
+           ppp_format_packet(p, n, ppp_vslp_printer, &bufinfo);
+           buf = bufinfo.ptr;
+           buflen = bufinfo.len - 1;
+           continue;
+#endif /* PRINTPKT_SUPPORT */
+       case 'B':
+           p = va_arg(args, unsigned char *);
+           for (n = prec; n > 0; --n) {
+               c = *p++;
+               if (fillch == ' ')
+                   OUTCHAR(' ');
+               OUTCHAR(hexchars[(c >> 4) & 0xf]);
+               OUTCHAR(hexchars[c & 0xf]);
+           }
+           continue;
+       default:
+           *buf++ = '%';
+           if (c != '%')
+               --fmt;          /* so %z outputs %z etc. */
+           --buflen;
+           continue;
+       }
+       if (base != 0) {
+           str = num + sizeof(num);
+           *--str = 0;
+           while (str > num + neg) {
+               *--str = hexchars[val % base];
+               val = val / base;
+               if (--prec <= 0 && val == 0)
+                   break;
+           }
+           switch (neg) {
+           case 1:
+               *--str = '-';
+               break;
+           case 2:
+               *--str = 'x';
+               *--str = '0';
+               break;
+           default:
+               break;
+           }
+           len = num + sizeof(num) - 1 - str;
+       } else {
+           len = strlen(str);
+           if (prec >= 0 && len > prec)
+               len = prec;
+       }
+       if (width > 0) {
+           if (width > buflen)
+               width = buflen;
+           if ((n = width - len) > 0) {
+               buflen -= n;
+               for (; n > 0; --n)
+                   *buf++ = fillch;
+           }
+       }
+       if (len > buflen)
+           len = buflen;
+       memcpy(buf, str, len);
+       buf += len;
+       buflen -= len;
+    }
+    *buf = 0;
+    return buf - buf0;
+}
+
+#if PRINTPKT_SUPPORT
+/*
+ * vslp_printer - used in processing a %P format
+ */
+static void ppp_vslp_printer(void *arg, const char *fmt, ...) {
+    int n;
+    va_list pvar;
+    struct buffer_info *bi;
+
+    va_start(pvar, fmt);
+    bi = (struct buffer_info *) arg;
+    n = ppp_vslprintf(bi->ptr, bi->len, fmt, pvar);
+    va_end(pvar);
+
+    bi->ptr += n;
+    bi->len -= n;
+}
+#endif /* PRINTPKT_SUPPORT */
+
+#if 0 /* UNUSED */
+/*
+ * log_packet - format a packet and log it.
+ */
+
+void
+log_packet(p, len, prefix, level)
+    u_char *p;
+    int len;
+    char *prefix;
+    int level;
+{
+       init_pr_log(prefix, level);
+       ppp_format_packet(p, len, pr_log, &level);
+       end_pr_log();
+}
+#endif /* UNUSED */
+
+#if PRINTPKT_SUPPORT
+/*
+ * ppp_format_packet - make a readable representation of a packet,
+ * calling `printer(arg, format, ...)' to output it.
+ */
+static void ppp_format_packet(const u_char *p, int len,
+               void (*printer) (void *, const char *, ...), void *arg) {
+    int i, n;
+    u_short proto;
+    const struct protent *protp;
+
+    if (len >= 2) {
+       GETSHORT(proto, p);
+       len -= 2;
+       for (i = 0; (protp = protocols[i]) != NULL; ++i)
+           if (proto == protp->protocol)
+               break;
+       if (protp != NULL) {
+           printer(arg, "[%s", protp->name);
+           n = (*protp->printpkt)(p, len, printer, arg);
+           printer(arg, "]");
+           p += n;
+           len -= n;
+       } else {
+           for (i = 0; (protp = protocols[i]) != NULL; ++i)
+               if (proto == (protp->protocol & ~0x8000))
+                   break;
+           if (protp != 0 && protp->data_name != 0) {
+               printer(arg, "[%s data]", protp->data_name);
+               if (len > 8)
+                   printer(arg, "%.8B ...", p);
+               else
+                   printer(arg, "%.*B", len, p);
+               len = 0;
+           } else
+               printer(arg, "[proto=0x%x]", proto);
+       }
+    }
+
+    if (len > 32)
+       printer(arg, "%.32B ...", p);
+    else
+       printer(arg, "%.*B", len, p);
+}
+#endif /* PRINTPKT_SUPPORT */
+
+#if 0 /* UNUSED */
+/*
+ * init_pr_log, end_pr_log - initialize and finish use of pr_log.
+ */
+
+static char line[256];         /* line to be logged accumulated here */
+static char *linep;            /* current pointer within line */
+static int llevel;             /* level for logging */
+
+void
+init_pr_log(prefix, level)
+     const char *prefix;
+     int level;
+{
+       linep = line;
+       if (prefix != NULL) {
+               ppp_strlcpy(line, prefix, sizeof(line));
+               linep = line + strlen(line);
+       }
+       llevel = level;
+}
+
+void
+end_pr_log()
+{
+       if (linep != line) {
+               *linep = 0;
+               ppp_log_write(llevel, line);
+       }
+}
+
+/*
+ * pr_log - printer routine for outputting to log
+ */
+void
+pr_log (void *arg, const char *fmt, ...)
+{
+       int l, n;
+       va_list pvar;
+       char *p, *eol;
+       char buf[256];
+
+       va_start(pvar, fmt);
+       n = ppp_vslprintf(buf, sizeof(buf), fmt, pvar);
+       va_end(pvar);
+
+       p = buf;
+       eol = strchr(buf, '\n');
+       if (linep != line) {
+               l = (eol == NULL)? n: eol - buf;
+               if (linep + l < line + sizeof(line)) {
+                       if (l > 0) {
+                               memcpy(linep, buf, l);
+                               linep += l;
+                       }
+                       if (eol == NULL)
+                               return;
+                       p = eol + 1;
+                       eol = strchr(p, '\n');
+               }
+               *linep = 0;
+               ppp_log_write(llevel, line);
+               linep = line;
+       }
+
+       while (eol != NULL) {
+               *eol = 0;
+               ppp_log_write(llevel, p);
+               p = eol + 1;
+               eol = strchr(p, '\n');
+       }
+
+       /* assumes sizeof(buf) <= sizeof(line) */
+       l = buf + n - p;
+       if (l > 0) {
+               memcpy(line, p, n);
+               linep = line + l;
+       }
+}
+#endif /* UNUSED */
+
+/*
+ * ppp_print_string - print a readable representation of a string using
+ * printer.
+ */
+void ppp_print_string(const u_char *p, int len, void (*printer) (void *, const char *, ...), void *arg) {
+    int c;
+
+    printer(arg, "\"");
+    for (; len > 0; --len) {
+       c = *p++;
+       if (' ' <= c && c <= '~') {
+           if (c == '\\' || c == '"')
+               printer(arg, "\\");
+           printer(arg, "%c", c);
+       } else {
+           switch (c) {
+           case '\n':
+               printer(arg, "\\n");
+               break;
+           case '\r':
+               printer(arg, "\\r");
+               break;
+           case '\t':
+               printer(arg, "\\t");
+               break;
+           default:
+               printer(arg, "\\%.3o", (u8_t)c);
+               /* no break */
+           }
+       }
+    }
+    printer(arg, "\"");
+}
+
+/*
+ * ppp_logit - does the hard work for fatal et al.
+ */
+static void ppp_logit(int level, const char *fmt, va_list args) {
+    char buf[1024];
+
+    ppp_vslprintf(buf, sizeof(buf), fmt, args);
+    ppp_log_write(level, buf);
+}
+
+static void ppp_log_write(int level, char *buf) {
+    LWIP_UNUSED_ARG(level); /* necessary if PPPDEBUG is defined to an empty function */
+    LWIP_UNUSED_ARG(buf);
+    PPPDEBUG(level, ("%s\n", buf) );
+#if 0
+    if (log_to_fd >= 0 && (level != LOG_DEBUG || debug)) {
+       int n = strlen(buf);
+
+       if (n > 0 && buf[n-1] == '\n')
+           --n;
+       if (write(log_to_fd, buf, n) != n
+           || write(log_to_fd, "\n", 1) != 1)
+           log_to_fd = -1;
+    }
+#endif
+}
+
+/*
+ * ppp_fatal - log an error message and die horribly.
+ */
+void ppp_fatal(const char *fmt, ...) {
+    va_list pvar;
+
+    va_start(pvar, fmt);
+    ppp_logit(LOG_ERR, fmt, pvar);
+    va_end(pvar);
+
+    LWIP_ASSERT("ppp_fatal", 0);   /* as promised */
+}
+
+/*
+ * ppp_error - log an error message.
+ */
+void ppp_error(const char *fmt, ...) {
+    va_list pvar;
+
+    va_start(pvar, fmt);
+    ppp_logit(LOG_ERR, fmt, pvar);
+    va_end(pvar);
+#if 0 /* UNUSED */
+    ++error_count;
+#endif /* UNUSED */
+}
+
+/*
+ * ppp_warn - log a warning message.
+ */
+void ppp_warn(const char *fmt, ...) {
+    va_list pvar;
+
+    va_start(pvar, fmt);
+    ppp_logit(LOG_WARNING, fmt, pvar);
+    va_end(pvar);
+}
+
+/*
+ * ppp_notice - log a notice-level message.
+ */
+void ppp_notice(const char *fmt, ...) {
+    va_list pvar;
+
+    va_start(pvar, fmt);
+    ppp_logit(LOG_NOTICE, fmt, pvar);
+    va_end(pvar);
+}
+
+/*
+ * ppp_info - log an informational message.
+ */
+void ppp_info(const char *fmt, ...) {
+    va_list pvar;
+
+    va_start(pvar, fmt);
+    ppp_logit(LOG_INFO, fmt, pvar);
+    va_end(pvar);
+}
+
+/*
+ * ppp_dbglog - log a debug message.
+ */
+void ppp_dbglog(const char *fmt, ...) {
+    va_list pvar;
+
+    va_start(pvar, fmt);
+    ppp_logit(LOG_DEBUG, fmt, pvar);
+    va_end(pvar);
+}
+
+#if PRINTPKT_SUPPORT
+/*
+ * ppp_dump_packet - print out a packet in readable form if it is interesting.
+ * Assumes len >= PPP_HDRLEN.
+ */
+void ppp_dump_packet(ppp_pcb *pcb, const char *tag, unsigned char *p, int len) {
+    int proto;
+
+    /*
+     * don't print data packets, i.e. IPv4, IPv6, VJ, and compressed packets.
+     */
+    proto = (p[0] << 8) + p[1];
+    if (proto < 0xC000 && (proto & ~0x8000) == proto)
+       return;
+
+    /*
+     * don't print valid LCP echo request/reply packets if the link is up.
+     */
+    if (proto == PPP_LCP && pcb->phase == PPP_PHASE_RUNNING && len >= 2 + HEADERLEN) {
+       unsigned char *lcp = p + 2;
+       int l = (lcp[2] << 8) + lcp[3];
+
+       if ((lcp[0] == ECHOREQ || lcp[0] == ECHOREP)
+           && l >= HEADERLEN && l <= len - 2)
+           return;
+    }
+
+    ppp_dbglog("%s %P", tag, p, len);
+}
+#endif /* PRINTPKT_SUPPORT */
+
+#if 0 /* Unused */
+
+/*
+ * complete_read - read a full `count' bytes from fd,
+ * unless end-of-file or an error other than EINTR is encountered.
+ */
+ssize_t
+complete_read(int fd, void *buf, size_t count)
+{
+       size_t done;
+       ssize_t nb;
+       char *ptr = buf;
+
+       for (done = 0; done < count; ) {
+               nb = read(fd, ptr, count - done);
+               if (nb < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       return -1;
+               }
+               if (nb == 0)
+                       break;
+               done += nb;
+               ptr += nb;
+       }
+       return done;
+}
+
+/* Procedures for locking the serial device using a lock file. */
+#ifndef LOCK_DIR
+#ifdef __linux__
+#define LOCK_DIR       "/var/lock"
+#else
+#ifdef SVR4
+#define LOCK_DIR       "/var/spool/locks"
+#else
+#define LOCK_DIR       "/var/spool/lock"
+#endif
+#endif
+#endif /* LOCK_DIR */
+
+static char lock_file[MAXPATHLEN];
+
+/*
+ * lock - create a lock file for the named device
+ */
+int
+lock(dev)
+    char *dev;
+{
+#ifdef LOCKLIB
+    int result;
+
+    result = mklock (dev, (void *) 0);
+    if (result == 0) {
+       ppp_strlcpy(lock_file, dev, sizeof(lock_file));
+       return 0;
+    }
+
+    if (result > 0)
+        ppp_notice("Device %s is locked by pid %d", dev, result);
+    else
+       ppp_error("Can't create lock file %s", lock_file);
+    return -1;
+
+#else /* LOCKLIB */
+
+    char lock_buffer[12];
+    int fd, pid, n;
+
+#ifdef SVR4
+    struct stat sbuf;
+
+    if (stat(dev, &sbuf) < 0) {
+       ppp_error("Can't get device number for %s: %m", dev);
+       return -1;
+    }
+    if ((sbuf.st_mode & S_IFMT) != S_IFCHR) {
+       ppp_error("Can't lock %s: not a character device", dev);
+       return -1;
+    }
+    ppp_slprintf(lock_file, sizeof(lock_file), "%s/LK.%03d.%03d.%03d",
+            LOCK_DIR, major(sbuf.st_dev),
+            major(sbuf.st_rdev), minor(sbuf.st_rdev));
+#else
+    char *p;
+    char lockdev[MAXPATHLEN];
+
+    if ((p = strstr(dev, "dev/")) != NULL) {
+       dev = p + 4;
+       strncpy(lockdev, dev, MAXPATHLEN-1);
+       lockdev[MAXPATHLEN-1] = 0;
+       while ((p = strrchr(lockdev, '/')) != NULL) {
+           *p = '_';
+       }
+       dev = lockdev;
+    } else
+       if ((p = strrchr(dev, '/')) != NULL)
+           dev = p + 1;
+
+    ppp_slprintf(lock_file, sizeof(lock_file), "%s/LCK..%s", LOCK_DIR, dev);
+#endif
+
+    while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) {
+       if (errno != EEXIST) {
+           ppp_error("Can't create lock file %s: %m", lock_file);
+           break;
+       }
+
+       /* Read the lock file to find out who has the device locked. */
+       fd = open(lock_file, O_RDONLY, 0);
+       if (fd < 0) {
+           if (errno == ENOENT) /* This is just a timing problem. */
+               continue;
+           ppp_error("Can't open existing lock file %s: %m", lock_file);
+           break;
+       }
+#ifndef LOCK_BINARY
+       n = read(fd, lock_buffer, 11);
+#else
+       n = read(fd, &pid, sizeof(pid));
+#endif /* LOCK_BINARY */
+       close(fd);
+       fd = -1;
+       if (n <= 0) {
+           ppp_error("Can't read pid from lock file %s", lock_file);
+           break;
+       }
+
+       /* See if the process still exists. */
+#ifndef LOCK_BINARY
+       lock_buffer[n] = 0;
+       pid = atoi(lock_buffer);
+#endif /* LOCK_BINARY */
+       if (pid == getpid())
+           return 1;           /* somebody else locked it for us */
+       if (pid == 0
+           || (kill(pid, 0) == -1 && errno == ESRCH)) {
+           if (unlink (lock_file) == 0) {
+               ppp_notice("Removed stale lock on %s (pid %d)", dev, pid);
+               continue;
+           }
+           ppp_warn("Couldn't remove stale lock on %s", dev);
+       } else
+           ppp_notice("Device %s is locked by pid %d", dev, pid);
+       break;
+    }
+
+    if (fd < 0) {
+       lock_file[0] = 0;
+       return -1;
+    }
+
+    pid = getpid();
+#ifndef LOCK_BINARY
+    ppp_slprintf(lock_buffer, sizeof(lock_buffer), "%10d\n", pid);
+    write (fd, lock_buffer, 11);
+#else
+    write(fd, &pid, sizeof (pid));
+#endif
+    close(fd);
+    return 0;
+
+#endif
+}
+
+/*
+ * relock - called to update our lockfile when we are about to detach,
+ * thus changing our pid (we fork, the child carries on, and the parent dies).
+ * Note that this is called by the parent, with pid equal to the pid
+ * of the child.  This avoids a potential race which would exist if
+ * we had the child rewrite the lockfile (the parent might die first,
+ * and another process could think the lock was stale if it checked
+ * between when the parent died and the child rewrote the lockfile).
+ */
+int
+relock(pid)
+    int pid;
+{
+#ifdef LOCKLIB
+    /* XXX is there a way to do this? */
+    return -1;
+#else /* LOCKLIB */
+
+    int fd;
+    char lock_buffer[12];
+
+    if (lock_file[0] == 0)
+       return -1;
+    fd = open(lock_file, O_WRONLY, 0);
+    if (fd < 0) {
+       ppp_error("Couldn't reopen lock file %s: %m", lock_file);
+       lock_file[0] = 0;
+       return -1;
+    }
+
+#ifndef LOCK_BINARY
+    ppp_slprintf(lock_buffer, sizeof(lock_buffer), "%10d\n", pid);
+    write (fd, lock_buffer, 11);
+#else
+    write(fd, &pid, sizeof(pid));
+#endif /* LOCK_BINARY */
+    close(fd);
+    return 0;
+
+#endif /* LOCKLIB */
+}
+
+/*
+ * unlock - remove our lockfile
+ */
+void
+unlock()
+{
+    if (lock_file[0]) {
+#ifdef LOCKLIB
+       (void) rmlock(lock_file, (void *) 0);
+#else
+       unlink(lock_file);
+#endif
+       lock_file[0] = 0;
+    }
+}
+
+#endif /* Unused */
+
+#endif /* PPP_SUPPORT */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/vj.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/ppp/vj.c
new file mode 100644 (file)
index 0000000..3fecba6
--- /dev/null
@@ -0,0 +1,685 @@
+/*
+ * Routines to compress and uncompess tcp packets (for transmission
+ * over low speed serial lines.
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ *   Initial distribution.
+ *
+ * Modified June 1993 by Paul Mackerras, paulus@cs.anu.edu.au,
+ * so that the entire packet being decompressed doesn't have
+ * to be in contiguous memory (just the compressed header).
+ *
+ * Modified March 1998 by Guy Lancaster, glanca@gesn.com,
+ * for a 16 bit processor.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && VJ_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "netif/ppp/ppp_impl.h"
+#include "netif/ppp/pppdebug.h"
+
+#include "netif/ppp/vj.h"
+
+#include <string.h>
+
+#if LINK_STATS
+#define INCR(counter) ++comp->stats.counter
+#else
+#define INCR(counter)
+#endif
+
+void
+vj_compress_init(struct vjcompress *comp)
+{
+  u8_t i;
+  struct cstate *tstate = comp->tstate;
+
+#if MAX_SLOTS == 0
+  memset((char *)comp, 0, sizeof(*comp));
+#endif
+  comp->maxSlotIndex = MAX_SLOTS - 1;
+  comp->compressSlot = 0;    /* Disable slot ID compression by default. */
+  for (i = MAX_SLOTS - 1; i > 0; --i) {
+    tstate[i].cs_id = i;
+    tstate[i].cs_next = &tstate[i - 1];
+  }
+  tstate[0].cs_next = &tstate[MAX_SLOTS - 1];
+  tstate[0].cs_id = 0;
+  comp->last_cs = &tstate[0];
+  comp->last_recv = 255;
+  comp->last_xmit = 255;
+  comp->flags = VJF_TOSS;
+}
+
+
+/* ENCODE encodes a number that is known to be non-zero.  ENCODEZ
+ * checks for zero (since zero has to be encoded in the long, 3 byte
+ * form).
+ */
+#define ENCODE(n) { \
+  if ((u16_t)(n) >= 256) { \
+    *cp++ = 0; \
+    cp[1] = (u8_t)(n); \
+    cp[0] = (u8_t)((n) >> 8); \
+    cp += 2; \
+  } else { \
+    *cp++ = (u8_t)(n); \
+  } \
+}
+#define ENCODEZ(n) { \
+  if ((u16_t)(n) >= 256 || (u16_t)(n) == 0) { \
+    *cp++ = 0; \
+    cp[1] = (u8_t)(n); \
+    cp[0] = (u8_t)((n) >> 8); \
+    cp += 2; \
+  } else { \
+    *cp++ = (u8_t)(n); \
+  } \
+}
+
+#define DECODEL(f) { \
+  if (*cp == 0) {\
+    u32_t tmp_ = lwip_ntohl(f) + ((cp[1] << 8) | cp[2]); \
+    (f) = lwip_htonl(tmp_); \
+    cp += 3; \
+  } else { \
+    u32_t tmp_ = lwip_ntohl(f) + (u32_t)*cp++; \
+    (f) = lwip_htonl(tmp_); \
+  } \
+}
+
+#define DECODES(f) { \
+  if (*cp == 0) {\
+    u16_t tmp_ = lwip_ntohs(f) + (((u16_t)cp[1] << 8) | cp[2]); \
+    (f) = lwip_htons(tmp_); \
+    cp += 3; \
+  } else { \
+    u16_t tmp_ = lwip_ntohs(f) + (u16_t)*cp++; \
+    (f) = lwip_htons(tmp_); \
+  } \
+}
+
+#define DECODEU(f) { \
+  if (*cp == 0) {\
+    (f) = lwip_htons(((u16_t)cp[1] << 8) | cp[2]); \
+    cp += 3; \
+  } else { \
+    (f) = lwip_htons((u16_t)*cp++); \
+  } \
+}
+
+/* Helper structures for unaligned *u32_t and *u16_t accesses */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct vj_u32_t {
+  PACK_STRUCT_FIELD(u32_t v);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct vj_u16_t {
+  PACK_STRUCT_FIELD(u16_t v);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/*
+ * vj_compress_tcp - Attempt to do Van Jacobson header compression on a
+ * packet.  This assumes that nb and comp are not null and that the first
+ * buffer of the chain contains a valid IP header.
+ * Return the VJ type code indicating whether or not the packet was
+ * compressed.
+ */
+u8_t
+vj_compress_tcp(struct vjcompress *comp, struct pbuf **pb)
+{
+  struct pbuf *np = *pb;
+  struct ip_hdr *ip = (struct ip_hdr *)np->payload;
+  struct cstate *cs = comp->last_cs->cs_next;
+  u16_t ilen = IPH_HL(ip);
+  u16_t hlen;
+  struct tcp_hdr *oth;
+  struct tcp_hdr *th;
+  u16_t deltaS, deltaA = 0;
+  u32_t deltaL;
+  u32_t changes = 0;
+  u8_t new_seq[16];
+  u8_t *cp = new_seq;
+
+  /*
+   * Check that the packet is IP proto TCP.
+   */
+  if (IPH_PROTO(ip) != IP_PROTO_TCP) {
+    return (TYPE_IP);
+  }
+
+  /*
+   * Bail if this is an IP fragment or if the TCP packet isn't
+   * `compressible' (i.e., ACK isn't set or some other control bit is
+   * set).
+   */
+  if ((IPH_OFFSET(ip) & PP_HTONS(0x3fff)) || np->tot_len < 40) {
+    return (TYPE_IP);
+  }
+  th = (struct tcp_hdr *)&((struct vj_u32_t*)ip)[ilen];
+  if ((TCPH_FLAGS(th) & (TCP_SYN|TCP_FIN|TCP_RST|TCP_ACK)) != TCP_ACK) {
+    return (TYPE_IP);
+  }
+
+  /* Check that the TCP/IP headers are contained in the first buffer. */
+  hlen = ilen + TCPH_HDRLEN(th);
+  hlen <<= 2;
+  if (np->len < hlen) {
+    PPPDEBUG(LOG_INFO, ("vj_compress_tcp: header len %d spans buffers\n", hlen));
+    return (TYPE_IP);
+  }
+
+  /* TCP stack requires that we don't change the packet payload, therefore we copy
+   * the whole packet before compression. */
+  np = pbuf_clone(PBUF_RAW, PBUF_RAM, *pb);
+  if (!np) {
+    return (TYPE_IP);
+  }
+
+  *pb = np;
+  ip = (struct ip_hdr *)np->payload;
+
+  /*
+   * Packet is compressible -- we're going to send either a
+   * COMPRESSED_TCP or UNCOMPRESSED_TCP packet.  Either way we need
+   * to locate (or create) the connection state.  Special case the
+   * most recently used connection since it's most likely to be used
+   * again & we don't have to do any reordering if it's used.
+   */
+  INCR(vjs_packets);
+  if (!ip4_addr_cmp(&ip->src, &cs->cs_ip.src)
+      || !ip4_addr_cmp(&ip->dest, &cs->cs_ip.dest)
+      || (*(struct vj_u32_t*)th).v != (((struct vj_u32_t*)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]).v) {
+    /*
+     * Wasn't the first -- search for it.
+     *
+     * States are kept in a circularly linked list with
+     * last_cs pointing to the end of the list.  The
+     * list is kept in lru order by moving a state to the
+     * head of the list whenever it is referenced.  Since
+     * the list is short and, empirically, the connection
+     * we want is almost always near the front, we locate
+     * states via linear search.  If we don't find a state
+     * for the datagram, the oldest state is (re-)used.
+     */
+    struct cstate *lcs;
+    struct cstate *lastcs = comp->last_cs;
+
+    do {
+      lcs = cs; cs = cs->cs_next;
+      INCR(vjs_searches);
+      if (ip4_addr_cmp(&ip->src, &cs->cs_ip.src)
+          && ip4_addr_cmp(&ip->dest, &cs->cs_ip.dest)
+          && (*(struct vj_u32_t*)th).v == (((struct vj_u32_t*)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]).v) {
+        goto found;
+      }
+    } while (cs != lastcs);
+
+    /*
+     * Didn't find it -- re-use oldest cstate.  Send an
+     * uncompressed packet that tells the other side what
+     * connection number we're using for this conversation.
+     * Note that since the state list is circular, the oldest
+     * state points to the newest and we only need to set
+     * last_cs to update the lru linkage.
+     */
+    INCR(vjs_misses);
+    comp->last_cs = lcs;
+    goto uncompressed;
+
+    found:
+    /*
+     * Found it -- move to the front on the connection list.
+     */
+    if (cs == lastcs) {
+      comp->last_cs = lcs;
+    } else {
+      lcs->cs_next = cs->cs_next;
+      cs->cs_next = lastcs->cs_next;
+      lastcs->cs_next = cs;
+    }
+  }
+
+  oth = (struct tcp_hdr *)&((struct vj_u32_t*)&cs->cs_ip)[ilen];
+  deltaS = ilen;
+
+  /*
+   * Make sure that only what we expect to change changed. The first
+   * line of the `if' checks the IP protocol version, header length &
+   * type of service.  The 2nd line checks the "Don't fragment" bit.
+   * The 3rd line checks the time-to-live and protocol (the protocol
+   * check is unnecessary but costless).  The 4th line checks the TCP
+   * header length.  The 5th line checks IP options, if any.  The 6th
+   * line checks TCP options, if any.  If any of these things are
+   * different between the previous & current datagram, we send the
+   * current datagram `uncompressed'.
+   */
+  if ((((struct vj_u16_t*)ip)[0]).v != (((struct vj_u16_t*)&cs->cs_ip)[0]).v
+      || (((struct vj_u16_t*)ip)[3]).v != (((struct vj_u16_t*)&cs->cs_ip)[3]).v
+      || (((struct vj_u16_t*)ip)[4]).v != (((struct vj_u16_t*)&cs->cs_ip)[4]).v
+      || TCPH_HDRLEN(th) != TCPH_HDRLEN(oth)
+      || (deltaS > 5 && BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2))
+      || (TCPH_HDRLEN(th) > 5 && BCMP(th + 1, oth + 1, (TCPH_HDRLEN(th) - 5) << 2))) {
+    goto uncompressed;
+  }
+
+  /*
+   * Figure out which of the changing fields changed.  The
+   * receiver expects changes in the order: urgent, window,
+   * ack, seq (the order minimizes the number of temporaries
+   * needed in this section of code).
+   */
+  if (TCPH_FLAGS(th) & TCP_URG) {
+    deltaS = lwip_ntohs(th->urgp);
+    ENCODEZ(deltaS);
+    changes |= NEW_U;
+  } else if (th->urgp != oth->urgp) {
+    /* argh! URG not set but urp changed -- a sensible
+     * implementation should never do this but RFC793
+     * doesn't prohibit the change so we have to deal
+     * with it. */
+    goto uncompressed;
+  }
+
+  if ((deltaS = (u16_t)(lwip_ntohs(th->wnd) - lwip_ntohs(oth->wnd))) != 0) {
+    ENCODE(deltaS);
+    changes |= NEW_W;
+  }
+
+  if ((deltaL = lwip_ntohl(th->ackno) - lwip_ntohl(oth->ackno)) != 0) {
+    if (deltaL > 0xffff) {
+      goto uncompressed;
+    }
+    deltaA = (u16_t)deltaL;
+    ENCODE(deltaA);
+    changes |= NEW_A;
+  }
+
+  if ((deltaL = lwip_ntohl(th->seqno) - lwip_ntohl(oth->seqno)) != 0) {
+    if (deltaL > 0xffff) {
+      goto uncompressed;
+    }
+    deltaS = (u16_t)deltaL;
+    ENCODE(deltaS);
+    changes |= NEW_S;
+  }
+
+  switch(changes) {
+  case 0:
+    /*
+     * Nothing changed. If this packet contains data and the
+     * last one didn't, this is probably a data packet following
+     * an ack (normal on an interactive connection) and we send
+     * it compressed.  Otherwise it's probably a retransmit,
+     * retransmitted ack or window probe.  Send it uncompressed
+     * in case the other side missed the compressed version.
+     */
+    if (IPH_LEN(ip) != IPH_LEN(&cs->cs_ip) &&
+      lwip_ntohs(IPH_LEN(&cs->cs_ip)) == hlen) {
+      break;
+    }
+    /* no break */
+    /* fall through */
+
+  case SPECIAL_I:
+  case SPECIAL_D:
+    /*
+     * actual changes match one of our special case encodings --
+     * send packet uncompressed.
+     */
+    goto uncompressed;
+
+  case NEW_S|NEW_A:
+    if (deltaS == deltaA && deltaS == lwip_ntohs(IPH_LEN(&cs->cs_ip)) - hlen) {
+      /* special case for echoed terminal traffic */
+      changes = SPECIAL_I;
+      cp = new_seq;
+    }
+    break;
+
+  case NEW_S:
+    if (deltaS == lwip_ntohs(IPH_LEN(&cs->cs_ip)) - hlen) {
+      /* special case for data xfer */
+      changes = SPECIAL_D;
+      cp = new_seq;
+    }
+    break;
+  default:
+     break;
+  }
+
+  deltaS = (u16_t)(lwip_ntohs(IPH_ID(ip)) - lwip_ntohs(IPH_ID(&cs->cs_ip)));
+  if (deltaS != 1) {
+    ENCODEZ(deltaS);
+    changes |= NEW_I;
+  }
+  if (TCPH_FLAGS(th) & TCP_PSH) {
+    changes |= TCP_PUSH_BIT;
+  }
+  /*
+   * Grab the cksum before we overwrite it below.  Then update our
+   * state with this packet's header.
+   */
+  deltaA = lwip_ntohs(th->chksum);
+  MEMCPY(&cs->cs_ip, ip, hlen);
+
+  /*
+   * We want to use the original packet as our compressed packet.
+   * (cp - new_seq) is the number of bytes we need for compressed
+   * sequence numbers.  In addition we need one byte for the change
+   * mask, one for the connection id and two for the tcp checksum.
+   * So, (cp - new_seq) + 4 bytes of header are needed.  hlen is how
+   * many bytes of the original packet to toss so subtract the two to
+   * get the new packet size.
+   */
+  deltaS = (u16_t)(cp - new_seq);
+  if (!comp->compressSlot || comp->last_xmit != cs->cs_id) {
+    comp->last_xmit = cs->cs_id;
+    hlen -= deltaS + 4;
+    if (pbuf_remove_header(np, hlen)){
+      /* Can we cope with this failing?  Just assert for now */
+      LWIP_ASSERT("pbuf_remove_header failed\n", 0);
+    }
+    cp = (u8_t*)np->payload;
+    *cp++ = (u8_t)(changes | NEW_C);
+    *cp++ = cs->cs_id;
+  } else {
+    hlen -= deltaS + 3;
+    if (pbuf_remove_header(np, hlen)) {
+      /* Can we cope with this failing?  Just assert for now */
+      LWIP_ASSERT("pbuf_remove_header failed\n", 0);
+    }
+    cp = (u8_t*)np->payload;
+    *cp++ = (u8_t)changes;
+  }
+  *cp++ = (u8_t)(deltaA >> 8);
+  *cp++ = (u8_t)deltaA;
+  MEMCPY(cp, new_seq, deltaS);
+  INCR(vjs_compressed);
+  return (TYPE_COMPRESSED_TCP);
+
+  /*
+   * Update connection state cs & send uncompressed packet (that is,
+   * a regular ip/tcp packet but with the 'conversation id' we hope
+   * to use on future compressed packets in the protocol field).
+   */
+uncompressed:
+  MEMCPY(&cs->cs_ip, ip, hlen);
+  IPH_PROTO_SET(ip, cs->cs_id);
+  comp->last_xmit = cs->cs_id;
+  return (TYPE_UNCOMPRESSED_TCP);
+}
+
+/*
+ * Called when we may have missed a packet.
+ */
+void
+vj_uncompress_err(struct vjcompress *comp)
+{
+  comp->flags |= VJF_TOSS;
+  INCR(vjs_errorin);
+}
+
+/*
+ * "Uncompress" a packet of type TYPE_UNCOMPRESSED_TCP.
+ * Return 0 on success, -1 on failure.
+ */
+int
+vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp)
+{
+  u32_t hlen;
+  struct cstate *cs;
+  struct ip_hdr *ip;
+
+  ip = (struct ip_hdr *)nb->payload;
+  hlen = IPH_HL(ip) << 2;
+  if (IPH_PROTO(ip) >= MAX_SLOTS
+      || hlen + sizeof(struct tcp_hdr) > nb->len
+      || (hlen += TCPH_HDRLEN_BYTES((struct tcp_hdr *)&((char *)ip)[hlen]))
+          > nb->len
+      || hlen > MAX_HDR) {
+    PPPDEBUG(LOG_INFO, ("vj_uncompress_uncomp: bad cid=%d, hlen=%d buflen=%d\n",
+      IPH_PROTO(ip), hlen, nb->len));
+    vj_uncompress_err(comp);
+    return -1;
+  }
+  cs = &comp->rstate[comp->last_recv = IPH_PROTO(ip)];
+  comp->flags &=~ VJF_TOSS;
+  IPH_PROTO_SET(ip, IP_PROTO_TCP);
+  /* copy from/to bigger buffers checked above instead of cs->cs_ip and ip
+     just to help static code analysis to see this is correct ;-) */
+  MEMCPY(&cs->cs_hdr, nb->payload, hlen);
+  cs->cs_hlen = (u16_t)hlen;
+  INCR(vjs_uncompressedin);
+  return 0;
+}
+
+/*
+ * Uncompress a packet of type TYPE_COMPRESSED_TCP.
+ * The packet is composed of a buffer chain and the first buffer
+ * must contain an accurate chain length.
+ * The first buffer must include the entire compressed TCP/IP header.
+ * This procedure replaces the compressed header with the uncompressed
+ * header and returns the length of the VJ header.
+ */
+int
+vj_uncompress_tcp(struct pbuf **nb, struct vjcompress *comp)
+{
+  u8_t *cp;
+  struct tcp_hdr *th;
+  struct cstate *cs;
+  struct vj_u16_t *bp;
+  struct pbuf *n0 = *nb;
+  u32_t tmp;
+  u32_t vjlen, hlen, changes;
+
+  INCR(vjs_compressedin);
+  cp = (u8_t*)n0->payload;
+  changes = *cp++;
+  if (changes & NEW_C) {
+    /*
+     * Make sure the state index is in range, then grab the state.
+     * If we have a good state index, clear the 'discard' flag.
+     */
+    if (*cp >= MAX_SLOTS) {
+      PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: bad cid=%d\n", *cp));
+      goto bad;
+    }
+
+    comp->flags &=~ VJF_TOSS;
+    comp->last_recv = *cp++;
+  } else {
+    /*
+     * this packet has an implicit state index.  If we've
+     * had a line error since the last time we got an
+     * explicit state index, we have to toss the packet.
+     */
+    if (comp->flags & VJF_TOSS) {
+      PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: tossing\n"));
+      INCR(vjs_tossed);
+      return (-1);
+    }
+  }
+  cs = &comp->rstate[comp->last_recv];
+  hlen = IPH_HL(&cs->cs_ip) << 2;
+  th = (struct tcp_hdr *)&((u8_t*)&cs->cs_ip)[hlen];
+  th->chksum = lwip_htons((*cp << 8) | cp[1]);
+  cp += 2;
+  if (changes & TCP_PUSH_BIT) {
+    TCPH_SET_FLAG(th, TCP_PSH);
+  } else {
+    TCPH_UNSET_FLAG(th, TCP_PSH);
+  }
+
+  switch (changes & SPECIALS_MASK) {
+  case SPECIAL_I:
+    {
+      u32_t i = lwip_ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen;
+      /* some compilers can't nest inline assembler.. */
+      tmp = lwip_ntohl(th->ackno) + i;
+      th->ackno = lwip_htonl(tmp);
+      tmp = lwip_ntohl(th->seqno) + i;
+      th->seqno = lwip_htonl(tmp);
+    }
+    break;
+
+  case SPECIAL_D:
+    /* some compilers can't nest inline assembler.. */
+    tmp = lwip_ntohl(th->seqno) + lwip_ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen;
+    th->seqno = lwip_htonl(tmp);
+    break;
+
+  default:
+    if (changes & NEW_U) {
+      TCPH_SET_FLAG(th, TCP_URG);
+      DECODEU(th->urgp);
+    } else {
+      TCPH_UNSET_FLAG(th, TCP_URG);
+    }
+    if (changes & NEW_W) {
+      DECODES(th->wnd);
+    }
+    if (changes & NEW_A) {
+      DECODEL(th->ackno);
+    }
+    if (changes & NEW_S) {
+      DECODEL(th->seqno);
+    }
+    break;
+  }
+  if (changes & NEW_I) {
+    DECODES(cs->cs_ip._id);
+  } else {
+    IPH_ID_SET(&cs->cs_ip, lwip_ntohs(IPH_ID(&cs->cs_ip)) + 1);
+    IPH_ID_SET(&cs->cs_ip, lwip_htons(IPH_ID(&cs->cs_ip)));
+  }
+
+  /*
+   * At this point, cp points to the first byte of data in the
+   * packet.  Fill in the IP total length and update the IP
+   * header checksum.
+   */
+  vjlen = (u16_t)(cp - (u8_t*)n0->payload);
+  if (n0->len < vjlen) {
+    /*
+     * We must have dropped some characters (crc should detect
+     * this but the old slip framing won't)
+     */
+    PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: head buffer %d too short %d\n",
+          n0->len, vjlen));
+    goto bad;
+  }
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+  tmp = n0->tot_len - vjlen + cs->cs_hlen;
+  IPH_LEN_SET(&cs->cs_ip, lwip_htons((u16_t)tmp));
+#else
+  IPH_LEN_SET(&cs->cs_ip, lwip_htons(n0->tot_len - vjlen + cs->cs_hlen));
+#endif
+
+  /* recompute the ip header checksum */
+  bp = (struct vj_u16_t*) &cs->cs_ip;
+  IPH_CHKSUM_SET(&cs->cs_ip, 0);
+  for (tmp = 0; hlen > 0; hlen -= 2) {
+    tmp += (*bp++).v;
+  }
+  tmp = (tmp & 0xffff) + (tmp >> 16);
+  tmp = (tmp & 0xffff) + (tmp >> 16);
+  IPH_CHKSUM_SET(&cs->cs_ip,  (u16_t)(~tmp));
+
+  /* Remove the compressed header and prepend the uncompressed header. */
+  if (pbuf_remove_header(n0, vjlen)) {
+    /* Can we cope with this failing?  Just assert for now */
+    LWIP_ASSERT("pbuf_remove_header failed\n", 0);
+    goto bad;
+  }
+
+  if(LWIP_MEM_ALIGN(n0->payload) != n0->payload) {
+    struct pbuf *np;
+
+#if IP_FORWARD
+    /* If IP forwarding is enabled we are using a PBUF_LINK packet type so
+     * the packet is being allocated with enough header space to be
+     * forwarded (to Ethernet for example).
+     */
+    np = pbuf_alloc(PBUF_LINK, n0->len + cs->cs_hlen, PBUF_POOL);
+#else /* IP_FORWARD */
+    np = pbuf_alloc(PBUF_RAW, n0->len + cs->cs_hlen, PBUF_POOL);
+#endif /* IP_FORWARD */
+    if(!np) {
+      PPPDEBUG(LOG_WARNING, ("vj_uncompress_tcp: realign failed\n"));
+      goto bad;
+    }
+
+    if (pbuf_remove_header(np, cs->cs_hlen)) {
+      /* Can we cope with this failing?  Just assert for now */
+      LWIP_ASSERT("pbuf_remove_header failed\n", 0);
+      goto bad;
+    }
+
+    pbuf_take(np, n0->payload, n0->len);
+
+    if(n0->next) {
+      pbuf_chain(np, n0->next);
+      pbuf_dechain(n0);
+    }
+    pbuf_free(n0);
+    n0 = np;
+  }
+
+  if (pbuf_add_header(n0, cs->cs_hlen)) {
+    struct pbuf *np;
+
+    LWIP_ASSERT("vj_uncompress_tcp: cs->cs_hlen <= PBUF_POOL_BUFSIZE", cs->cs_hlen <= PBUF_POOL_BUFSIZE);
+    np = pbuf_alloc(PBUF_RAW, cs->cs_hlen, PBUF_POOL);
+    if(!np) {
+      PPPDEBUG(LOG_WARNING, ("vj_uncompress_tcp: prepend failed\n"));
+      goto bad;
+    }
+    pbuf_cat(np, n0);
+    n0 = np;
+  }
+  LWIP_ASSERT("n0->len >= cs->cs_hlen", n0->len >= cs->cs_hlen);
+  MEMCPY(n0->payload, &cs->cs_ip, cs->cs_hlen);
+
+  *nb = n0;
+
+  return vjlen;
+
+bad:
+  vj_uncompress_err(comp);
+  return (-1);
+}
+
+#endif /* PPP_SUPPORT && VJ_SUPPORT */
diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/zepif.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/netif/zepif.c
new file mode 100644 (file)
index 0000000..de43b99
--- /dev/null
@@ -0,0 +1,300 @@
+/**
+ * @file
+ *
+ * @defgroup zepif ZEP - ZigBee Encapsulation Protocol
+ * @ingroup netifs
+ * A netif implementing the ZigBee Encapsulation Protocol (ZEP).
+ * This is used to tunnel 6LowPAN over UDP.
+ *
+ * Usage (there must be a default netif before!):
+ * @code{.c}
+ *   netif_add(&zep_netif, NULL, NULL, NULL, NULL, zepif_init, tcpip_6lowpan_input);
+ *   netif_create_ip6_linklocal_address(&zep_netif, 1);
+ *   netif_set_up(&zep_netif);
+ *   netif_set_link_up(&zep_netif);
+ * @endcode
+ */
+
+/*
+ * Copyright (c) 2018 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+
+#include "netif/zepif.h"
+
+#if LWIP_IPV6 && LWIP_UDP
+
+#include "netif/lowpan6.h"
+#include "lwip/udp.h"
+#include "lwip/timeouts.h"
+#include <string.h>
+
+/** Define this to 1 to loop back TX packets for testing */
+#ifndef ZEPIF_LOOPBACK
+#define ZEPIF_LOOPBACK    0
+#endif
+
+#define ZEP_MAX_DATA_LEN  127
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct zep_hdr {
+  PACK_STRUCT_FLD_8(u8_t prot_id[2]);
+  PACK_STRUCT_FLD_8(u8_t prot_version);
+  PACK_STRUCT_FLD_8(u8_t type);
+  PACK_STRUCT_FLD_8(u8_t channel_id);
+  PACK_STRUCT_FIELD(u16_t device_id);
+  PACK_STRUCT_FLD_8(u8_t crc_mode);
+  PACK_STRUCT_FLD_8(u8_t unknown_1);
+  PACK_STRUCT_FIELD(u32_t timestamp[2]);
+  PACK_STRUCT_FIELD(u32_t seq_num);
+  PACK_STRUCT_FLD_8(u8_t unknown_2[10]);
+  PACK_STRUCT_FLD_8(u8_t len);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+struct zepif_state {
+  struct zepif_init init;
+  struct udp_pcb *pcb;
+  u32_t seqno;
+};
+
+static u8_t zep_lowpan_timer_running;
+
+/* Helper function that calls the 6LoWPAN timer and reschedules itself */
+static void
+zep_lowpan_timer(void *arg)
+{
+  lowpan6_tmr();
+  if (zep_lowpan_timer_running) {
+    sys_timeout(LOWPAN6_TMR_INTERVAL, zep_lowpan_timer, arg);
+  }
+}
+
+/* Pass received pbufs into 6LowPAN netif */
+static void
+zepif_udp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p,
+               const ip_addr_t *addr, u16_t port)
+{
+  err_t err;
+  struct netif *netif_lowpan6 = (struct netif *)arg;
+  struct zep_hdr *zep;
+
+  LWIP_ASSERT("arg != NULL", arg != NULL);
+  LWIP_ASSERT("pcb != NULL", pcb != NULL);
+  LWIP_UNUSED_ARG(pcb); /* for LWIP_NOASSERT */
+  LWIP_UNUSED_ARG(addr);
+  LWIP_UNUSED_ARG(port);
+  if (p == NULL) {
+    return;
+  }
+
+  /* Parse and hide the ZEP header */
+  if (p->len < sizeof(struct zep_hdr)) {
+    /* need the zep_hdr in one piece */
+    goto err_return;
+  }
+  zep = (struct zep_hdr *)p->payload;
+  if (zep->prot_id[0] != 'E') {
+    goto err_return;
+  }
+  if (zep->prot_id[1] != 'X') {
+    goto err_return;
+  }
+  if (zep->prot_version != 2) {
+    /* we only support this version for now */
+    goto err_return;
+  }
+  if (zep->type != 1) {
+    goto err_return;
+  }
+  if (zep->crc_mode != 1) {
+    goto err_return;
+  }
+  if (zep->len != p->tot_len - sizeof(struct zep_hdr)) {
+    goto err_return;
+  }
+  /* everything seems to be OK, hide the ZEP header */
+  if (pbuf_remove_header(p, sizeof(struct zep_hdr))) {
+    goto err_return;
+  }
+  /* TODO Check CRC? */
+  /* remove CRC trailer */
+  pbuf_realloc(p, p->tot_len - 2);
+
+  /* Call into 6LoWPAN code. */
+  err = netif_lowpan6->input(p, netif_lowpan6);
+  if (err == ERR_OK) {
+    return;
+  }
+err_return:
+  pbuf_free(p);
+}
+
+/* Send 6LoWPAN TX packets as UDP broadcast */
+static err_t
+zepif_linkoutput(struct netif *netif, struct pbuf *p)
+{
+  err_t err;
+  struct pbuf *q;
+  struct zep_hdr *zep;
+  struct zepif_state *state;
+
+  LWIP_ASSERT("invalid netif", netif != NULL);
+  LWIP_ASSERT("invalid pbuf", p != NULL);
+
+  if (p->tot_len > ZEP_MAX_DATA_LEN) {
+    return ERR_VAL;
+  }
+  LWIP_ASSERT("TODO: support chained pbufs", p->next == NULL);
+
+  state = (struct zepif_state *)netif->state;
+  LWIP_ASSERT("state->pcb != NULL", state->pcb != NULL);
+
+  q = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct zep_hdr) + p->tot_len, PBUF_RAM);
+  if (q == NULL) {
+    return ERR_MEM;
+  }
+  zep = (struct zep_hdr *)q->payload;
+  memset(zep, 0, sizeof(struct zep_hdr));
+  zep->prot_id[0] = 'E';
+  zep->prot_id[1] = 'X';
+  zep->prot_version = 2;
+  zep->type = 1; /* Data */
+  zep->channel_id = 0; /* whatever */
+  zep->device_id = lwip_htons(1); /* whatever */
+  zep->crc_mode = 1;
+  zep->unknown_1 = 0xff;
+  zep->seq_num = lwip_htonl(state->seqno);
+  state->seqno++;
+  zep->len = (u8_t)p->tot_len;
+
+  err = pbuf_copy_partial_pbuf(q, p, p->tot_len, sizeof(struct zep_hdr));
+  if (err == ERR_OK) {
+#if ZEPIF_LOOPBACK
+    zepif_udp_recv(netif, state->pcb, pbuf_clone(PBUF_RAW, PBUF_RAM, q), NULL, 0);
+#endif
+    err = udp_sendto(state->pcb, q, state->init.zep_dst_ip_addr, state->init.zep_dst_udp_port);
+  }
+  pbuf_free(q);
+
+  return err;
+}
+
+/**
+ * @ingroup zepif
+ * Set up a raw 6LowPAN netif and surround it with input- and output
+ * functions for ZEP
+ */
+err_t
+zepif_init(struct netif *netif)
+{
+  err_t err;
+  struct zepif_init *init_state = (struct zepif_init *)netif->state;
+  struct zepif_state *state = (struct zepif_state *)mem_malloc(sizeof(struct zepif_state));
+
+  LWIP_ASSERT("zepif needs an input callback", netif->input != NULL);
+
+  if (state == NULL) {
+    return ERR_MEM;
+  }
+  memset(state, 0, sizeof(struct zepif_state));
+  if (init_state != NULL) {
+    memcpy(&state->init, init_state, sizeof(struct zepif_init));
+  }
+  if (state->init.zep_src_udp_port == 0) {
+    state->init.zep_src_udp_port = ZEPIF_DEFAULT_UDP_PORT;
+  }
+  if (state->init.zep_dst_udp_port == 0) {
+    state->init.zep_dst_udp_port = ZEPIF_DEFAULT_UDP_PORT;
+  }
+#if LWIP_IPV4
+  if (state->init.zep_dst_ip_addr == NULL) {
+    /* With IPv4 enabled, default to broadcasting packets if no address is set */
+    state->init.zep_dst_ip_addr = IP_ADDR_BROADCAST;
+  }
+#endif /* LWIP_IPV4 */
+
+  netif->state = NULL;
+
+  state->pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+  if (state->pcb == NULL) {
+    err = ERR_MEM;
+    goto err_ret;
+  }
+  err = udp_bind(state->pcb, state->init.zep_src_ip_addr, state->init.zep_src_udp_port);
+  if (err != ERR_OK) {
+    goto err_ret;
+  }
+  if (state->init.zep_netif != NULL) {
+    udp_bind_netif(state->pcb, state->init.zep_netif);
+  }
+  LWIP_ASSERT("udp_bind(lowpan6_broadcast_pcb) failed", err == ERR_OK);
+  ip_set_option(state->pcb, SOF_BROADCAST);
+  udp_recv(state->pcb, zepif_udp_recv, netif);
+
+  err = lowpan6_if_init(netif);
+  LWIP_ASSERT("lowpan6_if_init set a state", netif->state == NULL);
+  if (err == ERR_OK) {
+    netif->state = state;
+    netif->hwaddr_len = 6;
+    if (init_state != NULL) {
+      memcpy(netif->hwaddr, init_state->addr, 6);
+    } else {
+      u8_t i;
+      for (i = 0; i < 6; i++) {
+        netif->hwaddr[i] = i;
+      }
+      netif->hwaddr[0] &= 0xfc;
+    }
+    netif->linkoutput = zepif_linkoutput;
+
+    if (!zep_lowpan_timer_running) {
+      sys_timeout(LOWPAN6_TMR_INTERVAL, zep_lowpan_timer, NULL);
+      zep_lowpan_timer_running = 1;
+    }
+
+    return ERR_OK;
+  }
+
+err_ret:
+  if (state->pcb != NULL) {
+    udp_remove(state->pcb);
+  }
+  mem_free(state);
+  return err;
+}
+
+#endif /* LWIP_IPV6 && LWIP_UDP */
diff --git a/code/sdk/zhihui/4g/README_SLIP.md b/code/sdk/zhihui/4g/README_SLIP.md
new file mode 100644 (file)
index 0000000..0519ecb
--- /dev/null
@@ -0,0 +1 @@
+ 
\ No newline at end of file
index 72db1ba..62ff463 100644 (file)
@@ -1,14 +1,14 @@
 #===============================================================================
-# @brief    cmake file
+# @brief    cmake file - Cat1 Service with SLIP protocol support
 # Copyright (c) @CompanyNameMagicTag 2022-2022. All rights reserved.
 #===============================================================================
 
 set(COMPONENT_NAME "cat1Service")
 
+# 头文件目录
 set(PUBLIC_HEADER
 )
 
-
 set(PRIVATE_HEADER
 ${CMAKE_CURRENT_SOURCE_DIR}/Service
 ${ROOT_DIR}/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/include
@@ -19,6 +19,7 @@ ${ROOT_DIR}/open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/inc
 # ${ROOT_DIR}/open_source/openharmony/base/security/huks/frameworks/huks_standard/main/common/include
 )
 
+# 源文件列表
 set(CAT1SERVICE_SOURCES
 ${CMAKE_CURRENT_SOURCE_DIR}/Service/cat1_signlib.c
 ${CMAKE_CURRENT_SOURCE_DIR}/Service/cat1_server.c
@@ -33,17 +34,30 @@ ${CMAKE_CURRENT_SOURCE_DIR}/Service/sntp.c
 ${CMAKE_CURRENT_SOURCE_DIR}/Service/cat1_server_hal.c
 # ${CMAKE_CURRENT_SOURCE_DIR}/slip/uart_slip.c
 )
-# message(WARNING "lwipport_SRCS" ${lwipport_SRCS} "\n")
-# message(WARNING "PUBLIC_HEADER" ${PUBLIC_HEADER} "\n")
-# message(WARNING "LWIP_PORT_HEADER" ${LWIP_PORT_HEADER} "\n")
 
 set(SOURCES
     ${CAT1SERVICE_SOURCES}
 )
 
+# SLIP协议编译时配置
+set(PUBLIC_DEFINES
+    SUPPORT_CAT1_SERVICE
+    SLIP_MODULE_ENABLED=1
+    LWIP_SLIP=1
+    SLIP_SUPPORT=1
+    SLIP_USE_RX_THREAD=1
+    SLIP_RX_FROM_ISR=1
+    SLIP_IFNUM=0    
+)
+
+set(PRIVATE_DEFINES
+)
+
+# SLIP协议编译选项
 set(COMPONENT_PUBLIC_CCFLAGS
 )
 
+# 编译选项
 set(COMPONENT_CCFLAGS
     # -std=c++11
     -Wno-error
@@ -61,19 +75,15 @@ set(COMPONENT_CCFLAGS
     -Wno-error=unused-function
 )
 
+# 是否整体链接
 set(WHOLE_LINK
     true
 )
 
+# 是否为主组件
 set(MAIN_COMPONENT
     false
 )
 
-set(PUBLIC_DEFINES
-SUPPORT_CAT1_SERVICE
-)
-
-set(PRIVATE_DEFINES
-)
-
+# 构建组件
 build_component()
index 21381b1..d57dc28 100644 (file)
@@ -62,7 +62,7 @@ static ctl_pin_4g_t g_cat1_ctl_pins={
     S_AGPIO_L23, // pwr_pin
     1,
     S_AGPIO_L28,//reset_pin
-    1,
+    0,
     S_AGPIO_L31,//ring_pin
     1,
     S_AGPIO_L30,//wakemodem_pin
@@ -147,14 +147,14 @@ uint32_t cat1_api_cat1_bat_on(uint32_t pwron)
     }
     return 0;
 }
-uint32_t cat1_api_cat1_power_on(uint32_t pwron)
+uint32_t cat1_api_cat1_pwrkey_on(uint32_t pwron)
 {
 
     pin_t en_pin =  g_cat1_ctl_pins.pwr_pin;
     uint8_t en_value = g_cat1_ctl_pins.pwr_pin_en_value;
     if (en_pin == PIN_NONE)   
     {
-        printf("cat1_api_cat1_power_on en_pin is none\r\n");
+        printf("cat1_api_cat1_pwrkey_on en_pin is none\r\n");
         return 0xff;
     }
     if( pwron == 0)
@@ -243,106 +243,30 @@ bool cat1_api_is_cat1_enabled(void)
     }
 }
 
-uint32_t uapi_cat1_reset(uint32_t pwron)
-{
-//     pin_t reset_pin = S_AGPIO_L28;
-// #if defined(BOARD_LEINIAO)
-//     reset_pin = S_AGPIO_L24;
-// #endif
-
-//     if( pwron == 0)
-//     {
-//         uapi_gpio_set_dir(reset_pin, GPIO_DIRECTION_OUTPUT);
-//         uapi_gpio_set_val(reset_pin, GPIO_LEVEL_HIGH);
-//         printf("clear reset Cat1! L28 high \r\n");
-//     }else{
-//         uapi_gpio_set_dir(reset_pin, GPIO_DIRECTION_OUTPUT);
-//         uapi_gpio_set_val(reset_pin, GPIO_LEVEL_LOW); 
-//         printf("Reset Cat1! L28 low\r\n");
-//     }
-
-#if defined(BOARD_FD230)
-    if( pwron == 0)
-    {
-        uapi_gpio_set_dir(S_AGPIO_L28, GPIO_DIRECTION_OUTPUT);
-        uapi_gpio_set_val(S_AGPIO_L28, GPIO_LEVEL_HIGH);
-        printf("clear reset Cat1! L28 high \r\n");
-    }else{
-        uapi_gpio_set_dir(S_AGPIO_L28, GPIO_DIRECTION_OUTPUT);
-        uapi_gpio_set_val(S_AGPIO_L28, GPIO_LEVEL_LOW); 
-        printf("Reset Cat1! L28 low\r\n");
-    }
-#endif
-
-    return 0;
-}
-
-
-uint32_t uapi_cat1_power_on(uint32_t pwron)
-{
-
-    pin_t bat_en_pin =  S_AGPIO_L23;
-    if( pwron == 0)
-    {
-        uapi_gpio_set_dir(bat_en_pin, GPIO_DIRECTION_OUTPUT);
-        uapi_gpio_set_val(bat_en_pin, GPIO_LEVEL_LOW);
-        printf("Power Cat1! %d low  \r\n",bat_en_pin);
-    }else{
-        uapi_gpio_set_dir(bat_en_pin, GPIO_DIRECTION_OUTPUT);
-        uapi_gpio_set_val(bat_en_pin, GPIO_LEVEL_HIGH);
-        printf("Power Cat1! %d high \r\n",bat_en_pin);
-    }
-    
-    return 0;
-}
-
-uint32_t uapi_cat1_bat_on(uint32_t pwron)
-{
-    pin_t bat_en_pin = 0;
-#if defined(BOARD_W9) || defined(BOARD_N9)
-    bat_en_pin =  S_AGPIO_L17;
-#elif defined(BOARD_LEINIAO)
-    bat_en_pin =  S_AGPIO_L18;
-#endif
-    if( pwron == 0)
-    {
-        uapi_gpio_set_dir(bat_en_pin, GPIO_DIRECTION_OUTPUT);
-        uapi_gpio_set_val(bat_en_pin, GPIO_LEVEL_LOW);
-        printf("BAT OFF Cat1! %d low  \r\n",bat_en_pin);
-    }else{
-        uapi_gpio_set_dir(bat_en_pin, GPIO_DIRECTION_OUTPUT);
-        uapi_gpio_set_val(bat_en_pin, GPIO_LEVEL_HIGH);
-        printf("BAT ON Cat1! %d high \r\n",bat_en_pin);
-    }
-
-    
-    return 0;
-}
-
-void power_on_cat1(void)
+void cat1_api_power_on_cat1(void)
 {
     #if defined(BOARD_FD230)
-    uapi_cat1_power_on(1);    
-    uapi_cat1_reset(0);
+    cat1_api_cat1_pwrkey_on(1);    
+    cat1_api_cat1_reset(0);
     #elif (defined(BOARD_W9) || defined(BOARD_N9) || defined(BOARD_LEINIAO))
-    uapi_cat1_reset(0);
-    uapi_cat1_power_on(1);  
-    uapi_cat1_bat_on(1);
+    cat1_api_cat1_reset(0);
+    cat1_api_cat1_pwrkey_on(1);  
+    cat1_api_cat1_bat_on(1);
     osDelay(900);
-    uapi_cat1_power_on(0);  
+    cat1_api_cat1_pwrkey_on(0);  
     #endif
  
 }
 
-void power_off_cat1(void)
+void cat1_api_power_off_cat1(void)
 {
     #if defined(BOARD_FD230)
-    uapi_cat1_reset(1);
-    uapi_cat1_power_on(0); 
+    cat1_api_cat1_reset(1);
+    cat1_api_cat1_pwrkey_on(0); 
     #elif (defined(BOARD_W9) || defined(BOARD_N9) || defined(BOARD_LEINIAO))
-    uapi_cat1_bat_on(0);
-    uapi_cat1_reset(1);
-    uapi_cat1_power_on(1); 
+    cat1_api_cat1_bat_on(0);
+    cat1_api_cat1_reset(1);
+    cat1_api_cat1_pwrkey_on(1); 
     #endif
 }
 
index 2b6ad00..1bbdc4e 100644 (file)
@@ -396,7 +396,7 @@ void cat1_api_enable_cat1(bool isopen)
         tcpip_thread_resume();
         slipif_loop_thread_resume();
 #endif
-        power_on_cat1();
+        cat1_api_power_on_cat1();
         // if(!cat1_api_runtime_get_saveele_status())
         {
             cat1_api_runtime_set_cat1_powerstat(1);
@@ -412,7 +412,7 @@ void cat1_api_enable_cat1(bool isopen)
         tcpip_thread_suspend();
         slipif_loop_thread_suspend();
 #endif
-        power_off_cat1();
+        cat1_api_power_off_cat1();
         // if(!cat1_api_runtime_get_saveele_status())
         {
             cat1_api_runtime_set_cat1_powerstat(0);
index e33eb45..cb38508 100644 (file)
@@ -19,24 +19,20 @@ extern "C" {
 #include "platform_core.h"\r
 \r
 \r
-void cat1_api_gpio_init(void);\r
+\r
 void cat1_api_uart_init(void);\r
 \r
 /**set powerpin 1/0*/\r
-uint32_t cat1_api_cat1_power_on(uint32_t pwron);\r
+uint32_t cat1_api_cat1_pwrkey_on(uint32_t pwron);\r
 /**set resetpin 0/1*/\r
 uint32_t cat1_api_cat1_reset(uint32_t pwron);\r
 \r
 uint32_t cat1_api_cat1_bat_on(uint32_t pwron);\r
 \r
 /**set powerpin 1 ,set resetpin 0*/\r
-void power_on_cat1(void);\r
+void cat1_api_power_on_cat1(void);\r
 /**set powerpin 0 ,set resetpin 1*/\r
-void power_off_cat1(void);\r
-\r
-uint32_t uapi_cat1_reset(uint32_t pwron);\r
-uint32_t uapi_cat1_power_on(uint32_t pwron);\r
-uint32_t uapi_cat1_bat_on(uint32_t pwron);\r
+void cat1_api_power_off_cat1(void);\r
 \r
 void cat1_api_wakeup_cat1(uint8_t wakeup);\r
 void cat1_api_keep_wakeup(uint8_t keep_wakeup);\r
diff --git a/code/sdk/zhihui/4g/verify_slip_config.sh b/code/sdk/zhihui/4g/verify_slip_config.sh
new file mode 100644 (file)
index 0000000..acc3f56
--- /dev/null
@@ -0,0 +1,211 @@
+#!/bin/bash
+
+# SLIP配置验证脚本
+# 用于验证SLIP模块的编译时配置是否正确
+
+echo "=========================================="
+echo "SLIP模块配置验证脚本"
+echo "=========================================="
+
+# 检查配置文件是否存在
+CONFIG_FILE="cat1Service/CMakeLists.txt"
+if [ ! -f "$CONFIG_FILE" ]; then
+    echo "❌ 错误: 配置文件 $CONFIG_FILE 不存在"
+    exit 1
+fi
+
+echo "✅ 配置文件存在: $CONFIG_FILE"
+
+# 检查SLIP编译定义(在PUBLIC_DEFINES中)
+echo ""
+echo "检查SLIP编译定义 (PUBLIC_DEFINES):"
+echo "=================================="
+
+SLIP_DEFINES=(
+    "SLIP_MODULE_ENABLED=1"
+    "LWIP_SLIP=1" 
+    "SLIP_SUPPORT=1"
+    "SLIP_USE_RX_THREAD=1"
+    "SLIP_RX_FROM_ISR=1"
+    "SLIP_IFNUM=0"
+    "SLIP_UART_BUS=UART_BUS_0"
+    "SLIP_UART_BAUDRATE=921600"
+    "SLIP_UART_TX_PIN=S_MGPIO19"
+    "SLIP_UART_RX_PIN=S_MGPIO20"
+    "SLIP_UART_DATA_BIT=UART_DATA_BIT_8"
+    "SLIP_UART_PARITY=UART_PARITY_NONE"
+    "SLIP_UART_STOP_BIT=UART_STOP_BIT_1"
+    "SLIP_UART_FLOW_CTRL=UART_FLOW_CTRL_NONE"
+    "SLIP_DEBUG=LWIP_DBG_ON"
+)
+
+for define in "${SLIP_DEFINES[@]}"; do
+    if grep -q "$define" "$CONFIG_FILE"; then
+        echo "✅ $define"
+    else
+        echo "❌ 缺失: $define"
+    fi
+done
+
+# 检查基础服务定义
+echo ""
+echo "检查基础服务定义:"
+echo "=================="
+
+if grep -q "SUPPORT_CAT1_SERVICE" "$CONFIG_FILE"; then
+    echo "✅ SUPPORT_CAT1_SERVICE"
+else
+    echo "❌ 缺失: SUPPORT_CAT1_SERVICE"
+fi
+
+# 检查依赖组件
+echo ""
+echo "检查依赖模块存在性:"
+echo "=================="
+
+DEPENDS_COMPONENTS=(
+    "uart"
+    "osal" 
+    "lwip"
+    "securec"
+    "debug_print"
+)
+
+echo "注意:DEPENDS和LINK_LIBS在当前LiteOS-M构建系统中无效"
+echo "但以下模块在系统中确实存在:"
+
+for component in "${DEPENDS_COMPONENTS[@]}"; do
+    case $component in
+        "uart")
+            if [ -f "../../../drivers/drivers/driver/uart/CMakeLists.txt" ]; then
+                echo "✅ $component (drivers/drivers/driver/uart/)"
+            else
+                echo "❌ $component 模块不存在"
+            fi
+            ;;
+        "osal")
+            if [ -d "../../../kernel/osal" ]; then
+                echo "✅ $component (kernel/osal/)"
+            else
+                echo "❌ $component 模块不存在"
+            fi
+            ;;
+        "lwip")
+            if [ -d "../../../open_source/openharmony/third_party/lwip" ]; then
+                echo "✅ $component (third_party/lwip/)"
+            else
+                echo "❌ $component 模块不存在"
+            fi
+            ;;
+        "securec")
+            if find ../../../ -name "*securec*" -type f 2>/dev/null | head -1 >/dev/null; then
+                echo "✅ $component (安全C库)"
+            else
+                echo "❌ $component 模块不存在"
+            fi
+            ;;
+        "debug_print")
+            if [ -f "../../../middleware/utils/common_headers/debug_print.h" ]; then
+                echo "✅ $component (middleware/utils/common_headers/)"
+            else
+                echo "❌ $component 模块不存在"
+            fi
+            ;;
+    esac
+done
+
+# 检查链接库
+echo ""
+echo "检查链接库说明:"
+echo "=============="
+
+echo "注意:LINK_LIBS在LiteOS-M构建系统中无效"
+echo "实际链接由构建系统自动处理"
+
+# 检查SLIP相关代码使用
+echo ""
+echo "检查SLIP代码使用:"
+echo "================="
+
+SLIP_FUNCTIONS=(
+    "slipif_loop_thread_resume"
+    "slipif_loop_thread_suspend"
+)
+
+for func in "${SLIP_FUNCTIONS[@]}"; do
+    if find Service -name "*.c" -exec grep -l "$func" {} \; 2>/dev/null | head -1 >/dev/null; then
+        echo "✅ 找到SLIP函数调用: $func"
+    else
+        echo "⚠️  未找到SLIP函数调用: $func"
+    fi
+done
+
+# 检查配置结构
+echo ""
+echo "检查配置结构:"
+echo "============="
+
+CONFIG_SECTIONS=(
+    "PUBLIC_DEFINES"
+    "PRIVATE_DEFINES"
+    "COMPONENT_PUBLIC_CCFLAGS"
+    "COMPONENT_CCFLAGS"
+    "DEPENDS"
+    "WHOLE_LINK"
+    "MAIN_COMPONENT"
+)
+
+for section in "${CONFIG_SECTIONS[@]}"; do
+    if grep -q "set($section" "$CONFIG_FILE"; then
+        echo "✅ $section"
+    else
+        echo "❌ 缺失配置段: $section"
+    fi
+done
+
+# 检查lwIP配置
+echo ""
+echo "检查lwIP配置:"
+echo "============="
+
+LWIP_CONFIG="../../../open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/include/lwip/lwipopts_default.h"
+if [ -f "$LWIP_CONFIG" ]; then
+    echo "✅ lwIP配置文件存在"
+    
+    if grep -q "SLIP_MODULE_ENABLED.*PPP_MODULE_ENABLED" "$LWIP_CONFIG"; then
+        echo "✅ 找到SLIP/PPP冲突检测机制"
+    else
+        echo "⚠️  未找到SLIP/PPP冲突检测机制"
+    fi
+else
+    echo "❌ lwIP配置文件不存在: $LWIP_CONFIG"
+fi
+
+# 总结
+echo ""
+echo "=========================================="
+echo "配置验证完成"
+echo "=========================================="
+
+echo ""
+echo "配置结构对比:"
+echo "============"
+echo "SLIP模块: zhihui/4g/cat1Service/CMakeLists.txt"
+echo "PPP模块:  zhihui/ppp/CMakeLists.txt"
+echo ""
+echo "两个模块现在使用相同的配置结构:"
+echo "- PUBLIC_DEFINES: 协议相关定义"
+echo "- DEPENDS: 依赖组件"
+echo "- 规范的注释和分段"
+
+echo ""
+echo "使用说明:"
+echo "--------"
+echo "1. 编译时启用SLIP: cmake -DSLIP_MODULE_ENABLED=1 ..."
+echo "2. 编译时启用PPP:  cmake -DPPP_MODULE_ENABLED=1 ..."
+echo "3. 默认使用SLIP:   cmake ..."
+echo ""
+echo "硬件配置:"
+echo "--------"
+echo "SLIP: UART_BUS_0, TX=S_MGPIO19, RX=S_MGPIO20, 921600bps"
+echo "PPP:  UART_BUS_1, TX=S_MGPIO33, RX=S_MGPIO34, 115200bps" 
\ No newline at end of file
index 73d2359..874d31c 100644 (file)
@@ -23,3 +23,4 @@ add_subdirectory_if_exist(ai_old)
 add_subdirectory_if_exist(ai/media)
 add_subdirectory_if_exist(websocket)
 add_subdirectory_if_exist(4gTestcpp)
+add_subdirectory_if_exist(ppp)
diff --git a/code/sdk/zhihui/SERIAL_PROTOCOL_CONFIG.md b/code/sdk/zhihui/SERIAL_PROTOCOL_CONFIG.md
new file mode 100644 (file)
index 0000000..0519ecb
--- /dev/null
@@ -0,0 +1 @@
+ 
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/CMakeLists.txt b/code/sdk/zhihui/ppp/CMakeLists.txt
new file mode 100644 (file)
index 0000000..eedeb85
--- /dev/null
@@ -0,0 +1,101 @@
+# PPP拨号模块
+cmake_minimum_required(VERSION 3.16)
+
+# 设置模块名称
+set(COMPONENT_NAME ppp)
+
+
+set(PPP_AT_CMD_SOURCE 
+    ${CMAKE_CURRENT_SOURCE_DIR}/example/ppp_at_cmd.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/example/ppp_example.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/example/ppp_power_example.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/example/api_shell.c
+)
+# 源文件列表
+set(SOURCES
+    src/ppp_api.c
+    src/ppp_at_handler.c
+    src/ppp_serial.c
+    src/ppp_lwip.c
+    src/ppp_debug.c
+    src/ppp_power_ctl.c
+    src/ppp_cat1_api.c
+    ${PPP_AT_CMD_SOURCE}
+)
+
+# 头文件目录
+set(PUBLIC_HEADER
+    include/ppp_api.h
+    include/ppp_config.h
+    include/ppp_types.h
+    include/ppp_power_ctl.h
+    include/ppp_cat1_api.h
+)
+
+set(PRIVATE_HEADER
+    include/ppp_at_handler.h
+    include/ppp_serial.h
+    include/ppp_lwip.h
+    include/ppp_debug.h
+)
+
+# 包含目录
+set(INCLUDE_DIRS
+    include
+    ${ROOT_DIR}/include/middleware/utils/at
+    ${ROOT_DIR}/include/middleware/utils/dfx
+    ${ROOT_DIR}/include/kernel/osal
+    ${ROOT_DIR}/include/drivers/uart
+    ${ROOT_DIR}/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/include
+    ${ROOT_DIR}/open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/include
+)
+
+# PPP协议编译时配置
+set(PUBLIC_DEFINES
+    LWIP_PPP_SUPPORT=1
+    PPP_MODULE_ENABLED=1
+    PPP_SUPPORT=1
+    PPPOS_SUPPORT=1
+    PPP_IPV4_SUPPORT=1
+    PAP_SUPPORT=1
+    CHAP_SUPPORT=1
+    LWIP_PPP_API=1
+    PPP_NOTIFY_PHASE=1
+    MEMP_NUM_PPP_PCB=2
+    MEMP_NUM_PPPOS_INTERFACES=2
+    PPP_USE_PBUF_RAM=1
+    PPP_FCS_TABLE=1
+    PRINTPKT_SUPPORT=1
+    PPP_DEBUG=LWIP_DBG_ON
+    PPP_INPROC_IRQ_SAFE=0
+    SUPPORT_PPP_SERVICE
+    SUPPORT_AT_PPP
+)
+
+set(PRIVATE_DEFINES
+)
+
+# PPP协议编译选项
+set(COMPONENT_PUBLIC_CCFLAGS
+)
+
+# 编译选项
+set(COMPONENT_CCFLAGS
+    -Wall
+    -Wextra
+    -Werror
+    -O2
+)
+
+# 是否整体链接
+set(WHOLE_LINK
+    false
+)
+
+# 是否为主组件
+set(MAIN_COMPONENT
+    false
+)
+
+# 构建组件
+build_component() 
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/README.md b/code/sdk/zhihui/ppp/README.md
new file mode 100644 (file)
index 0000000..0519ecb
--- /dev/null
@@ -0,0 +1 @@
+ 
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/README_CAT1_COMPATIBILITY.md b/code/sdk/zhihui/ppp/README_CAT1_COMPATIBILITY.md
new file mode 100644 (file)
index 0000000..0519ecb
--- /dev/null
@@ -0,0 +1 @@
+ 
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/README_POWER_CONTROL.md b/code/sdk/zhihui/ppp/README_POWER_CONTROL.md
new file mode 100644 (file)
index 0000000..0519ecb
--- /dev/null
@@ -0,0 +1 @@
+ 
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/build_test.sh b/code/sdk/zhihui/ppp/build_test.sh
new file mode 100644 (file)
index 0000000..76f06d3
--- /dev/null
@@ -0,0 +1,195 @@
+#!/bin/bash
+
+# PPP模块编译测试脚本
+# 用于验证PPP模块的编译完整性
+
+echo "=========================================="
+echo "PPP Module Build Test"
+echo "=========================================="
+
+# 设置项目根目录
+PROJECT_ROOT=$(cd "$(dirname "$0")/../../.." && pwd)
+PPP_DIR="$PROJECT_ROOT/zhihui/ppp"
+
+echo "Project Root: $PROJECT_ROOT"
+echo "PPP Directory: $PPP_DIR"
+
+# 检查必要文件是否存在
+echo ""
+echo "Checking required files..."
+
+required_files=(
+    "include/ppp_api.h"
+    "include/ppp_config.h"
+    "include/ppp_types.h"
+    "include/ppp_at_handler.h"
+    "include/ppp_serial.h"
+    "include/ppp_lwip.h"
+    "include/ppp_debug.h"
+    "src/ppp_api.c"
+    "src/ppp_at_handler.c"
+    "src/ppp_serial.c"
+    "src/ppp_lwip.c"
+    "src/ppp_debug.c"
+    "CMakeLists.txt"
+    "README.md"
+)
+
+missing_files=0
+for file in "${required_files[@]}"; do
+    if [ -f "$PPP_DIR/$file" ]; then
+        echo "✓ $file"
+    else
+        echo "✗ $file (MISSING)"
+        missing_files=$((missing_files + 1))
+    fi
+done
+
+if [ $missing_files -gt 0 ]; then
+    echo ""
+    echo "ERROR: $missing_files required files are missing!"
+    exit 1
+fi
+
+echo ""
+echo "All required files found!"
+
+# 检查头文件包含关系
+echo ""
+echo "Checking header dependencies..."
+
+# 简单的语法检查(如果有gcc可用)
+if command -v gcc >/dev/null 2>&1; then
+    echo "Running syntax check with GCC..."
+    
+    # 创建临时测试文件
+    test_file="/tmp/ppp_test.c"
+    cat > "$test_file" << 'EOF'
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+// 模拟系统头文件
+#define ERRCODE_SUCC 0
+#define OSAL_SUCCESS 0
+#define OSAL_WAIT_FOREVER -1
+#define UART_BUS_1 1
+#define S_MGPIO33 33
+#define S_MGPIO34 34
+#define PIN_NONE 0xFF
+#define UART_DATA_BIT_8 8
+#define UART_PARITY_NONE 0
+#define UART_STOP_BIT_1 1
+#define UART_RX_CONDITION_FULL_OR_SUFFICIENT_DATA_OR_IDLE 1
+
+typedef int errcode_t;
+typedef void* osal_mutex;
+typedef void* osal_semaphore;
+typedef void* osal_timer;
+typedef void* osal_task;
+
+typedef struct {
+    uint8_t tx_pin;
+    uint8_t rx_pin;
+    uint8_t cts_pin;
+    uint8_t rts_pin;
+} uart_pin_config_t;
+
+typedef struct {
+    uint32_t baud_rate;
+    uint8_t data_bits;
+    uint8_t parity;
+    uint8_t stop_bits;
+} uart_attr_t;
+
+typedef struct {
+    uint32_t rx_buffer_size;
+    uint8_t *rx_buffer;
+} uart_buffer_config_t;
+
+// 模拟函数声明
+#define PRINT printf
+#define osal_kmalloc malloc
+#define osal_kfree free
+#define osal_get_tick() 0
+#define osal_msleep(x) 
+int osal_mutex_init(osal_mutex *mutex) { return 0; }
+int osal_mutex_destroy(osal_mutex mutex) { return 0; }
+int osal_mutex_lock(osal_mutex mutex) { return 0; }
+int osal_mutex_unlock(osal_mutex mutex) { return 0; }
+int osal_sem_init(osal_semaphore *sem, int value) { return 0; }
+int osal_sem_destroy(osal_semaphore sem) { return 0; }
+int osal_sem_wait(osal_semaphore sem, int timeout) { return 0; }
+int osal_sem_signal(osal_semaphore sem) { return 0; }
+int memset_s(void *dest, size_t destMax, int c, size_t count) { return 0; }
+int memcpy_s(void *dest, size_t destMax, const void *src, size_t count) { return 0; }
+int snprintf_s(char *strDest, size_t destMax, size_t count, const char *format, ...) { return 0; }
+
+EOF
+
+    # 添加PPP头文件内容进行语法检查
+    echo '#include "'$PPP_DIR'/include/ppp_config.h"' >> "$test_file"
+    echo '#include "'$PPP_DIR'/include/ppp_types.h"' >> "$test_file"
+    echo '#include "'$PPP_DIR'/include/ppp_debug.h"' >> "$test_file"
+    echo '#include "'$PPP_DIR'/include/ppp_api.h"' >> "$test_file"
+    
+    echo "int main() { return 0; }" >> "$test_file"
+    
+    if gcc -fsyntax-only -I"$PPP_DIR/include" "$test_file" 2>/dev/null; then
+        echo "✓ Header syntax check passed"
+    else
+        echo "✗ Header syntax check failed"
+        echo "Running detailed check..."
+        gcc -fsyntax-only -I"$PPP_DIR/include" "$test_file"
+    fi
+    
+    rm -f "$test_file"
+else
+    echo "GCC not available, skipping syntax check"
+fi
+
+# 统计代码行数
+echo ""
+echo "Code statistics:"
+total_lines=0
+for file in src/*.c include/*.h; do
+    if [ -f "$PPP_DIR/$file" ]; then
+        lines=$(wc -l < "$PPP_DIR/$file")
+        echo "  $file: $lines lines"
+        total_lines=$((total_lines + lines))
+    fi
+done
+echo "  Total: $total_lines lines"
+
+# 检查文档
+echo ""
+echo "Documentation:"
+if [ -f "$PPP_DIR/README.md" ]; then
+    readme_lines=$(wc -l < "$PPP_DIR/README.md")
+    echo "  README.md: $readme_lines lines"
+fi
+
+if [ -f "$PPP_DIR/example/ppp_example.c" ]; then
+    example_lines=$(wc -l < "$PPP_DIR/example/ppp_example.c")
+    echo "  Example code: $example_lines lines"
+fi
+
+echo ""
+echo "=========================================="
+echo "PPP Module Build Test Completed"
+echo "=========================================="
+echo ""
+echo "Summary:"
+echo "  ✓ All required files present"
+echo "  ✓ Header dependencies OK"
+echo "  ✓ Code statistics generated"
+echo "  ✓ Documentation available"
+echo ""
+echo "The PPP module is ready for integration!"
+echo ""
+echo "Next steps:"
+echo "1. Add PPP module to main CMakeLists.txt"
+echo "2. Configure lwIP with PPP support"
+echo "3. Test with actual hardware"
+echo "" 
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/doc/PPP拨号说明.txt b/code/sdk/zhihui/ppp/doc/PPP拨号说明.txt
new file mode 100644 (file)
index 0000000..ba79bbc
--- /dev/null
@@ -0,0 +1,21 @@
+版本说明:\r
+       该版本AT串口默认为波特率自适应模式,开机以自适应波特率列表连接串口,发送AT完成握手。\r
+自适应波特率(9600,57600,115200),固定波特率(4800,9600,57600,115200,230400,460800,750000,921600,1000000,2000000,4000000)\r
+\r
+拨号验证:\r
+1、连接AT串口,发送AT完成波特率握手\r
+2、发送AT+IPR=2000000,等待回复OK。更改AT串口为2M固定波特率,该指令掉电保持,下次开机AT串口波特率仍固定为2M。\r
+3、打开Ubuntu虚拟机\r
+4、将文件chat拷贝到 /etc/ppp/\r
+5、将文件gprs-dial拷贝到 /etc/ppp/peers/\r
+6、将AT串口连接到Ubuntu虚拟机,修改gprs-dial文件,填写正确的串口设备节点\r
+7、将Ubuntu虚拟机网卡先ifconfig down避免后面配置DNS问题\r
+8、执行pppd call gprs-dial &进行拨号,拨号完成会打印四个IP并创建ppp0网卡:\r
+       local  IP address 10.46.33.237\r
+       remote IP address 10.64.64.64\r
+       primary   DNS address 61.139.2.69\r
+       secondary DNS address 218.6.200.139\r
+9、修改/etc/resolv.conf,注释掉已配置的DNS,添加 nameserver 61.139.2.69,即将primary   DNS address添加为DNS IP\r
+10、测试网速,可以通过wget命令下载一个文件查看网速\r
+\r
+       
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/doc/chat b/code/sdk/zhihui/ppp/doc/chat
new file mode 100644 (file)
index 0000000..e8383a0
--- /dev/null
@@ -0,0 +1,8 @@
+TIMEOUT 5
+ABORT "BUSY"
+ABORT "ERROR"
+ABORT "NO CARRIER"
+'' \rAT
+OK 'AT+CGDCONT=3,"IP"'
+OK 'ATD*99***2#'
+CONNECTED ''
diff --git a/code/sdk/zhihui/ppp/doc/gprs-dial b/code/sdk/zhihui/ppp/doc/gprs-dial
new file mode 100644 (file)
index 0000000..85ddbff
--- /dev/null
@@ -0,0 +1,18 @@
+/dev/ttyUSB0
+2000000
+modem
+nocrtscts
+nocdtrcts
+local
+debug
+nodetach
+#nobsdcomp
+#nodeflate
+#novj
+usepeerdns
+defaultroute
+noipdefault
+ipcp-accept-local
+ipcp-accept-remote
+lock
+connect '/usr/sbin/chat -s -v -f /etc/ppp/chat'
diff --git a/code/sdk/zhihui/ppp/example/api_shell.c b/code/sdk/zhihui/ppp/example/api_shell.c
new file mode 100644 (file)
index 0000000..7582a1e
--- /dev/null
@@ -0,0 +1,799 @@
+/*
+ * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
+ * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
+ *
+ * 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.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used
+ *    to endorse or promote products derived from this software without specific prior written
+ *    permission.
+ *
+ * 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.
+ */
+
+#define LWIP_STATIC static
+#define DHCP_STATE_OFF 0
+#define LWIP_EXT_POLL_SUPPORT 1
+#define LOOPBACK_IF 0
+
+#include "lwip/def.h"
+#include "lwip/api.h"
+#include "lwip/inet.h"
+#include "lwip/netdb.h"
+#include "lwip/stats.h"
+#include "lwip/err.h"
+#include "lwip/ip_addr.h"
+#include "lwip/icmp.h"
+#include "lwip/tcpip.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/ip.h"
+#include "lwip/ip4.h"
+#include "lwip/prot/ip4.h"
+#include "lwip/sockets.h"
+#include "lwip/netif.h"
+#include "lwip/netif.h"
+
+/* Define htons function if not available */
+#ifndef htons
+static inline u16_t htons(u16_t n)
+{
+    return ((n & 0xFF) << 8) | ((n & 0xFF00) >> 8);
+}
+#endif
+#include "los_config.h"
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
+// #include "los_debug.h"
+#include "lwip/netif.h"
+#include "los_task.h"
+
+/* Forward Declarations [START] */
+#ifndef LWIP_TESTBED
+LWIP_STATIC
+#endif
+int print_netif(struct netif *netif, char *print_buf, unsigned int buf_len);
+
+#ifndef LWIP_TESTBED
+LWIP_STATIC
+#endif
+void lwip_ifconfig_show_internal(void *arg);
+
+#if LWIP_DNS
+LWIP_STATIC unsigned int get_hostip(const char *hname);
+
+#ifndef LWIP_TESTBED
+LWIP_STATIC
+#endif
+struct hostent *gethostnameinfo(const char *host, char *tmphstbuf, size_t hstbuflen);
+#endif /* LWIP_DNS */
+
+#define PRINT_BUF_LEN 1024
+
+struct ifconfig_option {
+    char iface[IFNAMSIZ];
+    unsigned int option;
+    ip_addr_t ip_addr;
+    ip_addr_t netmask;
+    ip_addr_t gw;
+    unsigned char ethaddr[6];
+    u16_t mtu;
+    /* when using telnet, print to the telnet socket will result in system  */
+    /* deadlock. So we cahe the prinf data to a buf, and when the tcpip      */
+    /* callback returns, then print the data out to the telnet socket       */
+    sys_sem_t cb_completed;
+    char cb_print_buf[PRINT_BUF_LEN];
+    unsigned int print_len;
+};
+
+static char result_data[256] = {0};
+static char hostname[20] = {0};
+
+void get_ping_hostname(char *databuf,uint8_t len)
+{
+    memcpy_s(databuf,len,hostname,strlen(hostname));
+}
+
+void get_ping_result(char *databuf,uint16_t len)
+{
+    if (databuf == NULL || len <= 0) 
+    {
+        PRINTK("Invalid input for get_ping_result. len : %d\n",len);
+        return;
+    }
+    size_t resultLen = strlen(result_data);
+    if (resultLen >= len) 
+    {
+        PRINTK("Insufficient buffer size for databuf.\n");
+        len = resultLen + 1;
+    }
+    memcpy(databuf, result_data, resultLen);
+    databuf[resultLen] = '\0';
+    // PRINTK("result_data : %s\n", result_data);
+    // PRINTK("databuf : %s\n", databuf);
+    // memset(result_data,0,sizeof(result_data));
+}
+
+static void get_log(char* str, int arrSize) 
+{
+    if (str == NULL || arrSize <= 0) {
+        PRINTK("Invalid input for get_log.\n");
+        return;
+    }
+    if (arrSize >= (int)sizeof(result_data)) {
+        PRINTK("Input size too large for result_data.\n");
+        arrSize = sizeof(result_data) - 1;
+    }
+    memcpy(result_data, str, arrSize);
+    result_data[arrSize] = '\0';
+    // PRINTK("result_data : %s\n", result_data);
+}
+
+#ifndef LWIP_TESTBED
+LWIP_STATIC
+#endif
+int print_netif(struct netif *netif, char *print_buf, unsigned int buf_len)
+{
+    int i, ret;
+    char *tmp = print_buf;
+
+    if (buf_len < 1) {
+        goto out;
+    }
+    /* Check if this is a loopback interface by name */
+    if (strstr(netif->name, "lo") != NULL) {
+        ret = snprintf_s(tmp, buf_len, (buf_len - 1), "%s\t", netif->name);
+    } else {
+        ret = snprintf_s(tmp, buf_len, (buf_len - 1), "%s%u\t", netif->name, netif->num);
+    }
+
+    if ((ret <= 0) || ((unsigned int)ret >= buf_len))
+        goto out;
+    tmp += ret;
+    buf_len -= (unsigned int)ret;
+#if LWIP_IPV4
+    ret = snprintf_s(tmp, buf_len, (buf_len - 1), "ip:%s ", ipaddr_ntoa(&netif->ip_addr));
+    if ((ret <= 0) || ((unsigned int)ret >= buf_len))
+        goto out;
+    tmp += ret;
+    buf_len -= (unsigned int)ret;
+
+    ret = snprintf_s(tmp, buf_len, (buf_len - 1), "netmask:%s ", ipaddr_ntoa(&netif->netmask));
+    if ((ret <= 0) || ((unsigned int)ret >= buf_len))
+        goto out;
+    tmp += ret;
+    buf_len -= (unsigned int)ret;
+
+    ret = snprintf_s(tmp, buf_len, (buf_len - 1), "gateway:%s\n", ipaddr_ntoa(&netif->gw));
+    if ((ret <= 0) || ((unsigned int)ret >= buf_len))
+        goto out;
+    tmp += ret;
+    buf_len -= (unsigned int)ret;
+#endif
+    ret = snprintf_s(tmp, buf_len, (buf_len - 1), "\tHWaddr ");
+    if ((ret <= 0) || ((unsigned int)ret >= buf_len))
+        goto out;
+    tmp += ret;
+    buf_len -= (unsigned int)ret;
+
+    for (i = 0; i < netif->hwaddr_len - 1; i++) {
+        ret = snprintf_s(tmp, buf_len, (buf_len - 1), "%02x:", netif->hwaddr[i]);
+        if ((ret <= 0) || ((unsigned int)ret >= buf_len))
+            goto out;
+        tmp += ret;
+        buf_len -= (unsigned int)ret;
+    }
+
+    ret = snprintf_s(tmp, buf_len, (buf_len - 1), "%02x", netif->hwaddr[i]);
+    if ((ret <= 0) || ((unsigned int)ret >= buf_len))
+        goto out;
+    tmp += ret;
+    buf_len -= (unsigned int)ret;
+
+    ret = snprintf_s(tmp, buf_len, (buf_len - 1), " MTU:%d %s", netif->mtu,
+                     netif->flags & NETIF_FLAG_UP ? "Running" : "Stop");
+    if ((ret <= 0) || ((unsigned int)ret >= buf_len))
+        goto out;
+    tmp += ret;
+    buf_len -= (unsigned int)ret;
+
+    if (netif_default == netif && netif_is_up(netif)) {
+        ret = snprintf_s(tmp, buf_len, (buf_len - 1), " %s", "Default");
+        if ((ret <= 0) || ((unsigned int)ret >= buf_len))
+            goto out;
+        tmp += ret;
+        buf_len -= (unsigned int)ret;
+    }
+
+    ret = snprintf_s(tmp, buf_len, (buf_len - 1), " %s\n", netif->flags & NETIF_FLAG_LINK_UP ? "Link UP" : "Link Down");
+    if ((ret <= 0) || ((unsigned int)ret >= buf_len))
+        goto out;
+    tmp += ret;
+
+out:
+    return (int)(tmp - print_buf);
+}
+
+#ifndef LWIP_TESTBED
+LWIP_STATIC
+#endif
+void lwip_ifconfig_show_internal(void *arg)
+{
+    struct netif *netif = NULL;
+    struct ifconfig_option *ifconfig_cmd = (struct ifconfig_option *)arg;
+    int ret;
+
+    if (netif_list == NULL) {
+        ret = snprintf_s(ifconfig_cmd->cb_print_buf, PRINT_BUF_LEN - ifconfig_cmd->print_len,
+                         ((PRINT_BUF_LEN - ifconfig_cmd->print_len) - 1), "Device not init\n");
+        if ((ret > 0) && ((unsigned int)ret < (PRINT_BUF_LEN - ifconfig_cmd->print_len))) {
+            ifconfig_cmd->print_len += (unsigned int)ret;
+        }
+        sys_sem_signal(&ifconfig_cmd->cb_completed);
+        return;
+    }
+
+    if (ifconfig_cmd->iface[0] == '\0') {
+        /* display all netif */
+        for (netif = netif_list; netif != NULL; netif = netif->next) {
+            ret = print_netif(netif, ifconfig_cmd->cb_print_buf + ifconfig_cmd->print_len,
+                              PRINT_BUF_LEN - ifconfig_cmd->print_len);
+            ifconfig_cmd->print_len += (unsigned int)ret;
+        }
+    } else {
+        netif = netif_find(ifconfig_cmd->iface);
+        if (netif == NULL) {
+            ret = snprintf_s(ifconfig_cmd->cb_print_buf + ifconfig_cmd->print_len,
+                             (PRINT_BUF_LEN - ifconfig_cmd->print_len),
+                             ((PRINT_BUF_LEN - ifconfig_cmd->print_len) - 1), "Device not found\n");
+            if ((ret > 0) && ((unsigned int)ret < (PRINT_BUF_LEN - ifconfig_cmd->print_len))) {
+                ifconfig_cmd->print_len += (unsigned int)ret;
+            }
+
+            sys_sem_signal(&ifconfig_cmd->cb_completed);
+            return;
+        }
+
+        ret = print_netif(netif, ifconfig_cmd->cb_print_buf + ifconfig_cmd->print_len,
+                          PRINT_BUF_LEN - ifconfig_cmd->print_len);
+        ifconfig_cmd->print_len += (unsigned int)ret;
+    }
+    sys_sem_signal(&ifconfig_cmd->cb_completed);
+}
+
+u32_t lwip_ifconfig(int argc, const char **argv)
+{
+    static struct ifconfig_option ifconfig_cmd;
+    err_t ret;
+
+    (void)memset_s(&ifconfig_cmd, sizeof(ifconfig_cmd), 0, sizeof(ifconfig_cmd));
+    if (sys_sem_new(&ifconfig_cmd.cb_completed, 0) != ERR_OK) {
+        PRINTK("%s: sys_sem_new fail\n", __FUNCTION__);
+        return 1;
+    }
+
+    /* Get the interface */
+
+    /* no more arguments, show all the interface state. */
+    ret = tcpip_callback(lwip_ifconfig_show_internal, &ifconfig_cmd);
+    if (ret != ERR_OK) {
+        sys_sem_free(&ifconfig_cmd.cb_completed);
+        PRINTK("ifconfig : internal error, l:%d err:%d\n", __LINE__, ret);
+        return 1;
+    }
+    (void)sys_arch_sem_wait(&ifconfig_cmd.cb_completed, 0);
+    sys_sem_free(&ifconfig_cmd.cb_completed);
+    ifconfig_cmd.cb_print_buf[PRINT_BUF_LEN - 1] = '\0';
+    PRINTK("%s", ifconfig_cmd.cb_print_buf);
+
+    return 0;
+}
+
+#if LWIP_DNS
+#ifndef LWIP_TESTBED
+LWIP_STATIC
+#endif
+struct hostent *gethostnameinfo(const char *host, char *tmphstbuf, size_t hstbuflen)
+{
+    static struct hostent hostbuf;
+    struct hostent *hp = NULL;
+    int res;
+    int herr;
+
+    if (tmphstbuf == NULL)
+        return NULL;
+    res = lwip_gethostbyname_r(host, &hostbuf, tmphstbuf, hstbuflen, &hp, &herr);
+    /*  Check for errors.  */
+    if (res || hp == NULL) {
+        return NULL;
+    }
+    return hp;
+}
+
+LWIP_STATIC unsigned int get_hostip(const char *hname)
+{
+    unsigned int ip = 0;
+    errno_t ret;
+    const size_t hstbuflen = 1024;
+    char *tmphstbuf = NULL;
+
+    if (hname == NULL || strlen(hname) == 0) {
+        PRINTK("get_hostip: invalid hostname\n");
+        return 0;
+    }
+
+    tmphstbuf = (char *)zalloc(sizeof(char) * hstbuflen);
+    if (tmphstbuf == NULL) {
+        PRINTK("get_hostip: failed to allocate DNS buffer\n");
+        return 0;
+    }
+
+    struct hostent *pent = gethostnameinfo(hname, tmphstbuf, hstbuflen);
+    if (pent == NULL || pent->h_addr == NULL) {
+        PRINTK("get_hostip: DNS resolution failed for %s\n", hname);
+        free(tmphstbuf);
+        return 0;
+    }
+    ret = memcpy_s(&ip, sizeof(ip), pent->h_addr, sizeof(ip));
+    if (ret != EOK) {
+        PRINTK("get_hostip: failed to copy resolved IP address\n");
+        free(tmphstbuf);
+        return 0;
+    }
+    free(tmphstbuf);
+    return ip;
+}
+
+#endif
+
+static int ping_taskid = -1;
+static int ping_kill = 0;
+#define LWIP_SHELL_CMD_PING_TIMEOUT 2000
+#define LWIP_SHELL_CMD_PING_RETRY_TIMES 4
+#define PING_ZERO_DATA_LEN 8
+
+static void lwip_ping_usage(void)
+{
+    PRINTK("Usage:\n  ping [ip]\n");
+}
+
+LWIP_STATIC int OsPingFunc(u32_t *parg)
+{
+    int sfd;
+    struct sockaddr_in to;
+    struct pbuf *pbuf_resp = NULL;
+    struct icmp_echo_hdr *iecho = NULL;
+    struct icmp_echo_hdr *iecho_resp = NULL;
+    struct ip_hdr *iphdr_resp = NULL;
+    u32_t iecho_len;
+    s16_t ip_hlen;
+    u32_t forever;
+    u32_t i;
+    u32_t succ_cnt = 0;
+    u32_t failed_cnt = 0;
+    struct timespec start, end;
+    long timout_ms;
+    struct pollfd pfd;
+    long rtt;
+    int ret = 0;
+    u32_t intrvl;
+    char *data_buf = NULL;
+    BOOL timeout_flag = FALSE;
+    char buf[50];
+
+    u32_t destip = parg[0];
+    u32_t cnt = parg[1];
+    u32_t interval = parg[2];
+    u32_t data_len = parg[3];
+    char str[128] = {0};
+    iecho_len = sizeof(struct icmp_echo_hdr) + data_len;
+    PRINTK("OsPingFunc: Creating RAW socket for ICMP, destip=0x%X, cnt=%u, data_len=%u\n", 
+           destip, cnt, data_len);
+    
+    /* Check network interface status */
+    struct netif *netif = netif_default;
+    if (netif == NULL) {
+        PRINTK("OsPingFunc: No default network interface available\n");
+        get_log("No default network interface",strlen("No default network interface"));
+        return -1;
+    }
+    if (!netif_is_up(netif)) {
+        PRINTK("OsPingFunc: Default network interface is down\n");
+        get_log("Network interface is down",strlen("Network interface is down"));
+        return -1;
+    }
+    if (!netif_is_link_up(netif)) {
+        PRINTK("OsPingFunc: Network link is down\n");
+        get_log("Network link is down",strlen("Network link is down"));
+        return -1;
+    }
+    PRINTK("OsPingFunc: Network interface OK, IP=%s\n", ipaddr_ntoa(&netif->ip_addr));
+    
+    /* Check routing for external destinations */
+    ip4_addr_t dest_addr;
+    dest_addr.addr = destip;
+    
+    /* Check if destination is local (172.16.1.x) */
+    ip4_addr_t local_net, local_mask;
+    local_net.addr = ip4_addr_get_u32(&netif->ip_addr) & ip4_addr_get_u32(&netif->netmask);
+    local_mask.addr = ip4_addr_get_u32(&netif->netmask);
+    
+    ip4_addr_t dest_net;
+    dest_net.addr = dest_addr.addr & local_mask.addr;
+    
+    if (local_net.addr == dest_net.addr) {
+        PRINTK("OsPingFunc: Destination is on LOCAL network (172.16.1.x)\n");
+    } else {
+        PRINTK("OsPingFunc: Destination is EXTERNAL, requires gateway routing\n");
+        PRINTK("OsPingFunc: Gateway: %s\n", ipaddr_ntoa(&netif->gw));
+        
+        /* Check if gateway is configured */
+        if (ip4_addr_get_u32(&netif->gw) == IPADDR_ANY) {
+            PRINTK("OsPingFunc: ❌ No gateway configured for external routing!\n");
+            get_log("No gateway configured", strlen("No gateway configured"));
+            return -1;
+        }
+    }
+    
+    sfd = lwip_socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
+    if (sfd < 0) {
+        PRINTK("Ping socket creation failed, errno=%d\n", errno);
+        get_log("Ping socket creation failed",strlen("Ping socket creation failed"));
+        return -1;
+    }
+    PRINTK("OsPingFunc: Socket created successfully, fd=%d\n", sfd);
+    PRINTK("OsPingFunc: Allocating pbuf, size=%u\n", IP_HLEN + sizeof(struct icmp_echo_hdr));
+    pbuf_resp = pbuf_alloc(PBUF_RAW, IP_HLEN + sizeof(struct icmp_echo_hdr), PBUF_RAM);
+    if (pbuf_resp == NULL) {
+        PRINTK("Ping: pbuf_resp malloc failed, size=%u\n", IP_HLEN + sizeof(struct icmp_echo_hdr));
+        get_log("Ping: pbuf_resp malloc failed",strlen("Ping: pbuf_resp malloc failed"));
+        ret = -1;
+        goto FAILURE;
+    }
+    PRINTK("OsPingFunc: pbuf allocated successfully\n");
+    
+    PRINTK("OsPingFunc: Allocating ICMP echo request, size=%u\n", iecho_len);
+    iecho = (struct icmp_echo_hdr *)mem_malloc(iecho_len);
+    if (iecho == NULL) {
+        PRINTK("Ping: echo request malloc failed, size=%u\n", iecho_len);
+        get_log("Ping: echo request malloc failed",strlen("Ping: echo request malloc failed"));
+        ret = -1;
+        goto FAILURE;
+    }
+    PRINTK("OsPingFunc: ICMP echo request allocated successfully\n");
+
+    to.sin_family = AF_INET;
+    to.sin_addr.s_addr = destip; /* already in network order */
+    to.sin_port = 0;
+    
+    /* Print destination info for debugging */
+    char dest_ip_str[16];
+    inet_ntop(AF_INET, &to.sin_addr, dest_ip_str, sizeof(dest_ip_str));
+    PRINTK("OsPingFunc: Target IP=%s (0x%X), socket family=%d\n", dest_ip_str, destip, to.sin_family);
+
+    if (data_len > PING_ZERO_DATA_LEN) {
+        (void)memset_s(iecho, sizeof(struct icmp_echo_hdr) + PING_ZERO_DATA_LEN,
+                       0, sizeof(struct icmp_echo_hdr) + PING_ZERO_DATA_LEN);
+        data_buf = (char *)iecho + sizeof(struct icmp_echo_hdr) + PING_ZERO_DATA_LEN;
+        for (i = 0; i < data_len - PING_ZERO_DATA_LEN; i++) {
+            *(data_buf + i) = i + 0x10;
+        }
+    } else {
+        (void)memset_s(iecho, sizeof(struct icmp_echo_hdr) + data_len, 0, sizeof(struct icmp_echo_hdr) + data_len);
+    }
+    iecho->id = htons((u16_t)LOS_CurTaskIDGet());
+    ICMPH_TYPE_SET(iecho, (u8_t)ICMP_ECHO);
+    forever = (cnt ? 0 : 1);
+    i = 0;
+    while (!ping_kill && (forever || (i < cnt))) {
+        iecho->seqno = htons((u16_t)i);
+        iecho->chksum = 0;
+#if (CHECKSUM_GEN_ICMP > 0)
+        iecho->chksum = inet_chksum((void *)iecho, iecho_len);
+#endif
+        PRINTK("OsPingFunc: Sending ICMP echo request [%u], len=%u to 0x%X\n", i, iecho_len, destip);
+        ret = sendto(sfd, iecho, iecho_len, 0, (struct sockaddr *)&to, (socklen_t)sizeof(to));
+        if (ret < 0) {
+            PRINTK("Ping: sending ICMP echo request failed, ret=%d, errno=%d\n", ret, errno);
+            perror("Ping: sendto failed");
+            get_log("Ping: sending ICMP echo request failed",strlen("Ping: sending ICMP echo request failed"));
+            goto FAILURE;
+        }
+        PRINTK("OsPingFunc: ICMP echo request sent successfully, bytes=%d\n", ret);
+
+        /* capture the start time to calculate RTT */
+        (void)clock_gettime(CLOCK_MONOTONIC_RAW, &start);
+
+        /* poll for ICMP echo response msg */
+        pfd.fd = sfd;
+
+        do {
+            pfd.events = POLLIN;
+            pfd.revents = 0;
+            timeout_flag = FALSE;
+            ret = lwip_poll(&pfd, 1, LWIP_SHELL_CMD_PING_TIMEOUT);
+            if (ret < 0) {
+                PRINTK("Ping: poll failed, ret=%d, errno=%d\n", ret, errno);
+                perror("Ping: poll failed");
+                goto FAILURE;
+            } else if (ret == 0) {
+                /* first type timeout event */
+                PRINTK("Ping: poll timeout after %dms\n", LWIP_SHELL_CMD_PING_TIMEOUT);
+                timeout_flag = TRUE;
+                break;
+            }
+            PRINTK("OsPingFunc: poll returned %d, ready to receive\n", ret);
+
+            ret = recv(sfd, pbuf_resp->payload, pbuf_resp->len, MSG_DONTWAIT);
+            if (ret < 0) {
+                PRINTK("Ping: recv echo reply failed, ret=%d, errno=%d\n", ret, errno);
+                perror("Ping: recv failed");
+                get_log("Ping: recv echo reply failed",strlen("Ping: recv echo reply failed"));
+                goto FAILURE;
+            }
+            PRINTK("OsPingFunc: received %d bytes\n", ret);
+
+            /* Accessing ip header and icmp header */
+            iphdr_resp = pbuf_resp->payload;
+
+            ip_hlen = (IPH_HL(iphdr_resp) << 2); /* 2: offset */
+            if (pbuf_header(pbuf_resp, -ip_hlen)) {
+                /* this failure will never happen, but failure handle is written just to be in safe side */
+                PRINTK("Ping : memory management failure\n");
+                get_log("Ping : memory management failure",strlen("Ping : memory management failure"));
+                goto FAILURE;
+            }
+            iecho_resp = (struct icmp_echo_hdr *)pbuf_resp->payload;
+            /* Reverting back pbuf to its original state */
+            if (pbuf_header(pbuf_resp, ip_hlen)) {
+                /* this failure will never happen, but failure handle is written just to be in safe side */
+                PRINTK("ping : memory management failure\n");
+                get_log("ping : memory management failure",strlen("ping : memory management failure"));
+                goto FAILURE;
+            }
+
+            if ((iphdr_resp->src.addr != to.sin_addr.s_addr) ||
+                ((ICMPH_TYPE(iecho_resp) == ICMP_ECHO) && (iphdr_resp->src.addr == to.sin_addr.s_addr))) {
+                /* second type timeout event */
+                (void)clock_gettime(CLOCK_MONOTONIC_RAW, &end);
+                timout_ms = ((end.tv_sec - start.tv_sec) * OS_SYS_MS_PER_SECOND + \
+                             (end.tv_nsec - start.tv_nsec) / OS_SYS_NS_PER_MS);
+                timout_ms = LWIP_SHELL_CMD_PING_TIMEOUT - timout_ms;
+            } else {
+                timout_ms = 0;
+                break;
+            }
+        } while (timout_ms >= 0);
+
+        /* all timeout events are true timeout */
+        if ((timout_ms < 0) || (timeout_flag == TRUE)) {
+            failed_cnt++;
+            i++;
+            PRINTK("\nPing: destination unreachable ...");
+            get_log("Ping: destination unreachable ...",strlen("Ping: destination unreachable ..."));
+            continue;
+        }
+        /* capture the end time to calculate round trip time */
+        (void)clock_gettime(CLOCK_MONOTONIC_RAW, &end);
+        rtt = ((end.tv_sec - start.tv_sec) * OS_SYS_MS_PER_SECOND + \
+               (end.tv_nsec - start.tv_nsec) / OS_SYS_NS_PER_MS);
+
+        if (iphdr_resp->src.addr == to.sin_addr.s_addr) {
+            switch (ICMPH_TYPE(iecho_resp)) {
+                case ICMP_ER:
+                    PRINTK("\n[%u]Reply from %s: ", i, inet_ntop(AF_INET, &to.sin_addr, buf, sizeof(buf)));
+                    if (rtt > 0) {
+                        PRINTK("time=%ims ", rtt);
+                        memset(str,0,sizeof(str));
+                        sprintf(str,"[%u]Reply from %s: time=%ldms ",i, inet_ntop(AF_INET, &to.sin_addr, buf, sizeof(buf)),rtt);
+                        get_log(str,strlen(str));
+                    } else {
+                        PRINTK("time<1ms ");
+                        sprintf(str,"[%u]Reply from %s: time<1ms ",i, inet_ntop(AF_INET, &to.sin_addr, buf, sizeof(buf)));
+                        get_log(str,strlen(str));
+                    }
+                    PRINTK("TTL=%u", iphdr_resp->_ttl);
+
+                    /* delay 1s for every successful ping */
+                    intrvl = interval;
+                    do {
+                        if (intrvl < 1000) { /* 1000: 1000ms = 1s */
+                            sys_msleep(intrvl);
+                            break;
+                        }
+                        intrvl -= 1000; /* 1000: 1000ms = 1s */
+                        sys_msleep(1000); /* 1000: 1000ms = 1s */
+                        if (ping_kill == 1) {
+                            break;
+                        }
+                    } while (intrvl > 0);
+                    succ_cnt++;
+                    break;
+                case ICMP_DUR:
+                    PRINTK("\nPing: destination host unreachable ...");
+                    get_log("Ping: destination host unreachable ...",strlen("Ping: destination host unreachable ..."));
+                    break;
+                case ICMP_SQ:
+                    PRINTK("\nPing: source quench ...");
+                    get_log("Ping: source quench ...",strlen("Ping: source quench ..."));
+                    break;
+                case ICMP_RD:
+                    PRINTK("\nPing: redirect ...");
+                    get_log("Ping: redirect ...",strlen("Ping: redirect ..."));
+                    break;
+                case ICMP_TE:
+                    PRINTK("\nPing: time exceeded ...");
+                    get_log("Ping: time exceeded ...",strlen("Ping: time exceeded ..."));
+                    break;
+                case ICMP_PP:
+                    PRINTK("\nPing: parameter problem ...");
+                    get_log("Ping: parameter problem ...",strlen("Ping: parameter problem ..."));
+                    break;
+                default:
+                    PRINTK("\nPing: unknown error ...");
+                    get_log("Ping: unknown error ...",strlen("Ping: unknown error ..."));
+                    break;
+            }
+            i++;
+        }
+    }
+    
+    PRINTK("\n--- %s ping statistics ---\n", inet_ntop(AF_INET, &to.sin_addr, buf, sizeof(buf)));
+    memset(str,0,sizeof(str));
+    sprintf(str,"%s ping statistics ---\n",inet_ntop(AF_INET, &to.sin_addr, buf, sizeof(buf)));
+    get_log(str,strlen(str));
+    PRINTK("%u packets transmitted, %u received, %u loss\n", i, succ_cnt, failed_cnt);
+    memset(str,0,sizeof(str));
+    sprintf(str,"%u packets transmitted, %u received, %u loss\n", i, succ_cnt, failed_cnt);
+    get_log(str,strlen(str));
+FAILURE:
+    ping_kill = 0;
+    (void)lwip_close(sfd);
+    if (pbuf_resp != NULL) {
+        (void)pbuf_free(pbuf_resp);
+    }
+    if (iecho != NULL) {
+        mem_free(iecho);
+    }
+    return ret;
+}
+
+static void ping_cmd(u32_t *parg)
+{
+    int ret;
+
+    ret = OsPingFunc(parg);
+    if (ret < 0) {
+        PRINTK("Ping cmd failed due to some errors\n");
+    }
+    free(parg);
+    ping_taskid = -1;
+}
+
+u32_t OsShellPing(int argc, const char *argv)
+{
+    int ret;
+    // u32_t i = 0;
+    u32_t count;
+    int count_set = 0;
+    u32_t interval = 1000; /* default ping interval */
+    u32_t data_len = 48; /* default data length */
+    ip4_addr_t dst_ipaddr;
+    TSK_INIT_PARAM_S stPingTask;
+    u32_t *parg = NULL;
+
+    /* Initialize dst_ipaddr to avoid random values */
+    dst_ipaddr.addr = IPADDR_NONE;
+
+    if ((argc < 1) || (argv == NULL)) {
+        PRINTK("Ping: require dest ipaddr at least\n");
+        goto ping_error;
+    }
+
+    if (!count_set) {
+        count = LWIP_SHELL_CMD_PING_RETRY_TIMES;
+    }
+
+    printf("%s %d name %s\n",__func__,__LINE__,argv);
+    
+    /* Safe hostname copying with buffer overflow protection */
+    memset(hostname, 0, sizeof(hostname));
+    size_t argv_len = strlen(argv);
+    if (argv_len >= sizeof(hostname)) {
+        PRINTK("Hostname too long (max %d chars)\n", (int)(sizeof(hostname) - 1));
+        goto ping_error;
+    }
+    if (memcpy_s(hostname, sizeof(hostname), argv, argv_len) != EOK) {
+        PRINTK("Failed to copy hostname\n");
+        goto ping_error;
+    }
+
+    /* Try direct IP address parsing first */
+    struct in_addr temp_addr;
+    if (inet_aton(argv, &temp_addr) != 0) {
+        /* Direct IP parsing succeeded */
+        dst_ipaddr.addr = temp_addr.s_addr; /* inet_aton returns network byte order */
+        printf("%s %d dst_ip 0x%X (direct IP)\n",__func__,__LINE__,dst_ipaddr.addr);
+    } else {
+        /* Direct IP parsing failed, try DNS resolution */
+#if LWIP_DNS
+        dst_ipaddr.addr = get_hostip(argv);
+        printf("%s %d dst_ip 0x%X (from DNS)\n",__func__,__LINE__,dst_ipaddr.addr);
+#else
+        PRINTK("DNS not supported and invalid IP address format: %s\n", argv);
+        goto ping_error;
+#endif /* LWIP_DNS */
+    }
+
+    if (dst_ipaddr.addr == IPADDR_NONE || dst_ipaddr.addr == IPADDR_ANY) {
+        PRINTK("Invalid dest ipaddr: NONE or ANY\n");
+        get_log("Invalid dest ipaddr: NONE or ANY",strlen("Invalid dest ipaddr: NONE or ANY"));
+        return LOS_NOK;
+    }
+
+    parg = (u32_t *)malloc(4 * sizeof(u32_t)); /* 4: number of args */
+    if (parg == NULL) {
+        PRINTK("Failed to allocate memory for ping arguments\n");
+        return LOS_NOK;
+    }
+    parg[0] = dst_ipaddr.addr;
+    parg[1] = count;
+    parg[2] = interval;
+    parg[3] = data_len;
+
+    /* start one task if ping forever or ping count greater than 60 */
+    if (count == 0 || count > LWIP_SHELL_CMD_PING_RETRY_TIMES) {
+        if (ping_taskid > 0) {
+            free(parg);
+            PRINTK("Ping task already running and only support one now\n");
+            return LOS_NOK;
+        }
+        stPingTask.pfnTaskEntry = (TSK_ENTRY_FUNC)ping_cmd;
+        stPingTask.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
+        stPingTask.pcName = "ping_task";
+        stPingTask.usTaskPrio = 8; /* 8: higher than shell */
+        stPingTask.uwResved = LOS_TASK_STATUS_DETACHED;
+        stPingTask.pArgs = (UINTPTR)parg;
+        ret = LOS_TaskCreate((UINT32 *)(&ping_taskid), &stPingTask);
+        if (ret != LOS_OK) {
+            PRINTK("ping_task create failed 0x%08x.\n", ret);
+            count = LWIP_SHELL_CMD_PING_RETRY_TIMES;
+        } else {
+            return LOS_OK;
+        }
+    }
+
+    /* two cases:
+     1, ping cout less than LWIP_SHELL_CMD_PING_RETRY_TIMES;
+     2, ping task create failed;
+    */
+    if (OsPingFunc(parg) < 0) {
+        PRINTK("Ping cmd failed due to some errors\n");
+    }
+
+    free(parg);
+
+    return LOS_OK;
+ping_error:
+    lwip_ping_usage();
+    return LOS_NOK;
+}
+
diff --git a/code/sdk/zhihui/ppp/example/ppp_at_cmd.c b/code/sdk/zhihui/ppp/example/ppp_at_cmd.c
new file mode 100644 (file)
index 0000000..5ab411a
--- /dev/null
@@ -0,0 +1,643 @@
+
+#include "at_cmd_api.h"
+#include "pm_control.h"
+#include "ppp_at_cmd.h"
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "ppp_log.h"
+
+/* 外部函数声明 */
+extern void ppp_dial_with_power_example(void);
+
+
+#ifdef __cplusplus
+#if __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+#define ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0]))
+#define CUSTOM_UTILS_MAX_ARGC            8
+#define CUSTOM_UTILS_MAX_ARGV_LEN        256
+
+#define CUSTOM_UTILS_SUCCESS             0
+#define CUSTOM_UTILS_FAILED              (-1)
+
+
+#include "app_at_process.h"
+#ifdef SUPPORT_ZHIHUI_WEBSOCKET
+#include "../websocket/include/ws_multi_test.h"
+#endif
+
+static int ConvertCmdToParam(uint8_t *para, uint32_t paraLen,
+        uint8_t argv[CUSTOM_UTILS_MAX_ARGC][CUSTOM_UTILS_MAX_ARGV_LEN], uint32_t* argc)
+{
+    uint32_t pos = 0;
+    *argc = 0;
+
+    for (uint32_t i = 0; i < paraLen; i++) {
+        if ((para[i] == ' ' || para[i] == '\n') && pos != 0) {
+            argv[*argc][pos] = '\0';
+            *argc += 1;
+            pos = 0;
+            if (*argc >= CUSTOM_UTILS_MAX_ARGC) {
+                return CUSTOM_UTILS_FAILED;
+            }
+        } else {
+            argv[*argc][pos] = para[i];
+            pos++;
+            if (pos >= CUSTOM_UTILS_MAX_ARGV_LEN) {
+                return CUSTOM_UTILS_FAILED;
+            }
+        }
+    }
+    if (pos != 0) {
+        *argc += 1;
+    }
+
+    return CUSTOM_UTILS_SUCCESS;
+}
+
+
+static uint32_t cat1_power(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    UNUSED(strCommonCmd);
+    uint8_t argv[CUSTOM_UTILS_MAX_ARGC][CUSTOM_UTILS_MAX_ARGV_LEN] = { 0 };
+    uint32_t argc = 0;
+
+    int ret = ConvertCmdToParam(para, paraLen, argv, &argc);
+    if (ret != CUSTOM_UTILS_SUCCESS) {
+        ppp_print_err("CAT1TEST ATCMD Convert Command Failed\n");
+        return ERRCODE_FAIL;
+    }
+
+    if (argc != 1) {
+        ppp_print_err("CAT1TEST ATCMD Set Parameter Failed\n");
+        return ERRCODE_FAIL;
+    }
+    // char *mode = (char*)argv[0];
+    int enabled = atoi((char*)argv[0]);
+    
+    ppp_print_info("CAT1TEST ATCMD  power  enable %d \n",enabled);
+
+    if(enabled == 1)
+    {
+        cat1_api_enable_cat1(true);
+    }else{
+        cat1_api_enable_cat1(false);
+    }
+
+    return ERRCODE_SUCC;
+}
+
+static uint32_t at_cat1_gettime(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    UNUSED(strCommonCmd);
+
+    
+    ppp_print_info("CAT1TEST ATCMD  gettime  \n");
+
+    // cat1_cmd_send(CMD_4G_GET_TIME,NULL,0);
+
+    return ERRCODE_SUCC;
+}
+
+static uint32_t at_cat1_getsignal(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    UNUSED(strCommonCmd);
+
+    
+    ppp_print_info("CAT1TEST ATCMD  getsignal  \n");
+
+    // cat1_cmd_send(CMD_4G_GET_SIGNAL,NULL,0);
+
+    return ERRCODE_SUCC;
+}
+static uint32_t at_cat1_getver(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    UNUSED(strCommonCmd);
+    ppp_print_info("CAT1TEST ATCMD  cat1 getver  \n");
+    // cat1_cmd_send(CMD_4G_GET_VERSION,NULL,0);
+
+    return ERRCODE_SUCC;
+}
+
+extern void LwipTestEnter(void);
+
+static uint32_t cat1_nettest(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    UNUSED(strCommonCmd);
+    ppp_print_info("CAT1TEST ATCMD  nettest  \n");
+    // LwipTestEnter();
+
+    return ERRCODE_SUCC;
+}
+static uint32_t cat1_wakeup(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    UNUSED(strCommonCmd);
+    uint8_t argv[CUSTOM_UTILS_MAX_ARGC][CUSTOM_UTILS_MAX_ARGV_LEN] = { 0 };
+    uint32_t argc = 0;
+
+    int ret = ConvertCmdToParam(para, paraLen, argv, &argc);
+    if (ret != CUSTOM_UTILS_SUCCESS) {
+        ppp_print_err("CAT1TEST ATCMD Convert Command Failed\n");
+        return ERRCODE_FAIL;
+    }
+    if (argc != 1) {
+        ppp_print_err("CAT1TEST ATCMD Set Parameter Failed\n");
+        return ERRCODE_FAIL;
+    }
+    // char *mode = (char*)argv[0];
+    int enabled = atoi((char*)argv[0]);
+    if(enabled == 0)
+    {
+        // cat1_api_keep_wakeup(0);
+        // cat1_api_wakeup_cat1(0);
+    }
+    else 
+    {
+        // cat1_api_keep_wakeup(1);
+        // cat1_api_wakeup_cat1(1);
+    }
+    ppp_print_info("CAT1TEST ATCMD  keepwakeup cat1  enabled = %d\n",enabled);
+
+    return ERRCODE_SUCC;
+}
+#include "api_shell.h"
+static uint32_t cat1_ping(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    UNUSED(strCommonCmd);
+    uint8_t argv[CUSTOM_UTILS_MAX_ARGC][CUSTOM_UTILS_MAX_ARGV_LEN] = { 0 };
+    uint32_t argc = 0;
+
+    int ret = ConvertCmdToParam(para, paraLen, argv, &argc);
+    if (ret != CUSTOM_UTILS_SUCCESS) {
+        ppp_print_err("CAT1TEST ATCMD Convert Command Failed\n");
+        return ERRCODE_FAIL;
+    }
+    // cat1_api_wakeup_cat1(1);
+    // cat1_api_keep_wakeup(1);
+    u32_t sret= OsShellPing( argc, (const char *)argv[0]);
+    ppp_print_info("CAT1TEST ATCMD  ping   ret = 0x%X\n",sret);
+    // cat1_api_keep_wakeup(0);
+    // cat1_api_wakeup_cat1(0);
+    return ERRCODE_SUCC;
+}
+static uint32_t cat1_ifconfig(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    UNUSED(strCommonCmd);
+    uint8_t argv[CUSTOM_UTILS_MAX_ARGC][CUSTOM_UTILS_MAX_ARGV_LEN] = { 0 };
+    uint32_t argc = 0;
+
+    int ret = ConvertCmdToParam(para, paraLen, argv, &argc);
+    if (ret != CUSTOM_UTILS_SUCCESS) {
+        ppp_print_err("CAT1TEST ATCMD Convert Command Failed\n");
+        return ERRCODE_FAIL;
+    }
+    u32_t sret = lwip_ifconfig( argc, (const char**)argv);
+    ppp_print_info("CAT1TEST ATCMD  ifconfig  ret = 0x%X\n",sret);
+
+    return ERRCODE_SUCC;
+}
+
+
+static uint32_t at_cat1_atcmd(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    UNUSED(strCommonCmd);
+    ppp_print_info("CAT1TEST ATCMD %s \n",para);
+    // cat1_cmd_send(CMD_4G_AT,para,strlen(para));
+
+    return ERRCODE_SUCC;
+}
+
+#if 0
+
+#include "http_client.h"
+
+static uint8_t g_http_download_started=0;
+/**
+ * uint8_t *server, 
+ * uint16_t port,
+ * uint8_t *uri, 
+ * uint8_t *otadatapath, 
+ * httpc_result_fn callback_result_fn, 
+ * httpc_headers_done_fn callback_headers_done_fn,  
+ * httpc_file_rx_fn callback_file_rx_fn
+*/
+static httpc_connection_t settings = {0};
+errcode_t http_single_download_start( uint8_t *server, uint16_t port,uint8_t *uri, uint8_t *otadatapath, httpc_result_fn callback_result_fn, httpc_headers_done_fn callback_headers_done_fn,  httpc_file_rx_fn callback_file_rx_fn)
+{
+
+    if( server == NULL || uri == NULL || otadatapath == NULL)
+    {
+        wstp_print("http_single_download_start  failed, param is null \n");
+        return ERRCODE_FAIL;
+    }
+    if(g_http_download_started==1)
+    {
+        wstp_print("http_single_download_start  failed, already started \n");
+        return ERRCODE_FAIL;
+    }else{
+        g_http_download_started = 1;
+    }
+
+    //通讯开始,唤醒cat1
+    //httpc_connection_t settings = {0};
+
+    settings.result_fn=callback_result_fn;
+    settings.file_rx_fn = callback_file_rx_fn;
+    settings.headers_done_fn=callback_headers_done_fn;
+
+    settings.use_post = 0 ;
+
+    httpc_state_t* connection=NULL;
+    ppp_print_info(" http_single_download_start Server: %s\n", server);
+    ppp_print_info(" http_single_download_start Port: %d\n", port);
+    ppp_print_info(" http_single_download_start URI: %s\n", uri);
+    ppp_print_info(" http_single_download_start Filepath: %s\n", otadatapath);
+    
+    err_t rett =  httpc_get_file_dns_to_disk(
+                    (const char*) server, 
+                    port,
+                    (const char*) uri,
+                    (const httpc_connection_t *)&settings,
+                     NULL, 
+                     (const char*) otadatapath, 
+                     &connection);
+    if (rett== ERRCODE_SUCC)
+    {
+        g_http_download_started = 1;
+    }
+    return rett;
+}
+
+/***
+ * stop函数要在result里面执行,表示下载结束
+ * */
+errcode_t http_single_download_stop(void)
+{
+    //通讯结束,休眠cat1
+    g_http_download_started = 0;
+    return ERRCODE_SUCC;
+}
+void httpc_download_result(void *arg, httpc_result_t httpc_result, u32_t rx_content_len, u32_t srv_res, err_t err)
+{
+    wstp_print("httpc_download_result , httpc_result_t %d, rx_content_len %d , srv_res %d, err %d\n",
+        httpc_result,rx_content_len,srv_res,err);
+
+    http_single_download_stop();
+    return ;
+}
+static uint32_t file_total_len = 0;
+err_t httpc_download_headers_done(httpc_state_t *connection, void *arg, struct pbuf *hdr, u16_t hdr_len, u32_t content_len)
+{
+    wstp_print("httpc_download_headers_done , hdr_len %d ,content_len %d\n",hdr_len,content_len);
+    file_total_len = content_len;
+    return 0;
+}
+err_t httpc_download_file_recv_ui(httpc_state_t *connection, u32_t rx_len,u32_t rx_total_len)
+{
+    wstp_print("httpc_download_file_recv_ui recv %d bytes total received %d filelen %d\r\n",rx_len,rx_total_len,file_total_len);
+    return 0;
+}
+
+static uint32_t custom_start_ota_download(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    UNUSED(strCommonCmd);
+
+    uint8_t argv[CUSTOM_UTILS_MAX_ARGC][CUSTOM_UTILS_MAX_ARGV_LEN] = { 0 };
+    uint32_t argc = 0;
+
+    int ret = ConvertCmdToParam(para, paraLen, argv, &argc);
+    if (ret != CUSTOM_UTILS_SUCCESS) {
+        wstp_print("CUSTOM_UTILSTEST ATCMD Convert Command Failed\n");
+        return ERRCODE_FAIL;
+    }
+
+    if (argc != 4) {
+        wstp_print("CUSTOM_UTILSTEST ATCMD Set Parameter Failed argc %d\nshould input:\nAT^HTTPDOWNLOAD=server port file_full_uri file_savepath\n",argc);
+        return ERRCODE_FAIL;
+    }
+
+
+    uint8_t *server = (uint8_t*)argv[0];
+    uint16_t port = atoi((const char*)argv[1]);
+    uint8_t *uri = (uint8_t*)argv[2];
+    uint8_t *otadatapath = (uint8_t*)argv[3];
+    wstp_print("custom_start_ota_download %s:%d %s to %s\r\n",server,port,uri,otadatapath);
+ 
+    err_t rett = http_single_download_start(server,port,uri,otadatapath,httpc_download_result,httpc_download_headers_done,httpc_download_file_recv_ui);
+
+    wstp_print("custom_start_ota_download %d \r\n",rett);
+    return ERRCODE_SUCC;
+}
+#else
+static uint32_t custom_start_ota_download(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    UNUSED(strCommonCmd);
+
+    uint8_t argv[CUSTOM_UTILS_MAX_ARGC][CUSTOM_UTILS_MAX_ARGV_LEN] = { 0 };
+    uint32_t argc = 0;
+
+    int ret = ConvertCmdToParam(para, paraLen, argv, &argc);
+    if (ret != CUSTOM_UTILS_SUCCESS) {
+        wstp_print("CUSTOM_UTILSTEST ATCMD Convert Command Failed\n");
+        return ERRCODE_FAIL;
+    }
+
+    if (argc != 4) {
+        wstp_print("CUSTOM_UTILSTEST ATCMD Set Parameter Failed argc %d\nshould input:\nAT^HTTPDOWNLOAD=server port file_full_uri file_savepath\n",argc);
+        return ERRCODE_FAIL;
+    }
+
+
+    uint8_t *server = (uint8_t*)argv[0];
+    uint16_t port = atoi((const char*)argv[1]);
+    uint8_t *uri = (uint8_t*)argv[2];
+    uint8_t *otadatapath = (uint8_t*)argv[3];
+    wstp_print("fake custom_start_ota_download %s:%d %s to %s\r\n",server,port,uri,otadatapath);
+ 
+    return ERRCODE_SUCC;
+}
+
+#endif
+
+static uint32_t custom_enable_cat1_debug_log(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    UNUSED(strCommonCmd);
+
+    uint8_t argv[CUSTOM_UTILS_MAX_ARGC][CUSTOM_UTILS_MAX_ARGV_LEN] = { 0 };
+    uint32_t argc = 0;
+
+    int ret = ConvertCmdToParam(para, paraLen, argv, &argc);
+    if (ret != CUSTOM_UTILS_SUCCESS) {
+        wstp_print("cat1 ATCMD Convert Command Failed\n");
+        return ERRCODE_FAIL;
+    }
+
+    if (argc <1) {
+        wstp_print("cat1 ATCMD Set Parameter Failed argc %d\n",argc);
+        return ERRCODE_FAIL;
+    }
+
+    uint16_t loglevel = atoi((const char*)argv[0]);
+    g_ppp_enable_debug=loglevel;    
+    wstp_print("Set PPP LOGLEVEL  %d\n",loglevel);
+    return ERRCODE_SUCC;
+}
+#ifdef SUPPORT_ZHIHUI_WEBSOCKET
+/**
+ * WebSocket测试启动AT指令处理函数
+ * 格式: AT^WEBSOCKETTEST=serverip,port,test_time
+ * 参数: serverip - 服务器IP地址
+ *       port - 服务器端口
+ *       test_time - 测试时间(秒)
+ */
+static uint32_t at_websocket_test_start(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    UNUSED(strCommonCmd);
+    uint8_t argv[CUSTOM_UTILS_MAX_ARGC][CUSTOM_UTILS_MAX_ARGV_LEN] = { 0 };
+    uint32_t argc = 0;
+
+    int ret = ConvertCmdToParam(para, paraLen, argv, &argc);
+    if (ret != CUSTOM_UTILS_SUCCESS) {
+        ppp_print_err("CAT1TEST ATCMD Convert Command Failed\n");
+        return ERRCODE_FAIL;
+    }
+
+    if (argc != 3) {
+        ppp_print_err("CAT1TEST ATCMD Set Parameter Failed argc %d\nshould input:\nAT^WEBSOCKETTEST=serverip,port,test_time\n", argc);
+        return ERRCODE_FAIL;
+    }
+
+    uint8_t *server_ip = (uint8_t*)argv[0];
+    uint16_t port = atoi((const char*)argv[1]);
+    uint32_t test_time = atoi((const char*)argv[2]);
+
+    ppp_print_info("CAT1TEST ATCMD WebSocket Test Start: %s:%d, duration: %d seconds\n", server_ip, port, test_time);
+
+    // 启动WebSocket测试,测试时间转换为毫秒
+    // int result = start_websocket_test((const char*)server_ip, port, test_time * 1000);
+    int result = 0;
+    if (result == 0) {
+        ppp_print_info("WebSocket测试启动成功\n");
+        return ERRCODE_SUCC;
+    } else {
+        ppp_print_err("WebSocket测试启动失败\n");
+        return ERRCODE_FAIL;
+    }
+}
+
+/**
+ * WebSocket测试停止AT指令处理函数
+ * 格式: AT^STOPWEBSOCKET
+ * 无参数
+ */
+static uint32_t at_websocket_test_stop(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    UNUSED(strCommonCmd);
+    UNUSED(para);
+    UNUSED(paraLen);
+
+    ppp_print_info("CAT1TEST ATCMD WebSocket Test Stop\n");
+
+    // 停止WebSocket测试
+    // stop_websocket_test();
+    ppp_print_info("WebSocket测试已停止\n");
+
+    return ERRCODE_SUCC;
+}
+
+/**
+ * WebSocket测试状态查询AT指令处理函数
+ * 格式: AT^WEBSOCKETSTATUS
+ * 无参数
+ */
+static uint32_t at_websocket_test_status(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    UNUSED(strCommonCmd);
+    UNUSED(para);
+    UNUSED(paraLen);
+
+    ppp_print_info("CAT1TEST ATCMD WebSocket Test Status\n");
+
+    // 获取测试状态
+    // int state = get_websocket_test_state();
+    int state = 0;
+    const char* state_str[] = {"空闲", "运行中", "已完成", "错误"};
+    
+    if (state >= 0 && state < 4) {
+        ppp_print_info("WebSocket测试状态: %s\n", state_str[state]);
+    } else {
+        ppp_print_info("WebSocket测试状态: 未知(%d)\n", state);
+    }
+
+    // 获取测试统计信息
+    uint32_t send_count, recv_count, lost_count;
+    // get_websocket_test_stats(&send_count, &recv_count, &lost_count);
+    send_count = 0;
+    recv_count = 0;
+    lost_count = 0;
+    
+    ppp_print_info("发送数据包数: %u\n", send_count);
+    ppp_print_info("接收数据包数: %u\n", recv_count);
+    ppp_print_info("丢失数据包数: %u\n", lost_count);
+    
+    if (send_count > 0) {
+        float loss_rate = (float)lost_count / send_count * 100.0f;
+        ppp_print_info("丢包率: %.2f%%\n", loss_rate);
+    }
+
+    return ERRCODE_SUCC;
+}
+
+/**
+ * WebSocket多连接测试启动AT指令处理函数
+ * 格式: AT^WSMULTITEST=serverip,port,client_count,test_time
+ * 示例: AT^WSMULTITEST=192.168.1.100,8080,3,60
+ */
+static uint32_t at_ws_multi_test_start(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    UNUSED(strCommonCmd);
+    
+    uint8_t argv[CUSTOM_UTILS_MAX_ARGC][CUSTOM_UTILS_MAX_ARGV_LEN];
+    uint32_t argc;
+    
+    if (ConvertCmdToParam(para, paraLen, argv, &argc) != CUSTOM_UTILS_SUCCESS || argc != 4) {
+        ppp_print_err("CAT1TEST ATCMD Set Parameter Failed argc %d\nshould input:\nAT^WSMULTITEST=serverip,port,client_count,test_time\n", argc);
+        return ERRCODE_FAIL;
+    }
+    
+    char *server_ip = (char*)argv[0];
+    uint16_t port = (uint16_t)atoi((char*)argv[1]);
+    uint8_t client_count = (uint8_t)atoi((char*)argv[2]);
+    uint32_t test_time = (uint32_t)atoi((char*)argv[3]);
+    
+    ppp_print_info("CAT1TEST ATCMD WebSocket Multi-Test Start: %s:%d, clients: %d, duration: %d seconds\n", 
+                    server_ip, port, client_count, test_time);
+    
+    // 初始化多连接测试模块
+    if (ws_multi_test_init() != 0) {
+        ppp_print_err("WebSocket多连接测试模块初始化失败\n");
+        return ERRCODE_FAIL;
+    }
+    
+    // 启动WebSocket多连接测试
+    int result = ws_multi_test_start(server_ip, port, client_count, test_time);
+    if (result == 0) {
+        ppp_print_info("WebSocket多连接测试启动成功\n");
+        return ERRCODE_SUCC;
+    } else {
+        ppp_print_err("WebSocket多连接测试启动失败\n");
+        return ERRCODE_FAIL;
+    }
+}
+
+/**
+ * WebSocket多连接测试停止AT指令处理函数
+ * 格式: AT^STOPWSMULTI
+ */
+static uint32_t at_ws_multi_test_stop(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    UNUSED(para);
+    UNUSED(paraLen);
+    UNUSED(strCommonCmd);
+    
+    ppp_print_info("CAT1TEST ATCMD WebSocket Multi-Test Stop\n");
+    
+    // 强制停止WebSocket多连接测试并清理所有资源
+    ws_multi_test_force_stop();
+    ppp_print_info("WebSocket多连接测试已强制停止并清理资源\n");
+    
+    return ERRCODE_SUCC;
+}
+
+/**
+ * WebSocket多连接测试状态查询AT指令处理函数
+ * 格式: AT^WSMULTISTATUS
+ */
+static uint32_t at_ws_multi_test_status(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    UNUSED(para);
+    UNUSED(paraLen);
+    UNUSED(strCommonCmd);
+    
+    ppp_print_info("CAT1TEST ATCMD WebSocket Multi-Test Status\n");
+    
+    // 获取测试状态
+    ws_test_state_t state = ws_multi_test_get_state();
+    const char *state_str[] = {"空闲", "启动中", "运行中", "停止中", "已停止", "错误"};
+    if (state >= 0 && state < 6) {
+        ppp_print_info("WebSocket多连接测试状态: %s\n", state_str[state]);
+    } else {
+        ppp_print_info("WebSocket多连接测试状态: 未知(%d)\n", state);
+    }
+    
+    // 获取统计信息
+    ws_test_stats_t stats;
+    if (ws_multi_test_get_stats(&stats) == 0) {
+        ppp_print_info("活跃客户端: %d\n", stats.active_clients);
+        ppp_print_info("总发送包数: %d\n", stats.total_sent_packets);
+        ppp_print_info("总接收包数: %d\n", stats.total_recv_packets);
+        ppp_print_info("总丢失包数: %d\n", stats.total_lost_packets);
+        ppp_print_info("总错误包数: %d\n", stats.total_error_packets);
+        ppp_print_info("总发送字节: %d\n", stats.total_sent_bytes);
+        ppp_print_info("总接收字节: %d\n", stats.total_recv_bytes);
+        ppp_print_info("平均丢包率: %.2f%%\n", stats.avg_loss_rate);
+        ppp_print_info("发送吞吐量: %.2f bytes/s\n", stats.avg_throughput_tx);
+        ppp_print_info("接收吞吐量: %.2f bytes/s\n", stats.avg_throughput_rx);
+        ppp_print_info("测试时长: %d 秒\n", stats.test_duration);
+        
+        // 打印每个客户端的详细信息
+        for (uint8_t i = 0; i < stats.active_clients && i < 5; i++) {
+            ws_client_stats_t client_stats;
+            if (ws_multi_test_get_client_stats(i, &client_stats) == 0) {
+                ppp_print_info("客户端[%d]: 发送=%d, 接收=%d, 丢失=%d, 连接=%s\n", 
+                               i, client_stats.sent_packets, client_stats.recv_packets, 
+                               client_stats.lost_packets, client_stats.is_connected ? "是" : "否");
+            }
+        }
+    } else {
+        ppp_print_err("获取统计信息失败\n");
+    }
+    
+    return ERRCODE_SUCC;
+}
+#endif
+static const at_cmd_table_t g_ppp_cmd[] = {
+    { (char *)"AT^CAT1POWER",       cat1_power, (char *)"1" },
+    { (char *)"AT^CAT1GETSIGNAL",   at_cat1_getsignal, (char *)"1" },
+    { (char *)"AT^CAT1GETTIME",     at_cat1_gettime, (char *)"1" },
+    { (char *)"AT^CAT1GETVER",      at_cat1_getver, (char *)"1" },
+    { (char *)"AT^CAT1NETTEST",     cat1_nettest, (char *)"1" },
+    { (char *)"AT^PING",            cat1_ping, (char *)"1" },
+    { (char *)"AT^WAKECAT1",        cat1_wakeup, (char *)"1" },
+    { (char *)"AT^IFCONFIG",        cat1_ifconfig, (char *)"1" },
+    { (char *)"AT^CAT1AT",          at_cat1_atcmd, (char *)"1" },
+    { (char *)"AT^HTTPDOWNLOAD",    custom_start_ota_download, (char *)" " },
+    { (char *)"AT^CAT1LOGLEVEL",    custom_enable_cat1_debug_log, (char *)" " },
+    { (char *)"AT^PPPDIAL",          ppp_dial_with_power_example, (char *)" " },
+#ifdef SUPPORT_ZHIHUI_WEBSOCKET
+    { (char *)"AT^WEBSOCKETTEST",   at_websocket_test_start, (char *)"serverip,port,test_time" },
+    { (char *)"AT^STOPWEBSOCKET",   at_websocket_test_stop, (char *)" " },
+    { (char *)"AT^WEBSOCKETSTATUS", at_websocket_test_status, (char *)" " },
+    { (char *)"AT^WSMULTITEST",     at_ws_multi_test_start, (char *)"serverip,port,client_count,test_time" },
+    { (char *)"AT^STOPWSMULTI",     at_ws_multi_test_stop, (char *)" " },
+    { (char *)"AT^WSMULTISTATUS",   at_ws_multi_test_status, (char *)" " },
+#endif
+};
+
+at_cmd_table_t *get_ppp_cmd_table(uint32_t *cmd_count)
+{
+    *cmd_count = sizeof(g_ppp_cmd) / sizeof(g_ppp_cmd[0]);
+    return (at_cmd_table_t *)g_ppp_cmd;
+}
+#ifdef __cplusplus
+#if __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __cplusplus */
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/example/ppp_example.c b/code/sdk/zhihui/ppp/example/ppp_example.c
new file mode 100644 (file)
index 0000000..aa0cd09
--- /dev/null
@@ -0,0 +1,235 @@
+#include "ppp_api.h"
+#include "ppp_config.h"
+#include "ppp_debug.h"
+#include "osal_task.h"
+#include "soc_osal.h"
+#include "debug_print.h"
+#include <stdio.h>
+#include <string.h>
+
+/* PPP事件回调函数 */
+static void ppp_event_callback(const ppp_event_t *event);
+
+/* 网络状态监控任务 */
+static void network_monitor_task(void *arg);
+
+/* 全局变量 */
+static bool g_ppp_connected = false;
+static osal_task *g_monitor_task = NULL;
+
+/**
+ * @brief PPP拨号示例主函数
+ */
+int ppp_example_main(void)
+{
+    PRINT("PPP Dial Example Started\r\n");
+
+    /* 配置PPP拨号参数 */
+    ppp_config_t config = {
+        .apn = "cmnet",                     // 中国移动APN
+        .username = "",                     // 用户名(通常为空)
+        .password = "",                     // 密码(通常为空)
+        .baudrate = 2000000,                // 2M波特率
+        .auto_baudrate = true,              // 启用自动波特率检测
+        .dial_timeout = 30000,              // 30秒拨号超时
+        .retry_count = 3                    // 重试3次
+        };
+
+    /* 初始化PPP拨号系统 */
+    errcode_t ret = zhihui_ppp_init(&config);
+
+    if (ret != ERRCODE_SUCC) {
+        PRINT("PPP init failed: error code %d\r\n", ret);
+        return -1;
+    }
+
+    /* 注册事件回调函数 */
+    ret = ppp_register_event_callback(ppp_event_callback);
+    if (ret != ERRCODE_SUCC) {
+        PRINT("PPP register callback failed\r\n");
+        zhihui_ppp_deinit();
+        return -1;
+    }
+
+    PRINT("PPP system initialized successfully\r\n");
+
+    /* 创建网络监控任务 */
+    /* 使用正确的OSAL任务创建方式 */
+    // osal_task_attr_t task_attr = {
+        // .name = "net_monitor",
+        // .stack_size = 2048,
+        // .priority = OSAL_TASK_PRIORITY_LOW,
+        // .detached = false
+        // };
+
+    g_monitor_task = osal_kthread_create(network_monitor_task, NULL, "net_monitor", 2048);
+    if (g_monitor_task == NULL) {
+        PRINT("Failed to create monitor task\r\n");
+        zhihui_ppp_deinit();
+        return -1;
+    }
+
+    /* 开始PPP拨号 */
+    PRINT("Starting PPP dial...\r\n");
+    ret = ppp_start_dial();
+    if (ret != ERRCODE_SUCC) {
+        return -1;
+    }
+
+    /* 等待连接建立或超时 */
+    uint32_t timeout = 60000; // 60秒超时
+    uint32_t start_time = osal_get_tick();
+    
+    while (!g_ppp_connected && (osal_get_tick() - start_time) < timeout) {
+        ppp_state_t state = ppp_get_state();
+        PRINT("PPP state: %d\r\n", state);
+        
+        if (state == PPP_STATE_ERROR) {
+            PRINT("PPP connection failed\r\n");
+            break;
+        }
+        
+        osal_msleep(1000);
+    }
+
+    if (g_ppp_connected) {
+        PRINT("PPP connection established successfully!\r\n");
+        
+        /* 获取网络信息 */
+        ppp_connection_info_t network_info;
+        ret = ppp_get_connection_info(&network_info);
+        if (ret == ERRCODE_SUCC) {
+            PRINT("Network Information:\r\n");
+            PRINT("  Local IP:      %u.%u.%u.%u\r\n",
+                  (network_info.local_ip >> 0) & 0xFF,
+                  (network_info.local_ip >> 8) & 0xFF,
+                  (network_info.local_ip >> 16) & 0xFF,
+                  (network_info.local_ip >> 24) & 0xFF);
+            PRINT("  Remote IP:     %u.%u.%u.%u\r\n",
+                  (network_info.remote_ip >> 0) & 0xFF,
+                  (network_info.remote_ip >> 8) & 0xFF,
+                  (network_info.remote_ip >> 16) & 0xFF,
+                  (network_info.remote_ip >> 24) & 0xFF);
+            PRINT("  Primary DNS:   %u.%u.%u.%u\r\n",
+                  (network_info.primary_dns >> 0) & 0xFF,
+                  (network_info.primary_dns >> 8) & 0xFF,
+                  (network_info.primary_dns >> 16) & 0xFF,
+                  (network_info.primary_dns >> 24) & 0xFF);
+            PRINT("  Secondary DNS: %u.%u.%u.%u\r\n",
+                  (network_info.secondary_dns >> 0) & 0xFF,
+                  (network_info.secondary_dns >> 8) & 0xFF,
+                  (network_info.secondary_dns >> 16) & 0xFF,
+                  (network_info.secondary_dns >> 24) & 0xFF);
+            PRINT("  MTU:           %u\r\n", network_info.mtu);
+        }
+
+        /* 保持连接一段时间进行测试 */
+        PRINT("Keeping connection for 30 seconds...\r\n");
+        osal_msleep(30000);
+
+        /* 断开连接 */
+        PRINT("Disconnecting PPP...\r\n");
+        ppp_stop_dial();
+        
+        /* 等待断开完成 */
+        timeout = 10000; // 10秒超时
+        start_time = osal_get_tick();
+        
+        while (g_ppp_connected && (osal_get_tick() - start_time) < timeout) {
+            osal_msleep(100);
+        }
+        
+        if (!g_ppp_connected) {
+            PRINT("PPP disconnected successfully\r\n");
+        } else {
+            PRINT("PPP disconnect timeout\r\n");
+        }
+    } else {
+        PRINT("PPP connection timeout\r\n");
+    }
+
+    /* 清理资源 */
+    if (g_monitor_task) {
+        osal_kthread_destroy(g_monitor_task, 1);
+        g_monitor_task = NULL;
+    }
+    
+    zhihui_ppp_deinit();
+    PRINT("PPP example completed\r\n");
+
+    return 0;
+}
+
+/**
+ * @brief PPP事件回调函数
+ */
+static void ppp_event_callback(const ppp_event_t *event)
+{
+    if (!event) {
+        return;
+    }
+
+    switch (event->type) {
+        case PPP_EVENT_CONNECTED:
+            PRINT("PPP Event: CONNECTED\r\n");
+            g_ppp_connected = true;
+            break;
+
+        case PPP_EVENT_DISCONNECTED:
+            PRINT("PPP Event: DISCONNECTED\r\n");
+            g_ppp_connected = false;
+            break;
+
+        case PPP_EVENT_ERROR:
+            PRINT("PPP Event: ERROR (code: %s)\r\n", 
+                  event->error_code);
+            g_ppp_connected = false;
+            break;
+
+        case PPP_EVENT_IP_ACQUIRED:
+            PRINT("PPP Event: IP_ACQUIRED\r\n");
+            break;
+
+        case PPP_EVENT_DNS_ACQUIRED:
+            PRINT("PPP Event: DNS_ACQUIRED\r\n");
+            break;
+
+        case PPP_EVENT_AT_RESPONSE:
+            PRINT("PPP Event: AT_RESPONSE\r\n");
+            break;
+
+        case PPP_EVENT_DATA_RECEIVED:
+            PRINT("PPP Event: DATA_RECEIVED (%u bytes)\r\n", event->data_len);
+            break;
+
+        default:
+            PRINT("PPP Event: UNKNOWN (%d)\r\n", event->type);
+            break;
+    }
+}
+
+/**
+ * @brief 网络状态监控任务
+ */
+static void network_monitor_task(void *arg)
+{
+    (void)arg;
+    
+    PRINT("Network monitor task started\r\n");
+
+    while (1) {
+        /* 获取统计信息 */
+        ppp_stats_t stats;
+
+        /* 每10秒输出一次统计信息 */
+        osal_msleep(10000);
+        
+        /* 如果PPP未连接且不在拨号过程中,退出监控任务 */
+        ppp_state_t state = ppp_get_state();
+        if (!g_ppp_connected && state == PPP_STATE_IDLE) {
+            break;
+        }
+    }
+
+    PRINT("Network monitor task exited\r\n");
+} 
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/example/ppp_power_example.c b/code/sdk/zhihui/ppp/example/ppp_power_example.c
new file mode 100644 (file)
index 0000000..7858ab1
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * @brief PPP模块电源控制使用示例
+ * @description 演示如何使用PPP模块的电源控制功能
+ * Copyright (c) CompanyNameMagicTag 2024-2024. All rights reserved.
+ */
+
+#include "ppp_api.h"
+#include "ppp_power_ctl.h"
+#include "ppp_debug.h"
+#include "osal_debug.h"
+#include "platform_core.h"
+
+// 外部声明cat1Service兼容接口(在ppp_cat1_api.c中实现)
+extern void cat1_api_enable_cat1(bool isopen);
+extern bool cat1_api_is_cat1_enabled(void);
+extern uint8_t cat1_api_runtime_get_cat1_powerstat(void);
+
+/**
+ * @brief PPP电源控制示例
+ */
+void ppp_power_control_example(void)
+{
+    PPP_DEBUG_PRINT("=== PPP Power Control Example ===\n");
+
+    // 1. 配置4G控制引脚
+    ppp_ctl_pin_4g_t pin_config = {
+        .bat_pin = S_AGPIO_L17,
+        .bat_pin_en_value = 1,
+        .pwr_pin = S_AGPIO_L23,
+        .pwr_pin_en_value = 1,
+        .reset_pin = S_AGPIO_L28,
+        .reset_pin_en_value = 0,
+        .ring_pin = PIN_NONE,
+        .ring_pin_en_value = 0,
+        .wakemodem_pin = PIN_NONE,
+        .wakemodem_pin_en_value = 0
+    };
+
+    // 2. 注册控制引脚配置
+    errcode_t ret = ppp_register_ctl_pin(&pin_config);
+    if (ret != ERRCODE_SUCC) {
+        PPP_DEBUG_PRINT("Failed to register control pins: %d\n", ret);
+        return;
+    }
+
+    PPP_DEBUG_PRINT("Control pins registered successfully\n");
+
+    // 3. 使用PPP专用接口控制4G电源
+    PPP_DEBUG_PRINT("Testing PPP-specific power control...\n");
+    
+    // 开启4G电源
+    ppp_api_enable_cat1(true);
+    osal_msleep(1000);
+    
+    // 检查电源状态
+    bool enabled = ppp_api_is_cat1_enabled();
+    PPP_DEBUG_PRINT("4G power status (PPP): %s\n", enabled ? "ON" : "OFF");
+    
+    osal_msleep(2000);
+    
+    // 关闭4G电源
+    ppp_api_enable_cat1(false);
+    osal_msleep(1000);
+    
+    enabled = ppp_api_is_cat1_enabled();
+    PPP_DEBUG_PRINT("4G power status (PPP): %s\n", enabled ? "ON" : "OFF");
+
+    // 4. 使用兼容接口控制4G电源(推荐方式)
+    PPP_DEBUG_PRINT("Testing compatible power control interface...\n");
+    
+    // 开启4G电源(通过兼容接口)
+    cat1_api_enable_cat1(true);
+    osal_msleep(1000);
+    
+    // 检查电源状态(通过兼容接口)
+    enabled = cat1_api_is_cat1_enabled();
+    PPP_DEBUG_PRINT("4G power status (Compatible): %s\n", enabled ? "ON" : "OFF");
+    
+    // 获取运行时状态
+    uint8_t power_stat = cat1_api_runtime_get_cat1_powerstat();
+    PPP_DEBUG_PRINT("4G runtime power status: %d\n", power_stat);
+    
+    osal_msleep(2000);
+    
+    // 关闭4G电源(通过兼容接口)
+    cat1_api_enable_cat1(false);
+    osal_msleep(1000);
+    
+    enabled = cat1_api_is_cat1_enabled();
+    PPP_DEBUG_PRINT("4G power status (Compatible): %s\n", enabled ? "ON" : "OFF");
+
+    PPP_DEBUG_PRINT("=== PPP Power Control Example Completed ===\n");
+}
+
+/**
+ * @brief PPP完整拨号示例(包含电源控制)
+ */
+void ppp_dial_with_power_example(void)
+{
+    PPP_DEBUG_PRINT("=== PPP Dial with Power Control Example ===\n");
+
+    // 1. 初始化PPP配置
+    ppp_config_t config = {
+        .apn = "cmnet",
+        .username = "",
+        .password = "",
+        .baudrate = 115200,
+        .auto_baudrate = true,
+        .dial_timeout = 30000,
+        .retry_count = 3
+    };
+
+    // 2. 初始化PPP模块
+    errcode_t ret = zhihui_ppp_init(&config);
+    if (ret != ERRCODE_SUCC) {
+        PPP_DEBUG_PRINT("Failed to initialize PPP: %d\n", ret);
+        return;
+    }
+
+    PPP_DEBUG_PRINT("PPP module initialized\n");
+
+    // 3. 启动PPP拨号(内部会自动控制4G电源)
+    ret = ppp_start_dial();
+    if (ret != ERRCODE_SUCC) {
+        PPP_DEBUG_PRINT("Failed to start PPP dial: %d\n", ret);
+        zhihui_ppp_deinit();
+        return;
+    }
+
+    PPP_DEBUG_PRINT("PPP dial started\n");
+
+    // 4. 等待连接建立(实际应用中应该通过事件回调处理)
+    PPP_DEBUG_PRINT("Waiting for connection...\n");
+    osal_msleep(10000);
+
+    // 5. 检查连接状态
+    ppp_state_t state = ppp_get_state();
+    PPP_DEBUG_PRINT("PPP state: %d\n", state);
+
+    // 6. 获取连接信息
+    ppp_connection_info_t conn_info;
+    ret = ppp_get_connection_info(&conn_info);
+    if (ret == ERRCODE_SUCC) {
+        PPP_DEBUG_PRINT("Connection info retrieved\n");
+        PPP_DEBUG_PRINT("  Connected: %s\n", conn_info.connected ? "Yes" : "No");
+        PPP_DEBUG_PRINT("  Local IP: 0x%08X\n", conn_info.local_ip);
+        PPP_DEBUG_PRINT("  Remote IP: 0x%08X\n", conn_info.remote_ip);
+        PPP_DEBUG_PRINT("  Primary DNS: 0x%08X\n", conn_info.primary_dns);
+        PPP_DEBUG_PRINT("  MTU: %d\n", conn_info.mtu);
+    }
+
+    // 7. 保持连接一段时间
+    PPP_DEBUG_PRINT("Keeping connection for 30 seconds...\n");
+    osal_msleep(30000);
+
+    // 8. 停止PPP连接(内部会自动控制4G电源)
+    ret = ppp_stop_dial();
+    if (ret != ERRCODE_SUCC) {
+        PPP_DEBUG_PRINT("Failed to stop PPP dial: %d\n", ret);
+    } else {
+        PPP_DEBUG_PRINT("PPP dial stopped\n");
+    }
+
+    // 9. 去初始化PPP模块
+    zhihui_ppp_deinit();
+    PPP_DEBUG_PRINT("PPP module deinitialized\n");
+
+    PPP_DEBUG_PRINT("=== PPP Dial with Power Control Example Completed ===\n");
+}
+
+/**
+ * @brief 主函数示例
+ */
+int main(void)
+{
+    // 系统初始化(根据实际系统调整)
+    // system_init();
+
+    PPP_DEBUG_PRINT("Starting PPP power control examples...\n");
+
+    // 运行电源控制示例
+    ppp_power_control_example();
+
+    osal_msleep(5000);
+
+    // 运行完整拨号示例
+    ppp_dial_with_power_example();
+
+    PPP_DEBUG_PRINT("All examples completed\n");
+
+    return 0;
+} 
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/include/api_shell.h b/code/sdk/zhihui/ppp/include/api_shell.h
new file mode 100644 (file)
index 0000000..31e01f6
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
+ * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
+ *
+ * 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.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used
+ *    to endorse or promote products derived from this software without specific prior written
+ *    permission.
+ *
+ * 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.
+ */
+
+#ifndef LWIP_API_SHELL_H
+#define LWIP_API_SHELL_H
+
+#include "arch/cc.h"
+#include "lwip/opt.h"
+
+#if defined (__cplusplus) && __cplusplus
+extern "C" {
+#endif
+
+u32_t lwip_ifconfig(int argc, const char **argv);
+u32_t OsShellPing(int argc, const char *argv);
+
+void get_ping_hostname(char *databuf,uint8_t len);
+void get_ping_result(char *databuf,uint16_t len);
+
+#ifndef LWIP_TESTBED
+static
+#endif
+int OsPingFunc(u32_t *parg);
+
+#if defined (__cplusplus) && __cplusplus
+}
+#endif
+#endif /* LWIP_HDR_API_SHELL_H */
diff --git a/code/sdk/zhihui/ppp/include/ppp_api.h b/code/sdk/zhihui/ppp/include/ppp_api.h
new file mode 100644 (file)
index 0000000..92ec12c
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef PPP_API_H
+#define PPP_API_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ppp_config.h"
+#include "ppp_types.h"
+#include "ppp_power_ctl.h"
+#include "errcode.h"
+
+/**
+ * @brief 初始化PPP模块
+ * @param config PPP配置参数
+ * @return 错误码
+ */
+errcode_t zhihui_ppp_init(const ppp_config_t *config);
+
+/**
+ * @brief PPP模块去初始化
+ * @return 错误码
+ */
+errcode_t zhihui_ppp_deinit(void);
+
+/**
+ * @brief 启动PPP拨号
+ * @return 错误码
+ */
+errcode_t ppp_start_dial(void);
+
+/**
+ * @brief 停止PPP连接
+ * @return 错误码
+ */
+errcode_t ppp_stop_dial(void);
+
+/**
+ * @brief 获取PPP连接状态
+ * @return PPP状态
+ */
+ppp_state_t ppp_get_state(void);
+
+/**
+ * @brief 注册PPP事件回调函数
+ * @param callback 事件回调函数
+ * @return 错误码
+ */
+errcode_t ppp_register_event_callback(ppp_event_callback_t callback);
+
+/**
+ * @brief 获取PPP连接信息
+ * @param info 连接信息结构指针
+ * @return 错误码
+ */
+errcode_t ppp_get_connection_info(ppp_connection_info_t *info);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PPP_API_H */ 
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/include/ppp_at_cmd.h b/code/sdk/zhihui/ppp/include/ppp_at_cmd.h
new file mode 100644 (file)
index 0000000..6bd9a9e
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) @CompanyNameMagicTag 2022-2022. All rights reserved. \n
+ *
+ * Description: Provides at cmd register func \n
+ * Author: @CompanyNameTag \n
+ * History: \n
+ * 2022-07-20, Create file. \n
+ */
+#ifndef PPP_AT_CMD_H
+#define PPP_AT_CMD_H
+
+#include <stdint.h>
+#include <stdio.h>
+#include "errcode.h"
+#include "soc_osal.h"
+#include "at_cmd_api.h"
+
+#ifdef __cplusplus
+#if __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+at_cmd_table_t *get_ppp_cmd_table(uint32_t *cmd_count);
+
+#ifdef __cplusplus
+#if __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+#endif
diff --git a/code/sdk/zhihui/ppp/include/ppp_at_handler.h b/code/sdk/zhihui/ppp/include/ppp_at_handler.h
new file mode 100644 (file)
index 0000000..50f9341
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef PPP_AT_HANDLER_H
+#define PPP_AT_HANDLER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "ppp_config.h"
+#include "ppp_types.h"
+
+/**
+ * @brief 初始化AT命令处理模块
+ * @param uart_bus UART总线号
+ * @return 错误码
+ */
+ppp_error_t ppp_at_handler_init(uint8_t uart_bus);
+
+/**
+ * @brief 反初始化AT命令处理模块
+ */
+void ppp_at_handler_deinit(void);
+
+/**
+ * @brief 发送AT命令
+ * @param command AT命令字符串
+ * @param expected_response 期望的响应字符串
+ * @param timeout 超时时间(ms)
+ * @param response 响应结构体指针
+ * @return 错误码
+ */
+ppp_error_t ppp_at_send_command(const char *command, const char *expected_response, 
+                                uint32_t timeout, ppp_at_response_t *response);
+
+/**
+ * @brief 测试AT连接
+ * @return 错误码
+ */
+ppp_error_t ppp_at_test_connection(void);
+
+/**
+ * @brief 设置波特率
+ * @param baudrate 波特率
+ * @return 错误码
+ */
+ppp_error_t ppp_at_set_baudrate(uint32_t baudrate);
+
+/**
+ * @brief 自动检测波特率
+ * @return 错误码
+ */
+ppp_error_t ppp_at_auto_detect_baudrate(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PPP_AT_HANDLER_H */ 
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/include/ppp_cat1_api.h b/code/sdk/zhihui/ppp/include/ppp_cat1_api.h
new file mode 100644 (file)
index 0000000..64acdcc
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * @brief PPP模块的cat1 API兼容接口声明
+ * @description 提供与cat1Service相同的接口声明,让UI和其他服务无需修改
+ * Copyright (c) CompanyNameMagicTag 2024-2024. All rights reserved.
+ */
+
+#ifndef __PPP_CAT1_API_H
+#define __PPP_CAT1_API_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "errcode.h"
+#include "platform_core.h"
+
+// 前向声明,避免重复定义
+typedef enum _4g_cmd CMD_4G_T;
+typedef struct ctl_pin_4g_s ctl_pin_4g_t;
+typedef struct time_ctl_funcs_s time_ctl_funcs_t;
+typedef errcode_t (*f_cat1_msg_callback)(int msg, void *msg_buf, uint16_t msglen);
+
+/********************** 主要接口 **********************/
+
+/**
+ * @brief cat1 服务初始化
+ * @return 成功 ERRCODE_SUCC 或者其他失败ERRCODE
+ */
+errcode_t cat1_server_init(void);
+
+/**
+ * @brief cat1 开关机设置, 输入 true 开机, false 关机
+ */
+void cat1_api_enable_cat1(bool isopen);
+
+/**
+ * @brief 获取cat1是否使能(开机)状态
+ */
+bool cat1_api_is_cat1_enabled(void);
+
+/**
+ * @brief 串口接收函数,加入到uart_porting 中断处理中
+ */
+void cat1_uart_receive_handler(uint8_t *buffer, int32_t size);
+
+/**
+ * @brief return cmd uart rx enable flag
+ */
+bool cat1_api_custom_is_cmd_rx_enabled(void);
+
+/********************** 运行时状态获取接口 **********************/
+
+/**
+ * @brief 获取cat1电源开启状态
+ * @return 1 开机,0 关机
+ */
+uint8_t cat1_api_runtime_get_cat1_powerstat(void);
+
+/**
+ * @brief 获取cat1网络状态
+ * @return 1 网络在线,0 断网
+ */
+uint8_t cat1_api_runtime_get_cat1_networkstat(void);
+
+/**
+ * @brief 获取cat1信号强度
+ * @return 1~31 或者 99,99代表无网络
+ */
+int cat1_api_runtime_get_cat1_signal(void);
+
+/**
+ * @brief 获取sim卡状态
+ * @return 1 有sim卡,0 无sim卡
+ */
+int cat1_api_runtime_get_cat1_simstat(void);
+
+/**
+ * @brief 获取网络类型
+ * @return 0 "无网络", 1 "GSM", 2 "3G", 4 "4G/LTE"
+ */
+uint8_t cat1_api_runtime_get_cat1_networktype(void);
+
+/**
+ * @brief 获取Cat1运营商信息
+ * @return 返回运营商字符串
+ */
+char* cat1_api_runtime_get_cat1_operator(void);
+
+/**
+ * @brief 获取Cat1 IMEI号
+ * @return 返回imei字符串
+ */
+char* cat1_api_runtime_get_cat1_imei(void);
+
+/**
+ * @brief 获取Cat1主应用版本号
+ * @return 返回主应用版本号
+ */
+char* cat1_api_runtime_get_cat1_pyver(void);
+
+/**
+ * @brief 获取modem版本
+ * @return modem版本字符串
+ */
+char* cat1_api_runtime_get_cat1_modemver(void);
+
+/********************** 运行时状态设置接口 **********************/
+
+/**
+ * @brief 设置cat1电源状态
+ */
+void cat1_api_runtime_set_cat1_powerstat(uint8_t cat1_powerOn);
+
+/**
+ * @brief 设置cat1网络状态
+ */
+void cat1_api_runtime_set_cat1_networkstat(uint8_t network_on);
+
+/**
+ * @brief 设置cat1信号强度
+ */
+void cat1_api_runtime_set_cat1_signal(int signal);
+
+/**
+ * @brief 设置sim卡状态
+ */
+void cat1_api_runtime_set_cat1_simstat(int simstat);
+
+/**
+ * @brief 设置网络类型
+ */
+void cat1_api_runtime_set_cat1_networktype(uint8_t networktype);
+
+/**
+ * @brief 设置Cat1运营商信息
+ */
+void cat1_api_runtime_set_cat1_operator(char* cat1_operator);
+
+/**
+ * @brief 设置Cat1 IMEI号
+ */
+void cat1_api_runtime_set_cat1_imei(char* imei);
+
+/**
+ * @brief 设置Cat1主应用版本号
+ */
+void cat1_api_runtime_set_cat1_pyver(char* pyver);
+
+/**
+ * @brief 设置modem版本
+ */
+void cat1_api_runtime_set_cat1_modemver(char* modemver);
+
+/********************** 控制接口 **********************/
+
+/**
+ * @brief 唤醒Cat1
+ */
+void cat1_api_wakeup_cat1(uint8_t wakeup);
+
+/**
+ * @brief 保持唤醒Cat1
+ */
+void cat1_api_keep_wakeup(uint8_t keep_wakeup);
+
+/**
+ * @brief 注册控制4G的gpio值
+ */
+errcode_t cat1_register_ctl_pin(ctl_pin_4g_t ctl_pin);
+
+/**
+ * @brief cat1唤醒蓝牙中断处理函数
+ */
+void cat1_ring_isr_callback(pin_t pin, uintptr_t param);
+
+/********************** 其他接口 **********************/
+
+/**
+ * @brief 发送指令给Cat1
+ */
+errcode_t cat1_cmd_send(CMD_4G_T cmd, void *cmd_buf, uint16_t cmdlen);
+
+/**
+ * @brief 注册消息回调函数
+ */
+errcode_t cat1_register_msg_callback(f_cat1_msg_callback func);
+
+/**
+ * @brief 注册时间控制函数
+ */
+errcode_t cat1_register_time_ctl_functions(time_ctl_funcs_t *funcs);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __PPP_CAT1_API_H */ 
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/include/ppp_config.h b/code/sdk/zhihui/ppp/include/ppp_config.h
new file mode 100644 (file)
index 0000000..b9dce07
--- /dev/null
@@ -0,0 +1,76 @@
+#ifndef PPP_CONFIG_H
+#define PPP_CONFIG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/* PPP配置参数 - UART配置统一使用SIO (在lwipopts_default.h中定义) */
+
+/* 支持的波特率列表(自适应模式) */
+#define PPP_BAUDRATE_AUTO_LIST      {9600, 57600, 115200}
+#define PPP_BAUDRATE_AUTO_COUNT     3
+
+/* 支持的固定波特率列表 */
+#define PPP_BAUDRATE_FIXED_LIST     {4800, 9600, 57600, 115200, 230400, 460800, 750000, 921600, 1000000, 2000000, 4000000}
+#define PPP_BAUDRATE_FIXED_COUNT    11
+
+/* AT命令配置 */
+#define PPP_AT_CMD_TIMEOUT          5000                // AT命令超时时间(ms)
+#define PPP_AT_CMD_MAX_LEN          256                 // AT命令最大长度
+#define PPP_AT_RESPONSE_MAX_LEN     1024                // AT响应最大长度
+#define PPP_AT_RETRY_COUNT          3                   // AT命令重试次数
+
+/* PPP拨号配置 */
+#define PPP_DIAL_TIMEOUT            30000               // 拨号超时时间(ms)
+#define PPP_DIAL_RETRY_COUNT        3                   // 拨号重试次数
+#define PPP_KEEPALIVE_INTERVAL      30                  // 保活间隔(秒)
+
+/* 缓冲区大小配置 */
+#define PPP_RX_BUFFER_SIZE          2048                // 接收缓冲区大小
+#define PPP_TX_BUFFER_SIZE          1024                // 发送缓冲区大小
+
+/* PPP网络配置 */
+#define PPP_MTU_SIZE                1500                // PPP MTU大小
+#define PPP_MRU_SIZE                1500                // PPP MRU大小
+
+/* 调试配置 */
+#define PPP_DEBUG_ENABLE            1                   // 启用调试输出
+#define PPP_DEBUG_LEVEL             3                   // 调试级别 (0-4)
+
+/* PPP状态定义 */
+typedef enum {
+    PPP_STATE_IDLE = 0,                                 // 空闲状态
+    PPP_STATE_INITIALIZED,                              // 已初始化状态
+    PPP_STATE_INIT,                                     // 初始化状态
+    PPP_STATE_AT_HANDSHAKE,                            // AT握手状态
+    PPP_STATE_SET_BAUDRATE,                            // 设置波特率状态
+    PPP_STATE_DIALING,                                 // 拨号状态
+    PPP_STATE_CONNECTED,                               // 已连接状态
+    PPP_STATE_DISCONNECTING,                           // 断开连接状态
+    PPP_STATE_ERROR                                    // 错误状态
+} ppp_state_t;
+
+/* PPP错误码定义 */
+typedef enum {
+    PPP_ERR_OK = 0,                                    // 成功
+    PPP_ERR_TIMEOUT,                                   // 超时
+    PPP_ERR_AT_FAILED,                                 // AT命令失败
+    PPP_ERR_DIAL_FAILED,                              // 拨号失败
+    PPP_ERR_NO_CARRIER,                               // 无载波
+    PPP_ERR_BUSY,                                      // 忙
+    PPP_ERR_NO_DIALTONE,                              // 无拨号音
+    PPP_ERR_INVALID_PARAM,                            // 无效参数
+    PPP_ERR_MEMORY,                                    // 内存错误
+    PPP_ERR_UART,                                      // 串口错误
+    PPP_ERR_UNKNOWN                                    // 未知错误
+} ppp_error_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PPP_CONFIG_H */ 
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/include/ppp_debug.h b/code/sdk/zhihui/ppp/include/ppp_debug.h
new file mode 100644 (file)
index 0000000..c2d0336
--- /dev/null
@@ -0,0 +1,95 @@
+#ifndef PPP_DEBUG_H
+#define PPP_DEBUG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include "ppp_config.h"
+#include "debug_print.h"
+
+/* 调试级别定义 */
+#define PPP_DEBUG_LEVEL_NONE    0
+#define PPP_DEBUG_LEVEL_ERROR   1
+#define PPP_DEBUG_LEVEL_WARN    2
+#define PPP_DEBUG_LEVEL_INFO    3
+#define PPP_DEBUG_LEVEL_DEBUG   4
+
+#if PPP_DEBUG_ENABLE
+
+#define PPP_DEBUG_ERROR(fmt, ...) \
+    do { \
+        if (PPP_DEBUG_LEVEL >= PPP_DEBUG_LEVEL_ERROR) { \
+            PRINT("[PPP ERROR] " fmt "\r\n", ##__VA_ARGS__); \
+        } \
+    } while(0)
+
+#define PPP_DEBUG_WARN(fmt, ...) \
+    do { \
+        if (PPP_DEBUG_LEVEL >= PPP_DEBUG_LEVEL_WARN) { \
+            PRINT("[PPP WARN] " fmt "\r\n", ##__VA_ARGS__); \
+        } \
+    } while(0)
+
+#define PPP_DEBUG_INFO(fmt, ...) \
+    do { \
+        if (PPP_DEBUG_LEVEL >= PPP_DEBUG_LEVEL_INFO) { \
+            PRINT("[PPP INFO] " fmt "\r\n", ##__VA_ARGS__); \
+        } \
+    } while(0)
+
+#define PPP_DEBUG_DEBUG(fmt, ...) \
+    do { \
+        if (PPP_DEBUG_LEVEL >= PPP_DEBUG_LEVEL_DEBUG) { \
+            PRINT("[PPP DEBUG] " fmt "\r\n", ##__VA_ARGS__); \
+        } \
+    } while(0)
+
+#define PPP_DEBUG_HEXDUMP(data, len) \
+    do { \
+        if (PPP_DEBUG_LEVEL >= PPP_DEBUG_LEVEL_DEBUG) { \
+            ppp_debug_hexdump(data, len); \
+        } \
+    } while(0)
+
+/* 兼容性宏 - 用于旧代码 */
+#define PPP_DEBUG_PRINT(fmt, ...) PPP_DEBUG_INFO(fmt, ##__VA_ARGS__)
+
+#else
+
+#define PPP_DEBUG_ERROR(fmt, ...)
+#define PPP_DEBUG_WARN(fmt, ...)
+#define PPP_DEBUG_INFO(fmt, ...)
+#define PPP_DEBUG_DEBUG(fmt, ...)
+#define PPP_DEBUG_HEXDUMP(data, len)
+#define PPP_DEBUG_PRINT(fmt, ...)
+
+#endif /* PPP_DEBUG_ENABLE */
+
+/**
+ * @brief 十六进制数据转储
+ * @param data 数据指针
+ * @param len 数据长度
+ */
+void ppp_debug_hexdump(const uint8_t *data, uint32_t len);
+
+/**
+ * @brief 获取错误码字符串
+ * @param error_code 错误码
+ * @return 错误码字符串
+ */
+const char *ppp_debug_error_string(ppp_error_t error_code);
+
+/**
+ * @brief 获取状态字符串
+ * @param state PPP状态
+ * @return 状态字符串
+ */
+const char *ppp_debug_state_string(ppp_state_t state);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PPP_DEBUG_H */ 
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/include/ppp_log.h b/code/sdk/zhihui/ppp/include/ppp_log.h
new file mode 100644 (file)
index 0000000..19c5d01
--- /dev/null
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
+ *
+ * Description: Board log port. \n
+ *
+ * History: \n
+ * 2023-05-06, Create file. \n
+ */
+#ifndef PPP_LOG_PORT_H
+#define PPP_LOG_PORT_H
+
+#include <stdio.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+extern uint8_t g_ppp_enable_debug;
+typedef enum{
+    PPP_LOG_LEVEL_ERROR,
+    PPP_LOG_LEVEL_WARNING,
+    PPP_LOG_LEVEL_INFO,
+    PPP_LOG_LEVEL_DBG,
+    PPP_LOG_LEVEL_MAX
+} PPP_LOG_LEVEL_E;
+/* 通过printf直接打印,不需要日志时需要注释掉define后半部分. */
+#define ppp_print_dbg(fmt, args...)         if (g_ppp_enable_debug>=PPP_LOG_LEVEL_DBG)  printf("[PPP_DBG]" fmt "\n", ##args)
+#define ppp_print_info(fmt, args...)         if (g_ppp_enable_debug>=PPP_LOG_LEVEL_INFO)  printf("[PPP_INFO]" fmt "\n", ##args)
+#define ppp_print_warning(fmt, args...)      if (g_ppp_enable_debug>=PPP_LOG_LEVEL_WARNING) printf("[PPP_WRN]" fmt "\n", ##args)
+#define ppp_print_err(fmt, args...)          if (g_ppp_enable_debug>=PPP_LOG_LEVEL_ERROR) printf("[PPP_ERR]" fmt "\n", ##args)
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/code/sdk/zhihui/ppp/include/ppp_lwip.h b/code/sdk/zhihui/ppp/include/ppp_lwip.h
new file mode 100644 (file)
index 0000000..f31b3a9
--- /dev/null
@@ -0,0 +1,109 @@
+#ifndef PPP_LWIP_H
+#define PPP_LWIP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "ppp_types.h"
+
+/* 前向声明 */
+struct netif;
+
+/* PPP lwIP事件类型 */
+typedef enum {
+    PPP_LWIP_EVENT_CONNECTED = 0,                       // 连接成功
+    PPP_LWIP_EVENT_DISCONNECTED,                        // 连接断开
+    PPP_LWIP_EVENT_ERROR,                               // 发生错误
+    PPP_LWIP_EVENT_STATUS_CHANGE,                       // 状态变化
+    PPP_LWIP_EVENT_IP_ACQUIRED,                         // 获取到IP地址
+    PPP_LWIP_EVENT_DNS_ACQUIRED                         // 获取到DNS地址
+} ppp_lwip_event_type_t;
+
+/* PPP lwIP事件结构 */
+typedef struct {
+    ppp_lwip_event_type_t type;                         // 事件类型
+    int error_code;                                     // 错误码
+    void *data;                                         // 事件数据
+    uint32_t data_len;                                  // 数据长度
+} ppp_lwip_event_t;
+
+/* PPP lwIP事件回调函数类型 */
+typedef void (*ppp_lwip_event_callback_t)(const ppp_lwip_event_t *event, void *arg);
+
+/* PPP lwIP配置结构 */
+typedef struct {
+    const char *username;                               // 用户名
+    const char *password;                               // 密码
+    ppp_lwip_event_callback_t event_callback;          // 事件回调函数
+    void *event_callback_arg;                           // 回调函数参数
+} ppp_lwip_config_t;
+
+/* PPP lwIP网络信息结构 */
+typedef struct {
+    bool connected;                                     // 连接状态
+    uint32_t local_ip;                                  // 本地IP地址
+    uint32_t remote_ip;                                 // 远程IP地址
+    uint32_t primary_dns;                               // 主DNS服务器
+    uint32_t secondary_dns;                             // 辅DNS服务器
+    uint16_t mtu;                                       // MTU大小
+} ppp_lwip_network_info_t;
+
+/**
+ * @brief 初始化PPP lwIP集成
+ * @param config 配置参数
+ * @return PPP错误码
+ */
+ppp_error_t ppp_lwip_init(const ppp_lwip_config_t *config);
+
+/**
+ * @brief 反初始化PPP lwIP集成
+ */
+void ppp_lwip_deinit(void);
+
+/**
+ * @brief 连接PPP
+ * @return PPP错误码
+ */
+ppp_error_t ppp_lwip_connect(void);
+
+/**
+ * @brief 断开PPP连接
+ * @return PPP错误码
+ */
+ppp_error_t ppp_lwip_disconnect(void);
+
+/**
+ * @brief 输入PPP数据
+ * @param data 数据指针
+ * @param length 数据长度
+ * @return PPP错误码
+ */
+ppp_error_t ppp_lwip_input(const uint8_t *data, uint32_t length);
+
+/**
+ * @brief 获取PPP连接状态
+ * @return true表示已连接,false表示未连接
+ */
+bool ppp_lwip_is_connected(void);
+
+/**
+ * @brief 获取网络信息
+ * @param info 网络信息结构指针
+ * @return PPP错误码
+ */
+ppp_error_t ppp_lwip_get_network_info(ppp_lwip_network_info_t *info);
+
+/**
+ * @brief 获取网络接口指针
+ * @return 网络接口指针
+ */
+struct netif *ppp_lwip_get_netif(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PPP_LWIP_H */ 
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/include/ppp_power_ctl.h b/code/sdk/zhihui/ppp/include/ppp_power_ctl.h
new file mode 100644 (file)
index 0000000..c1851fb
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * @brief PPP模块电源控制接口
+ * @description 提供PPP模块专用的4G电源控制功能,与SLIP模块互斥使用
+ * Copyright (c) CompanyNameMagicTag 2024-2024. All rights reserved.
+ */
+
+#ifndef __PPP_POWER_CTL_H
+#define __PPP_POWER_CTL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "platform_core.h"
+#include "errcode.h"
+
+/**
+ * @brief PPP模块4G电源控制引脚配置结构体
+ */
+typedef struct {
+    pin_t bat_pin;              // 控制Cat1 battery 的gpio,没有控制,设置为 PIN_NONE 即可
+    uint8_t bat_pin_en_value;   // battery引脚使能值
+    pin_t pwr_pin;              // 控制cat1开机键的gpio
+    uint8_t pwr_pin_en_value;   // 电源引脚使能值
+    pin_t reset_pin;            // 控制cat1重置键的gpio
+    uint8_t reset_pin_en_value; // 复位引脚使能值
+    pin_t ring_pin;             // cat1唤醒蓝牙用的gpio
+    uint8_t ring_pin_en_value;  // 唤醒引脚使能值
+    pin_t wakemodem_pin;        // 蓝牙唤醒cat1用的gpio
+    uint8_t wakemodem_pin_en_value; // 唤醒modem引脚使能值
+} ppp_ctl_pin_4g_t;
+
+/**
+ * @brief PPP模块GPIO初始化
+ */
+void ppp_api_gpio_init(void);
+
+/**
+ * @brief PPP模块UART初始化
+ */
+void ppp_api_uart_init(void);
+
+/**
+ * @brief PPP模块设置电源引脚状态
+ * @param pwron 1-开启电源,0-关闭电源
+ * @return 0-成功,其他-失败
+ */
+uint32_t ppp_api_cat1_power_on(uint32_t pwron);
+
+/**
+ * @brief PPP模块设置复位引脚状态
+ * @param pwron 1-复位,0-释放复位
+ * @return 0-成功,其他-失败
+ */
+uint32_t ppp_api_cat1_reset(uint32_t pwron);
+
+/**
+ * @brief PPP模块设置电池引脚状态
+ * @param pwron 1-开启电池,0-关闭电池
+ * @return 0-成功,其他-失败
+ */
+uint32_t ppp_api_cat1_bat_on(uint32_t pwron);
+
+/**
+ * @brief PPP模块4G上电序列
+ */
+void ppp_power_on_cat1(void);
+
+/**
+ * @brief PPP模块4G下电序列
+ */
+void ppp_power_off_cat1(void);
+
+/**
+ * @brief PPP模块检查4G是否使能
+ * @return true-已使能,false-未使能
+ */
+bool ppp_api_is_cat1_enabled(void);
+
+/**
+ * @brief PPP模块唤醒Cat1
+ * @param wakeup 1-唤醒,0-不唤醒
+ */
+void ppp_api_wakeup_cat1(uint8_t wakeup);
+
+/**
+ * @brief PPP模块保持唤醒Cat1
+ * @param keep_wakeup 1-保持唤醒,0-允许休眠
+ */
+void ppp_api_keep_wakeup(uint8_t keep_wakeup);
+
+/**
+ * @brief PPP模块挂起
+ */
+void ppp_suspend(void);
+
+/**
+ * @brief PPP模块恢复
+ */
+void ppp_resume(void);
+
+/**
+ * @brief PPP模块注册4G控制引脚配置
+ * @param ctl_pin 引脚配置结构体指针
+ * @return ERRCODE_SUCC-成功,其他-失败
+ */
+errcode_t ppp_register_ctl_pin(ppp_ctl_pin_4g_t *ctl_pin);
+
+/**
+ * @brief PPP模块Cat1唤醒中断回调函数
+ * @param pin 中断引脚
+ * @param param 参数
+ */
+void ppp_ring_isr_callback(pin_t pin, uintptr_t param);
+
+/**
+ * @brief PPP模块专用的4G使能控制接口
+ * @param isopen true-开启4G,false-关闭4G
+ * @note 此接口与SLIP模块互斥,只能有一个模块控制4G电源
+ */
+void ppp_api_enable_cat1(bool isopen);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __PPP_POWER_CTL_H */ 
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/include/ppp_serial.h b/code/sdk/zhihui/ppp/include/ppp_serial.h
new file mode 100644 (file)
index 0000000..1a2c334
--- /dev/null
@@ -0,0 +1,101 @@
+#ifndef PPP_SERIAL_H
+#define PPP_SERIAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "ppp_types.h"
+
+/* 串口接收回调函数类型 */
+typedef void (*ppp_serial_rx_callback_t)(const uint8_t *data, uint16_t length, void *arg);
+
+/* 串口配置结构 */
+typedef struct {
+    uint8_t uart_bus;                                   // 串口总线号
+    uint32_t baudrate;                                  // 波特率
+    uint8_t tx_pin;                                     // TX引脚
+    uint8_t rx_pin;                                     // RX引脚
+    uint32_t rx_buffer_size;                            // 接收缓冲区大小
+    uint32_t tx_buffer_size;                            // 发送缓冲区大小
+    ppp_serial_rx_callback_t rx_callback;              // 接收回调函数
+    void *rx_callback_arg;                              // 回调函数参数
+} ppp_serial_config_t;
+
+/* 串口统计信息结构 */
+typedef struct {
+    uint32_t tx_bytes;                                  // 发送字节数
+    uint32_t rx_bytes;                                  // 接收字节数
+    uint32_t tx_errors;                                 // 发送错误数
+    uint32_t rx_errors;                                 // 接收错误数
+    uint32_t rx_buffer_usage;                           // 接收缓冲区使用量
+    uint32_t rx_buffer_size;                            // 接收缓冲区大小
+} ppp_serial_stats_t;
+
+/**
+ * @brief 初始化PPP串口
+ * @param config 串口配置
+ * @return PPP错误码
+ */
+ppp_error_t ppp_serial_init(const ppp_serial_config_t *config);
+
+/**
+ * @brief 反初始化PPP串口
+ */
+void ppp_serial_deinit(void);
+
+/**
+ * @brief 发送数据
+ * @param data 数据指针
+ * @param length 数据长度
+ * @return PPP错误码
+ */
+ppp_error_t ppp_serial_write(const uint8_t *data, uint32_t length);
+
+/**
+ * @brief 读取数据
+ * @param buffer 接收缓冲区
+ * @param buffer_size 缓冲区大小
+ * @param bytes_read 实际读取的字节数
+ * @return PPP错误码
+ */
+ppp_error_t ppp_serial_read(uint8_t *buffer, uint32_t buffer_size, uint32_t *bytes_read);
+
+/**
+ * @brief 等待数据接收
+ * @param timeout_ms 超时时间(毫秒)
+ * @return PPP错误码
+ */
+ppp_error_t ppp_serial_wait_data(uint32_t timeout_ms);
+
+/**
+ * @brief 获取可读数据长度
+ * @return 可读数据长度
+ */
+uint32_t ppp_serial_available(void);
+
+/**
+ * @brief 清空接收缓冲区
+ */
+void ppp_serial_flush_rx(void);
+
+/**
+ * @brief 设置串口波特率
+ * @param baudrate 波特率
+ * @return PPP错误码
+ */
+ppp_error_t ppp_serial_set_baudrate(uint32_t baudrate);
+
+/**
+ * @brief 获取统计信息
+ * @param stats 统计信息结构指针
+ */
+void ppp_serial_get_stats(ppp_serial_stats_t *stats);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PPP_SERIAL_H */ 
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/include/ppp_types.h b/code/sdk/zhihui/ppp/include/ppp_types.h
new file mode 100644 (file)
index 0000000..4e43cb7
--- /dev/null
@@ -0,0 +1,150 @@
+#ifndef PPP_TYPES_H
+#define PPP_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "ppp_config.h"
+
+/* 前向声明,避免直接包含lwIP头文件 */
+struct netif;
+struct ip4_addr;
+
+/* PPP配置结构(简化版,用于新API) */
+typedef struct {
+    char apn[64];                                       // APN接入点名称
+    char username[32];                                  // 用户名
+    char password[32];                                  // 密码
+    uint32_t baudrate;                                  // 串口波特率
+    bool auto_baudrate;                                 // 是否自动波特率检测
+    uint32_t dial_timeout;                              // 拨号超时时间(ms)
+    uint8_t retry_count;                                // 重试次数
+} ppp_config_t;
+
+/* PPP连接信息结构(简化版,用于新API) */
+typedef struct {
+    uint32_t local_ip;                                  // 本地IP地址
+    uint32_t remote_ip;                                 // 远程IP地址
+    uint32_t primary_dns;                               // 主DNS服务器
+    uint32_t secondary_dns;                             // 辅DNS服务器
+    uint16_t mtu;                                       // MTU大小
+    bool connected;                                     // 连接状态
+    uint32_t connect_time;                              // 连接时间(秒)
+} ppp_connection_info_t;
+
+/* PPP拨号参数结构(原有的,保持兼容性) */
+typedef struct {
+    char apn[64];                                       // APN接入点名称
+    char username[32];                                  // 用户名
+    char password[32];                                  // 密码
+    uint32_t baudrate;                                  // 串口波特率
+    bool auto_baudrate;                                 // 是否自动波特率检测
+    uint32_t dial_timeout;                              // 拨号超时时间(ms)
+    uint8_t retry_count;                                // 重试次数
+} ppp_dial_config_t;
+
+/* PPP网络信息结构 */
+typedef struct {
+    uint32_t local_ip;                                  // 本地IP地址
+    uint32_t remote_ip;                                 // 远程IP地址
+    uint32_t primary_dns;                               // 主DNS服务器
+    uint32_t secondary_dns;                             // 辅DNS服务器
+    uint32_t netmask;                                   // 子网掩码
+    uint32_t gateway;                                   // 网关地址
+    uint16_t mtu;                                       // MTU大小
+    bool connected;                                     // 连接状态
+} ppp_network_info_t;
+
+/* PPP统计信息结构 */
+typedef struct {
+    uint32_t tx_packets;                                // 发送包数
+    uint32_t rx_packets;                                // 接收包数
+    uint32_t tx_bytes;                                  // 发送字节数
+    uint32_t rx_bytes;                                  // 接收字节数
+    uint32_t tx_errors;                                 // 发送错误数
+    uint32_t rx_errors;                                 // 接收错误数
+    uint32_t connect_time;                              // 连接时间(秒)
+    uint32_t last_activity;                             // 最后活动时间戳
+} ppp_stats_t;
+
+/* PPP事件类型 */
+typedef enum {
+    PPP_EVENT_CONNECTED = 0,                            // 连接成功
+    PPP_EVENT_DISCONNECTED,                             // 连接断开
+    PPP_EVENT_ERROR,                                    // 发生错误
+    PPP_EVENT_IP_ACQUIRED,                              // 获取到IP地址
+    PPP_EVENT_DNS_ACQUIRED,                             // 获取到DNS地址
+    PPP_EVENT_AT_RESPONSE,                              // AT命令响应
+    PPP_EVENT_DATA_RECEIVED                             // 接收到数据
+} ppp_event_type_t;
+
+/* PPP事件数据结构 */
+typedef struct {
+    ppp_event_type_t type;                              // 事件类型
+    ppp_error_t error_code;                             // 错误码
+    void *data;                                         // 事件数据
+    uint32_t data_len;                                  // 数据长度
+} ppp_event_t;
+
+/* PPP事件回调函数类型 */
+typedef void (*ppp_event_callback_t)(const ppp_event_t *event);
+
+/* PPP状态枚举 - 定义在ppp_config.h中 */
+
+/* PPP控制块结构 */
+typedef struct {
+    ppp_state_t state;                                  // 当前状态
+    ppp_dial_config_t config;                           // 拨号配置
+    ppp_network_info_t network;                         // 网络信息
+    ppp_stats_t stats;                                  // 统计信息
+    
+    /* lwIP相关 */
+    struct netif *netif;                                // 网络接口指针
+    void *ppp_pcb;                                      // PPP控制块
+    
+    /* 串口相关 */
+    uint8_t uart_bus;                                   // 串口总线
+    uint8_t *rx_buffer;                                 // 接收缓冲区
+    uint8_t *tx_buffer;                                 // 发送缓冲区
+    uint32_t rx_buffer_size;                            // 接收缓冲区大小
+    uint32_t tx_buffer_size;                            // 发送缓冲区大小
+    
+    /* 同步相关 */
+    void *mutex;                                        // 互斥锁
+    void *semaphore;                                    // 信号量
+    void *task_handle;                                  // 任务句柄
+    
+    /* 回调函数 */
+    ppp_event_callback_t event_callback;               // 事件回调函数
+    
+    /* 内部状态 */
+    uint32_t last_at_time;                              // 最后AT命令时间
+    uint32_t connect_start_time;                        // 连接开始时间
+    bool initialized;                                   // 初始化标志
+    bool running;                                       // 运行标志
+} ppp_context_t;
+
+/* AT命令结构 */
+typedef struct {
+    char command[PPP_AT_CMD_MAX_LEN];                   // AT命令
+    char expected_response[64];                         // 期望响应
+    uint32_t timeout;                                   // 超时时间(ms)
+    uint8_t retry_count;                                // 重试次数
+} ppp_at_cmd_t;
+
+/* AT响应结构 */
+typedef struct {
+    char response[PPP_AT_RESPONSE_MAX_LEN];             // 响应内容
+    uint32_t length;                                    // 响应长度
+    bool success;                                       // 是否成功
+    ppp_error_t error_code;                             // 错误码
+} ppp_at_response_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PPP_TYPES_H */ 
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/src/ppp_api.c b/code/sdk/zhihui/ppp/src/ppp_api.c
new file mode 100644 (file)
index 0000000..c6df73c
--- /dev/null
@@ -0,0 +1,151 @@
+#include "ppp_api.h"
+#include "ppp_debug.h"
+#include "ppp_power_ctl.h"
+#include "osal_debug.h"
+#include "schedule/osal_task.h"
+#include "securec.h"
+#include "ppp_log.h"
+// PPP连接状态
+static ppp_state_t g_ppp_state = PPP_STATE_IDLE;
+
+// PPP配置参数
+static ppp_config_t g_ppp_config = {0};
+
+uint8_t g_ppp_enable_debug = PPP_LOG_LEVEL_INFO;
+
+// 外部声明cat1_api_enable_cat1函数(在ppp_cat1_api.c中实现)
+extern void cat1_api_enable_cat1(bool isopen);
+
+/**
+ * @brief 初始化PPP模块
+ */
+errcode_t zhihui_ppp_init(const ppp_config_t *config)
+{
+    if (config == NULL) {
+        PPP_DEBUG_PRINT("PPP init: invalid config parameter\n");
+        return ERRCODE_INVALID_PARAM;
+    }
+
+    // 复制配置参数
+    (void)memcpy_s(&g_ppp_config, sizeof(ppp_config_t), 
+                   config, sizeof(ppp_config_t));
+
+    // 初始化电源控制
+    ppp_api_gpio_init();
+    ppp_api_uart_init();
+
+    g_ppp_state = PPP_STATE_INITIALIZED;
+    PPP_DEBUG_PRINT("PPP module initialized successfully\n");
+
+    return ERRCODE_SUCC;
+}
+
+/**
+ * @brief 启动PPP拨号
+ */
+errcode_t ppp_start_dial(void)
+{
+    if (g_ppp_state != PPP_STATE_INITIALIZED) {
+        PPP_DEBUG_PRINT("PPP start dial: module not initialized\n");
+        return ERRCODE_FAIL;
+    }
+
+    PPP_DEBUG_PRINT("Starting PPP dial sequence\n");
+
+    // 1. 启动4G电源(通过兼容接口)
+    cat1_api_enable_cat1(true);
+
+    // 2. 等待4G模块启动
+    osal_msleep(2000);
+
+    // 3. 初始化AT命令处理
+    // TODO: 实现AT命令初始化
+
+    // 4. 启动PPP协议栈
+    // TODO: 实现PPP协议栈启动
+
+    g_ppp_state = PPP_STATE_DIALING;
+    PPP_DEBUG_PRINT("PPP dial started\n");
+
+    return ERRCODE_SUCC;
+}
+
+/**
+ * @brief 停止PPP连接
+ */
+errcode_t ppp_stop_dial(void)
+{
+    PPP_DEBUG_PRINT("Stopping PPP connection\n");
+
+    // 1. 停止PPP协议栈
+    // TODO: 实现PPP协议栈停止
+
+    // 2. 关闭4G电源(通过兼容接口)
+    cat1_api_enable_cat1(false);
+
+    g_ppp_state = PPP_STATE_IDLE;
+    PPP_DEBUG_PRINT("PPP connection stopped\n");
+
+    return ERRCODE_SUCC;
+}
+
+/**
+ * @brief 获取PPP连接状态
+ */
+ppp_state_t ppp_get_state(void)
+{
+    return g_ppp_state;
+}
+
+/**
+ * @brief 注册PPP事件回调函数
+ */
+errcode_t ppp_register_event_callback(ppp_event_callback_t callback)
+{
+    if (callback == NULL) {
+        PPP_DEBUG_PRINT("PPP register callback: invalid parameter\n");
+        return ERRCODE_INVALID_PARAM;
+    }
+
+    // TODO: 实现事件回调注册
+    PPP_DEBUG_PRINT("PPP event callback registered\n");
+
+    return ERRCODE_SUCC;
+}
+
+/**
+ * @brief 获取PPP连接信息
+ */
+errcode_t ppp_get_connection_info(ppp_connection_info_t *info)
+{
+    if (info == NULL) {
+        PPP_DEBUG_PRINT("PPP get connection info: invalid parameter\n");
+        return ERRCODE_INVALID_PARAM;
+    }
+
+    // TODO: 实现连接信息获取
+    (void)memset_s(info, sizeof(ppp_connection_info_t), 0, sizeof(ppp_connection_info_t));
+
+    PPP_DEBUG_PRINT("PPP connection info retrieved\n");
+
+    return ERRCODE_SUCC;
+}
+
+/**
+ * @brief PPP模块去初始化
+ */
+errcode_t zhihui_ppp_deinit(void)
+{
+    // 停止连接
+    if (g_ppp_state != PPP_STATE_IDLE) {
+        ppp_stop_dial();
+    }
+
+    // 清理资源
+    (void)memset_s(&g_ppp_config, sizeof(ppp_config_t), 0, sizeof(ppp_config_t));
+
+    g_ppp_state = PPP_STATE_IDLE;
+    PPP_DEBUG_PRINT("PPP module deinitialized\n");
+
+    return ERRCODE_SUCC;
+} 
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/src/ppp_at_handler.c b/code/sdk/zhihui/ppp/src/ppp_at_handler.c
new file mode 100644 (file)
index 0000000..827fde9
--- /dev/null
@@ -0,0 +1,410 @@
+#include "ppp_at_handler.h"
+#include "ppp_config.h"
+#include "ppp_types.h"
+#include "ppp_debug.h"
+#include "uart.h"
+#include "lwip/opt.h"
+#include "schedule/osal_task.h"
+#include "time/osal_timer.h"
+#include "lock/osal_mutex.h"
+#include "soc_osal.h"
+#include "securec.h"
+#include <string.h>
+#include <stdio.h>
+
+/* AT命令处理上下文 */
+static struct {
+    bool initialized;
+    uint8_t uart_bus;
+    uint32_t current_baudrate;
+    bool auto_baudrate_mode;
+    ppp_at_response_t last_response;
+    osal_timer *at_timer;
+    osal_mutex *at_mutex;
+} g_at_ctx = {0};
+
+/* 支持的波特率列表 */
+static const uint32_t g_auto_baudrates[] = PPP_BAUDRATE_AUTO_LIST;
+static const uint32_t g_fixed_baudrates[] = PPP_BAUDRATE_FIXED_LIST;
+
+/* AT命令响应解析 */
+static bool ppp_at_parse_response(const char *response, const char *expected);
+static ppp_error_t ppp_at_wait_response(ppp_at_response_t *response, uint32_t timeout);
+static void ppp_at_timeout_handler(void *arg);
+
+/**
+ * @brief 初始化AT命令处理模块
+ */
+ppp_error_t ppp_at_handler_init(uint8_t uart_bus)
+{
+    if (g_at_ctx.initialized) {
+        PPP_DEBUG_WARN("AT handler already initialized");
+        return PPP_ERR_OK;
+    }
+
+    g_at_ctx.uart_bus = uart_bus;
+    g_at_ctx.current_baudrate = SIO_UART_BAUDRATE;
+    g_at_ctx.auto_baudrate_mode = true;
+
+    /* 创建互斥锁 */
+    if (osal_mutex_init(&g_at_ctx.at_mutex) != OSAL_SUCCESS) {
+        PPP_DEBUG_ERROR("Failed to create AT mutex");
+        return PPP_ERR_MEMORY;
+    }
+
+    /* 创建定时器 */
+    if (osal_timer_create(&g_at_ctx.at_timer, ppp_at_timeout_handler, NULL) != OSAL_SUCCESS) {
+        PPP_DEBUG_ERROR("Failed to create AT timer");
+        osal_mutex_destroy(g_at_ctx.at_mutex);
+        return PPP_ERR_MEMORY;
+    }
+
+    g_at_ctx.initialized = true;
+    PPP_DEBUG_INFO("AT handler initialized on UART%d", uart_bus);
+    
+    return PPP_ERR_OK;
+}
+
+/**
+ * @brief 反初始化AT命令处理模块
+ */
+void ppp_at_handler_deinit(void)
+{
+    if (!g_at_ctx.initialized) {
+        return;
+    }
+
+    if (g_at_ctx.at_timer) {
+        osal_timer_destroy(g_at_ctx.at_timer);
+        g_at_ctx.at_timer = NULL;
+    }
+
+    if (g_at_ctx.at_mutex) {
+        osal_mutex_destroy(g_at_ctx.at_mutex);
+        g_at_ctx.at_mutex = NULL;
+    }
+
+    memset_s(&g_at_ctx, sizeof(g_at_ctx), 0, sizeof(g_at_ctx));
+    PPP_DEBUG_INFO("AT handler deinitialized");
+}
+
+/**
+ * @brief 执行波特率自适应握手
+ */
+ppp_error_t ppp_at_baudrate_handshake(void)
+{
+    if (!g_at_ctx.initialized) {
+        return PPP_ERR_INVALID_PARAM;
+    }
+
+    PPP_DEBUG_INFO("Starting baudrate handshake");
+
+    /* 尝试自适应波特率列表 */
+    for (int i = 0; i < PPP_BAUDRATE_AUTO_COUNT; i++) {
+        uint32_t baudrate = g_auto_baudrates[i];
+        
+        PPP_DEBUG_DEBUG("Trying baudrate: %u", baudrate);
+        
+        /* 配置串口波特率 */
+        uart_attr_t uart_attr = {
+            .baud_rate = baudrate,
+            .data_bits = UART_DATA_BIT_8,
+            .parity = UART_PARITY_NONE,
+            .stop_bits = UART_STOP_BIT_1,
+        };
+        
+        if (uapi_uart_set_attr(g_at_ctx.uart_bus, &uart_attr) != ERRCODE_SUCC) {
+            PPP_DEBUG_WARN("Failed to set baudrate %u", baudrate);
+            continue;
+        }
+
+        /* 发送AT命令测试 */
+        ppp_at_response_t response;
+        ppp_error_t ret = ppp_at_send_command("AT", "OK", 1000, &response);
+        
+        if (ret == PPP_ERR_OK) {
+            g_at_ctx.current_baudrate = baudrate;
+            PPP_DEBUG_INFO("Baudrate handshake successful at %u", baudrate);
+            return PPP_ERR_OK;
+        }
+        
+        osal_msleep(100); // 短暂延时后尝试下一个波特率
+    }
+
+    PPP_DEBUG_ERROR("Baudrate handshake failed");
+    return PPP_ERR_AT_FAILED;
+}
+
+/**
+ * @brief 设置固定波特率
+ */
+ppp_error_t ppp_at_set_baudrate(uint32_t baudrate)
+{
+    if (!g_at_ctx.initialized) {
+        return PPP_ERR_INVALID_PARAM;
+    }
+
+    /* 检查波特率是否支持 */
+    bool supported = false;
+    for (int i = 0; i < PPP_BAUDRATE_FIXED_COUNT; i++) {
+        if (g_fixed_baudrates[i] == baudrate) {
+            supported = true;
+            break;
+        }
+    }
+
+    if (!supported) {
+        PPP_DEBUG_ERROR("Unsupported baudrate: %u", baudrate);
+        return PPP_ERR_INVALID_PARAM;
+    }
+
+    /* 发送AT+IPR命令设置波特率 */
+    char cmd[64];
+    snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "AT+IPR=%u", baudrate);
+    
+    ppp_at_response_t response;
+    ppp_error_t ret = ppp_at_send_command(cmd, "OK", PPP_AT_CMD_TIMEOUT, &response);
+    
+    if (ret != PPP_ERR_OK) {
+        PPP_DEBUG_ERROR("Failed to set baudrate to %u", baudrate);
+        return ret;
+    }
+
+    /* 更新串口配置 */
+    uart_attr_t uart_attr = {
+        .baud_rate = baudrate,
+        .data_bits = UART_DATA_BIT_8,
+        .parity = UART_PARITY_NONE,
+        .stop_bits = UART_STOP_BIT_1,
+    };
+    
+    if (uapi_uart_set_attr(g_at_ctx.uart_bus, &uart_attr) != ERRCODE_SUCC) {
+        PPP_DEBUG_ERROR("Failed to update UART baudrate");
+        return PPP_ERR_UART;
+    }
+
+    g_at_ctx.current_baudrate = baudrate;
+    g_at_ctx.auto_baudrate_mode = false;
+    
+    PPP_DEBUG_INFO("Baudrate set to %u (fixed)", baudrate);
+    return PPP_ERR_OK;
+}
+
+/**
+ * @brief 发送AT命令
+ */
+ppp_error_t ppp_at_send_command(const char *command, const char *expected_response, 
+                                uint32_t timeout, ppp_at_response_t *response)
+{
+    if (!g_at_ctx.initialized || !command || !response) {
+        return PPP_ERR_INVALID_PARAM;
+    }
+
+    osal_mutex_lock(g_at_ctx.at_mutex);
+
+    PPP_DEBUG_DEBUG("Sending AT command: %s", command);
+
+    /* 清空响应缓冲区 */
+    memset_s(response, sizeof(ppp_at_response_t), 0, sizeof(ppp_at_response_t));
+
+    /* 发送命令 */
+    char cmd_with_crlf[PPP_AT_CMD_MAX_LEN + 4];
+    snprintf_s(cmd_with_crlf, sizeof(cmd_with_crlf), sizeof(cmd_with_crlf) - 1, "%s\r\n", command);
+    
+    uint32_t cmd_len = strlen(cmd_with_crlf);
+    if (uapi_uart_write(g_at_ctx.uart_bus, (const uint8_t *)cmd_with_crlf, cmd_len, 0) != cmd_len) {
+        PPP_DEBUG_ERROR("Failed to send AT command");
+        osal_mutex_unlock(g_at_ctx.at_mutex);
+        return PPP_ERR_UART;
+    }
+
+    /* 等待响应 */
+    ppp_error_t ret = ppp_at_wait_response(response, timeout);
+    
+    if (ret == PPP_ERR_OK && expected_response) {
+        if (!ppp_at_parse_response(response->response, expected_response)) {
+            PPP_DEBUG_WARN("Unexpected response: %s", response->response);
+            response->success = false;
+            response->error_code = PPP_ERR_AT_FAILED;
+            ret = PPP_ERR_AT_FAILED;
+        } else {
+            response->success = true;
+            response->error_code = PPP_ERR_OK;
+        }
+    }
+
+    osal_mutex_unlock(g_at_ctx.at_mutex);
+    return ret;
+}
+
+/**
+ * @brief 发送PPP拨号命令
+ */
+ppp_error_t ppp_at_dial_ppp(const char *dial_string)
+{
+    if (!g_at_ctx.initialized || !dial_string) {
+        return PPP_ERR_INVALID_PARAM;
+    }
+
+    PPP_DEBUG_INFO("Starting PPP dial: %s", dial_string);
+
+    /* 发送拨号命令 */
+    ppp_at_response_t response;
+    ppp_error_t ret = ppp_at_send_command(dial_string, "CONNECT", PPP_DIAL_TIMEOUT, &response);
+    
+    if (ret != PPP_ERR_OK) {
+        PPP_DEBUG_ERROR("PPP dial failed");
+        return ret;
+    }
+
+    PPP_DEBUG_INFO("PPP dial successful, entering data mode");
+    return PPP_ERR_OK;
+}
+
+/**
+ * @brief 挂断PPP连接
+ */
+ppp_error_t ppp_at_hangup_ppp(void)
+{
+    if (!g_at_ctx.initialized) {
+        return PPP_ERR_INVALID_PARAM;
+    }
+
+    PPP_DEBUG_INFO("Hanging up PPP connection");
+
+    /* 发送+++切换到命令模式 */
+    osal_msleep(1000); // 等待1秒
+    
+    const char *escape_seq = "+++";
+    if (uapi_uart_write(g_at_ctx.uart_bus, (const uint8_t *)escape_seq, 3, 0) != 3) {
+        PPP_DEBUG_ERROR("Failed to send escape sequence");
+        return PPP_ERR_UART;
+    }
+    
+    osal_msleep(1000); // 等待1秒
+
+    /* 发送ATH挂断命令 */
+    ppp_at_response_t response;
+    ppp_error_t ret = ppp_at_send_command("ATH", "OK", PPP_AT_CMD_TIMEOUT, &response);
+    
+    if (ret != PPP_ERR_OK) {
+        PPP_DEBUG_ERROR("Failed to hangup PPP connection");
+        return ret;
+    }
+
+    PPP_DEBUG_INFO("PPP connection hung up");
+    return PPP_ERR_OK;
+}
+
+/**
+ * @brief 获取当前波特率
+ */
+uint32_t ppp_at_get_current_baudrate(void)
+{
+    return g_at_ctx.current_baudrate;
+}
+
+/**
+ * @brief 检查是否为自动波特率模式
+ */
+bool ppp_at_is_auto_baudrate_mode(void)
+{
+    return g_at_ctx.auto_baudrate_mode;
+}
+
+/* 内部函数实现 */
+
+/**
+ * @brief 解析AT命令响应
+ */
+static bool ppp_at_parse_response(const char *response, const char *expected)
+{
+    if (!response || !expected) {
+        return false;
+    }
+
+    return strstr(response, expected) != NULL;
+}
+
+/**
+ * @brief 等待AT命令响应
+ */
+static ppp_error_t ppp_at_wait_response(ppp_at_response_t *response, uint32_t timeout)
+{
+    if (!response) {
+        return PPP_ERR_INVALID_PARAM;
+    }
+
+    /* 设置并启动超时定时器 */
+    if (osal_timer_mod(g_at_ctx.at_timer, timeout) != OSAL_SUCCESS) {
+        PPP_DEBUG_ERROR("Failed to set AT timer timeout");
+        return PPP_ERR_MEMORY;
+    }
+    
+    if (osal_timer_start(g_at_ctx.at_timer) != OSAL_SUCCESS) {
+        PPP_DEBUG_ERROR("Failed to start AT timer");
+        return PPP_ERR_MEMORY;
+    }
+
+    uint32_t start_time = osal_get_tick();
+    uint32_t bytes_received = 0;
+    bool response_complete = false;
+
+    while (!response_complete && (osal_get_tick() - start_time) < timeout) {
+        uint8_t buffer[256];
+        uint32_t bytes_read = 0;
+        
+        /* 读取串口数据 */
+        int32_t ret = uapi_uart_read(g_at_ctx.uart_bus, buffer, sizeof(buffer) - 1, 100);
+        
+        if (ret > 0) {
+            bytes_read = (uint32_t)ret;
+            buffer[bytes_read] = '\0';
+            
+            /* 追加到响应缓冲区 */
+            if (bytes_received + bytes_read < PPP_AT_RESPONSE_MAX_LEN - 1) {
+                memcpy_s(response->response + bytes_received, 
+                        PPP_AT_RESPONSE_MAX_LEN - bytes_received,
+                        buffer, bytes_read);
+                bytes_received += bytes_read;
+                response->response[bytes_received] = '\0';
+                response->length = bytes_received;
+                
+                /* 检查是否收到完整响应 */
+                if (strstr(response->response, "OK") || 
+                    strstr(response->response, "ERROR") ||
+                    strstr(response->response, "CONNECT") ||
+                    strstr(response->response, "NO CARRIER") ||
+                    strstr(response->response, "BUSY") ||
+                    strstr(response->response, "NO DIALTONE")) {
+                    response_complete = true;
+                }
+            } else {
+                PPP_DEBUG_WARN("AT response buffer overflow");
+                break;
+            }
+        }
+        
+        osal_msleep(10); // 短暂延时
+    }
+
+    /* 停止定时器 */
+    osal_timer_stop(g_at_ctx.at_timer);
+
+    if (!response_complete) {
+        PPP_DEBUG_ERROR("AT command timeout");
+        return PPP_ERR_TIMEOUT;
+    }
+
+    PPP_DEBUG_DEBUG("AT response: %s", response->response);
+    return PPP_ERR_OK;
+}
+
+/**
+ * @brief AT命令超时处理
+ */
+static void ppp_at_timeout_handler(void *arg)
+{
+    (void)arg;
+    PPP_DEBUG_WARN("AT command timeout");
+} 
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/src/ppp_cat1_api.c b/code/sdk/zhihui/ppp/src/ppp_cat1_api.c
new file mode 100644 (file)
index 0000000..59b1f0e
--- /dev/null
@@ -0,0 +1,435 @@
+/*
+ * @brief PPP模块的cat1 API兼容实现
+ * @description 实现与cat1Service相同的接口,让UI和其他服务无需修改
+ * Copyright (c) CompanyNameMagicTag 2024-2024. All rights reserved.
+ */
+
+#include "ppp_power_ctl.h"
+#include "ppp_debug.h"
+#include "ppp_types.h"
+#include "errcode.h"
+#include "securec.h"
+#include "platform_core.h"
+#include <string.h>
+
+// 兼容cat1Service的类型定义
+typedef enum _4g_message {
+    MESSAGE_4G_POWERON,//无参数 (BOOTUP)
+    MESSAGE_4G_POWEROFF,//无参数
+    MESSAGE_4G_NETWORK_CONNECTED,//无参数
+    MESSAGE_4G_NETWORK_DISCONNECTED,//无参数
+    MESSAGE_4G_NETWORK_STAT_CHANGED,//含signal,sim,运营商,
+    MESSAGE_4G_CALL_COMMING,//带电话号
+    MESSAGE_4G_CALL_CONNECTED,//带电话号
+    MESSAGE_4G_CALL_DISCONNECTED,//带电话号
+    MESSAGE_4G_SMS_RECEIVED, //返回值为字符串
+    MESSAGE_4G_AT_RECEIVED, //返回值为字符串
+    MESSAGE_4G_TIME_SYNCED,//时间同步完成
+    MESSAGE_4G_VERSION_SYNCED,//版本同步完成
+    MESSAGE_4G_MAX,
+} MESSAGE_4G_T;
+
+typedef enum _4g_cmd {
+    CMD_4G_POWERON,//无参数
+    CMD_4G_POWEROFF,//无参数
+    CMD_4G_GET_SIGNAL,//无参数
+    CMD_4G_GET_VERSION,//无参数
+    CMD_4G_GET_TIME,//无参数
+    CMD_4G_MAKE_CALL,//需要输入参数 电话号
+    CMD_4G_ANSWER_CALL,//无参数
+    CMD_4G_HUNGUP_CALL,//无参数
+    CMD_4G_SEND_DTMF, //需要输入参数 DTMF NUM(单个数字) , 包括 0 ~ 9, * , #
+    CMD_4G_AT, //需要AT指令,要求结尾带\r\n
+    CMD_4G_MAX,
+} CMD_4G_T;
+
+typedef struct {
+    uint8_t bat_pin; //控制Cat1 battery 的gpio,没有控制,设置为 PIN_NONE 即可
+    uint8_t bat_pin_en_value;
+    uint8_t pwr_pin; //控制cat1开机键的gpio
+    uint8_t pwr_pin_en_value;
+    uint8_t reset_pin; //控制cat1重置键的gpio
+    uint8_t reset_pin_en_value;
+    uint8_t ring_pin; //cat1唤醒蓝牙用的gpio
+    uint8_t ring_pin_en_value;
+    uint8_t wakemodem_pin; //蓝牙唤醒cat1用的gpio
+    uint8_t wakemodem_pin_en_value;
+} ctl_pin_4g_t;
+
+// 消息回调函数类型
+typedef errcode_t (*f_cat1_msg_callback)(MESSAGE_4G_T msg, void *msg_buf, uint16_t msglen);
+
+// 时间控制函数类型
+typedef uint16_t (*f_get_timezone)(void);
+typedef void (*f_set_timezone)(uint16_t timezone_quarters);
+typedef uint32_t (*f_sync_time_utc)(uint32_t timestamp, uint32_t timezone_quarters);
+
+typedef struct {
+    f_get_timezone get_timezone;
+    f_set_timezone set_timezone;
+    f_sync_time_utc sync_time_utc;
+} time_ctl_funcs_t;
+
+// PPP模块的运行时状态结构(与cat1Service保持一致)
+typedef struct {
+    uint8_t cat1_powerOn;
+    uint8_t volte;
+    uint8_t network_on;
+    uint8_t cat1_network_type;
+    int sim;
+    int signal;
+    char opera[64];
+    char imei[20];
+    char pyVer[20];
+    char modemVer[64];
+} ppp_cat1_runtime_status_t;
+
+// PPP模块的运行时状态
+static ppp_cat1_runtime_status_t g_ppp_runtime = {0};
+
+// PPP模块的控制引脚配置
+static ppp_ctl_pin_4g_t g_ppp_ctl_pins = {0};
+
+// PPP模块的初始化状态
+static bool g_ppp_cat1_initialized = false;
+
+/********************** 电源控制接口 **********************/
+
+/**
+ * @brief cat1 开关机设置, 输入 true 开机, false 关机
+ */
+void cat1_api_enable_cat1(bool isopen)
+{
+    PPP_DEBUG_PRINT("PPP: cat1_api_enable_cat1(%s)\n", isopen ? "true" : "false");
+    
+    if (isopen) {
+        if (g_ppp_runtime.cat1_powerOn) {
+            PPP_DEBUG_PRINT("PPP: Cat1 already powered on\n");
+            return;
+        }
+        
+        // PPP模块开启4G
+        ppp_power_on_cat1();
+        g_ppp_runtime.cat1_powerOn = 1;
+        
+        // 可以在这里添加PPP特定的初始化逻辑
+        // 例如:启动PPP协议栈等
+        
+    } else {
+        if (!g_ppp_runtime.cat1_powerOn) {
+            PPP_DEBUG_PRINT("PPP: Cat1 already powered off\n");
+            return;
+        }
+        
+        // PPP模块关闭4G
+        ppp_power_off_cat1();
+        g_ppp_runtime.cat1_powerOn = 0;
+        
+        // 清理网络状态
+        g_ppp_runtime.network_on = 0;
+        g_ppp_runtime.signal = 99; // 无信号
+        g_ppp_runtime.sim = 0;
+        g_ppp_runtime.cat1_network_type = 0;
+    }
+}
+
+/**
+ * @brief 获取cat1是否使能(开机)状态
+ */
+bool cat1_api_is_cat1_enabled(void)
+{
+    return ppp_api_is_cat1_enabled();
+}
+
+/**
+ * @brief cat1 服务初始化
+ */
+errcode_t cat1_server_init(void)
+{
+    PPP_DEBUG_PRINT("PPP: cat1_server_init\n");
+    
+    if (g_ppp_cat1_initialized) {
+        return ERRCODE_SUCC;
+    }
+    
+    // 初始化PPP电源控制
+    ppp_api_gpio_init();
+    ppp_api_uart_init();
+    
+    // 初始化运行时状态
+    (void)memset_s(&g_ppp_runtime, sizeof(ppp_cat1_runtime_status_t), 0, sizeof(ppp_cat1_runtime_status_t));
+    g_ppp_runtime.signal = 99; // 初始无信号
+    
+    g_ppp_cat1_initialized = true;
+    
+    return ERRCODE_SUCC;
+}
+
+/**
+ * @brief 串口接收函数,加入到uart_porting 中断处理中
+ */
+void cat1_uart_receive_handler(uint8_t *buffer, int32_t size)
+{
+    PPP_DEBUG_PRINT("PPP: cat1_uart_receive_handler, size=%d\n", size);
+    // TODO: 实现PPP数据接收处理
+    (void)buffer;
+    (void)size;
+}
+
+/**
+ * @brief return cmd uart rx enable flag
+ */
+bool cat1_api_custom_is_cmd_rx_enabled(void)
+{
+    // PPP模块默认启用接收
+    return true;
+}
+
+/********************** 运行时状态获取接口 **********************/
+
+/**
+ * @brief 获取cat1电源开启状态
+ */
+uint8_t cat1_api_runtime_get_cat1_powerstat(void)
+{
+    return g_ppp_runtime.cat1_powerOn;
+}
+
+/**
+ * @brief 获取cat1网络状态
+ */
+uint8_t cat1_api_runtime_get_cat1_networkstat(void)
+{
+    return g_ppp_runtime.network_on;
+}
+
+/**
+ * @brief 获取cat1信号强度
+ */
+int cat1_api_runtime_get_cat1_signal(void)
+{
+    return g_ppp_runtime.signal;
+}
+
+/**
+ * @brief 获取sim卡状态
+ */
+int cat1_api_runtime_get_cat1_simstat(void)
+{
+    return g_ppp_runtime.sim;
+}
+
+/**
+ * @brief 获取网络类型
+ */
+uint8_t cat1_api_runtime_get_cat1_networktype(void)
+{
+    return g_ppp_runtime.cat1_network_type;
+}
+
+/**
+ * @brief 获取Cat1运营商信息
+ */
+char* cat1_api_runtime_get_cat1_operator(void)
+{
+    return g_ppp_runtime.opera;
+}
+
+/**
+ * @brief 获取Cat1 IMEI号
+ */
+char* cat1_api_runtime_get_cat1_imei(void)
+{
+    return g_ppp_runtime.imei;
+}
+
+/**
+ * @brief 获取Cat1主应用版本号
+ */
+char* cat1_api_runtime_get_cat1_pyver(void)
+{
+    return g_ppp_runtime.pyVer;
+}
+
+/**
+ * @brief 获取modem版本
+ */
+char* cat1_api_runtime_get_cat1_modemver(void)
+{
+    return g_ppp_runtime.modemVer;
+}
+
+/********************** 运行时状态设置接口 **********************/
+
+/**
+ * @brief 设置cat1电源状态
+ */
+void cat1_api_runtime_set_cat1_powerstat(uint8_t cat1_powerOn)
+{
+    g_ppp_runtime.cat1_powerOn = cat1_powerOn;
+}
+
+/**
+ * @brief 设置cat1网络状态
+ */
+void cat1_api_runtime_set_cat1_networkstat(uint8_t network_on)
+{
+    g_ppp_runtime.network_on = network_on;
+}
+
+/**
+ * @brief 设置cat1信号强度
+ */
+void cat1_api_runtime_set_cat1_signal(int signal)
+{
+    g_ppp_runtime.signal = signal;
+}
+
+/**
+ * @brief 设置sim卡状态
+ */
+void cat1_api_runtime_set_cat1_simstat(int simstat)
+{
+    g_ppp_runtime.sim = simstat;
+}
+
+/**
+ * @brief 设置网络类型
+ */
+void cat1_api_runtime_set_cat1_networktype(uint8_t networktype)
+{
+    g_ppp_runtime.cat1_network_type = networktype;
+}
+
+/**
+ * @brief 设置Cat1运营商信息
+ */
+void cat1_api_runtime_set_cat1_operator(char* cat1_operator)
+{
+    if (cat1_operator != NULL) {
+        (void)strncpy_s(g_ppp_runtime.opera, sizeof(g_ppp_runtime.opera), 
+                        cat1_operator, sizeof(g_ppp_runtime.opera) - 1);
+        g_ppp_runtime.opera[sizeof(g_ppp_runtime.opera) - 1] = '\0';
+    }
+}
+
+/**
+ * @brief 设置Cat1 IMEI号
+ */
+void cat1_api_runtime_set_cat1_imei(char* imei)
+{
+    if (imei != NULL) {
+        (void)strncpy_s(g_ppp_runtime.imei, sizeof(g_ppp_runtime.imei), 
+                        imei, sizeof(g_ppp_runtime.imei) - 1);
+        g_ppp_runtime.imei[sizeof(g_ppp_runtime.imei) - 1] = '\0';
+    }
+}
+
+/**
+ * @brief 设置Cat1主应用版本号
+ */
+void cat1_api_runtime_set_cat1_pyver(char* pyver)
+{
+    if (pyver != NULL) {
+        (void)strncpy_s(g_ppp_runtime.pyVer, sizeof(g_ppp_runtime.pyVer), 
+                        pyver, sizeof(g_ppp_runtime.pyVer) - 1);
+        g_ppp_runtime.pyVer[sizeof(g_ppp_runtime.pyVer) - 1] = '\0';
+    }
+}
+
+/**
+ * @brief 设置modem版本
+ */
+void cat1_api_runtime_set_cat1_modemver(char* modemver)
+{
+    if (modemver != NULL) {
+        (void)strncpy_s(g_ppp_runtime.modemVer, sizeof(g_ppp_runtime.modemVer), 
+                        modemver, sizeof(g_ppp_runtime.modemVer) - 1);
+        g_ppp_runtime.modemVer[sizeof(g_ppp_runtime.modemVer) - 1] = '\0';
+    }
+}
+
+/********************** 控制接口 **********************/
+
+/**
+ * @brief 唤醒Cat1
+ */
+void cat1_api_wakeup_cat1(uint8_t wakeup)
+{
+    ppp_api_wakeup_cat1(wakeup);
+}
+
+/**
+ * @brief 保持唤醒Cat1
+ */
+void cat1_api_keep_wakeup(uint8_t keep_wakeup)
+{
+    ppp_api_keep_wakeup(keep_wakeup);
+}
+
+/**
+ * @brief 注册控制4G的gpio值
+ */
+errcode_t cat1_register_ctl_pin(ctl_pin_4g_t ctl_pin)
+{
+    // 转换为PPP模块的引脚配置结构
+    ppp_ctl_pin_4g_t ppp_pin_config = {
+        .bat_pin = ctl_pin.bat_pin,
+        .bat_pin_en_value = ctl_pin.bat_pin_en_value,
+        .pwr_pin = ctl_pin.pwr_pin,
+        .pwr_pin_en_value = ctl_pin.pwr_pin_en_value,
+        .reset_pin = ctl_pin.reset_pin,
+        .reset_pin_en_value = ctl_pin.reset_pin_en_value,
+        .ring_pin = ctl_pin.ring_pin,
+        .ring_pin_en_value = ctl_pin.ring_pin_en_value,
+        .wakemodem_pin = ctl_pin.wakemodem_pin,
+        .wakemodem_pin_en_value = ctl_pin.wakemodem_pin_en_value
+    };
+    
+    return ppp_register_ctl_pin(&ppp_pin_config);
+}
+
+/**
+ * @brief cat1唤醒蓝牙中断处理函数
+ */
+void cat1_ring_isr_callback(pin_t pin, uintptr_t param)
+{
+    ppp_ring_isr_callback(pin, param);
+}
+
+/********************** 其他接口(暂时返回默认值或空实现) **********************/
+
+/**
+ * @brief 发送指令给Cat1
+ */
+errcode_t cat1_cmd_send(CMD_4G_T cmd, void *cmd_buf, uint16_t cmdlen)
+{
+    PPP_DEBUG_PRINT("PPP: cat1_cmd_send cmd=%d, len=%d\n", cmd, cmdlen);
+    // TODO: 实现PPP模块的AT命令发送
+    (void)cmd;
+    (void)cmd_buf;
+    (void)cmdlen;
+    return ERRCODE_SUCC;
+}
+
+/**
+ * @brief 注册消息回调函数
+ */
+errcode_t cat1_register_msg_callback(f_cat1_msg_callback func)
+{
+    PPP_DEBUG_PRINT("PPP: cat1_register_msg_callback\n");
+    // TODO: 实现PPP模块的消息回调注册
+    (void)func;
+    return ERRCODE_SUCC;
+}
+
+/**
+ * @brief 注册时间控制函数
+ */
+errcode_t cat1_register_time_ctl_functions(time_ctl_funcs_t *funcs)
+{
+    PPP_DEBUG_PRINT("PPP: cat1_register_time_ctl_functions\n");
+    // TODO: 实现PPP模块的时间控制函数注册
+    (void)funcs;
+    return ERRCODE_SUCC;
+} 
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/src/ppp_debug.c b/code/sdk/zhihui/ppp/src/ppp_debug.c
new file mode 100644 (file)
index 0000000..44e098a
--- /dev/null
@@ -0,0 +1,119 @@
+#include "ppp_debug.h"
+#include "ppp_config.h"
+#include "ppp_types.h"
+#include <stdio.h>
+
+#if PPP_DEBUG_ENABLE
+
+/**
+ * @brief 十六进制数据转储
+ */
+void ppp_debug_hexdump(const uint8_t *data, uint32_t len)
+{
+    if (!data || len == 0) {
+        return;
+    }
+
+    PRINT("[PPP HEXDUMP] Length: %u bytes\r\n", len);
+    
+    for (uint32_t i = 0; i < len; i += 16) {
+        /* 打印地址 */
+        PRINT("%08X: ", i);
+        
+        /* 打印十六进制数据 */
+        for (uint32_t j = 0; j < 16; j++) {
+            if (i + j < len) {
+                PRINT("%02X ", data[i + j]);
+            } else {
+                PRINT("   ");
+            }
+            
+            if (j == 7) {
+                PRINT(" ");
+            }
+        }
+        
+        PRINT(" |");
+        
+        /* 打印ASCII字符 */
+        for (uint32_t j = 0; j < 16 && i + j < len; j++) {
+            uint8_t c = data[i + j];
+            if (c >= 32 && c <= 126) {
+                PRINT("%c", c);
+            } else {
+                PRINT(".");
+            }
+        }
+        
+        PRINT("|\r\n");
+    }
+}
+
+#else
+
+void ppp_debug_hexdump(const uint8_t *data, uint32_t len)
+{
+    (void)data;
+    (void)len;
+}
+
+#endif /* PPP_DEBUG_ENABLE */
+
+/**
+ * @brief 获取错误码字符串
+ */
+const char *ppp_debug_error_string(ppp_error_t error_code)
+{
+    switch (error_code) {
+        case PPP_ERR_OK:
+            return "OK";
+        case PPP_ERR_TIMEOUT:
+            return "TIMEOUT";
+        case PPP_ERR_AT_FAILED:
+            return "AT_FAILED";
+        case PPP_ERR_DIAL_FAILED:
+            return "DIAL_FAILED";
+        case PPP_ERR_NO_CARRIER:
+            return "NO_CARRIER";
+        case PPP_ERR_BUSY:
+            return "BUSY";
+        case PPP_ERR_NO_DIALTONE:
+            return "NO_DIALTONE";
+        case PPP_ERR_INVALID_PARAM:
+            return "INVALID_PARAM";
+        case PPP_ERR_MEMORY:
+            return "MEMORY";
+        case PPP_ERR_UART:
+            return "UART";
+        case PPP_ERR_UNKNOWN:
+        default:
+            return "UNKNOWN";
+    }
+}
+
+/**
+ * @brief 获取状态字符串
+ */
+const char *ppp_debug_state_string(ppp_state_t state)
+{
+    switch (state) {
+        case PPP_STATE_IDLE:
+            return "IDLE";
+        case PPP_STATE_INIT:
+            return "INIT";
+        case PPP_STATE_AT_HANDSHAKE:
+            return "AT_HANDSHAKE";
+        case PPP_STATE_SET_BAUDRATE:
+            return "SET_BAUDRATE";
+        case PPP_STATE_DIALING:
+            return "DIALING";
+        case PPP_STATE_CONNECTED:
+            return "CONNECTED";
+        case PPP_STATE_DISCONNECTING:
+            return "DISCONNECTING";
+        case PPP_STATE_ERROR:
+            return "ERROR";
+        default:
+            return "UNKNOWN";
+    }
+} 
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/src/ppp_lwip.c b/code/sdk/zhihui/ppp/src/ppp_lwip.c
new file mode 100644 (file)
index 0000000..c513de1
--- /dev/null
@@ -0,0 +1,389 @@
+#include "ppp_lwip.h"
+#include "ppp_config.h"
+#include "ppp_types.h"
+#include "ppp_debug.h"
+#include "ppp_serial.h"
+#include "securec.h"
+#include <string.h>
+
+/* lwIP相关头文件 - 需要在编译时确保PPP支持已启用 */
+#ifdef LWIP_PPP_SUPPORT
+#include "lwip/netif.h"
+#include "lwip/tcpip.h"
+#include "lwip/ip4_addr.h"
+#include "lwip/dns.h"
+#include "netif/ppp/ppp.h"
+#include "netif/ppp/pppos.h"
+#endif
+
+/* PPP lwIP上下文 */
+typedef struct {
+    bool initialized;
+    struct netif *netif;
+#ifdef LWIP_PPP_SUPPORT
+    ppp_pcb *ppp_pcb;
+#else
+    void *ppp_pcb;  /* 占位符,当PPP支持未启用时 */
+#endif
+    ppp_lwip_event_callback_t event_callback;
+    void *event_callback_arg;
+    bool connected;
+    uint32_t local_ip;
+    uint32_t remote_ip;
+    uint32_t primary_dns;
+    uint32_t secondary_dns;
+} ppp_lwip_context_t;
+
+static ppp_lwip_context_t g_lwip_ctx = {0};
+
+/* 内部函数声明 */
+#ifdef LWIP_PPP_SUPPORT
+static void ppp_lwip_status_callback(ppp_pcb *pcb, int err_code, void *ctx);
+static void ppp_lwip_link_status_callback(ppp_pcb *pcb, int err_code, void *ctx);
+static u32_t ppp_lwip_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx);
+#endif
+
+/**
+ * @brief 初始化PPP lwIP集成
+ */
+ppp_error_t ppp_lwip_init(const ppp_lwip_config_t *config)
+{
+#ifndef LWIP_PPP_SUPPORT
+    PPP_DEBUG_ERROR("lwIP PPP support not enabled");
+    return PPP_ERR_INVALID_PARAM;
+#else
+    if (!config || g_lwip_ctx.initialized) {
+        return PPP_ERR_INVALID_PARAM;
+    }
+
+    PPP_DEBUG_INFO("Initializing PPP lwIP integration");
+
+    /* 分配网络接口 */
+    g_lwip_ctx.netif = (struct netif *)osal_kmalloc(sizeof(struct netif));
+    if (!g_lwip_ctx.netif) {
+        PPP_DEBUG_ERROR("Failed to allocate netif");
+        return PPP_ERR_MEMORY;
+    }
+
+    memset_s(g_lwip_ctx.netif, sizeof(struct netif), 0, sizeof(struct netif));
+
+    /* 创建PPPoS接口 */
+    g_lwip_ctx.ppp_pcb = pppos_create(g_lwip_ctx.netif,
+                                      ppp_lwip_output_callback,
+                                      ppp_lwip_link_status_callback,
+                                      &g_lwip_ctx);
+
+    if (!g_lwip_ctx.ppp_pcb) {
+        PPP_DEBUG_ERROR("Failed to create PPPoS interface");
+        osal_kfree(g_lwip_ctx.netif);
+        return PPP_ERR_MEMORY;
+    }
+
+    /* 设置认证信息 */
+    if (config->username && config->password) {
+        ppp_set_auth(g_lwip_ctx.ppp_pcb, PPPAUTHTYPE_PAP, config->username, config->password);
+        PPP_DEBUG_INFO("PPP authentication configured (PAP)");
+    }
+
+    /* 设置状态回调 */
+    ppp_set_notify_phase_callback(g_lwip_ctx.ppp_pcb, ppp_lwip_status_callback);
+
+    /* 保存回调函数 */
+    g_lwip_ctx.event_callback = config->event_callback;
+    g_lwip_ctx.event_callback_arg = config->event_callback_arg;
+
+    g_lwip_ctx.initialized = true;
+    PPP_DEBUG_INFO("PPP lwIP integration initialized");
+
+    return PPP_ERR_OK;
+#endif
+}
+
+/**
+ * @brief 反初始化PPP lwIP集成
+ */
+void ppp_lwip_deinit(void)
+{
+#ifdef LWIP_PPP_SUPPORT
+    if (!g_lwip_ctx.initialized) {
+        return;
+    }
+
+    PPP_DEBUG_INFO("Deinitializing PPP lwIP integration");
+
+    /* 断开连接 */
+    if (g_lwip_ctx.connected) {
+        ppp_lwip_disconnect();
+    }
+
+    /* 删除PPP控制块 */
+    if (g_lwip_ctx.ppp_pcb) {
+        ppp_free(g_lwip_ctx.ppp_pcb);
+        g_lwip_ctx.ppp_pcb = NULL;
+    }
+
+    /* 释放网络接口 */
+    if (g_lwip_ctx.netif) {
+        osal_kfree(g_lwip_ctx.netif);
+        g_lwip_ctx.netif = NULL;
+    }
+
+    memset_s(&g_lwip_ctx, sizeof(g_lwip_ctx), 0, sizeof(g_lwip_ctx));
+    PPP_DEBUG_INFO("PPP lwIP integration deinitialized");
+#endif
+}
+
+/**
+ * @brief 连接PPP
+ */
+ppp_error_t ppp_lwip_connect(void)
+{
+#ifndef LWIP_PPP_SUPPORT
+    return PPP_ERR_INVALID_PARAM;
+#else
+    if (!g_lwip_ctx.initialized) {
+        return PPP_ERR_INVALID_PARAM;
+    }
+
+    if (g_lwip_ctx.connected) {
+        PPP_DEBUG_WARN("PPP already connected");
+        return PPP_ERR_OK;
+    }
+
+    PPP_DEBUG_INFO("Connecting PPP");
+
+    /* 启动PPP连接 */
+    err_t err = ppp_connect(g_lwip_ctx.ppp_pcb, 0);
+    if (err != ERR_OK) {
+        PPP_DEBUG_ERROR("Failed to start PPP connection: %d", err);
+        return PPP_ERR_DIAL_FAILED;
+    }
+
+    PPP_DEBUG_INFO("PPP connection started");
+    return PPP_ERR_OK;
+#endif
+}
+
+/**
+ * @brief 断开PPP连接
+ */
+ppp_error_t ppp_lwip_disconnect(void)
+{
+#ifndef LWIP_PPP_SUPPORT
+    return PPP_ERR_INVALID_PARAM;
+#else
+    if (!g_lwip_ctx.initialized) {
+        return PPP_ERR_INVALID_PARAM;
+    }
+
+    if (!g_lwip_ctx.connected) {
+        PPP_DEBUG_WARN("PPP not connected");
+        return PPP_ERR_OK;
+    }
+
+    PPP_DEBUG_INFO("Disconnecting PPP");
+
+    /* 关闭PPP连接 */
+    err_t err = ppp_close(g_lwip_ctx.ppp_pcb, 0);
+    if (err != ERR_OK) {
+        PPP_DEBUG_ERROR("Failed to close PPP connection: %d", err);
+        return PPP_ERR_DIAL_FAILED;
+    }
+
+    g_lwip_ctx.connected = false;
+    PPP_DEBUG_INFO("PPP disconnection initiated");
+    return PPP_ERR_OK;
+#endif
+}
+
+/**
+ * @brief 输入PPP数据
+ */
+ppp_error_t ppp_lwip_input(const uint8_t *data, uint32_t length)
+{
+#ifndef LWIP_PPP_SUPPORT
+    return PPP_ERR_INVALID_PARAM;
+#else
+    if (!g_lwip_ctx.initialized || !data || length == 0) {
+        return PPP_ERR_INVALID_PARAM;
+    }
+
+    PPP_DEBUG_DEBUG("PPP input %u bytes", length);
+    PPP_DEBUG_HEXDUMP(data, length);
+
+    /* 将数据传递给PPPoS */
+    pppos_input(g_lwip_ctx.ppp_pcb, (u8_t *)data, length);
+
+    return PPP_ERR_OK;
+#endif
+}
+
+/**
+ * @brief 获取PPP连接状态
+ */
+bool ppp_lwip_is_connected(void)
+{
+    return g_lwip_ctx.connected;
+}
+
+/**
+ * @brief 获取网络信息
+ */
+ppp_error_t ppp_lwip_get_network_info(ppp_lwip_network_info_t *info)
+{
+#ifndef LWIP_PPP_SUPPORT
+    return PPP_ERR_INVALID_PARAM;
+#else
+    if (!g_lwip_ctx.initialized || !info) {
+        return PPP_ERR_INVALID_PARAM;
+    }
+
+    memset_s(info, sizeof(ppp_lwip_network_info_t), 0, sizeof(ppp_lwip_network_info_t));
+
+    info->connected = g_lwip_ctx.connected;
+    info->local_ip = g_lwip_ctx.local_ip;
+    info->remote_ip = g_lwip_ctx.remote_ip;
+    info->primary_dns = g_lwip_ctx.primary_dns;
+    info->secondary_dns = g_lwip_ctx.secondary_dns;
+
+    if (g_lwip_ctx.netif) {
+        info->mtu = g_lwip_ctx.netif->mtu;
+    }
+
+    return PPP_ERR_OK;
+#endif
+}
+
+/**
+ * @brief 获取网络接口指针
+ */
+struct netif *ppp_lwip_get_netif(void)
+{
+    return g_lwip_ctx.netif;
+}
+
+/* 内部函数实现 */
+
+#ifdef LWIP_PPP_SUPPORT
+/**
+ * @brief PPP状态回调函数
+ */
+static void ppp_lwip_status_callback(ppp_pcb *pcb, int err_code, void *ctx)
+{
+    (void)pcb;
+    (void)ctx;
+
+    PPP_DEBUG_INFO("PPP status callback: err_code=%d", err_code);
+
+    ppp_lwip_event_t event;
+    memset_s(&event, sizeof(event), 0, sizeof(event));
+
+    switch (err_code) {
+        case PPPERR_NONE:
+            /* 连接成功 */
+            g_lwip_ctx.connected = true;
+            event.type = PPP_LWIP_EVENT_CONNECTED;
+            
+            /* 获取IP地址信息 */
+            if (g_lwip_ctx.netif) {
+                g_lwip_ctx.local_ip = ip4_addr_get_u32(&g_lwip_ctx.netif->ip_addr);
+                g_lwip_ctx.remote_ip = ip4_addr_get_u32(&g_lwip_ctx.netif->gw);
+                
+                /* 获取DNS服务器地址 */
+                const ip_addr_t *dns1 = dns_getserver(0);
+                const ip_addr_t *dns2 = dns_getserver(1);
+                if (dns1) {
+                    g_lwip_ctx.primary_dns = ip4_addr_get_u32(ip_2_ip4(dns1));
+                }
+                if (dns2) {
+                    g_lwip_ctx.secondary_dns = ip4_addr_get_u32(ip_2_ip4(dns2));
+                }
+                
+                PPP_DEBUG_INFO("PPP connected - Local IP: %u.%u.%u.%u", 
+                              (g_lwip_ctx.local_ip >> 0) & 0xFF,
+                              (g_lwip_ctx.local_ip >> 8) & 0xFF,
+                              (g_lwip_ctx.local_ip >> 16) & 0xFF,
+                              (g_lwip_ctx.local_ip >> 24) & 0xFF);
+            }
+            break;
+
+        case PPPERR_PARAM:
+        case PPPERR_OPEN:
+        case PPPERR_DEVICE:
+        case PPPERR_ALLOC:
+        case PPPERR_USER:
+        case PPPERR_CONNECT:
+        case PPPERR_AUTHFAIL:
+        case PPPERR_PROTOCOL:
+        case PPPERR_PEERDEAD:
+        case PPPERR_IDLETIMEOUT:
+        case PPPERR_CONNECTTIME:
+        case PPPERR_LOOPBACK:
+            /* 连接错误 */
+            g_lwip_ctx.connected = false;
+            event.type = PPP_LWIP_EVENT_ERROR;
+            event.error_code = err_code;
+            PPP_DEBUG_ERROR("PPP connection error: %d", err_code);
+            break;
+
+        default:
+            /* 其他状态 */
+            event.type = PPP_LWIP_EVENT_STATUS_CHANGE;
+            event.error_code = err_code;
+            break;
+    }
+
+    /* 调用用户回调函数 */
+    if (g_lwip_ctx.event_callback) {
+        g_lwip_ctx.event_callback(&event, g_lwip_ctx.event_callback_arg);
+    }
+}
+
+/**
+ * @brief PPP链路状态回调函数
+ */
+static void ppp_lwip_link_status_callback(ppp_pcb *pcb, int err_code, void *ctx)
+{
+    (void)pcb;
+    (void)ctx;
+
+    PPP_DEBUG_INFO("PPP link status callback: err_code=%d", err_code);
+
+    if (err_code == PPPERR_NONE) {
+        PPP_DEBUG_INFO("PPP link up");
+    } else {
+        PPP_DEBUG_WARN("PPP link down: %d", err_code);
+        g_lwip_ctx.connected = false;
+        
+        /* 通知链路断开 */
+        if (g_lwip_ctx.event_callback) {
+            ppp_lwip_event_t event;
+            memset_s(&event, sizeof(event), 0, sizeof(event));
+            event.type = PPP_LWIP_EVENT_DISCONNECTED;
+            event.error_code = err_code;
+            g_lwip_ctx.event_callback(&event, g_lwip_ctx.event_callback_arg);
+        }
+    }
+}
+
+/**
+ * @brief PPP输出回调函数
+ */
+static u32_t ppp_lwip_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx)
+{
+    (void)pcb;
+    (void)ctx;
+
+    PPP_DEBUG_DEBUG("PPP output %u bytes", len);
+    PPP_DEBUG_HEXDUMP(data, len);
+
+    /* 通过串口发送数据 */
+    ppp_error_t ret = ppp_serial_write(data, len);
+    if (ret != PPP_ERR_OK) {
+        PPP_DEBUG_ERROR("Failed to send PPP data: %d", ret);
+        return 0;
+    }
+
+    return len;
+}
+#endif /* LWIP_PPP_SUPPORT */ 
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/src/ppp_power_ctl.c b/code/sdk/zhihui/ppp/src/ppp_power_ctl.c
new file mode 100644 (file)
index 0000000..923a524
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ * @brief PPP模块电源控制实现
+ * @description 提供PPP模块专用的4G电源控制功能,与SLIP模块互斥使用
+ * Copyright (c) CompanyNameMagicTag 2024-2024. All rights reserved.
+ */
+
+#include "ppp_power_ctl.h"
+#include "ppp_debug.h"
+#include "gpio.h"
+#include "gpio_porting.h"
+#include "platform_core.h"
+#include "pinctrl_porting.h"
+#include "osal_debug.h"
+#include "securec.h"
+
+// PPP模块的4G控制引脚配置
+static ppp_ctl_pin_4g_t g_ppp_cat1_ctl_pins = {
+    #if defined(BOARD_FD230)
+    PIN_NONE, //bat_pin
+    1,
+    S_AGPIO_L23, // pwr_pin
+    1,
+    S_AGPIO_L28,//reset_pin
+    0,
+    S_AGPIO_L31,//ring_pin
+    1,
+    S_AGPIO_L30,//wakemodem_pin
+    1,
+    #elif defined(BOARD_L22)
+    S_AGPIO_L14, //bat_pin
+    1,
+    S_AGPIO_L23, // pwr_pin
+    1,
+    PIN_NONE,//reset_pin
+    0,
+    S_AGPIO_L31,//ring_pin
+    1,
+    S_AGPIO_L30,//wakemodem_pin
+    1,
+    #elif defined(BOARD_W9) || defined(BOARD_N9)
+    S_AGPIO_L17, //bat_pin
+    1,
+    S_AGPIO_L23, // pwr_pin
+    1,
+    S_AGPIO_L28,//reset_pin
+    0,
+    S_AGPIO_L31,//ring_pin
+    1,
+    S_AGPIO_L30,//wakemodem_pin
+    1,
+    #elif defined(BOARD_LEINIAO)
+    S_AGPIO_L18, //bat_pin
+    1,
+    S_AGPIO_L23, // pwr_pin
+    1,
+    S_AGPIO_L24,//reset_pin
+    0,
+    PIN_NONE,//ring_pin
+    0,
+    PIN_NONE,//wakemodem_pin
+    0,
+    #else
+    PIN_NONE, //bat_pin
+    1,
+    S_AGPIO_L23, // pwr_pin
+    1,
+    S_AGPIO_L28,//reset_pin
+    0,
+    S_AGPIO_L31,//ring_pin
+    1,
+    S_AGPIO_L30,//wakemodem_pin
+    1,
+    #endif
+};
+
+// PPP模块的4G电源状态
+static bool g_ppp_cat1_power_status = false;
+
+/**
+ * @brief PPP模块GPIO初始化
+ */
+void ppp_api_gpio_init(void)
+{
+    PPP_DEBUG_PRINT("PPP GPIO init\n");
+    
+    pin_t gpiopin=g_ppp_cat1_ctl_pins.bat_pin;
+    if (gpiopin!=PIN_NONE)
+    {
+        uapi_pin_set_mode(gpiopin, (pin_mode_t)HAL_PIO_FUNC_GPIO);
+        uapi_gpio_set_dir(gpiopin,GPIO_DIRECTION_OUTPUT);
+    }
+    gpiopin=g_ppp_cat1_ctl_pins.pwr_pin;
+    if (gpiopin!=PIN_NONE)
+    {
+        uapi_pin_set_mode(gpiopin, (pin_mode_t)HAL_PIO_FUNC_GPIO);
+        uapi_gpio_set_dir(gpiopin,GPIO_DIRECTION_OUTPUT);
+    }
+    gpiopin=g_ppp_cat1_ctl_pins.reset_pin;
+    if (gpiopin!=PIN_NONE)
+    {
+        uapi_pin_set_mode(gpiopin, (pin_mode_t)HAL_PIO_FUNC_GPIO);
+        uapi_gpio_set_dir(gpiopin,GPIO_DIRECTION_OUTPUT);
+    }
+    gpiopin=g_ppp_cat1_ctl_pins.wakemodem_pin;
+    if (gpiopin!=PIN_NONE)
+    {
+        uapi_pin_set_mode(gpiopin, (pin_mode_t)HAL_PIO_FUNC_GPIO);
+        uapi_gpio_set_dir(gpiopin,GPIO_DIRECTION_OUTPUT);
+    }
+
+    gpiopin=g_ppp_cat1_ctl_pins.ring_pin;
+    if (gpiopin!=PIN_NONE)
+    {
+        uapi_pin_set_mode(gpiopin, (pin_mode_t)HAL_PIO_FUNC_GPIO);
+        uapi_gpio_set_dir(gpiopin,GPIO_DIRECTION_INPUT);
+        if (g_ppp_cat1_ctl_pins.ring_pin_en_value)
+            uapi_gpio_register_isr_func(gpiopin, GPIO_INTERRUPT_RISING_EDGE,  ppp_ring_isr_callback );
+        else
+            uapi_gpio_register_isr_func(gpiopin, GPIO_INTERRUPT_FALLING_EDGE,  ppp_ring_isr_callback );
+    }
+}
+
+/**
+ * @brief PPP模块UART初始化
+ */
+void ppp_api_uart_init(void)
+{
+    PPP_DEBUG_PRINT("PPP UART init\n");
+    // UART初始化逻辑可以在这里添加
+}
+
+/**
+ * @brief PPP模块设置电源引脚状态
+ */
+uint32_t ppp_api_cat1_power_on(uint32_t pwron)
+{
+    pin_t en_pin = g_ppp_cat1_ctl_pins.pwr_pin;
+    uint8_t en_value = g_ppp_cat1_ctl_pins.pwr_pin_en_value;
+    
+    if (en_pin == PIN_NONE) {
+        PPP_DEBUG_PRINT("PPP: cat1_power_on en_pin is none\n");
+        return 0xff;
+    }
+    
+    if (pwron == 0) {
+        uapi_gpio_set_dir(en_pin, GPIO_DIRECTION_OUTPUT);
+        if (en_value) {
+            uapi_gpio_set_val(en_pin, GPIO_LEVEL_LOW);
+            PPP_DEBUG_PRINT("PPP: Power Cat1 Off! pin %d value low\n", en_pin);
+        } else {
+            uapi_gpio_set_val(en_pin, GPIO_LEVEL_HIGH);
+            PPP_DEBUG_PRINT("PPP: Power Cat1 Off! pin %d value high\n", en_pin);
+        }
+    } else {
+        uapi_gpio_set_dir(en_pin, GPIO_DIRECTION_OUTPUT);
+        if (en_value) {
+            uapi_gpio_set_val(en_pin, GPIO_LEVEL_HIGH);
+            PPP_DEBUG_PRINT("PPP: Power Cat1 On! pin %d value high\n", en_pin);
+        } else {
+            uapi_gpio_set_val(en_pin, GPIO_LEVEL_LOW);
+            PPP_DEBUG_PRINT("PPP: Power Cat1 On! pin %d value low\n", en_pin);
+        }
+    }
+    return 0;
+}
+
+/**
+ * @brief PPP模块设置复位引脚状态
+ */
+uint32_t ppp_api_cat1_reset(uint32_t pwron)
+{
+    pin_t en_pin = g_ppp_cat1_ctl_pins.reset_pin;
+    uint8_t en_value = g_ppp_cat1_ctl_pins.reset_pin_en_value;
+
+    if (en_pin == PIN_NONE) {
+        PPP_DEBUG_PRINT("PPP: cat1_reset en_pin is none\n");
+        return 0xff;
+    }
+
+    if (pwron == 0) {
+        uapi_gpio_set_dir(en_pin, GPIO_DIRECTION_OUTPUT);
+        if (en_value) {
+            uapi_gpio_set_val(en_pin, GPIO_LEVEL_LOW);
+            PPP_DEBUG_PRINT("PPP: Cat1 reset Off! pin %d value low\n", en_pin);
+        } else {
+            uapi_gpio_set_val(en_pin, GPIO_LEVEL_HIGH);
+            PPP_DEBUG_PRINT("PPP: Cat1 reset Off! pin %d value high\n", en_pin);
+        }
+    } else {
+        uapi_gpio_set_dir(en_pin, GPIO_DIRECTION_OUTPUT);
+        if (en_value) {
+            uapi_gpio_set_val(en_pin, GPIO_LEVEL_HIGH);
+            PPP_DEBUG_PRINT("PPP: Cat1 reset On! pin %d value high\n", en_pin);
+        } else {
+            uapi_gpio_set_val(en_pin, GPIO_LEVEL_LOW);
+            PPP_DEBUG_PRINT("PPP: Cat1 reset On! pin %d value low\n", en_pin);
+        }
+    }
+    return 0;
+}
+
+/**
+ * @brief PPP模块设置电池引脚状态
+ */
+uint32_t ppp_api_cat1_bat_on(uint32_t pwron)
+{
+    pin_t en_pin = g_ppp_cat1_ctl_pins.bat_pin;
+    uint8_t en_value = g_ppp_cat1_ctl_pins.bat_pin_en_value;
+
+    if (en_pin == PIN_NONE) {
+        PPP_DEBUG_PRINT("PPP: cat1_bat_on en_pin is none\n");
+        return 0xff;
+    }
+
+    if (pwron == 0) {
+        uapi_gpio_set_dir(en_pin, GPIO_DIRECTION_OUTPUT);
+        if (en_value) {
+            uapi_gpio_set_val(en_pin, GPIO_LEVEL_LOW);
+            PPP_DEBUG_PRINT("PPP: Cat1 bat Off! pin %d value low\n", en_pin);
+        } else {
+            uapi_gpio_set_val(en_pin, GPIO_LEVEL_HIGH);
+            PPP_DEBUG_PRINT("PPP: Cat1 bat Off! pin %d value high\n", en_pin);
+        }
+    } else {
+        uapi_gpio_set_dir(en_pin, GPIO_DIRECTION_OUTPUT);
+        if (en_value) {
+            uapi_gpio_set_val(en_pin, GPIO_LEVEL_HIGH);
+            PPP_DEBUG_PRINT("PPP: Cat1 bat On! pin %d value high\n", en_pin);
+        } else {
+            uapi_gpio_set_val(en_pin, GPIO_LEVEL_LOW);
+            PPP_DEBUG_PRINT("PPP: Cat1 bat On! pin %d value low\n", en_pin);
+        }
+    }
+    return 0;
+}
+
+/**
+ * @brief PPP模块检查4G是否使能
+ */
+bool ppp_api_is_cat1_enabled(void)
+{
+#if defined(BOARD_W9) || defined(BOARD_N9) || defined(BOARD_LEINIAO)
+    pin_t en_pin = g_ppp_cat1_ctl_pins.bat_pin;
+    uint8_t en_value = g_ppp_cat1_ctl_pins.bat_pin_en_value;
+#else
+    pin_t en_pin = g_ppp_cat1_ctl_pins.pwr_pin;
+    uint8_t en_value = g_ppp_cat1_ctl_pins.pwr_pin_en_value;
+#endif
+    
+    if (en_pin == PIN_NONE) {
+        PPP_DEBUG_PRINT("PPP: is_cat1_enabled en_pin is none\n");
+        return false;
+    }
+    
+    if (en_value == uapi_gpio_get_output_val(en_pin)) {
+        PPP_DEBUG_PRINT("PPP: is_cat1_enabled en_value is %d\n", en_value);
+        return true;
+    } else {
+        PPP_DEBUG_PRINT("PPP: is_cat1_enabled en_value is %d\n", en_value);
+        return false;
+    }
+}
+
+/**
+ * @brief PPP模块4G上电序列
+ */
+void ppp_power_on_cat1(void)
+{
+#if defined(BOARD_FD230)
+    ppp_api_cat1_power_on(1);    
+    ppp_api_cat1_reset(0);
+#elif (defined(BOARD_W9) || defined(BOARD_N9) || defined(BOARD_LEINIAO))
+    ppp_api_cat1_power_on(1);  
+    ppp_api_cat1_bat_on(1);
+    osal_msleep(900);  // 使用OSAL延时函数
+    ppp_api_cat1_power_on(0);  
+#endif
+    
+    g_ppp_cat1_power_status = true;
+    PPP_DEBUG_PRINT("PPP: Cat1 power on sequence completed\n");
+}
+
+/**
+ * @brief PPP模块4G下电序列
+ */
+void ppp_power_off_cat1(void)
+{
+#if defined(BOARD_FD230)
+    ppp_api_cat1_reset(1);
+    ppp_api_cat1_power_on(0);
+#elif (defined(BOARD_W9) || defined(BOARD_N9) || defined(BOARD_LEINIAO))
+    ppp_api_cat1_power_on(0);
+    ppp_api_cat1_bat_on(0);
+#endif
+    
+    g_ppp_cat1_power_status = false;
+    PPP_DEBUG_PRINT("PPP: Cat1 power off sequence completed\n");
+}
+
+/**
+ * @brief PPP模块唤醒Cat1
+ */
+void ppp_api_wakeup_cat1(uint8_t wakeup)
+{
+    pin_t wake_pin = g_ppp_cat1_ctl_pins.wakemodem_pin;
+    uint8_t wake_value = g_ppp_cat1_ctl_pins.wakemodem_pin_en_value;
+    
+    if (wake_pin == PIN_NONE) {
+        PPP_DEBUG_PRINT("PPP: wakeup_cat1 wake_pin is none\n");
+        return;
+    }
+    
+    uapi_gpio_set_dir(wake_pin, GPIO_DIRECTION_OUTPUT);
+    if (wakeup) {
+        if (wake_value) {
+            uapi_gpio_set_val(wake_pin, GPIO_LEVEL_HIGH);
+        } else {
+            uapi_gpio_set_val(wake_pin, GPIO_LEVEL_LOW);
+        }
+        PPP_DEBUG_PRINT("PPP: Wakeup Cat1 pin %d\n", wake_pin);
+    } else {
+        if (wake_value) {
+            uapi_gpio_set_val(wake_pin, GPIO_LEVEL_LOW);
+        } else {
+            uapi_gpio_set_val(wake_pin, GPIO_LEVEL_HIGH);
+        }
+        PPP_DEBUG_PRINT("PPP: Release wakeup Cat1 pin %d\n", wake_pin);
+    }
+}
+
+/**
+ * @brief PPP模块保持唤醒Cat1
+ */
+void ppp_api_keep_wakeup(uint8_t keep_wakeup)
+{
+    // 保持唤醒逻辑实现
+    PPP_DEBUG_PRINT("PPP: Keep wakeup Cat1: %d\n", keep_wakeup);
+}
+
+/**
+ * @brief PPP模块挂起
+ */
+void ppp_suspend(void)
+{
+    PPP_DEBUG_PRINT("PPP: Module suspended\n");
+}
+
+/**
+ * @brief PPP模块恢复
+ */
+void ppp_resume(void)
+{
+    PPP_DEBUG_PRINT("PPP: Module resumed\n");
+}
+
+/**
+ * @brief PPP模块注册4G控制引脚配置
+ */
+errcode_t ppp_register_ctl_pin(ppp_ctl_pin_4g_t *ctl_pin)
+{
+    if (ctl_pin == NULL) {
+        PPP_DEBUG_PRINT("PPP: register_ctl_pin invalid parameter\n");
+        return ERRCODE_INVALID_PARAM;
+    }
+    
+    (void)memcpy_s(&g_ppp_cat1_ctl_pins, sizeof(ppp_ctl_pin_4g_t), 
+                   ctl_pin, sizeof(ppp_ctl_pin_4g_t));
+    
+    PPP_DEBUG_PRINT("PPP: Control pins registered successfully\n");
+    return ERRCODE_SUCC;
+}
+
+/**
+ * @brief PPP模块Cat1唤醒中断回调函数
+ */
+void ppp_ring_isr_callback(pin_t pin, uintptr_t param)
+{
+    (void)param;
+    PPP_DEBUG_PRINT("PPP: Cat1 ring interrupt on pin %d\n", pin);
+    // 中断处理逻辑
+}
+
+/**
+ * @brief PPP模块专用的4G使能控制接口
+ */
+void ppp_api_enable_cat1(bool isopen)
+{
+    PPP_DEBUG_PRINT("PPP: Enable Cat1: %s\n", isopen ? "true" : "false");
+    
+    if (isopen) {
+        if (g_ppp_cat1_power_status) {
+            PPP_DEBUG_PRINT("PPP: Cat1 already powered on\n");
+            return;
+        }
+        
+        // PPP模块开启4G
+        ppp_power_on_cat1();
+        
+        // 可以在这里添加PPP特定的初始化逻辑
+        // 例如:启动PPP协议栈等
+        
+    } else {
+        if (!g_ppp_cat1_power_status) {
+            PPP_DEBUG_PRINT("PPP: Cat1 already powered off\n");
+            return;
+        }
+        
+        // PPP模块关闭4G
+        ppp_power_off_cat1();
+        
+        // 可以在这里添加PPP特定的清理逻辑
+        // 例如:停止PPP协议栈等
+    }
+}
\ No newline at end of file
diff --git a/code/sdk/zhihui/ppp/src/ppp_serial.c b/code/sdk/zhihui/ppp/src/ppp_serial.c
new file mode 100644 (file)
index 0000000..bedea5f
--- /dev/null
@@ -0,0 +1,456 @@
+#include "ppp_serial.h"
+#include "ppp_config.h"
+#include "ppp_types.h"
+#include "ppp_debug.h"
+#include "uart.h"
+#include "schedule/osal_task.h"
+#include "semaphore/osal_semaphore.h"
+#include "lock/osal_mutex.h"
+#include "memory/osal_addr.h"
+#include "soc_osal.h"
+#include "securec.h"
+#include <string.h>
+
+/* 串口上下文结构 */
+typedef struct {
+    bool initialized;
+    uint8_t uart_bus;
+    uart_pin_config_t pin_config;
+    uart_attr_t uart_attr;
+    uart_buffer_config_t buffer_config;
+    
+    /* 接收缓冲区 */
+    uint8_t *rx_buffer;
+    uint32_t rx_buffer_size;
+    uint32_t rx_head;
+    uint32_t rx_tail;
+    uint32_t rx_count;
+    
+    /* 发送缓冲区 */
+    uint8_t *tx_buffer;
+    uint32_t tx_buffer_size;
+    
+    /* 同步对象 */
+    osal_mutex *rx_mutex;
+    osal_semaphore *rx_semaphore;
+    
+    /* 回调函数 */
+    ppp_serial_rx_callback_t rx_callback;
+    void *rx_callback_arg;
+    
+    /* 统计信息 */
+    uint32_t tx_bytes;
+    uint32_t rx_bytes;
+    uint32_t tx_errors;
+    uint32_t rx_errors;
+} ppp_serial_context_t;
+
+static ppp_serial_context_t g_serial_ctx = {0};
+
+/* 内部函数声明 */
+static void ppp_serial_rx_callback(const void *buffer, uint16_t length, bool remaining);
+static uint32_t ppp_serial_buffer_available_space(void);
+static uint32_t ppp_serial_buffer_data_count(void);
+
+/**
+ * @brief 初始化PPP串口
+ */
+ppp_error_t ppp_serial_init(const ppp_serial_config_t *config)
+{
+    if (!config || g_serial_ctx.initialized) {
+        return PPP_ERR_INVALID_PARAM;
+    }
+
+    PPP_DEBUG_INFO("Initializing PPP serial on UART%d", config->uart_bus);
+
+    /* 分配接收缓冲区 */
+    g_serial_ctx.rx_buffer = (uint8_t *)osal_kmalloc(config->rx_buffer_size, OSAL_GFP_KERNEL);
+    if (!g_serial_ctx.rx_buffer) {
+        PPP_DEBUG_ERROR("Failed to allocate RX buffer");
+        return PPP_ERR_MEMORY;
+    }
+
+    /* 分配发送缓冲区 */
+    g_serial_ctx.tx_buffer = (uint8_t *)osal_kmalloc(config->tx_buffer_size, OSAL_GFP_KERNEL);
+    if (!g_serial_ctx.tx_buffer) {
+        PPP_DEBUG_ERROR("Failed to allocate TX buffer");
+        osal_kfree(g_serial_ctx.rx_buffer);
+        return PPP_ERR_MEMORY;
+    }
+
+    /* 创建互斥锁 */
+    if (osal_mutex_init(&g_serial_ctx.rx_mutex) != OSAL_SUCCESS) {
+        PPP_DEBUG_ERROR("Failed to create RX mutex");
+        osal_kfree(g_serial_ctx.rx_buffer);
+        osal_kfree(g_serial_ctx.tx_buffer);
+        return PPP_ERR_MEMORY;
+    }
+
+    /* 创建信号量 */
+    if (osal_sem_init(&g_serial_ctx.rx_semaphore, 0) != OSAL_SUCCESS) {
+        PPP_DEBUG_ERROR("Failed to create RX semaphore");
+        osal_mutex_destroy(g_serial_ctx.rx_mutex);
+        osal_kfree(g_serial_ctx.rx_buffer);
+        osal_kfree(g_serial_ctx.tx_buffer);
+        return PPP_ERR_MEMORY;
+    }
+
+    /* 配置串口参数 */
+    g_serial_ctx.uart_bus = config->uart_bus;
+    g_serial_ctx.rx_buffer_size = config->rx_buffer_size;
+    g_serial_ctx.tx_buffer_size = config->tx_buffer_size;
+    g_serial_ctx.rx_callback = config->rx_callback;
+    g_serial_ctx.rx_callback_arg = config->rx_callback_arg;
+
+    /* 配置串口引脚 */
+    g_serial_ctx.pin_config.tx_pin = config->tx_pin;
+    g_serial_ctx.pin_config.rx_pin = config->rx_pin;
+    g_serial_ctx.pin_config.cts_pin = PIN_NONE;
+    g_serial_ctx.pin_config.rts_pin = PIN_NONE;
+
+    /* 配置串口属性 */
+    g_serial_ctx.uart_attr.baud_rate = config->baudrate;
+    g_serial_ctx.uart_attr.data_bits = UART_DATA_BIT_8;
+    g_serial_ctx.uart_attr.parity = UART_PARITY_NONE;
+    g_serial_ctx.uart_attr.stop_bits = UART_STOP_BIT_1;
+
+    /* 配置串口缓冲区 */
+    g_serial_ctx.buffer_config.rx_buffer_size = 256; // 硬件缓冲区
+    g_serial_ctx.buffer_config.rx_buffer = (uint8_t *)osal_kmalloc(256, OSAL_GFP_KERNEL);
+    if (!g_serial_ctx.buffer_config.rx_buffer) {
+        PPP_DEBUG_ERROR("Failed to allocate UART RX buffer");
+        osal_sem_destroy(g_serial_ctx.rx_semaphore);
+        osal_mutex_destroy(g_serial_ctx.rx_mutex);
+        osal_kfree(g_serial_ctx.rx_buffer);
+        osal_kfree(g_serial_ctx.tx_buffer);
+        return PPP_ERR_MEMORY;
+    }
+
+    /* 初始化串口 */
+    errcode_t ret = uapi_uart_init(g_serial_ctx.uart_bus, 
+                                   &g_serial_ctx.pin_config,
+                                   &g_serial_ctx.uart_attr,
+                                   NULL,
+                                   &g_serial_ctx.buffer_config);
+    if (ret != ERRCODE_SUCC) {
+        PPP_DEBUG_ERROR("Failed to initialize UART: %d", ret);
+        osal_kfree(g_serial_ctx.buffer_config.rx_buffer);
+        osal_sem_destroy(g_serial_ctx.rx_semaphore);
+        osal_mutex_destroy(g_serial_ctx.rx_mutex);
+        osal_kfree(g_serial_ctx.rx_buffer);
+        osal_kfree(g_serial_ctx.tx_buffer);
+        return PPP_ERR_UART;
+    }
+
+    /* 注册接收回调 */
+    ret = uapi_uart_register_rx_callback(g_serial_ctx.uart_bus,
+                                         UART_RX_CONDITION_FULL_OR_SUFFICIENT_DATA_OR_IDLE,
+                                         1, // 接收到1个字节就触发回调
+                                         ppp_serial_rx_callback);
+    if (ret != ERRCODE_SUCC) {
+        PPP_DEBUG_ERROR("Failed to register UART RX callback: %d", ret);
+        uapi_uart_deinit(g_serial_ctx.uart_bus);
+        osal_kfree(g_serial_ctx.buffer_config.rx_buffer);
+        osal_sem_destroy(g_serial_ctx.rx_semaphore);
+        osal_mutex_destroy(g_serial_ctx.rx_mutex);
+        osal_kfree(g_serial_ctx.rx_buffer);
+        osal_kfree(g_serial_ctx.tx_buffer);
+        return PPP_ERR_UART;
+    }
+
+    /* 初始化缓冲区状态 */
+    g_serial_ctx.rx_head = 0;
+    g_serial_ctx.rx_tail = 0;
+    g_serial_ctx.rx_count = 0;
+
+    /* 清零统计信息 */
+    g_serial_ctx.tx_bytes = 0;
+    g_serial_ctx.rx_bytes = 0;
+    g_serial_ctx.tx_errors = 0;
+    g_serial_ctx.rx_errors = 0;
+
+    g_serial_ctx.initialized = true;
+    PPP_DEBUG_INFO("PPP serial initialized successfully");
+
+    return PPP_ERR_OK;
+}
+
+/**
+ * @brief 反初始化PPP串口
+ */
+void ppp_serial_deinit(void)
+{
+    if (!g_serial_ctx.initialized) {
+        return;
+    }
+
+    PPP_DEBUG_INFO("Deinitializing PPP serial");
+
+    /* 反初始化串口 */
+    uapi_uart_deinit(g_serial_ctx.uart_bus);
+
+    /* 释放资源 */
+    if (g_serial_ctx.buffer_config.rx_buffer) {
+        osal_kfree(g_serial_ctx.buffer_config.rx_buffer);
+    }
+    
+    if (g_serial_ctx.rx_semaphore) {
+        osal_sem_destroy(g_serial_ctx.rx_semaphore);
+    }
+    
+    if (g_serial_ctx.rx_mutex) {
+        osal_mutex_destroy(g_serial_ctx.rx_mutex);
+    }
+    
+    if (g_serial_ctx.rx_buffer) {
+        osal_kfree(g_serial_ctx.rx_buffer);
+    }
+    
+    if (g_serial_ctx.tx_buffer) {
+        osal_kfree(g_serial_ctx.tx_buffer);
+    }
+
+    memset_s(&g_serial_ctx, sizeof(g_serial_ctx), 0, sizeof(g_serial_ctx));
+    PPP_DEBUG_INFO("PPP serial deinitialized");
+}
+
+/**
+ * @brief 发送数据
+ */
+ppp_error_t ppp_serial_write(const uint8_t *data, uint32_t length)
+{
+    if (!g_serial_ctx.initialized || !data || length == 0) {
+        return PPP_ERR_INVALID_PARAM;
+    }
+
+    if (length > g_serial_ctx.tx_buffer_size) {
+        PPP_DEBUG_ERROR("Data length exceeds TX buffer size");
+        return PPP_ERR_INVALID_PARAM;
+    }
+
+    PPP_DEBUG_DEBUG("Sending %u bytes", length);
+    PPP_DEBUG_HEXDUMP(data, length);
+
+    /* 复制数据到发送缓冲区 */
+    memcpy_s(g_serial_ctx.tx_buffer, g_serial_ctx.tx_buffer_size, data, length);
+
+    /* 发送数据 */
+    int32_t bytes_written = uapi_uart_write(g_serial_ctx.uart_bus, g_serial_ctx.tx_buffer, length, 1000);
+    
+    if (bytes_written < 0) {
+        PPP_DEBUG_ERROR("UART write failed: %d", bytes_written);
+        g_serial_ctx.tx_errors++;
+        return PPP_ERR_UART;
+    }
+
+    if ((uint32_t)bytes_written != length) {
+        PPP_DEBUG_WARN("Partial write: %d/%u bytes", bytes_written, length);
+        g_serial_ctx.tx_errors++;
+        return PPP_ERR_UART;
+    }
+
+    g_serial_ctx.tx_bytes += (uint32_t)bytes_written;
+    PPP_DEBUG_DEBUG("Sent %u bytes successfully", bytes_written);
+
+    return PPP_ERR_OK;
+}
+
+/**
+ * @brief 读取数据
+ */
+ppp_error_t ppp_serial_read(uint8_t *buffer, uint32_t buffer_size, uint32_t *bytes_read)
+{
+    if (!g_serial_ctx.initialized || !buffer || !bytes_read || buffer_size == 0) {
+        return PPP_ERR_INVALID_PARAM;
+    }
+
+    *bytes_read = 0;
+
+    osal_mutex_lock(g_serial_ctx.rx_mutex);
+
+    uint32_t available = ppp_serial_buffer_data_count();
+    if (available == 0) {
+        osal_mutex_unlock(g_serial_ctx.rx_mutex);
+        return PPP_ERR_OK; // 没有数据可读
+    }
+
+    uint32_t to_read = (available < buffer_size) ? available : buffer_size;
+    
+    for (uint32_t i = 0; i < to_read; i++) {
+        buffer[i] = g_serial_ctx.rx_buffer[g_serial_ctx.rx_tail];
+        g_serial_ctx.rx_tail = (g_serial_ctx.rx_tail + 1) % g_serial_ctx.rx_buffer_size;
+        g_serial_ctx.rx_count--;
+    }
+
+    *bytes_read = to_read;
+
+    osal_mutex_unlock(g_serial_ctx.rx_mutex);
+
+    PPP_DEBUG_DEBUG("Read %u bytes", to_read);
+    PPP_DEBUG_HEXDUMP(buffer, to_read);
+
+    return PPP_ERR_OK;
+}
+
+/**
+ * @brief 等待数据接收
+ */
+ppp_error_t ppp_serial_wait_data(uint32_t timeout_ms)
+{
+    if (!g_serial_ctx.initialized) {
+        return PPP_ERR_INVALID_PARAM;
+    }
+
+    /* 检查是否已有数据 */
+    osal_mutex_lock(g_serial_ctx.rx_mutex);
+    bool has_data = (g_serial_ctx.rx_count > 0);
+    osal_mutex_unlock(g_serial_ctx.rx_mutex);
+
+    if (has_data) {
+        return PPP_ERR_OK;
+    }
+
+    /* 等待数据到达 */
+    if (osal_sem_wait(g_serial_ctx.rx_semaphore, timeout_ms) != OSAL_SUCCESS) {
+        return PPP_ERR_TIMEOUT;
+    }
+
+    return PPP_ERR_OK;
+}
+
+/**
+ * @brief 获取可读数据长度
+ */
+uint32_t ppp_serial_available(void)
+{
+    if (!g_serial_ctx.initialized) {
+        return 0;
+    }
+
+    osal_mutex_lock(g_serial_ctx.rx_mutex);
+    uint32_t count = ppp_serial_buffer_data_count();
+    osal_mutex_unlock(g_serial_ctx.rx_mutex);
+
+    return count;
+}
+
+/**
+ * @brief 清空接收缓冲区
+ */
+void ppp_serial_flush_rx(void)
+{
+    if (!g_serial_ctx.initialized) {
+        return;
+    }
+
+    osal_mutex_lock(g_serial_ctx.rx_mutex);
+    g_serial_ctx.rx_head = 0;
+    g_serial_ctx.rx_tail = 0;
+    g_serial_ctx.rx_count = 0;
+    osal_mutex_unlock(g_serial_ctx.rx_mutex);
+
+    PPP_DEBUG_DEBUG("RX buffer flushed");
+}
+
+/**
+ * @brief 设置串口波特率
+ */
+ppp_error_t ppp_serial_set_baudrate(uint32_t baudrate)
+{
+    if (!g_serial_ctx.initialized) {
+        return PPP_ERR_INVALID_PARAM;
+    }
+
+    PPP_DEBUG_INFO("Setting baudrate to %u", baudrate);
+
+    g_serial_ctx.uart_attr.baud_rate = baudrate;
+    
+    errcode_t ret = uapi_uart_set_attr(g_serial_ctx.uart_bus, &g_serial_ctx.uart_attr);
+    if (ret != ERRCODE_SUCC) {
+        PPP_DEBUG_ERROR("Failed to set baudrate: %d", ret);
+        return PPP_ERR_UART;
+    }
+
+    PPP_DEBUG_INFO("Baudrate set to %u successfully", baudrate);
+    return PPP_ERR_OK;
+}
+
+/**
+ * @brief 获取统计信息
+ */
+void ppp_serial_get_stats(ppp_serial_stats_t *stats)
+{
+    if (!g_serial_ctx.initialized || !stats) {
+        return;
+    }
+
+    stats->tx_bytes = g_serial_ctx.tx_bytes;
+    stats->rx_bytes = g_serial_ctx.rx_bytes;
+    stats->tx_errors = g_serial_ctx.tx_errors;
+    stats->rx_errors = g_serial_ctx.rx_errors;
+    stats->rx_buffer_usage = g_serial_ctx.rx_count;
+    stats->rx_buffer_size = g_serial_ctx.rx_buffer_size;
+}
+
+/* 内部函数实现 */
+
+/**
+ * @brief 串口接收回调函数
+ */
+static void ppp_serial_rx_callback(const void *buffer, uint16_t length, bool remaining)
+{
+    (void)remaining;
+    
+    if (!buffer || length == 0) {
+        return;
+    }
+
+    const uint8_t *data = (const uint8_t *)buffer;
+
+    osal_mutex_lock(g_serial_ctx.rx_mutex);
+
+    /* 检查缓冲区空间 */
+    uint32_t available_space = ppp_serial_buffer_available_space();
+    if (available_space < length) {
+        PPP_DEBUG_WARN("RX buffer overflow, dropping %u bytes", length - available_space);
+        g_serial_ctx.rx_errors++;
+        length = available_space;
+    }
+
+    /* 将数据复制到环形缓冲区 */
+    for (uint16_t i = 0; i < length; i++) {
+        g_serial_ctx.rx_buffer[g_serial_ctx.rx_head] = data[i];
+        g_serial_ctx.rx_head = (g_serial_ctx.rx_head + 1) % g_serial_ctx.rx_buffer_size;
+        g_serial_ctx.rx_count++;
+    }
+
+    g_serial_ctx.rx_bytes += length;
+
+    osal_mutex_unlock(g_serial_ctx.rx_mutex);
+
+    /* 通知有数据到达 */
+    osal_sem_signal(g_serial_ctx.rx_semaphore);
+
+    /* 调用用户回调函数 */
+    if (g_serial_ctx.rx_callback) {
+        g_serial_ctx.rx_callback(data, length, g_serial_ctx.rx_callback_arg);
+    }
+
+    PPP_DEBUG_DEBUG("Received %u bytes", length);
+}
+
+/**
+ * @brief 获取缓冲区可用空间
+ */
+static uint32_t ppp_serial_buffer_available_space(void)
+{
+    return g_serial_ctx.rx_buffer_size - g_serial_ctx.rx_count;
+}
+
+/**
+ * @brief 获取缓冲区数据数量
+ */
+static uint32_t ppp_serial_buffer_data_count(void)
+{
+    return g_serial_ctx.rx_count;
+} 
\ No newline at end of file
index b0d621d..2a11547 100644 (file)
@@ -16,6 +16,8 @@
 #endif
 #ifdef SUPPORT_CAT1_SERVICE
 #include "cat1_tcpServer.h"
+#include "pdu.h"
+#include "lwip/apps/sntp.h"
 #endif
 // #include "message_notification.h"
 // #include "MessageModel.h"
@@ -634,7 +636,7 @@ static uint32_t custom_utils_pdudec(uint8_t *para, uint32_t paraLen, char *strCo
     const char *inpdustr = (const char*)argv[0];
      
     
-    #ifdef SUPPORT_CAT1_SERVICE
+    #if defined(SUPPORT_CAT1_SERVICE) && !defined(SUPPORT_PPP_SERVICE)
     pdu_decode_example(inpdustr,0,0,0);
     #endif
 
@@ -699,6 +701,7 @@ static uint32_t custom_test_sntp(uint8_t *para, uint32_t paraLen, char *strCommo
 {
     UNUSED(strCommonCmd);
     
+#if defined(SUPPORT_CAT1_SERVICE) && !defined(SUPPORT_PPP_SERVICE)
     if(sntp_enabled()==0)
     {
         sntp_init();
@@ -706,6 +709,7 @@ static uint32_t custom_test_sntp(uint8_t *para, uint32_t paraLen, char *strCommo
         sntp_stop();
         sntp_init();
     }
+#endif
     wstp_print("CUSTOM_UTILSTEST ATCMD custom_test_sntp sntp_init \n");
     return ERRCODE_SUCC;
 }
@@ -985,14 +989,14 @@ static uint32_t custom_get_aging_data(uint8_t *para, uint32_t paraLen, char *str
 }
 static uint32_t custom_cat1_start_ota_pyupdate(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-#ifdef SUPPORT_CAT1_SERVICE
+#if defined(SUPPORT_CAT1_SERVICE) && !defined(SUPPORT_PPP_SERVICE)
     Cat1_OTA(CAT1_TCP_START_OTA);
 #endif
     return ERRCODE_SUCC;
 }
 static uint32_t custom_cat1_stop_ota_pyupdate(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-#ifdef SUPPORT_CAT1_SERVICE
+#if defined(SUPPORT_CAT1_SERVICE) && !defined(SUPPORT_PPP_SERVICE)
     Cat1_OTA(CAT1_TCP_STOP_OTA);
 #endif
     return ERRCODE_SUCC;
@@ -1000,7 +1004,7 @@ static uint32_t custom_cat1_stop_ota_pyupdate(uint8_t *para, uint32_t paraLen, c
 static uint32_t custom_cat1_send_bak_mode(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
 
-#ifdef SUPPORT_CAT1_SERVICE
+#if defined(SUPPORT_CAT1_SERVICE) && !defined(SUPPORT_PPP_SERVICE)
     send_bak_mode_toCat1();
 #endif
     return ERRCODE_SUCC;