第 2 章 - 安装和使用 Azure RTOS NetX PPPoE 服务器

本章介绍了与安装、设置和使用 Azure RTOS NetX PPPoE 服务器组件相关的各种问题。

产品分发

https://github.com/azure-rtos/netx 中提供了 NetX PPPoE 服务器。 该程序包包含两个源文件和一个 PDF 文件(其中包含本文档),如下所示:

  • nx_pppoe_server.h:NetX PPPoE 服务器的头文件
  • nx_pppoe_server.c:NetX PPPoE 服务器的 C 源文件
  • nx_pppoe_server.pdf:NetX PPPoE 服务器的 PDF 说明
  • demo_netx_pppoe_server.c:NetX PPPoE 服务器演示

PPPoE 服务器安装

若要使用 NetX PPPoE 服务器,应将前面提到的整个分发包复制到安装了 NetX 的目录。 例如,如果 NetX 安装在“\threadx\arm7\green”目录中,则应将 nx_pppoe_server.h 和 nx_pppoe_server.c 文件复制到该目录中。

使用 PPPoE 服务器

使用 NetX PPPoE 服务器非常容易。 总体上,应用程序代码必须先包含 tx_api.h 和 nx_api.h,然后包含 nx_pppoe_server.h,才能分别使用 ThreadX 和 NetX。 包含 nx_pppoe_server.h 之后,应用程序代码就可以进行本指南后面部分所述的 PPPoE 服务器函数调用。 在生成过程中,应用程序还必须包含 nx_pppoe_server.c。 此文件必须采用与其他应用程序文件相同的方式进行编译,并且其对象窗体必须与该应用程序的文件一起链接。 这就是使用 NetX PPPoE 服务器所需的一切。

小型示例系统

下面的示例演示了如何使用 NetX PPPoE 服务器,如图 1.1 所述。 在此示例中,第 50 行引入了 PPPoE 服务器 include 文件 nx_pppoe_server.h。 接下来,在第 248 行的*“thread_0_entry*”中创建了 PPPoE 服务器。 请注意,应该先创建 IP 实例,然后再创建 PPPoE 服务器。 IP 实例是在第 165 行中创建并初始化的。 HTTP 服务器控制块“pppoe_server”在前面的第 79 行中定义为全局变量。 notify 函数是在第 257 行设置的。 请注意,必须设置 pppoe_session_data_receive notify 函数。 成功创建 IP 和 PPPoE 服务器后,在第 272 行,PPPoE 服务器通过调用 nx_pppoe_server_enable 建立 PPPoE 会话。

通常,PPPoE 模块应与 PPP 模块配合使用。 在此示例中,在第 49 行引入了 PPP 服务器 include 文件 nx_ppp.h。 接下来,在第 174 行创建了 PPP 服务器。 第 182 行设置用于发送 PPP 数据包的函数。 第 188-200 行设置 IP 地址,并定义 pap 协议。 第 115-139 行设置 pap 协议的用户名和密码。

在 PPPoE 会话建立之后, 在第 281 行,应用程序可以调用 nx_pppoe_server_session_get 来获取会话信息(客户端 MAC 地址和会话 ID)。

当应用程序不再处理 PPP 流量时,应用程序可以调用 PppCloseInd 或 nx_pppoe_server_session_terminate 来终止 PPPoE 会话。

注意

在此示例中,PPPoE 服务器与普通 IP 堆栈同时运行,并共享同一个以太网驱动程序。 为普通 IP 实例(第 165 行)和 PPPoE 服务器实例(第 248 行)传递相同的以太网驱动程序。

注意

函数由 PPPoE 实现提供,供已定义的 NX_PPPoE_SERVER_SESSION_CONTROL_ENABELE 下的软件调用。

如果定义了此项,则会启用控制 PPPoE 会话的功能。

在应用程序调用特定 API 之前,PPPoE 服务器不会自动响应请求。如果未定义此项,则 PPPoE 服务器会自动响应请求。 默认情况下,它在 nx_pppoe_server.h 中启用。

请注意,将 NX_PHYSICAL_HEADER 重新定义为 24,确保有足够的空间可用于填充物理标头。 物理标头:14(以太网标头)+ 6(PPPoE 标头)+ 2(PPP 标头)+ 2(四字节对齐)。

