mirror of
https://github.com/lwip-tcpip/lwip.git
synced 2024-12-28 09:19:53 +00:00
b55412a0c4
The overall lwIP design on data flows (netif,udp,tcp) is to use a user defined callback to get data from stack and a static function to send data to stack, which makes perfect sense. The SIO port was an exception, the PPP stack never really used the SIO port by only using the sio_send() function (and the ignominious sio_read_abort() function a while back). The way the SIO port is currently designed adds a tight coupling between the lwIP port and the user code if the user need to do specific user code if the current uart used is the PPPoS uart, which is not nice, especially because all the lwIP stack is quite clean at this subject. While we are at stabilizing the PPP API, change this behavior before it's too late by replacing the static sio_write() calls to a user defined callback.
422 lines
12 KiB
Plaintext
422 lines
12 KiB
Plaintext
PPP interface for lwIP
|
|
|
|
Author: Sylvain Rochet
|
|
|
|
Table of Contents:
|
|
|
|
1 - Supported PPP protocols and features
|
|
2 - Raw API PPP example for all protocols
|
|
3 - PPPoS input path (raw API, IRQ safe API, TCPIP API)
|
|
4 - Thread safe PPP API (PPPAPI)
|
|
5 - Upgrading from lwIP <= 1.4.x to lwIP >= 1.5.x
|
|
|
|
|
|
|
|
1 Supported PPP protocols and features
|
|
======================================
|
|
|
|
Supported Low level protocols:
|
|
* PPP over serial using HDLC-like framing, such as wired dialup modems
|
|
or mobile telecommunications GPRS/EDGE/UMTS/HSPA+/LTE modems
|
|
* PPP over Ethernet, such as xDSL modems
|
|
* PPP over L2TP (Layer 2 Tunneling Protocol) LAC (L2TP Access Concentrator),
|
|
IP tunnel over UDP, such as VPN access
|
|
|
|
Supported auth protocols:
|
|
* PAP, Password Authentication Protocol
|
|
* CHAP, Challenge-Handshake Authentication Protocol, also known as CHAP-MD5
|
|
* MSCHAPv1, Microsoft version of CHAP, version 1
|
|
* MSCHAPv2, Microsoft version of CHAP, version 2
|
|
* EAP, Extensible Authentication Protocol
|
|
|
|
Supported address protocols:
|
|
* IPCP, IP Control Protocol, IPv4 addresses negotiation
|
|
* IP6CP, IPv6 Control Protocol, IPv6 link-local addresses negotiation
|
|
|
|
Supported encryption protocols:
|
|
* MPPE, Microsoft Point-to-Point Encryption
|
|
|
|
Supported compression or miscellaneous protocols, for serial links only:
|
|
* PFC, Protocol Field Compression
|
|
* ACFC, Address-and-Control-Field-Compression
|
|
* ACCM, Asynchronous-Control-Character-Map
|
|
* VJ, Van Jacobson TCP/IP Header Compression
|
|
|
|
|
|
|
|
2 Raw API PPP example for all protocols
|
|
=======================================
|
|
|
|
As usual, raw API for lwIP means the lightweight API which *MUST* only be used
|
|
for NO_SYS=1 systems or called inside lwIP core thread for NO_SYS=0 systems.
|
|
|
|
/*
|
|
* Globals
|
|
* =======
|
|
*/
|
|
|
|
/* The PPP control block */
|
|
ppp_pcb *ppp;
|
|
|
|
/* The PPP IP interface */
|
|
struct netif ppp_netif;
|
|
|
|
|
|
/*
|
|
* PPP status callback
|
|
* ===================
|
|
*
|
|
* PPP status callback is called on PPP status change (up, down, …) from lwIP
|
|
* core thread
|
|
*/
|
|
|
|
/* PPP status callback example */
|
|
static void status_cb(ppp_pcb *pcb, int err_code, void *ctx) {
|
|
struct netif *pppif = ppp_netif(pcb);
|
|
LWIP_UNUSED_ARG(ctx);
|
|
|
|
switch(err_code) {
|
|
case PPPERR_NONE: {
|
|
#if LWIP_DNS
|
|
ip_addr_t ns;
|
|
#endif /* LWIP_DNS */
|
|
printf("status_cb: Connected\n");
|
|
#if PPP_IPV4_SUPPORT
|
|
printf(" our_ipaddr = %s\n", ipaddr_ntoa(&pppif->ip_addr));
|
|
printf(" his_ipaddr = %s\n", ipaddr_ntoa(&pppif->gw));
|
|
printf(" netmask = %s\n", ipaddr_ntoa(&pppif->netmask));
|
|
#if LWIP_DNS
|
|
ns = dns_getserver(0);
|
|
printf(" dns1 = %s\n", ipaddr_ntoa(&ns));
|
|
ns = dns_getserver(1);
|
|
printf(" dns2 = %s\n", ipaddr_ntoa(&ns));
|
|
#endif /* LWIP_DNS */
|
|
#endif /* PPP_IPV4_SUPPORT */
|
|
#if PPP_IPV6_SUPPORT
|
|
printf(" our6_ipaddr = %s\n", ip6addr_ntoa(netif_ip6_addr(pppif, 0)));
|
|
#endif /* PPP_IPV6_SUPPORT */
|
|
break;
|
|
}
|
|
case PPPERR_PARAM: {
|
|
printf("status_cb: Invalid parameter\n");
|
|
break;
|
|
}
|
|
case PPPERR_OPEN: {
|
|
printf("status_cb: Unable to open PPP session\n");
|
|
break;
|
|
}
|
|
case PPPERR_DEVICE: {
|
|
printf("status_cb: Invalid I/O device for PPP\n");
|
|
break;
|
|
}
|
|
case PPPERR_ALLOC: {
|
|
printf("status_cb: Unable to allocate resources\n");
|
|
break;
|
|
}
|
|
case PPPERR_USER: {
|
|
printf("status_cb: User interrupt\n");
|
|
break;
|
|
}
|
|
case PPPERR_CONNECT: {
|
|
printf("status_cb: Connection lost\n");
|
|
break;
|
|
}
|
|
case PPPERR_AUTHFAIL: {
|
|
printf("status_cb: Failed authentication challenge\n");
|
|
break;
|
|
}
|
|
case PPPERR_PROTOCOL: {
|
|
printf("status_cb: Failed to meet protocol\n");
|
|
break;
|
|
}
|
|
case PPPERR_PEERDEAD: {
|
|
printf("status_cb: Connection timeout\n");
|
|
break;
|
|
}
|
|
case PPPERR_IDLETIMEOUT: {
|
|
printf("status_cb: Idle Timeout\n");
|
|
break;
|
|
}
|
|
case PPPERR_CONNECTTIME: {
|
|
printf("status_cb: Max connect time reached\n");
|
|
break;
|
|
}
|
|
case PPPERR_LOOPBACK: {
|
|
printf("status_cb: Loopback detected\n");
|
|
break;
|
|
}
|
|
default: {
|
|
printf("status_cb: Unknown error code %d\n", err_code);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This should be in the switch case, this is put outside of the switch
|
|
* case for example readability.
|
|
*/
|
|
|
|
if (err_code == PPPERR_NONE) {
|
|
return;
|
|
}
|
|
|
|
/* ppp_close() was previously called, don't reconnect */
|
|
if (err_code == PPPERR_USER) {
|
|
/* ppp_free(); -- can be called here */
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Try to reconnect in 30 seconds, if you need a modem chatscript you have
|
|
* to do a much better signaling here ;-)
|
|
*/
|
|
ppp_connect(pcb, 30);
|
|
}
|
|
|
|
|
|
/*
|
|
* Creating a new PPPoS session
|
|
* ============================
|
|
*
|
|
* In lwIP, PPPoS is not PPPoSONET, in lwIP PPPoS is PPPoSerial.
|
|
*/
|
|
|
|
#include "netif/ppp/pppos.h"
|
|
|
|
/*
|
|
* PPPoS serial output callback
|
|
*
|
|
* ppp_pcb, PPP control block
|
|
* data, buffer to write to serial port
|
|
* len, length of the data buffer
|
|
* ctx, optional user-provided callback context pointer
|
|
*
|
|
* Return value: len if write succeed
|
|
*/
|
|
static u32_t output_cb(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) {
|
|
return uart_write(UART, data, len);
|
|
}
|
|
|
|
/*
|
|
* Create a new PPPoS interface
|
|
*
|
|
* ppp_netif, netif to use for this PPP link, i.e. PPP IP interface
|
|
* output_cb, PPPoS serial output callback
|
|
* status_cb, PPP status callback, called on PPP status change (up, down, …)
|
|
* ctx_cb, optional user-provided callback context pointer
|
|
*/
|
|
ppp = pppos_create(&ppp_netif,
|
|
output_cb, status_cb, ctx_cb);
|
|
|
|
|
|
/*
|
|
* Creating a new PPPoE session
|
|
* ============================
|
|
*/
|
|
|
|
#include "netif/ppp/pppoe.h"
|
|
|
|
/*
|
|
* Create a new PPPoE interface
|
|
*
|
|
* ppp_netif, netif to use for this PPP link, i.e. PPP IP interface
|
|
* ethif, already existing and setup Ethernet interface to use
|
|
* service_name, PPPoE service name discriminator (not supported yet)
|
|
* concentrator_name, PPPoE concentrator name discriminator (not supported yet)
|
|
* status_cb, PPP status callback, called on PPP status change (up, down, …)
|
|
* ctx_cb, optional user-provided callback context pointer
|
|
*/
|
|
ppp = pppoe_create(&ppp_netif,
|
|
ðif,
|
|
service_name, concentrator_name,
|
|
status_cb, ctx_cb);
|
|
|
|
|
|
/*
|
|
* Creating a new PPPoL2TP session
|
|
* ===============================
|
|
*/
|
|
|
|
#include "netif/ppp/pppol2tp.h"
|
|
|
|
/*
|
|
* Create a new PPPoL2TP interface
|
|
*
|
|
* ppp_netif, netif to use for this PPP link, i.e. PPP IP interface
|
|
* netif, optional already existing and setup output netif, necessary if you
|
|
* want to set this interface as default route to settle the chicken
|
|
* and egg problem with VPN links
|
|
* ipaddr, IP to connect to
|
|
* port, UDP port to connect to (usually 1701)
|
|
* secret, L2TP secret to use
|
|
* secret_len, size in bytes of the L2TP secret
|
|
* status_cb, PPP status callback, called on PPP status change (up, down, …)
|
|
* ctx_cb, optional user-provided callback context pointer
|
|
*/
|
|
ppp = pppol2tp_create(&ppp_netif,
|
|
struct netif *netif, ip_addr_t *ipaddr, u16_t port,
|
|
u8_t *secret, u8_t secret_len,
|
|
ppp_link_status_cb_fn link_status_cb, void *ctx_cb);
|
|
|
|
|
|
/*
|
|
* PPP link configuration
|
|
* ======================
|
|
*/
|
|
|
|
/* Auth configuration, this is pretty self-explanatory */
|
|
ppp_set_auth(ppp, PPPAUTHTYPE_ANY, "login", "password");
|
|
|
|
/* Set this interface as default route */
|
|
ppp_set_default(ppp);
|
|
|
|
|
|
/*
|
|
* Initiate PPP connection
|
|
* =======================
|
|
*/
|
|
|
|
/*
|
|
* Initiate PPP negotiation, without waiting (holdoff=0), can only be called
|
|
* if PPP session is in the dead state (i.e. disconnected).
|
|
*/
|
|
u16_t holdoff = 0;
|
|
ppp_connect(ppp, holdoff);
|
|
|
|
|
|
/*
|
|
* Closing PPP connection
|
|
* ======================
|
|
*/
|
|
|
|
/*
|
|
* Initiate the end of the PPP session, without carrier lost signal
|
|
* (nocarrier=0), meaning a clean shutdown of PPP protocols.
|
|
* You can call this function at anytime.
|
|
*/
|
|
u8_t nocarrier = 0;
|
|
ppp_close(ppp, nocarrier);
|
|
/*
|
|
* Then you must wait your status_cb() to be called, it may takes from a few
|
|
* seconds to several tens of seconds depending on the current PPP state.
|
|
*/
|
|
|
|
/*
|
|
* Freeing a PPP connection
|
|
* ========================
|
|
*/
|
|
|
|
/*
|
|
* Free the PPP control block, can only be called if PPP session is in the
|
|
* dead state (i.e. disconnected). You need to call ppp_close() before.
|
|
*/
|
|
ppp_free(ppp);
|
|
|
|
|
|
|
|
3 PPPoS input path (raw API, IRQ safe API, TCPIP API)
|
|
=====================================================
|
|
|
|
Received data on serial port should be sent to lwIP using the pppos_input()
|
|
function or the pppos_input_sys() function.
|
|
|
|
If NO_SYS is 1 and if PPP_INPROC_IRQ_SAFE is 0 (the default), pppos_input()
|
|
is not IRQ safe and then *MUST* only be called inside your main loop.
|
|
|
|
Whatever the NO_SYS value, if PPP_INPROC_IRQ_SAFE is 1, pppos_input() is IRQ
|
|
safe and can be safely called from an interrupt context, using that is going
|
|
to reduce your need of buffer if pppos_input() is called byte after byte in
|
|
your rx serial interrupt.
|
|
|
|
if NO_SYS is 0, the thread safe way outside an interrupt context is to use
|
|
the pppos_input_tcpip() function to pass input data to the lwIP core thread
|
|
using the TCPIP API. This is thread safe in all cases but you should avoid
|
|
passing data byte after byte because it uses heavy locking (mailbox) and it
|
|
allocates pbuf, better fill them !
|
|
|
|
if NO_SYS is 0 and if PPP_INPROC_IRQ_SAFE is 1, you may also use pppos_input()
|
|
from an RX thread, however pppos_input() is not thread safe by itself. You can
|
|
do that *BUT* you should NEVER call pppos_connect(), pppos_listen() and
|
|
ppp_free() if pppos_input() can still be running, doing this is NOT thread safe
|
|
at all. Using PPP_INPROC_IRQ_SAFE from an RX thread is discouraged unless you
|
|
really know what you are doing, your move ;-)
|
|
|
|
|
|
/*
|
|
* Fonction to call for received data
|
|
*
|
|
* ppp, PPP control block
|
|
* buffer, input buffer
|
|
* buffer_len, buffer length in bytes
|
|
*/
|
|
void pppos_input(ppp, buffer, buffer_len);
|
|
|
|
or
|
|
|
|
void pppos_input_tcpip(ppp, buffer, buffer_len);
|
|
|
|
|
|
4 Thread safe PPP API (PPPAPI)
|
|
==============================
|
|
|
|
There is a thread safe API for all corresponding ppp_* functions, you have to
|
|
enable LWIP_PPP_API in your lwipopts.h file, then see include/lwip/pppapi.h,
|
|
this is actually pretty obvious.
|
|
|
|
|
|
|
|
5 Upgrading from lwIP <= 1.4.x to lwIP >= 1.5.x
|
|
===============================================
|
|
|
|
PPP API was fully reworked between 1.4.x and 1.5.x releases. However porting
|
|
from previous lwIP version is pretty easy:
|
|
|
|
* Previous PPP API used an integer to identify PPP sessions, we are now
|
|
using ppp_pcb* control block, therefore all functions changed from "int ppp"
|
|
to "ppp_pcb *ppp"
|
|
|
|
* struct netif was moved outside the PPP structure, you have to provide a netif
|
|
for PPP interface in pppoX_create() functions
|
|
|
|
* PPP session are not started automatically after you created them anymore,
|
|
you have to call ppp_connect(), this way you can configure the session before
|
|
starting it.
|
|
|
|
* Previous PPP API used CamelCase, we are now using snake_case.
|
|
|
|
* Previous PPP API mixed PPPoS and PPPoE calls, this isn't the case anymore,
|
|
PPPoS functions are now prefixed pppos_ and PPPoE functions are now prefixed
|
|
pppoe_, common functions are now prefixed ppp_.
|
|
|
|
* New PPPERR_ error codes added, check you have all of them in your status
|
|
callback function
|
|
|
|
* Only the following include files should now be used in user application:
|
|
#include "lwip/pppapi.h"
|
|
#include "netif/ppp/pppos.h"
|
|
#include "netif/ppp/pppoe.h"
|
|
#include "netif/ppp/pppol2tp.h"
|
|
|
|
Functions from ppp.h can be used, but you don't need to include this header
|
|
file as it is already included by above header files.
|
|
|
|
* PPP_INPROC_OWNTHREAD was broken by design and was removed, you have to create
|
|
your own serial rx thread
|
|
|
|
* PPP_INPROC_MULTITHREADED option was misnamed and confusing and was renamed
|
|
PPP_INPROC_IRQ_SAFE, please read the "PPPoS input path" documentation above
|
|
because you might have been fooled by that
|
|
|
|
* If you used tcpip_callback_with_block() on ppp_ functions you may wish to use
|
|
the PPPAPI API instead.
|
|
|
|
* ppp_sighup and ppp_close functions were merged using an optional argument
|
|
"nocarrier" on ppp_close.
|
|
|
|
* DNS servers are now only remotely asked if LWIP_DNS is set and we are now
|
|
unconditionally registering them, which is probably the wanted behavior in
|
|
almost all cases. If you need it conditional contact us and we will made it
|
|
conditional.
|
|
|
|
* PPPoS now requires a serial output callback
|