#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
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
#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
-/*\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
'BOARD_LEINIAO',
# cat1 相关配置
'FT_SINGLE_UART',
- 'ENABLE_SLIP',
- 'SUPPORT_CAT1_SERVICE',
+ # 'ENABLE_SLIP',
+ # 'SUPPORT_CAT1_SERVICE',
'SUPPORT_TCPIP',
'CONFIG_EFUSE_READ_TO_RAM',
"vibrator_service",
# "-:lwip",
"thunderbird_toys",#雷鸟
- "cat1Service",
- "cat1Test",
+ # "cat1Service",
+ # "cat1Test",
+ "ppp",
"audio_manager",
# "CustomUIcomponentTool",
"nativeabilityfwk",
${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
${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)
)
endif()
+
+
+
+
+
set(PUBLIC_HEADER
)
#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
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;
}
/**
{
uart_bus_t bus = (uart_bus_t)(uintptr_t)fd;
- if (!g_slip_uart_initialized) {
+ if (!g_sio_uart_initialized) {
return;
}
/* 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 */
}
}
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;
}
{
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;
}
/**
{
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;
}
/**
{
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;
}
/**
{
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);
}
/**
*/
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
/* 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;
#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
/*
--- /dev/null
+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.
--- /dev/null
+# 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})
--- /dev/null
+#
+# 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)
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+<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>
+
+ </td></tr>
+ </table>
+</body>
+</html>
--- /dev/null
+<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>
+
+ </td></tr>
+ </table>
+</body>
+</html>
+
--- /dev/null
+#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
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+#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 */
--- /dev/null
+#!/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");
--- /dev/null
+/**
+ * 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;
+}
--- /dev/null
+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'.
--- /dev/null
+/*
+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
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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 <hostname>, '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 <hostname>.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 <hostname>.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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+ /**
+ * @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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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<OID2 1: OID1 >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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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, ðaddr);
+
+ /* 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, ðaddr)) {
+ 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, ðaddr)) {
+ 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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/*
+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 */
--- /dev/null
+/*
+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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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, ×tamps, 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(×tamps);
+
+#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 */
--- /dev/null
+/**
+ *
+ * @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 */
#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();
--- /dev/null
+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.
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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;
+}
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+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.
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+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/
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*****************************************************************************
+* 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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*****************************************************************************
+* 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(ðhdr->dest.addr, &sc->sc_dest.addr, sizeof(ethhdr->dest.addr));
+ MEMCPY(ðhdr->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(ðhdr->dest.addr, dest, sizeof(ethhdr->dest.addr));
+ MEMCPY(ðhdr->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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/**
+ * @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 */
--- /dev/null
+
\ No newline at end of file
#===============================================================================
-# @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
# ${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
${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
-Wno-error=unused-function
)
+# 是否整体链接
set(WHOLE_LINK
true
)
+# 是否为主组件
set(MAIN_COMPONENT
false
)
-set(PUBLIC_DEFINES
-SUPPORT_CAT1_SERVICE
-)
-
-set(PRIVATE_DEFINES
-)
-
+# 构建组件
build_component()
S_AGPIO_L23, // pwr_pin
1,
S_AGPIO_L28,//reset_pin
- 1,
+ 0,
S_AGPIO_L31,//ring_pin
1,
S_AGPIO_L30,//wakemodem_pin
}
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)
}
}
-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
}
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);
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);
#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
--- /dev/null
+#!/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
add_subdirectory_if_exist(ai/media)
add_subdirectory_if_exist(websocket)
add_subdirectory_if_exist(4gTestcpp)
+add_subdirectory_if_exist(ppp)
--- /dev/null
+
\ No newline at end of file
--- /dev/null
+# 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
--- /dev/null
+
\ No newline at end of file
--- /dev/null
+
\ No newline at end of file
--- /dev/null
+
\ No newline at end of file
--- /dev/null
+#!/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
--- /dev/null
+版本说明:\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
--- /dev/null
+TIMEOUT 5
+ABORT "BUSY"
+ABORT "ERROR"
+ABORT "NO CARRIER"
+'' \rAT
+OK 'AT+CGDCONT=3,"IP"'
+OK 'ATD*99***2#'
+CONNECTED ''
--- /dev/null
+/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'
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+
+#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
--- /dev/null
+#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
--- /dev/null
+/*
+ * @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
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+#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
--- /dev/null
+/**
+ * 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
--- /dev/null
+#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
--- /dev/null
+/*
+ * @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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+/**
+ * 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
--- /dev/null
+#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
--- /dev/null
+/*
+ * @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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+/*
+ * @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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+/*
+ * @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
--- /dev/null
+#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
#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"
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
{
UNUSED(strCommonCmd);
+#if defined(SUPPORT_CAT1_SERVICE) && !defined(SUPPORT_PPP_SERVICE)
if(sntp_enabled()==0)
{
sntp_init();
sntp_stop();
sntp_init();
}
+#endif
wstp_print("CUSTOM_UTILSTEST ATCMD custom_test_sntp sntp_init \n");
return ERRCODE_SUCC;
}
}
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;
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;