/**************************************************************************/
/**************************************************************************/
/**                                                                       */
/** NetX PPPoE Server stack Component                                     */
/**                                                                       */
/** This is a small demo of the high-performance NetX PPPoE Server        */
/** stack. This demo includes IP instance, PPPoE Server and PPP Server    */
/** stack. Create one IP instance includes two interfaces to support      */
/** for normal IP stack and PPPoE Server, PPPoE Server can use the        */
/** mutex of IP instance to send PPPoE message when share one Ethernet    */
/** driver. PPPoE Server work with normal IP instance at the same time.   */
/**                                                                       */
/** Note1: Substitute your Ethernet driver instead of                     */
/** _nx_ram_network_driver before run this demo                           */
/**                                                                       */
/** Note2: Prerequisite for using PPPoE.                                  */
/** Redefine NX_PHYSICAL_HEADER to 24 to ensure enough space for filling  */
/** in physical header. Physical header:14(Ethernet header)               */
/** + 6(PPPoE header) + 2(PPP header) + 2(four-byte aligment)             */
/**                                                                       */
/**************************************************************************/
/**************************************************************************/


/*****************************************************************/
/*                            NetX Stack                         */
/*****************************************************************/

                                      /***************************/
                                      /* PPP Server              */
                                      /***************************/

                                      /***************************/
                                      /* PPPoE Server            */
                                      /***************************/
/***************************/         /***************************/
/* Normal Ethernet Type    */         /* PPPoE Ethernet Type     */
/***************************/         /***************************/
/***************************/         /***************************/
/* Interface 0             */         /* Interface 1             */
/***************************/         /***************************/

/*****************************************************************/
/*                     Ethernet Dirver                           */
/*****************************************************************/

#include     "tx_api.h"
#include     "nx_api.h"
#include     "nx_ppp.h"
#include     "nx_pppoe_server.h"

/* Defined NX_PPP_PPPOE_ENABLE if use Express Logic's PPP, since PPP module has been modified to match PPPoE moduler under this definition. */
#ifdef NX_PPP_PPPOE_ENABLE

/*   If the driver is not initialized in other module, define */
/*   NX_PPPOE_SERVER_INITIALIZE_DRIVER_ENABLE to initialize the driver in PPPoE module. */
/*   In this demo, the driver has been initialized in IP module. */

#ifdef NX_PPPOE_SERVER_INITIALIZE_DRIVER_ENABLE

/*   NX_PPPOE_SERVER_SESSION_CONTROL_ENABLE: If defined, enables the feature that */
/*   controls the PPPoE session. PPPoE server does not automatically response to the */
/*   request until application call specific API. */

/* Define the block size. */
#define     NX_PACKET_POOL_SIZE     ((1536 + sizeof(NX_PACKET)) * 30)
#define     DEMO_STACK_SIZE         2048
#define     PPPOE_THREAD_SIZE       2048

/* Define the ThreadX and NetX object control blocks... */
TX_THREAD           thread_0;

/* Define the packet pool and IP instance for normal IP instnace. */
NX_PACKET_POOL      pool_0;
NX_IP               ip_0;

/* Define the PPP Server instance. */
NX_PPP              ppp_server;

/* Define the PPPoE Server instance. */
NX_PPPOE_SERVER     pppoe_server;

/* Define the counters. */
CHAR                *pointer;
ULONG               error_counter;

/* Define thread prototypes. */
void     thread_0_entry(ULONG thread_input);

/***** Substitute your PPP driver entry function here *********/
extern void     _nx_ppp_driver(NX_IP_DRIVER *driver_req_ptr);

/***** Substitute your Ethernet driver entry function here *********/
extern void     _nx_ram_network_driver(NX_IP_DRIVER *driver_req_ptr);

/* Define the callback functions. */
void     PppDiscoverReq(UINT interfaceHandle);
void     PppOpenReq(UINT interfaceHandle, ULONG length, UCHAR *data);
void     PppCloseRsp(UINT interfaceHandle);
void     PppCloseReq(UINT interfaceHandle);
void     PppTransmitDataReq(UINT interfaceHandle, ULONG length, UCHAR *data, UINT packet_id);
void     PppReceiveDataRsp(UINT interfaceHandle, UCHAR *data);

