123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529 |
- 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 - Notify phase callback (PPP_NOTIFY_PHASE)
- 6 - 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
- const 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);
- /* OR ppp_listen(pcb); */
- }
- /*
- * 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, const void *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);
- /*
- * Initiate PPP client connection
- * ==============================
- */
- /* Set this interface as default route */
- ppp_set_default(ppp);
- /*
- * Basic PPP client configuration. Can only be set if PPP session is in the
- * dead state (i.e. disconnected). We don't need to provide thread-safe
- * equivalents through PPPAPI because those helpers are only changing
- * structure members while session is inactive for lwIP core. Configuration
- * only need to be done once.
- */
- /* Ask the peer for up to 2 DNS server addresses. */
- ppp_set_usepeerdns(ppp, 1);
- /* Auth configuration, this is pretty self-explanatory */
- ppp_set_auth(ppp, PPPAUTHTYPE_ANY, "login", "password");
- /*
- * 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);
- /*
- * Initiate PPP server listener
- * ============================
- */
- /*
- * Basic PPP server configuration. Can only be set if PPP session is in the
- * dead state (i.e. disconnected). We don't need to provide thread-safe
- * equivalents through PPPAPI because those helpers are only changing
- * structure members while session is inactive for lwIP core. Configuration
- * only need to be done once.
- */
- ip4_addr_t addr;
- /* Set our address */
- IP4_ADDR(&addr, 192,168,0,1);
- ppp_set_ipcp_ouraddr(ppp, &addr);
- /* Set peer(his) address */
- IP4_ADDR(&addr, 192,168,0,2);
- ppp_set_ipcp_hisaddr(ppp, &addr);
- /* Set primary DNS server */
- IP4_ADDR(&addr, 192,168,10,20);
- ppp_set_ipcp_dnsaddr(ppp, 0, &addr);
- /* Set secondary DNS server */
- IP4_ADDR(&addr, 192,168,10,21);
- ppp_set_ipcp_dnsaddr(ppp, 1, &addr);
- /* Auth configuration, this is pretty self-explanatory */
- ppp_set_auth(ppp, PPPAUTHTYPE_ANY, "login", "password");
- /* Require peer to authenticate */
- ppp_set_auth_required(ppp, 1);
- /*
- * Only for PPPoS, the PPP session should be up and waiting for input.
- *
- * Note: for PPPoS, ppp_connect() and ppp_listen() are actually the same thing.
- * The listen call is meant for future support of PPPoE and PPPoL2TP server
- * mode, where we will need to negotiate the incoming PPPoE session or L2TP
- * session before initiating PPP itself. We need this call because there is
- * two passive modes for PPPoS, ppp_set_passive and ppp_set_silent.
- */
- ppp_set_silent(pppos, 1);
- /*
- * Initiate PPP listener (i.e. wait for an incoming connection), can only
- * be called if PPP session is in the dead state (i.e. disconnected).
- */
- ppp_listen(ppp);
- /*
- * 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 ;-)
- /*
- * Function 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 Notify phase callback (PPP_NOTIFY_PHASE)
- ==========================================
- Notify phase callback, enabled using the PPP_NOTIFY_PHASE config option, let
- you configure a callback that is called on each PPP internal state change.
- This is different from the status callback which only warns you about
- up(running) and down(dead) events.
- Notify phase callback can be used, for example, to set a LED pattern depending
- on the current phase of the PPP session. Here is a callback example which
- tries to mimic what we usually see on xDSL modems while they are negotiating
- the link, which should be self-explanatory:
- static void ppp_notify_phase_cb(ppp_pcb *pcb, u8_t phase, void *ctx) {
- switch (phase) {
- /* Session is down (either permanently or briefly) */
- case PPP_PHASE_DEAD:
- led_set(PPP_LED, LED_OFF);
- break;
- /* We are between two sessions */
- case PPP_PHASE_HOLDOFF:
- led_set(PPP_LED, LED_SLOW_BLINK);
- break;
- /* Session just started */
- case PPP_PHASE_INITIALIZE:
- led_set(PPP_LED, LED_FAST_BLINK);
- break;
- /* Session is running */
- case PPP_PHASE_RUNNING:
- led_set(PPP_LED, LED_ON);
- break;
- default:
- break;
- }
- }
- 6 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 if
- ppp_set_usepeerdns() is set to true, they are now automatically registered
- using the dns_setserver() function so you don't need to do that in the PPP
- callback anymore.
- * 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
|