mirror of
https://github.com/lwip-tcpip/lwip.git
synced 2024-08-27 00:22:59 +00:00
425 lines
12 KiB
Plaintext
425 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 >= 2.0.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_tcpip() 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/netif/ppp/pppapi.h, this is actually pretty obvious.
|
|
|
|
|
|
|
|
5 Upgrading from lwIP <= 1.4.x to lwIP >= 2.0.x
|
|
===============================================
|
|
|
|
PPP API was fully reworked between 1.4.x and 2.0.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 "netif/ppp/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 does not use the SIO API anymore, as such it now requires a serial
|
|
output callback in place of sio_write
|
|
|
|
* PPP_MAXIDLEFLAG is now in ms instead of jiffies
|