/* Define the porting layer function for Express Logic's PPP to simulate TTP's PPP. */
/* Functions to be provided by PPP for calling by the PPPoE Stack. */
void     ppp_server_packet_send(NX_PACKET *packet_ptr);

/* Define main entry point. */

int main()
{

    /* Enter the ThreadX kernel. */
    tx_kernel_enter();
}

UINT verify_login(CHAR *name, CHAR *password)
{

    if ((name[0] == 'm') &&
        (name[1] == 'y') &&
        (name[2] == 'n') &&
        (name[3] == 'a') &&
        (name[4] == 'm') &&
        (name[5] == 'e') &&
        (name[6] == (CHAR) 0) &&
        (password[0] == 'm') &&
        (password[1] == 'y') &&
        (password[2] == 'p') &&
        (password[3] == 'a') &&
        (password[4] == 's') &&
        (password[5] == 's') &&
        (password[6] == 'w') &&
        (password[7] == 'o') &&
        (password[8] == 'r') &&
        (password[9] == 'd') &&
        (password[10] == (CHAR) 0))
            return(NX_SUCCESS);
    else
        return(NX_PPP_ERROR);
}

/* Define what the initial system looks like. */

void tx_application_define(void *first_unused_memory)
{

    UINT status;

    /* Setup the working pointer. */
    pointer = (CHAR *) first_unused_memory;

    /* Initialize the NetX system. */
    nx_system_initialize();

    /* Create a packet pool for normal IP instance. */
    status = nx_packet_pool_create(&pool_0, "NetX Main Packet Pool",
                                    (1536 + sizeof(NX_PACKET)),
                                    pointer, NX_PACKET_POOL_SIZE);
                                    pointer = pointer + NX_PACKET_POOL_SIZE;

    /* Check for error. */
    if (status)
        error_counter++;

    /* Create an normal IP instance. */
    status = nx_ip_create(&ip_0, "NetX IP Instance", IP_ADDRESS(192, 168, 100, 43),
                        0xFFFFFF00UL, &pool_0, _nx_ram_network_driver,
                        pointer, 2048, 1);
                        pointer = pointer + 2048;

    /* Check for error. */
    if (status)
        error_counter++;

    /* Create the PPP instance. */
    status = nx_ppp_create(&ppp_server, "PPP Instance", &ip_0, pointer, 2048, 1,
                            &pool_0, NX_NULL, NX_NULL);
    pointer = pointer + 2048;

    /* Check for PPP create error. */
    if (status)
        error_counter++;

    /* Set the PPP packet send function. */
    status = nx_ppp_packet_send_set(&ppp_server, ppp_server_packet_send);

    /* Check for PPP packet send function set error. */
    if (status)
        error_counter++;

    /* Define IP address. This PPP instance is effectively the server since it has both IP addresses. */
    status = nx_ppp_ip_address_assign(&ppp_server, IP_ADDRESS(192, 168, 10, 43),                                         IP_ADDRESS(192, 168, 10, 44));

    /* Check for PPP IP address assign error. */
    if (status)
        error_counter++;

    /* Setup PAP, this PPP instance is effectively the server since it will verify the name and password. */
    status = nx_ppp_pap_enable(&ppp_server, NX_NULL, verify_login);

    /* Check for PPP PAP enable error. */
    if (status)
        error_counter++;

    /* Attach an interface for PPP. */
    status = nx_ip_interface_attach(&ip_0, "Second Interface For PPP", 
                            IP_ADDRESS(0, 0, 0, 0), 0, nx_ppp_driver);

    /* Check for error. */
    if (status)
        error_counter++;

    /* Enable ARP and supply ARP cache memory for Normal IP Instance. */
    status = nx_arp_enable(&ip_0, (void *) pointer, 1024);
    pointer = pointer + 1024;

    /* Check for ARP enable errors. */
    if (status)
        error_counter++;

    /* Enable ICMP */
    status = nx_icmp_enable(&ip_0);
    if(status)
        error_counter++;

    /* Enable UDP traffic. */
    status = nx_udp_enable(&ip_0);
    if (status)
        error_counter++;

    /* Enable TCP traffic. */
    status = nx_tcp_enable(&ip_0);
    if (status)
        error_counter++;

    /* Create the main thread. */
    tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0,
                    pointer, DEMO_STACK_SIZE,
                    4, 4, TX_NO_TIME_SLICE, TX_AUTO_START);
                    pointer = pointer + DEMO_STACK_SIZE;
}

/* Define the test threads. */

void     thread_0_entry(ULONG thread_input)
{
UINT     status;
ULONG    ip_status;

    /* Create the PPPoE instance. */
    status = nx_pppoe_server_create(&pppoe_server, (UCHAR *)"PPPoE Server", &ip_0, 0,
                    _nx_ram_network_driver, &pool_0, pointer, PPPOE_THREAD_SIZE, 4);
    pointer = pointer + PPPOE_THREAD_SIZE;
    if (status)
    {
        error_counter++;
        return;
    }

    /* Set the callback notify function. */
    status = nx_pppoe_server_callback_notify_set(&pppoe_server, PppDiscoverReq,
        PppOpenReq, PppCloseRsp, PppCloseReq, PppTransmitDataReq, PppReceiveDataRsp);

    if (status)
    {
        error_counter++;
        return;
    }

    #ifdef NX_PPPOE_SERVER_SESSION_CONTROL_ENABLE
        /* Call function function to set the default service Name. */
        /* PppInitInd(length, aData); */
    #endif /* NX_PPPOE_SERVER_SESSION_CONTROL_ENABLE */

    /* Enable PPPoE Server. */
    status = nx_pppoe_server_enable(&pppoe_server);
    if (status)
    {
        error_counter++;
        return;
    }

    /* Get the PPPoE Client physical address and Session ID after establish PPPoE Session. */
    /*
        status = nx_pppoe_server_session_get(&pppoe_server, interfaceHandle, &client_mac_msw, &client_mac_lsw, &session_id);
        if (status)
            error_counter++;
    */

    /* Wait for the link to come up. */
    status = nx_ip_interface_status_check(&ip_0, 1, NX_IP_ADDRESS_RESOLVED,
                                            &ip_status, NX_WAIT_FOREVER);
    if (status)
    {
        error_counter++;
        return;
    }

    #ifdef NX_PPPOE_SERVER_SESSION_CONTROL_ENABLE
        /* Call PPPoE function to terminate the PPPoE Session. */
        /* PppCloseInd(interfaceHandle, causeCode); */
    #endif /* NX_PPPOE_SERVER_SESSION_CONTROL_ENABLE */

}

void     PppDiscoverReq(UINT interfaceHandle)
{

    /* Receive the PPPoE Discovery Initiation Message. */
    
    #ifdef NX_PPPOE_SERVER_SESSION_CONTROL_ENABLE
        /* Call PPPoE function to allow TTP's software to define the Service Name field of the PADO packet. */
        PppDiscoverCnf(0, NX_NULL, interfaceHandle);
    #endif /* NX_PPPOE_SERVER_SESSION_CONTROL_ENABLE */
}

void     PppOpenReq(UINT interfaceHandle, ULONG length, UCHAR *data)
{

    /* Get the notify that receive the PPPoE Discovery Request Message. */

    #ifdef NX_PPPOE_SERVER_SESSION_CONTROL_ENABLE
        /* Call PPPoE function to allow TTP's software to accept the PPPoE session. */
        PppOpenCnf(NX_TRUE, interfaceHandle);
    #endif /* NX_PPPOE_SERVER_SESSION_CONTROL_ENABLE */
}

void     PppCloseRsp(UINT interfaceHandle)
{

    /* Get the notify that receive the PPPoE Discovery Terminate Message. */

    #ifdef NX_PPPOE_SERVER_SESSION_CONTROL_ENABLE
        /* Call PPPoE function to allow TTP's software to confirm that the handle has been freed. */
        PppCloseCnf(interfaceHandle);
    #endif /* NX_PPPOE_SERVER_SESSION_CONTROL_ENABLE */
}

void     PppCloseReq(UINT interfaceHandle)
{

    /* Get the notify that PPPoE Discovery Terminate Message has been sent. */

}

void     PppTransmitDataReq(UINT interfaceHandle, ULONG length, UCHAR *data, UINT packet_id)
{

    NX_PACKET *packet_ptr;

    /* Get the notify that receive the PPPoE Session data. */

    /* Call PPP Server to receive the PPP data fame. */
    packet_ptr = (NX_PACKET *)(packet_id);
    nx_ppp_packet_receive(&ppp_server, packet_ptr);

    #ifdef NX_PPPOE_SERVER_SESSION_CONTROL_ENABLE
        /* Call PPPoE function to confirm that the data has been processed. */
        PppTransmitDataCnf(interfaceHandle, data, packet_id);
    #endif /* NX_PPPOE_SERVER_SESSION_CONTROL_ENABLE */
}

void     PppReceiveDataRsp(UINT interfaceHandle, UCHAR *data)
{

    /* Get the notify that the PPPoE Session data has been sent. */

}

/* PPP Server send function. */
void     ppp_server_packet_send(NX_PACKET *packet_ptr)
{

    /* For Express Logic's PPP test, the session should be the first session, so set interfaceHandle as 0. */
    UINT interfaceHandle = 0;

    #ifdef NX_PPPOE_SERVER_SESSION_CONTROL_ENABLE
        while(packet_ptr)
        {

            /* Call functions to be provided by PPPoE for TTP. */
            PppReceiveDataInd(interfaceHandle, (packet_ptr -> nx_packet_append_ptr -
            packet_ptr -> nx_packet_prepend_ptr), packet_ptr -> nx_packet_prepend_ptr);

            /* Move to the next packet structure. */
            packet_ptr = packet_ptr -> nx_packet_next;
        }
    #else
        /* Directly Call PPPoE send function to send out the data through PPPoE module. */
        nx_pppoe_server_session_packet_send(&pppoe_server, interfaceHandle, packet_ptr);
    #endif /* NX_PPPOE_SERVER_SESSION_CONTROL_ENABLE */
}
#endif /* NX_PPPOE_SERVER_INITIALIZE_DRIVER_ENABLE */

#endif /* NX_PPP_PPPOE_ENABLE */

图 1.1 与 NetX 配合使用的 PPPoE 服务器示例

配置选项

可通过几个配置选项生成适用于 NetX 的 PPPoE 服务器。 以下列表详细介绍了每个配置选项:

  • NX_DISABLE_ERROR_CHECKING:定义后,此选项会删除基本的 PPPoE 服务器错误检查。 通常会在调试应用程序后使用此选项。

  • NX_PPPOE_SERVER_SESSION_CONTROL_ENABLE:如果定义了此项,则会启用控制 PPPOE 会话的功能。 在应用程序调用特定 API 之前,PPPoE 服务器不会自动响应请求。

  • NX_PPPOE_SERVER_INITIALIZE_DRIVER_ENABLE:如果定义了此项,则会启用在 PPPoE 模块中初始化以太网驱动程序的功能, 默认情况下会禁用该功能。

  • NX_PPPOE_SERVER_THREAD_TIME_SLICE:PPPOE 服务器线程的时间切片选项。 默认情况下,此值为 TX_NO_TIME_SLICE。

  • NX_PPPOE_SERVER_MAX_CLIENT_SESSION_NUMBER:此项定义并发客户端会话的最大数量。 默认情况下,此值为 10。

  • NX_PPPOE_SERVER_MAX_HOST_UNIQ_SIZE:此项定义 Host-Uniq 的最大大小。 默认情况下,此值为 32。

  • NX_PPPOE_SERVER_MAX_RELAY_SESSION_ID_SIZE:此项定义 Relay-Session-Id 的最大大小。默认情况下,此值为 12。

  • NX_PPPOE_SERVER_MIN_PACKET_PAYLOAD_SIZE:指定 PPPoE 服务器的最小数据包有效负载大小。 如果数据包有效负载大小大于此值,则可以避免数据包链接。 默认情况下,此值为 1520(以太网的最大有效负载大小 1500 + 以太网标头 14 + CRC 2 + 四字节对齐 4)。

  • NX_PPPOE_SERVER_PACKET_TIMEOUT:此项定义分配数据包时或将数据追加到数据包时的等待部分(以时钟周期为单位)。 默认情况下,此值为 NX_IP_PERIODIC_RATE(100 个时钟周期)。

  • NX_PPPOE_SERVER_START_SESSION_ID:此项定义分配到 PPPoE 会话的起始会话 ID。 默认情况下,此值为 0X4944(ID 的 ASCII 值)。