aboutsummaryrefslogtreecommitdiff
path: root/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif')
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/FILES24
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/etharp.c1531
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ethernet.c314
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ethernetif.c335
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/lowpan6.c1193
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/PPPD_FOLLOWUP473
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/auth.c2510
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ccp.c1740
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/chap-md5.c126
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/chap-new.c677
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/chap_ms.c962
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/demand.c465
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/eap.c2423
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ecp.c191
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/eui64.c56
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/fsm.c799
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ipcp.c2418
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ipv6cp.c1533
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/lcp.c2790
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/magic.c294
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/mppe.c412
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/multilink.c609
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/README22
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/arc4.c101
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/des.c422
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/md4.c281
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/md5.c300
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/sha1.c335
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ppp.c1647
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppapi.c427
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppcrypt.c66
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppoe.c1191
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppol2tp.c1131
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppos.c875
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/upap.c677
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/utils.c959
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/vj.c695
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/slipif.c555
38 files changed, 31559 insertions, 0 deletions
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/FILES b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/FILES
new file mode 100644
index 0000000..a3ff431
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/FILES
@@ -0,0 +1,24 @@
+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.
+
+ethernetif.c
+ An example of how an Ethernet device driver could look. This
+ file can be used as a "skeleton" for developing new Ethernet
+ network device drivers. It uses the etharp.c ARP code.
+
+lowpan6.c
+ A 6LoWPAN implementation as a netif.
+
+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.
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/etharp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/etharp.c
new file mode 100644
index 0000000..4e853ae
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/etharp.c
@@ -0,0 +1,1531 @@
+/**
+ * @file
+ * Address Resolution Protocol module for IP over Ethernet
+ *
+ * Functionally, ARP is divided into two parts. The first maps an IP address
+ * to a physical address when sending a packet, and the second part answers
+ * requests from other machines for our physical address.
+ *
+ * This implementation complies with RFC 826 (Ethernet ARP). It supports
+ * Gratuitious ARP from RFC3220 (IP Mobility Support for IPv4) section 4.6
+ * if an interface calls etharp_gratuitous(our_netif) upon address change.
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * Copyright (c) 2003-2004 Leon Woestenberg <leon.woestenberg@axon.tv>
+ * Copyright (c) 2003-2004 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ARP || LWIP_ETHERNET
+
+#include "lwip/ip_addr.h"
+#include "lwip/def.h"
+#include "lwip/ip.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "netif/etharp.h"
+#include "lwip/ip6.h"
+
+#if PPPOE_SUPPORT
+#include "netif/ppp/pppoe.h"
+#endif /* PPPOE_SUPPORT */
+
+#include <string.h>
+
+const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}};
+const struct eth_addr ethzero = {{0,0,0,0,0,0}};
+
+/** The 24-bit IANA multicast OUI is 01-00-5e: */
+#define LL_MULTICAST_ADDR_0 0x01
+#define LL_MULTICAST_ADDR_1 0x00
+#define LL_MULTICAST_ADDR_2 0x5e
+
+#if LWIP_ARP /* don't build if not configured for use in lwipopts.h */
+
+/** the time an ARP entry stays valid after its last update,
+ * for ARP_TMR_INTERVAL = 1000, this is
+ * (60 * 5) seconds = 5 minutes.
+ */
+#define ARP_MAXAGE 300
+
+/** Re-request a used ARP entry 1 minute before it would expire to prevent
+ * breaking a steadily used connection because the ARP entry timed out. */
+#define ARP_AGE_REREQUEST_USED_UNICAST (ARP_MAXAGE - 30)
+#define ARP_AGE_REREQUEST_USED_BROADCAST (ARP_MAXAGE - 15)
+
+/** the time an ARP entry stays pending after first request,
+ * for ARP_TMR_INTERVAL = 1000, this is
+ * 10 seconds.
+ *
+ * @internal Keep this number at least 2, otherwise it might
+ * run out instantly if the timeout occurs directly after a request.
+ */
+#define ARP_MAXPENDING 5
+
+#define HWTYPE_ETHERNET 1
+
+enum etharp_state {
+ ETHARP_STATE_EMPTY = 0,
+ ETHARP_STATE_PENDING,
+ ETHARP_STATE_STABLE,
+ ETHARP_STATE_STABLE_REREQUESTING_1,
+ ETHARP_STATE_STABLE_REREQUESTING_2
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ ,ETHARP_STATE_STATIC
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+};
+
+struct etharp_entry {
+#if ARP_QUEUEING
+ /** Pointer to queue of pending outgoing packets on this ARP entry. */
+ struct etharp_q_entry *q;
+#else /* ARP_QUEUEING */
+ /** Pointer to a single pending outgoing packet on this ARP entry. */
+ struct pbuf *q;
+#endif /* ARP_QUEUEING */
+ ip_addr_t ipaddr;
+ struct netif *netif;
+ struct eth_addr ethaddr;
+ u16_t ctime;
+ u8_t state;
+};
+
+static struct etharp_entry arp_table[ARP_TABLE_SIZE];
+
+#if !LWIP_NETIF_HWADDRHINT
+static u8_t etharp_cached_entry;
+#endif /* !LWIP_NETIF_HWADDRHINT */
+
+/** Try hard to create a new entry - we want the IP address to appear in
+ the cache (even if this means removing an active entry or so). */
+#define ETHARP_FLAG_TRY_HARD 1
+#define ETHARP_FLAG_FIND_ONLY 2
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+#define ETHARP_FLAG_STATIC_ENTRY 4
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+
+#if LWIP_NETIF_HWADDRHINT
+#define ETHARP_SET_HINT(netif, hint) if (((netif) != NULL) && ((netif)->addr_hint != NULL)) \
+ *((netif)->addr_hint) = (hint);
+#else /* LWIP_NETIF_HWADDRHINT */
+#define ETHARP_SET_HINT(netif, hint) (etharp_cached_entry = (hint))
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+
+/* Some checks, instead of etharp_init(): */
+#if (LWIP_ARP && (ARP_TABLE_SIZE > 0x7f))
+ #error "ARP_TABLE_SIZE must fit in an s8_t, you have to reduce it in your lwipopts.h"
+#endif
+
+
+static err_t etharp_request_dst(struct netif *netif, const ip_addr_t *ipaddr, const struct eth_addr* hw_dst_addr);
+
+
+#if ARP_QUEUEING
+/**
+ * Free a complete queue of etharp entries
+ *
+ * @param q a qeueue of etharp_q_entry's to free
+ */
+static void
+free_etharp_q(struct etharp_q_entry *q)
+{
+ struct etharp_q_entry *r;
+ LWIP_ASSERT("q != NULL", q != NULL);
+ LWIP_ASSERT("q->p != NULL", q->p != NULL);
+ while (q) {
+ r = q;
+ q = q->next;
+ LWIP_ASSERT("r->p != NULL", (r->p != NULL));
+ pbuf_free(r->p);
+ memp_free(MEMP_ARP_QUEUE, r);
+ }
+}
+#else /* ARP_QUEUEING */
+
+/** Compatibility define: free the queued pbuf */
+#define free_etharp_q(q) pbuf_free(q)
+
+#endif /* ARP_QUEUEING */
+
+/** Clean up ARP table entries */
+static void
+etharp_free_entry(int i)
+{
+ /* remove from SNMP ARP index tree */
+ snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr);
+ /* and empty packet queue */
+ if (arp_table[i].q != NULL) {
+ /* remove all queued packets */
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_free_entry: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].q)));
+ free_etharp_q(arp_table[i].q);
+ arp_table[i].q = NULL;
+ }
+ /* recycle entry for re-use */
+ arp_table[i].state = ETHARP_STATE_EMPTY;
+#ifdef LWIP_DEBUG
+ /* for debugging, clean out the complete entry */
+ arp_table[i].ctime = 0;
+ arp_table[i].netif = NULL;
+ ip_addr_set_zero(&arp_table[i].ipaddr);
+ arp_table[i].ethaddr = ethzero;
+#endif /* LWIP_DEBUG */
+}
+
+/**
+ * Clears expired entries in the ARP table.
+ *
+ * This function should be called every ARP_TMR_INTERVAL milliseconds (1 second),
+ * in order to expire entries in the ARP table.
+ */
+void
+etharp_tmr(void)
+{
+ u8_t i;
+
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer\n"));
+ /* remove expired entries from the ARP table */
+ for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+ u8_t state = arp_table[i].state;
+ if (state != ETHARP_STATE_EMPTY
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ && (state != ETHARP_STATE_STATIC)
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+ ) {
+ arp_table[i].ctime++;
+ if ((arp_table[i].ctime >= ARP_MAXAGE) ||
+ ((arp_table[i].state == ETHARP_STATE_PENDING) &&
+ (arp_table[i].ctime >= ARP_MAXPENDING))) {
+ /* pending or stable entry has become old! */
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired %s entry %"U16_F".\n",
+ arp_table[i].state >= ETHARP_STATE_STABLE ? "stable" : "pending", (u16_t)i));
+ /* clean up entries that have just been expired */
+ etharp_free_entry(i);
+ } else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING_1) {
+ /* Don't send more than one request every 2 seconds. */
+ arp_table[i].state = ETHARP_STATE_STABLE_REREQUESTING_2;
+ } else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING_2) {
+ /* Reset state to stable, so that the next transmitted packet will
+ re-send an ARP request. */
+ arp_table[i].state = ETHARP_STATE_STABLE;
+ }
+ /* still pending entry? (not expired) */
+ else if (arp_table[i].state == ETHARP_STATE_PENDING) {
+ /* resend an ARP query here? */
+ if (etharp_request(arp_table[i].netif, &arp_table[i].ipaddr) != ERR_OK) {
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Search the ARP table for a matching or new entry.
+ *
+ * If an IP address is given, return a pending or stable ARP entry that matches
+ * the address. If no match is found, create a new entry with this address set,
+ * but in state ETHARP_EMPTY. The caller must check and possibly change the
+ * state of the returned entry.
+ *
+ * If ipaddr is NULL, return a initialized new entry in state ETHARP_EMPTY.
+ *
+ * In all cases, attempt to create new entries from an empty entry. If no
+ * empty entries are available and ETHARP_FLAG_TRY_HARD flag is set, recycle
+ * old entries. Heuristic choose the least important entry for recycling.
+ *
+ * @param ipaddr IP address to find in ARP cache, or to add if not found.
+ * @param flags @see definition of ETHARP_FLAG_*
+ * @param netif netif related to this address (used for NETIF_HWADDRHINT)
+ *
+ * @return The ARP entry index that matched or is created, ERR_MEM if no
+ * entry is found or could be recycled.
+ */
+static s8_t
+etharp_find_entry(const ip_addr_t *ipaddr, u8_t flags, struct netif* netif)
+{
+ s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE;
+ s8_t empty = ARP_TABLE_SIZE;
+ u8_t i = 0;
+ /* oldest entry with packets on queue */
+ s8_t old_queue = ARP_TABLE_SIZE;
+ /* its age */
+ u16_t age_queue = 0, age_pending = 0, age_stable = 0;
+
+ LWIP_UNUSED_ARG(netif);
+
+ /**
+ * a) do a search through the cache, remember candidates
+ * b) select candidate entry
+ * c) create new entry
+ */
+
+ /* a) in a single search sweep, do all of this
+ * 1) remember the first empty entry (if any)
+ * 2) remember the oldest stable entry (if any)
+ * 3) remember the oldest pending entry without queued packets (if any)
+ * 4) remember the oldest pending entry with queued packets (if any)
+ * 5) search for a matching IP entry, either pending or stable
+ * until 5 matches, or all entries are searched for.
+ */
+
+ for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+ u8_t state = arp_table[i].state;
+ /* no empty entry found yet and now we do find one? */
+ if ((empty == ARP_TABLE_SIZE) && (state == ETHARP_STATE_EMPTY)) {
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_find_entry: found empty entry %"U16_F"\n", (u16_t)i));
+ /* remember first empty entry */
+ empty = i;
+ } else if (state != ETHARP_STATE_EMPTY) {
+ LWIP_ASSERT("state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE",
+ state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE);
+ /* if given, does IP address match IP address in ARP entry? */
+ if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)
+#if ETHARP_TABLE_MATCH_NETIF
+ && ((netif == NULL) || (netif == arp_table[i].netif))
+#endif /* ETHARP_TABLE_MATCH_NETIF */
+ ) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: found matching entry %"U16_F"\n", (u16_t)i));
+ /* found exact IP address match, simply bail out */
+ return i;
+ }
+ /* pending entry? */
+ if (state == ETHARP_STATE_PENDING) {
+ /* pending with queued packets? */
+ if (arp_table[i].q != NULL) {
+ if (arp_table[i].ctime >= age_queue) {
+ old_queue = i;
+ age_queue = arp_table[i].ctime;
+ }
+ } else
+ /* pending without queued packets? */
+ {
+ if (arp_table[i].ctime >= age_pending) {
+ old_pending = i;
+ age_pending = arp_table[i].ctime;
+ }
+ }
+ /* stable entry? */
+ } else if (state >= ETHARP_STATE_STABLE) {
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ /* don't record old_stable for static entries since they never expire */
+ if (state < ETHARP_STATE_STATIC)
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+ {
+ /* remember entry with oldest stable entry in oldest, its age in maxtime */
+ if (arp_table[i].ctime >= age_stable) {
+ old_stable = i;
+ age_stable = arp_table[i].ctime;
+ }
+ }
+ }
+ }
+ }
+ /* { we have no match } => try to create a new entry */
+
+ /* don't create new entry, only search? */
+ if (((flags & ETHARP_FLAG_FIND_ONLY) != 0) ||
+ /* or no empty entry found and not allowed to recycle? */
+ ((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_FLAG_TRY_HARD) == 0))) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty entry found and not allowed to recycle\n"));
+ return (s8_t)ERR_MEM;
+ }
+
+ /* b) choose the least destructive entry to recycle:
+ * 1) empty entry
+ * 2) oldest stable entry
+ * 3) oldest pending entry without queued packets
+ * 4) oldest pending entry with queued packets
+ *
+ * { ETHARP_FLAG_TRY_HARD is set at this point }
+ */
+
+ /* 1) empty entry available? */
+ if (empty < ARP_TABLE_SIZE) {
+ i = empty;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting empty entry %"U16_F"\n", (u16_t)i));
+ } else {
+ /* 2) found recyclable stable entry? */
+ if (old_stable < ARP_TABLE_SIZE) {
+ /* recycle oldest stable*/
+ i = old_stable;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i));
+ /* no queued packets should exist on stable entries */
+ LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL);
+ /* 3) found recyclable pending entry without queued packets? */
+ } else if (old_pending < ARP_TABLE_SIZE) {
+ /* recycle oldest pending */
+ i = old_pending;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i));
+ /* 4) found recyclable pending entry with queued packets? */
+ } else if (old_queue < ARP_TABLE_SIZE) {
+ /* recycle oldest pending (queued packets are free in etharp_free_entry) */
+ i = old_queue;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q)));
+ /* no empty or recyclable entries found */
+ } else {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty or recyclable entries found\n"));
+ return (s8_t)ERR_MEM;
+ }
+
+ /* { empty or recyclable entry found } */
+ LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
+ etharp_free_entry(i);
+ }
+
+ LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
+ LWIP_ASSERT("arp_table[i].state == ETHARP_STATE_EMPTY",
+ arp_table[i].state == ETHARP_STATE_EMPTY);
+
+ /* IP address given? */
+ if (ipaddr != NULL) {
+ /* set IP address */
+ ip_addr_copy(arp_table[i].ipaddr, *ipaddr);
+ }
+ arp_table[i].ctime = 0;
+#if ETHARP_TABLE_MATCH_NETIF
+ arp_table[i].netif = netif;
+#endif /* ETHARP_TABLE_MATCH_NETIF*/
+ return (err_t)i;
+}
+
+/**
+ * Send an IP packet on the network using netif->linkoutput
+ * The ethernet header is filled in before sending.
+ *
+ * @params netif the lwIP network interface on which to send the packet
+ * @params p the packet to send, p->payload pointing to the (uninitialized) ethernet header
+ * @params src the source MAC address to be copied into the ethernet header
+ * @params dst the destination MAC address to be copied into the ethernet header
+ * @return ERR_OK if the packet was sent, any other err_t on failure
+ */
+static err_t
+etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst)
+{
+ struct eth_hdr *ethhdr = (struct eth_hdr *)p->payload;
+#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
+ struct eth_vlan_hdr *vlanhdr;
+#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
+
+ LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
+ (netif->hwaddr_len == ETHARP_HWADDR_LEN));
+#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
+ ethhdr->type = PP_HTONS(ETHTYPE_VLAN);
+ vlanhdr = (struct eth_vlan_hdr*)(((u8_t*)ethhdr) + SIZEOF_ETH_HDR);
+ vlanhdr->prio_vid = 0;
+ vlanhdr->tpid = PP_HTONS(ETHTYPE_IP);
+ if (!LWIP_HOOK_VLAN_SET(netif, ethhdr, vlanhdr)) {
+ /* packet shall not contain VLAN header, so hide it and set correct ethertype */
+ pbuf_header(p, -SIZEOF_VLAN_HDR);
+ ethhdr = (struct eth_hdr *)p->payload;
+#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
+ ethhdr->type = PP_HTONS(ETHTYPE_IP);
+#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
+ }
+#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
+ ETHADDR32_COPY(&ethhdr->dest, dst);
+ ETHADDR16_COPY(&ethhdr->src, src);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_send_ip: sending packet %p\n", (void *)p));
+ /* send the packet */
+ return netif->linkoutput(netif, p);
+}
+
+/**
+ * Update (or insert) a IP/MAC address pair in the ARP cache.
+ *
+ * If a pending entry is resolved, any queued packets will be sent
+ * at this point.
+ *
+ * @param netif netif related to this entry (used for NETIF_ADDRHINT)
+ * @param ipaddr IP address of the inserted ARP entry.
+ * @param ethaddr Ethernet address of the inserted ARP entry.
+ * @param flags @see definition of ETHARP_FLAG_*
+ *
+ * @return
+ * - ERR_OK Successfully updated ARP cache.
+ * - ERR_MEM If we could not add a new ARP entry when ETHARP_FLAG_TRY_HARD was set.
+ * - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
+ *
+ * @see pbuf_free()
+ */
+static err_t
+etharp_update_arp_entry(struct netif *netif, const ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags)
+{
+ s8_t i;
+ LWIP_ASSERT("netif->hwaddr_len == ETHARP_HWADDR_LEN", netif->hwaddr_len == ETHARP_HWADDR_LEN);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
+ ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr),
+ ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2],
+ ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5]));
+ /* non-unicast address? */
+ if (ip_addr_isany(ipaddr) ||
+ ip_addr_isbroadcast(ipaddr, netif) ||
+ ip_addr_ismulticast(ipaddr)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: will not add non-unicast IP address to ARP cache\n"));
+ return ERR_ARG;
+ }
+ /* find or create ARP entry */
+ i = etharp_find_entry(ipaddr, flags, netif);
+ /* bail out if no entry could be found */
+ if (i < 0) {
+ return (err_t)i;
+ }
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ if (flags & ETHARP_FLAG_STATIC_ENTRY) {
+ /* record static type */
+ arp_table[i].state = ETHARP_STATE_STATIC;
+ } else
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+ {
+ /* mark it stable */
+ arp_table[i].state = ETHARP_STATE_STABLE;
+ }
+
+ /* record network interface */
+ arp_table[i].netif = netif;
+ /* insert in SNMP ARP index tree */
+ snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr);
+
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i));
+ /* update address */
+ ETHADDR32_COPY(&arp_table[i].ethaddr, ethaddr);
+ /* reset time stamp */
+ arp_table[i].ctime = 0;
+ /* this is where we will send out queued packets! */
+#if ARP_QUEUEING
+ while (arp_table[i].q != NULL) {
+ struct pbuf *p;
+ /* remember remainder of queue */
+ struct etharp_q_entry *q = arp_table[i].q;
+ /* pop first item off the queue */
+ arp_table[i].q = q->next;
+ /* get the packet pointer */
+ p = q->p;
+ /* now queue entry can be freed */
+ memp_free(MEMP_ARP_QUEUE, q);
+#else /* ARP_QUEUEING */
+ if (arp_table[i].q != NULL) {
+ struct pbuf *p = arp_table[i].q;
+ arp_table[i].q = NULL;
+#endif /* ARP_QUEUEING */
+ /* send the queued IP packet */
+ etharp_send_ip(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr);
+ /* free the queued IP packet */
+ pbuf_free(p);
+ }
+ return ERR_OK;
+}
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+/** Add a new static entry to the ARP table. If an entry exists for the
+ * specified IP address, this entry is overwritten.
+ * If packets are queued for the specified IP address, they are sent out.
+ *
+ * @param ipaddr IP address for the new static entry
+ * @param ethaddr ethernet address for the new static entry
+ * @return @see return values of etharp_add_static_entry
+ */
+err_t
+etharp_add_static_entry(const ip_addr_t *ipaddr, struct eth_addr *ethaddr)
+{
+ struct netif *netif;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_add_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
+ ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr),
+ ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2],
+ ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5]));
+
+ netif = ip_route(ipaddr);
+ if (netif == NULL) {
+ return ERR_RTE;
+ }
+
+ return etharp_update_arp_entry(netif, ipaddr, ethaddr, ETHARP_FLAG_TRY_HARD | ETHARP_FLAG_STATIC_ENTRY);
+}
+
+/** Remove a static entry from the ARP table previously added with a call to
+ * etharp_add_static_entry.
+ *
+ * @param ipaddr IP address of the static entry to remove
+ * @return ERR_OK: entry removed
+ * ERR_MEM: entry wasn't found
+ * ERR_ARG: entry wasn't a static entry but a dynamic one
+ */
+err_t
+etharp_remove_static_entry(const ip_addr_t *ipaddr)
+{
+ s8_t i;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_remove_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
+
+ /* find or create ARP entry */
+ i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY, NULL);
+ /* bail out if no entry could be found */
+ if (i < 0) {
+ return (err_t)i;
+ }
+
+ if (arp_table[i].state != ETHARP_STATE_STATIC) {
+ /* entry wasn't a static entry, cannot remove it */
+ return ERR_ARG;
+ }
+ /* entry found, free it */
+ etharp_free_entry(i);
+ return ERR_OK;
+}
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+
+/**
+ * Remove all ARP table entries of the specified netif.
+ *
+ * @param netif points to a network interface
+ */
+void etharp_cleanup_netif(struct netif *netif)
+{
+ u8_t i;
+
+ for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+ u8_t state = arp_table[i].state;
+ if ((state != ETHARP_STATE_EMPTY) && (arp_table[i].netif == netif)) {
+ etharp_free_entry(i);
+ }
+ }
+}
+
+/**
+ * Finds (stable) ethernet/IP address pair from ARP table
+ * using interface and IP address index.
+ * @note the addresses in the ARP table are in network order!
+ *
+ * @param netif points to interface index
+ * @param ipaddr points to the (network order) IP address index
+ * @param eth_ret points to return pointer
+ * @param ip_ret points to return pointer
+ * @return table index if found, -1 otherwise
+ */
+s8_t
+etharp_find_addr(struct netif *netif, const ip_addr_t *ipaddr,
+ struct eth_addr **eth_ret, const ip_addr_t **ip_ret)
+{
+ s8_t i;
+
+ LWIP_ASSERT("eth_ret != NULL && ip_ret != NULL",
+ eth_ret != NULL && ip_ret != NULL);
+
+ LWIP_UNUSED_ARG(netif);
+
+ i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY, netif);
+ if((i >= 0) && (arp_table[i].state >= ETHARP_STATE_STABLE)) {
+ *eth_ret = &arp_table[i].ethaddr;
+ *ip_ret = &arp_table[i].ipaddr;
+ return i;
+ }
+ return -1;
+}
+
+#if ETHARP_TRUST_IP_MAC
+/**
+ * Updates the ARP table using the given IP packet.
+ *
+ * Uses the incoming IP packet's source address to update the
+ * ARP cache for the local network. The function does not alter
+ * or free the packet. This function must be called before the
+ * packet p is passed to the IP layer.
+ *
+ * @param netif The lwIP network interface on which the IP packet pbuf arrived.
+ * @param p The IP packet that arrived on netif.
+ *
+ * @return NULL
+ *
+ * @see pbuf_free()
+ */
+static void
+etharp_ip_input(struct netif *netif, struct pbuf *p)
+{
+ struct eth_hdr *ethhdr;
+ struct ip_hdr *iphdr;
+ ip_addr_t iphdr_src;
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+
+ /* Only insert an entry if the source IP address of the
+ incoming IP packet comes from a host on the local network. */
+ ethhdr = (struct eth_hdr *)p->payload;
+ iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
+#if ETHARP_SUPPORT_VLAN
+ if (ethhdr->type == PP_HTONS(ETHTYPE_VLAN)) {
+ iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR);
+ }
+#endif /* ETHARP_SUPPORT_VLAN */
+
+ ip_addr_copy(iphdr_src, iphdr->src);
+
+ /* source is not on the local network? */
+ if (!ip_addr_netcmp(&iphdr_src, &(netif->ip_addr), &(netif->netmask))) {
+ /* do nothing */
+ return;
+ }
+
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_ip_input: updating ETHARP table.\n"));
+ /* update the source IP address in the cache, if present */
+ /* @todo We could use ETHARP_FLAG_TRY_HARD if we think we are going to talk
+ * back soon (for example, if the destination IP address is ours. */
+ etharp_update_arp_entry(netif, &iphdr_src, &(ethhdr->src), ETHARP_FLAG_FIND_ONLY);
+}
+#endif /* ETHARP_TRUST_IP_MAC */
+
+/**
+ * Responds to ARP requests to us. Upon ARP replies to us, add entry to cache
+ * send out queued IP packets. Updates cache with snooped address pairs.
+ *
+ * Should be called for incoming ARP packets. The pbuf in the argument
+ * is freed by this function.
+ *
+ * @param netif The lwIP network interface on which the ARP packet pbuf arrived.
+ * @param ethaddr Ethernet address of netif.
+ * @param p The ARP packet that arrived on netif. Is freed by this function.
+ *
+ * @return NULL
+ *
+ * @see pbuf_free()
+ */
+static void
+etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
+{
+ struct etharp_hdr *hdr;
+ struct eth_hdr *ethhdr;
+ /* these are aligned properly, whereas the ARP header fields might not be */
+ ip_addr_t sipaddr, dipaddr;
+ u8_t for_us;
+#if LWIP_AUTOIP
+ const u8_t * ethdst_hwaddr;
+#endif /* LWIP_AUTOIP */
+
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+
+ /* drop short ARP packets: we have to check for p->len instead of p->tot_len here
+ since a struct etharp_hdr is pointed to p->payload, so it musn't be chained! */
+ if (p->len < SIZEOF_ETHARP_PACKET) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("etharp_arp_input: packet dropped, too short (%"S16_F"/%"S16_F")\n", p->tot_len,
+ (s16_t)SIZEOF_ETHARP_PACKET));
+ ETHARP_STATS_INC(etharp.lenerr);
+ ETHARP_STATS_INC(etharp.drop);
+ pbuf_free(p);
+ return;
+ }
+
+ ethhdr = (struct eth_hdr *)p->payload;
+ hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
+#if ETHARP_SUPPORT_VLAN
+ if (ethhdr->type == PP_HTONS(ETHTYPE_VLAN)) {
+ hdr = (struct etharp_hdr *)(((u8_t*)ethhdr) + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR);
+ }
+#endif /* ETHARP_SUPPORT_VLAN */
+
+ /* RFC 826 "Packet Reception": */
+ if ((hdr->hwtype != PP_HTONS(HWTYPE_ETHERNET)) ||
+ (hdr->hwlen != ETHARP_HWADDR_LEN) ||
+ (hdr->protolen != sizeof(ip_addr_t)) ||
+ (hdr->proto != PP_HTONS(ETHTYPE_IP))) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("etharp_arp_input: packet dropped, wrong hw type, hwlen, proto, protolen or ethernet type (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n",
+ hdr->hwtype, hdr->hwlen, hdr->proto, hdr->protolen));
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ pbuf_free(p);
+ return;
+ }
+ ETHARP_STATS_INC(etharp.recv);
+
+#if LWIP_AUTOIP
+ /* We have to check if a host already has configured our random
+ * created link local address and continuously check if there is
+ * a host with this IP-address so we can detect collisions */
+ autoip_arp_reply(netif, hdr);
+#endif /* LWIP_AUTOIP */
+
+ /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
+ * structure packing (not using structure copy which breaks strict-aliasing rules). */
+ IPADDR2_COPY(&sipaddr, &hdr->sipaddr);
+ IPADDR2_COPY(&dipaddr, &hdr->dipaddr);
+
+ /* this interface is not configured? */
+ if (ip_addr_isany(&netif->ip_addr)) {
+ for_us = 0;
+ } else {
+ /* ARP packet directed to us? */
+ for_us = (u8_t)ip_addr_cmp(&dipaddr, &(netif->ip_addr));
+ }
+
+ /* ARP message directed to us?
+ -> add IP address in ARP cache; assume requester wants to talk to us,
+ can result in directly sending the queued packets for this host.
+ ARP message not directed to us?
+ -> update the source IP address in the cache, if present */
+ etharp_update_arp_entry(netif, &sipaddr, &(hdr->shwaddr),
+ for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY);
+
+ /* now act on the message itself */
+ switch (hdr->opcode) {
+ /* ARP request? */
+ case PP_HTONS(ARP_REQUEST):
+ /* ARP request. If it asked for our address, we send out a
+ * reply. In any case, we time-stamp any existing ARP entry,
+ * and possibly send out an IP packet that was queued on it. */
+
+ LWIP_DEBUGF (ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP request\n"));
+ /* ARP request for our address? */
+ if (for_us) {
+
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: replying to ARP request for our IP address\n"));
+ /* Re-use pbuf to send ARP reply.
+ Since we are re-using an existing pbuf, we can't call etharp_raw since
+ that would allocate a new pbuf. */
+ hdr->opcode = htons(ARP_REPLY);
+
+ IPADDR2_COPY(&hdr->dipaddr, &hdr->sipaddr);
+ IPADDR2_COPY(&hdr->sipaddr, &netif->ip_addr);
+
+ LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
+ (netif->hwaddr_len == ETHARP_HWADDR_LEN));
+#if LWIP_AUTOIP
+ /* If we are using Link-Local, all ARP packets that contain a Link-Local
+ * 'sender IP address' MUST be sent using link-layer broadcast instead of
+ * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */
+ ethdst_hwaddr = ip_addr_islinklocal(&netif->ip_addr) ? (u8_t*)(ethbroadcast.addr) : hdr->shwaddr.addr;
+#endif /* LWIP_AUTOIP */
+
+ ETHADDR16_COPY(&hdr->dhwaddr, &hdr->shwaddr);
+#if LWIP_AUTOIP
+ ETHADDR16_COPY(&ethhdr->dest, ethdst_hwaddr);
+#else /* LWIP_AUTOIP */
+ ETHADDR16_COPY(&ethhdr->dest, &hdr->shwaddr);
+#endif /* LWIP_AUTOIP */
+ ETHADDR16_COPY(&hdr->shwaddr, ethaddr);
+ ETHADDR16_COPY(&ethhdr->src, ethaddr);
+
+ /* hwtype, hwaddr_len, proto, protolen and the type in the ethernet header
+ are already correct, we tested that before */
+
+ /* return ARP reply */
+ netif->linkoutput(netif, p);
+ /* we are not configured? */
+ } else if (ip_addr_isany(&netif->ip_addr)) {
+ /* { for_us == 0 and netif->ip_addr.addr == 0 } */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: we are unconfigured, ARP request ignored.\n"));
+ /* request was not directed to us */
+ } else {
+ /* { for_us == 0 and netif->ip_addr.addr != 0 } */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP request was not for us.\n"));
+ }
+ break;
+ case PP_HTONS(ARP_REPLY):
+ /* ARP reply. We already updated the ARP cache earlier. */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP reply\n"));
+#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK)
+ /* DHCP wants to know about ARP replies from any host with an
+ * IP address also offered to us by the DHCP server. We do not
+ * want to take a duplicate IP address on a single network.
+ * @todo How should we handle redundant (fail-over) interfaces? */
+ dhcp_arp_reply(netif, &sipaddr);
+#endif /* (LWIP_DHCP && DHCP_DOES_ARP_CHECK) */
+ break;
+ default:
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP unknown opcode type %"S16_F"\n", htons(hdr->opcode)));
+ ETHARP_STATS_INC(etharp.err);
+ break;
+ }
+ /* free ARP packet */
+ pbuf_free(p);
+}
+
+/** Just a small helper function that sends a pbuf to an ethernet address
+ * in the arp_table specified by the index 'arp_idx'.
+ */
+static err_t
+etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, u8_t arp_idx)
+{
+ LWIP_ASSERT("arp_table[arp_idx].state >= ETHARP_STATE_STABLE",
+ arp_table[arp_idx].state >= ETHARP_STATE_STABLE);
+ /* if arp table entry is about to expire: re-request it,
+ but only if its state is ETHARP_STATE_STABLE to prevent flooding the
+ network with ARP requests if this address is used frequently. */
+ if (arp_table[arp_idx].state == ETHARP_STATE_STABLE) {
+ if (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED_BROADCAST) {
+ /* issue a standard request using broadcast */
+ if (etharp_request(netif, &arp_table[arp_idx].ipaddr) == ERR_OK) {
+ arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING_1;
+ }
+ } else if (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED_UNICAST) {
+ /* issue a unicast request (for 15 seconds) to prevent unnecessary broadcast */
+ if (etharp_request_dst(netif, &arp_table[arp_idx].ipaddr, &arp_table[arp_idx].ethaddr) == ERR_OK) {
+ arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING_1;
+ }
+ }
+ }
+
+ return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr),
+ &arp_table[arp_idx].ethaddr);
+}
+
+/**
+ * Resolve and fill-in Ethernet address header for outgoing IP packet.
+ *
+ * For IP multicast and broadcast, corresponding Ethernet addresses
+ * are selected and the packet is transmitted on the link.
+ *
+ * For unicast addresses, the packet is submitted to etharp_query(). In
+ * case the IP address is outside the local network, the IP address of
+ * the gateway is used.
+ *
+ * @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 ipaddr The IP address of the packet destination.
+ *
+ * @return
+ * - ERR_RTE No route to destination (no gateway to external networks),
+ * or the return type of either etharp_query() or etharp_send_ip().
+ */
+err_t
+etharp_output(struct netif *netif, struct pbuf *q, const ip_addr_t *ipaddr)
+{
+ struct eth_addr *dest;
+ struct eth_addr mcastaddr;
+ const ip_addr_t *dst_addr = ipaddr;
+
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+ LWIP_ASSERT("q != NULL", q != NULL);
+ LWIP_ASSERT("ipaddr != NULL", ipaddr != NULL);
+
+ /* make room for Ethernet header - should not fail */
+#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
+ if (pbuf_header(q, sizeof(struct eth_hdr) + SIZEOF_VLAN_HDR) != 0) {
+#else /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
+ if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) {
+#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
+ /* bail out */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("etharp_output: could not allocate room for header.\n"));
+ LINK_STATS_INC(link.lenerr);
+ return ERR_BUF;
+ }
+
+ /* Determine on destination hardware address. Broadcasts and multicasts
+ * are special, other IP addresses are looked up in the ARP table. */
+
+ /* broadcast destination IP address? */
+ if (ip_addr_isbroadcast(ipaddr, netif)) {
+ /* broadcast on Ethernet also */
+ dest = (struct eth_addr *)&ethbroadcast;
+ /* multicast destination IP address? */
+ } else if (ip_addr_ismulticast(ipaddr)) {
+ /* Hash IP multicast address to MAC address.*/
+ mcastaddr.addr[0] = LL_MULTICAST_ADDR_0;
+ mcastaddr.addr[1] = LL_MULTICAST_ADDR_1;
+ mcastaddr.addr[2] = LL_MULTICAST_ADDR_2;
+ mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f;
+ mcastaddr.addr[4] = ip4_addr3(ipaddr);
+ mcastaddr.addr[5] = ip4_addr4(ipaddr);
+ /* destination Ethernet address is multicast */
+ dest = &mcastaddr;
+ /* unicast destination IP address? */
+ } else {
+ s8_t i;
+ /* outside local network? if so, this can neither be a global broadcast nor
+ a subnet broadcast. */
+ if (!ip_addr_netcmp(ipaddr, &(netif->ip_addr), &(netif->netmask)) &&
+ !ip_addr_islinklocal(ipaddr)) {
+#if LWIP_AUTOIP
+ struct ip_hdr *iphdr = (struct ip_hdr*)((u8_t*)q->payload +
+#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
+ SIZEOF_VLAN_HDR +
+#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
+ sizeof(struct eth_hdr));
+ /* According to RFC 3297, chapter 2.6.2 (Forwarding Rules), a packet with
+ a link-local source address must always be "directly to its destination
+ on the same physical link. The host MUST NOT send the packet to any
+ router for forwarding". */
+ if (!ip_addr_islinklocal(&iphdr->src))
+#endif /* LWIP_AUTOIP */
+ {
+#ifdef LWIP_HOOK_ETHARP_GET_GW
+ /* For advanced routing, a single default gateway might not be enough, so get
+ the IP address of the gateway to handle the current destination address. */
+ dst_addr = LWIP_HOOK_ETHARP_GET_GW(netif, ipaddr);
+ if(dst_addr == NULL)
+#endif /* LWIP_HOOK_ETHARP_GET_GW */
+ {
+ /* interface has default gateway? */
+ if (!ip_addr_isany(&netif->gw)) {
+ /* send to hardware address of default gateway IP address */
+ dst_addr = &(netif->gw);
+ /* no default gateway available */
+ } else {
+ /* no route to destination error (default gateway missing) */
+ return ERR_RTE;
+ }
+ }
+ }
+ }
+#if LWIP_NETIF_HWADDRHINT
+ if (netif->addr_hint != NULL) {
+ /* per-pcb cached entry was given */
+ u8_t etharp_cached_entry = *(netif->addr_hint);
+ if (etharp_cached_entry < ARP_TABLE_SIZE) {
+#endif /* LWIP_NETIF_HWADDRHINT */
+ if ((arp_table[etharp_cached_entry].state >= ETHARP_STATE_STABLE) &&
+ (ip_addr_cmp(dst_addr, &arp_table[etharp_cached_entry].ipaddr))) {
+ /* the per-pcb-cached entry is stable and the right one! */
+ ETHARP_STATS_INC(etharp.cachehit);
+ return etharp_output_to_arp_index(netif, q, etharp_cached_entry);
+ }
+#if LWIP_NETIF_HWADDRHINT
+ }
+ }
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+ /* find stable entry: do this here since this is a critical path for
+ throughput and etharp_find_entry() is kind of slow */
+ for (i = 0; i < ARP_TABLE_SIZE; i++) {
+ if ((arp_table[i].state >= ETHARP_STATE_STABLE) &&
+ (ip_addr_cmp(dst_addr, &arp_table[i].ipaddr))) {
+ /* found an existing, stable entry */
+ ETHARP_SET_HINT(netif, i);
+ return etharp_output_to_arp_index(netif, q, i);
+ }
+ }
+ /* no stable entry found, use the (slower) query function:
+ queue on destination Ethernet address belonging to ipaddr */
+ return etharp_query(netif, dst_addr, q);
+ }
+
+ /* continuation for multicast/broadcast destinations */
+ /* obtain source Ethernet address of the given interface */
+ /* send packet directly on the link */
+ return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), dest);
+}
+
+/**
+ * Send an ARP request for the given IP address and/or queue a packet.
+ *
+ * If the IP address was not yet in the cache, a pending ARP cache entry
+ * is added and an ARP request is sent for the given address. The packet
+ * is queued on this entry.
+ *
+ * If the IP address was already pending in the cache, a new ARP request
+ * is sent for the given address. The packet is queued on this entry.
+ *
+ * If the IP address was already stable in the cache, and a packet is
+ * given, it is directly sent and no ARP request is sent out.
+ *
+ * If the IP address was already stable in the cache, and no packet is
+ * given, an ARP request is sent out.
+ *
+ * @param netif The lwIP network interface on which ipaddr
+ * must be queried for.
+ * @param ipaddr The IP address to be resolved.
+ * @param q If non-NULL, a pbuf that must be delivered to the IP address.
+ * q is not freed by this function.
+ *
+ * @note q must only be ONE packet, not a packet queue!
+ *
+ * @return
+ * - ERR_BUF Could not make room for Ethernet header.
+ * - ERR_MEM Hardware address unknown, and no more ARP entries available
+ * to query for address or queue the packet.
+ * - ERR_MEM Could not queue packet due to memory shortage.
+ * - ERR_RTE No route to destination (no gateway to external networks).
+ * - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
+ *
+ */
+err_t
+etharp_query(struct netif *netif, const ip_addr_t *ipaddr, struct pbuf *q)
+{
+ struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr;
+ err_t result = ERR_MEM;
+ int is_new_entry = 0;
+ s8_t i; /* ARP entry index */
+
+ /* non-unicast address? */
+ if (ip_addr_isbroadcast(ipaddr, netif) ||
+ ip_addr_ismulticast(ipaddr) ||
+ ip_addr_isany(ipaddr)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: will not add non-unicast IP address to ARP cache\n"));
+ return ERR_ARG;
+ }
+
+ /* find entry in ARP cache, ask to create entry if queueing packet */
+ i = etharp_find_entry(ipaddr, ETHARP_FLAG_TRY_HARD, netif);
+
+ /* could not find or create entry? */
+ if (i < 0) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not create ARP entry\n"));
+ if (q) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: packet dropped\n"));
+ ETHARP_STATS_INC(etharp.memerr);
+ }
+ return (err_t)i;
+ }
+
+ /* mark a fresh entry as pending (we just sent a request) */
+ if (arp_table[i].state == ETHARP_STATE_EMPTY) {
+ is_new_entry = 1;
+ arp_table[i].state = ETHARP_STATE_PENDING;
+ /* record network interface for re-sending arp request in etharp_tmr */
+ arp_table[i].netif = netif;
+ }
+
+ /* { i is either a STABLE or (new or existing) PENDING entry } */
+ LWIP_ASSERT("arp_table[i].state == PENDING or STABLE",
+ ((arp_table[i].state == ETHARP_STATE_PENDING) ||
+ (arp_table[i].state >= ETHARP_STATE_STABLE)));
+
+ /* do we have a new entry? or an implicit query request? */
+ if (is_new_entry || (q == NULL)) {
+ /* try to resolve it; send out ARP request */
+ result = etharp_request(netif, ipaddr);
+ if (result != ERR_OK) {
+ /* ARP request couldn't be sent */
+ /* We don't re-send arp request in etharp_tmr, but we still queue packets,
+ since this failure could be temporary, and the next packet calling
+ etharp_query again could lead to sending the queued packets. */
+ }
+ if (q == NULL) {
+ return result;
+ }
+ }
+
+ /* packet given? */
+ LWIP_ASSERT("q != NULL", q != NULL);
+ /* stable entry? */
+ if (arp_table[i].state >= ETHARP_STATE_STABLE) {
+ /* we have a valid IP->Ethernet address mapping */
+ ETHARP_SET_HINT(netif, i);
+ /* send the packet */
+ result = etharp_send_ip(netif, q, srcaddr, &(arp_table[i].ethaddr));
+ /* pending entry? (either just created or already pending */
+ } else if (arp_table[i].state == ETHARP_STATE_PENDING) {
+ /* entry is still pending, queue the given packet 'q' */
+ struct pbuf *p;
+ int copy_needed = 0;
+ /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but
+ * to copy the whole queue into a new PBUF_RAM (see bug #11400)
+ * PBUF_ROMs can be left as they are, since ROM must not get changed. */
+ p = q;
+ while (p) {
+ LWIP_ASSERT("no packet queues allowed!", (p->len != p->tot_len) || (p->next == 0));
+ if(p->type != PBUF_ROM) {
+ copy_needed = 1;
+ break;
+ }
+ p = p->next;
+ }
+ if(copy_needed) {
+ /* copy the whole packet into new pbufs */
+ p = pbuf_alloc(PBUF_RAW_TX, p->tot_len, PBUF_RAM);
+ if(p != NULL) {
+ if (pbuf_copy(p, q) != ERR_OK) {
+ pbuf_free(p);
+ p = NULL;
+ }
+ }
+ } else {
+ /* referencing the old pbuf is enough */
+ p = q;
+ pbuf_ref(p);
+ }
+ /* packet could be taken over? */
+ if (p != NULL) {
+ /* queue packet ... */
+#if ARP_QUEUEING
+ struct etharp_q_entry *new_entry;
+ /* allocate a new arp queue entry */
+ new_entry = (struct etharp_q_entry *)memp_malloc(MEMP_ARP_QUEUE);
+ if (new_entry != NULL) {
+ unsigned int qlen = 0;
+ new_entry->next = 0;
+ new_entry->p = p;
+ if(arp_table[i].q != NULL) {
+ /* queue was already existent, append the new entry to the end */
+ struct etharp_q_entry *r;
+ r = arp_table[i].q;
+ qlen++;
+ while (r->next != NULL) {
+ r = r->next;
+ qlen++;
+ }
+ r->next = new_entry;
+ } else {
+ /* queue did not exist, first item in queue */
+ arp_table[i].q = new_entry;
+ }
+#if ARP_QUEUE_LEN
+ if (qlen >= ARP_QUEUE_LEN) {
+ struct etharp_q_entry *old;
+ old = arp_table[i].q;
+ arp_table[i].q = arp_table[i].q->next;
+ pbuf_free(old->p);
+ memp_free(MEMP_ARP_QUEUE, old);
+ }
+#endif
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
+ result = ERR_OK;
+ } else {
+ /* the pool MEMP_ARP_QUEUE is empty */
+ pbuf_free(p);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
+ result = ERR_MEM;
+ }
+#else /* ARP_QUEUEING */
+ /* always queue one packet per ARP request only, freeing a previously queued packet */
+ if (arp_table[i].q != NULL) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: dropped previously queued packet %p for ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
+ pbuf_free(arp_table[i].q);
+ }
+ arp_table[i].q = p;
+ result = ERR_OK;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
+#endif /* ARP_QUEUEING */
+ } else {
+ ETHARP_STATS_INC(etharp.memerr);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
+ result = ERR_MEM;
+ }
+ }
+ return result;
+}
+
+/**
+ * Send a raw ARP packet (opcode and all addresses can be modified)
+ *
+ * @param netif the lwip network interface on which to send the ARP packet
+ * @param ethsrc_addr the source MAC address for the ethernet header
+ * @param ethdst_addr the destination MAC address for the ethernet header
+ * @param hwsrc_addr the source MAC address for the ARP protocol header
+ * @param ipsrc_addr the source IP address for the ARP protocol header
+ * @param hwdst_addr the destination MAC address for the ARP protocol header
+ * @param ipdst_addr the destination IP address for the ARP protocol header
+ * @param opcode the type of the ARP packet
+ * @return ERR_OK if the ARP packet has been sent
+ * ERR_MEM if the ARP packet couldn't be allocated
+ * any other err_t on failure
+ */
+#if !LWIP_AUTOIP
+static
+#endif /* LWIP_AUTOIP */
+err_t
+etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
+ const struct eth_addr *ethdst_addr,
+ const struct eth_addr *hwsrc_addr, const ip_addr_t *ipsrc_addr,
+ const struct eth_addr *hwdst_addr, const ip_addr_t *ipdst_addr,
+ const u16_t opcode)
+{
+ struct pbuf *p;
+ err_t result = ERR_OK;
+ struct eth_hdr *ethhdr;
+#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
+ struct eth_vlan_hdr *vlanhdr;
+#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
+ struct etharp_hdr *hdr;
+#if LWIP_AUTOIP
+ const u8_t * ethdst_hwaddr;
+#endif /* LWIP_AUTOIP */
+
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+
+ /* allocate a pbuf for the outgoing ARP request packet */
+ p = pbuf_alloc(PBUF_RAW_TX, SIZEOF_ETHARP_PACKET_TX, PBUF_RAM);
+ /* could allocate a pbuf for an ARP request? */
+ if (p == NULL) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("etharp_raw: could not allocate pbuf for ARP request.\n"));
+ ETHARP_STATS_INC(etharp.memerr);
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("check that first pbuf can hold struct etharp_hdr",
+ (p->len >= SIZEOF_ETHARP_PACKET_TX));
+
+ ethhdr = (struct eth_hdr *)p->payload;
+#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
+ vlanhdr = (struct eth_vlan_hdr*)(((u8_t*)ethhdr) + SIZEOF_ETH_HDR);
+ hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR);
+#else /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
+ hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
+#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_raw: sending raw ARP packet.\n"));
+ hdr->opcode = htons(opcode);
+
+ LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
+ (netif->hwaddr_len == ETHARP_HWADDR_LEN));
+#if LWIP_AUTOIP
+ /* If we are using Link-Local, all ARP packets that contain a Link-Local
+ * 'sender IP address' MUST be sent using link-layer broadcast instead of
+ * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */
+ ethdst_hwaddr = ip_addr_islinklocal(ipsrc_addr) ? (u8_t*)(ethbroadcast.addr) : ethdst_addr->addr;
+#endif /* LWIP_AUTOIP */
+ /* Write the ARP MAC-Addresses */
+ ETHADDR16_COPY(&hdr->shwaddr, hwsrc_addr);
+ ETHADDR16_COPY(&hdr->dhwaddr, hwdst_addr);
+ /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
+ * structure packing. */
+ IPADDR2_COPY(&hdr->sipaddr, ipsrc_addr);
+ IPADDR2_COPY(&hdr->dipaddr, ipdst_addr);
+
+ hdr->hwtype = PP_HTONS(HWTYPE_ETHERNET);
+ hdr->proto = PP_HTONS(ETHTYPE_IP);
+ /* set hwlen and protolen */
+ hdr->hwlen = ETHARP_HWADDR_LEN;
+ hdr->protolen = sizeof(ip_addr_t);
+
+#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
+ ethhdr->type = PP_HTONS(ETHTYPE_VLAN);
+ vlanhdr->tpid = PP_HTONS(ETHTYPE_ARP);
+ vlanhdr->prio_vid = 0;
+ if (!LWIP_HOOK_VLAN_SET(netif, ethhdr, vlanhdr)) {
+ /* packet shall not contain VLAN header, so hide it and set correct ethertype */
+ pbuf_header(p, -SIZEOF_VLAN_HDR);
+ ethhdr = (struct eth_hdr *)p->payload;
+#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
+ ethhdr->type = PP_HTONS(ETHTYPE_ARP);
+#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
+ }
+#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
+
+ /* Write the Ethernet MAC-Addresses */
+#if LWIP_AUTOIP
+ ETHADDR16_COPY(&ethhdr->dest, ethdst_hwaddr);
+#else /* LWIP_AUTOIP */
+ ETHADDR16_COPY(&ethhdr->dest, ethdst_addr);
+#endif /* LWIP_AUTOIP */
+ ETHADDR16_COPY(&ethhdr->src, ethsrc_addr);
+
+ /* send ARP query */
+ result = netif->linkoutput(netif, p);
+ ETHARP_STATS_INC(etharp.xmit);
+ /* free ARP query packet */
+ pbuf_free(p);
+ p = NULL;
+ /* could not allocate pbuf for ARP request */
+
+ return result;
+}
+
+/**
+ * Send an ARP request packet asking for ipaddr to a specific eth address.
+ * Used to send unicast request to refresh the ARP table just before an entry
+ * times out
+ *
+ * @param netif the lwip network interface on which to send the request
+ * @param ipaddr the IP address for which to ask
+ * @param hw_dst_addr the ethernet address to send this packet to
+ * @return ERR_OK if the request has been sent
+ * ERR_MEM if the ARP packet couldn't be allocated
+ * any other err_t on failure
+ */
+static err_t
+etharp_request_dst(struct netif *netif, const ip_addr_t *ipaddr, const struct eth_addr* hw_dst_addr)
+{
+ return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, hw_dst_addr,
+ (struct eth_addr *)netif->hwaddr, &netif->ip_addr, &ethzero,
+ ipaddr, ARP_REQUEST);
+}
+
+/**
+ * Send an ARP request packet asking for ipaddr.
+ *
+ * @param netif the lwip network interface on which to send the request
+ * @param ipaddr the IP address for which to ask
+ * @return ERR_OK if the request has been sent
+ * ERR_MEM if the ARP packet couldn't be allocated
+ * any other err_t on failure
+ */
+err_t
+etharp_request(struct netif *netif, const ip_addr_t *ipaddr)
+{
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_request: sending ARP request.\n"));
+ return etharp_request_dst(netif, ipaddr, &ethbroadcast);
+}
+#endif /* LWIP_ARP */
+
+/**
+ * Process received ethernet frames. Using this function instead of directly
+ * calling ip_input and passing ARP frames through etharp in ethernetif_input,
+ * the ARP cache is protected from concurrent access.
+ *
+ * @param p the received packet, p->payload pointing to the ethernet header
+ * @param netif the network interface on which the packet was received
+ */
+err_t
+ethernet_input(struct pbuf *p, struct netif *netif)
+{
+ struct eth_hdr* ethhdr;
+ u16_t type;
+#if LWIP_ARP || ETHARP_SUPPORT_VLAN || LWIP_IPV6
+ s16_t ip_hdr_offset = SIZEOF_ETH_HDR;
+#endif /* LWIP_ARP || ETHARP_SUPPORT_VLAN */
+
+ if (p->len <= SIZEOF_ETH_HDR) {
+ /* a packet with only an ethernet header (or less) is not valid for us */
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ goto free_and_return;
+ }
+
+ /* points to packet payload, which starts with an Ethernet header */
+ ethhdr = (struct eth_hdr *)p->payload;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE,
+ ("ethernet_input: dest:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", src:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", type:%"X16_F"\n",
+ (unsigned)ethhdr->dest.addr[0], (unsigned)ethhdr->dest.addr[1], (unsigned)ethhdr->dest.addr[2],
+ (unsigned)ethhdr->dest.addr[3], (unsigned)ethhdr->dest.addr[4], (unsigned)ethhdr->dest.addr[5],
+ (unsigned)ethhdr->src.addr[0], (unsigned)ethhdr->src.addr[1], (unsigned)ethhdr->src.addr[2],
+ (unsigned)ethhdr->src.addr[3], (unsigned)ethhdr->src.addr[4], (unsigned)ethhdr->src.addr[5],
+ (unsigned)htons(ethhdr->type)));
+
+ type = ethhdr->type;
+#if ETHARP_SUPPORT_VLAN
+ if (type == PP_HTONS(ETHTYPE_VLAN)) {
+ struct eth_vlan_hdr *vlan = (struct eth_vlan_hdr*)(((char*)ethhdr) + SIZEOF_ETH_HDR);
+ if (p->len <= SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) {
+ /* a packet with only an ethernet/vlan header (or less) is not valid for us */
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ goto free_and_return;
+ }
+#if defined(LWIP_HOOK_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) /* if not, allow all VLANs */
+#ifdef LWIP_HOOK_VLAN_CHECK
+ if (!LWIP_HOOK_VLAN_CHECK(netif, ethhdr, vlan)) {
+#elif defined(ETHARP_VLAN_CHECK_FN)
+ if (!ETHARP_VLAN_CHECK_FN(ethhdr, vlan)) {
+#elif defined(ETHARP_VLAN_CHECK)
+ if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) {
+#endif
+ /* silently ignore this packet: not for our VLAN */
+ pbuf_free(p);
+ return ERR_OK;
+ }
+#endif /* defined(LWIP_HOOK_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) */
+ type = vlan->tpid;
+ ip_hdr_offset = SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR;
+ }
+#endif /* ETHARP_SUPPORT_VLAN */
+
+#if LWIP_ARP_FILTER_NETIF
+ netif = LWIP_ARP_FILTER_NETIF_FN(p, netif, htons(type));
+#endif /* LWIP_ARP_FILTER_NETIF*/
+
+ if (ethhdr->dest.addr[0] & 1) {
+ /* this might be a multicast or broadcast packet */
+ if (ethhdr->dest.addr[0] == LL_MULTICAST_ADDR_0) {
+ if ((ethhdr->dest.addr[1] == LL_MULTICAST_ADDR_1) &&
+ (ethhdr->dest.addr[2] == LL_MULTICAST_ADDR_2)) {
+ /* mark the pbuf as link-layer multicast */
+ p->flags |= PBUF_FLAG_LLMCAST;
+ }
+ } else if (eth_addr_cmp(&ethhdr->dest, &ethbroadcast)) {
+ /* mark the pbuf as link-layer broadcast */
+ p->flags |= PBUF_FLAG_LLBCAST;
+ }
+ }
+
+ switch (type) {
+#if LWIP_ARP
+ /* IP packet? */
+ case PP_HTONS(ETHTYPE_IP):
+ if (!(netif->flags & NETIF_FLAG_ETHARP)) {
+ goto free_and_return;
+ }
+#if ETHARP_TRUST_IP_MAC
+ /* update ARP table */
+ etharp_ip_input(netif, p);
+#endif /* ETHARP_TRUST_IP_MAC */
+ /* skip Ethernet header */
+ if ((p->len < ip_hdr_offset) || pbuf_header(p, (s16_t)-ip_hdr_offset)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("ethernet_input: IPv4 packet dropped, too short (%"S16_F"/%"S16_F")\n",
+ p->tot_len, ip_hdr_offset));
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("Can't move over header in packet"));
+ goto free_and_return;
+ } else {
+ /* pass to IP layer */
+ ip_input(p, netif);
+ }
+ break;
+
+ case PP_HTONS(ETHTYPE_ARP):
+ if (!(netif->flags & NETIF_FLAG_ETHARP)) {
+ goto free_and_return;
+ }
+ /* pass p to ARP module */
+ etharp_arp_input(netif, (struct eth_addr*)(netif->hwaddr), p);
+ break;
+#endif /* LWIP_ARP */
+#if PPPOE_SUPPORT
+ case PP_HTONS(ETHTYPE_PPPOEDISC): /* PPP Over Ethernet Discovery Stage */
+ pppoe_disc_input(netif, p);
+ break;
+
+ case PP_HTONS(ETHTYPE_PPPOE): /* PPP Over Ethernet Session Stage */
+ pppoe_data_input(netif, p);
+ break;
+#endif /* PPPOE_SUPPORT */
+
+#if LWIP_IPV6
+ case PP_HTONS(ETHTYPE_IPV6): /* IPv6 */
+ /* skip Ethernet header */
+ if((p->len < ip_hdr_offset) || pbuf_header(p, (s16_t)-ip_hdr_offset)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("ethernet_input: IPv6 packet dropped, too short (%"S16_F"/%"S16_F")\n",
+ p->tot_len, ip_hdr_offset));
+ goto free_and_return;
+ } else {
+ /* pass to IPv6 layer */
+ ip6_input(p, netif);
+ }
+ break;
+#endif /* LWIP_IPV6 */
+
+ default:
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ goto free_and_return;
+ }
+
+ /* This means the pbuf is freed or consumed,
+ so the caller doesn't have to free it again */
+ return ERR_OK;
+
+free_and_return:
+ pbuf_free(p);
+ return ERR_OK;
+}
+#endif /* LWIP_ARP || LWIP_ETHERNET */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ethernet.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ethernet.c
new file mode 100644
index 0000000..52ea423
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ethernet.c
@@ -0,0 +1,314 @@
+/**
+ * @file
+ * Ethernet common functions
+ *
+ * @defgroup ethernet Ethernet
+ * @ingroup callbackstyle_api
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * Copyright (c) 2003-2004 Leon Woestenberg <leon.woestenberg@axon.tv>
+ * Copyright (c) 2003-2004 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ARP || LWIP_ETHERNET
+
+#include "netif/ethernet.h"
+#include "lwip/def.h"
+#include "lwip/stats.h"
+#include "lwip/etharp.h"
+#include "lwip/ip.h"
+#include "lwip/snmp.h"
+
+#include <string.h>
+
+#include "netif/ppp/ppp_opts.h"
+#if PPPOE_SUPPORT
+#include "netif/ppp/pppoe.h"
+#endif /* PPPOE_SUPPORT */
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}};
+const struct eth_addr ethzero = {{0,0,0,0,0,0}};
+
+/**
+ * @ingroup lwip_nosys
+ * Process received ethernet frames. Using this function instead of directly
+ * calling ip_input and passing ARP frames through etharp in ethernetif_input,
+ * the ARP cache is protected from concurrent access.\n
+ * Don't call directly, pass to netif_add() and call netif->input().
+ *
+ * @param p the received packet, p->payload pointing to the ethernet header
+ * @param netif the network interface on which the packet was received
+ *
+ * @see LWIP_HOOK_UNKNOWN_ETH_PROTOCOL
+ * @see ETHARP_SUPPORT_VLAN
+ * @see LWIP_HOOK_VLAN_CHECK
+ */
+err_t
+ethernet_input(struct pbuf *p, struct netif *netif)
+{
+ struct eth_hdr* ethhdr;
+ u16_t type;
+#if LWIP_ARP || ETHARP_SUPPORT_VLAN || LWIP_IPV6
+ s16_t ip_hdr_offset = SIZEOF_ETH_HDR;
+#endif /* LWIP_ARP || ETHARP_SUPPORT_VLAN */
+
+ if (p->len <= SIZEOF_ETH_HDR) {
+ /* a packet with only an ethernet header (or less) is not valid for us */
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ MIB2_STATS_NETIF_INC(netif, ifinerrors);
+ goto free_and_return;
+ }
+
+ /* points to packet payload, which starts with an Ethernet header */
+ ethhdr = (struct eth_hdr *)p->payload;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE,
+ ("ethernet_input: dest:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", src:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", type:%"X16_F"\n",
+ (unsigned)ethhdr->dest.addr[0], (unsigned)ethhdr->dest.addr[1], (unsigned)ethhdr->dest.addr[2],
+ (unsigned)ethhdr->dest.addr[3], (unsigned)ethhdr->dest.addr[4], (unsigned)ethhdr->dest.addr[5],
+ (unsigned)ethhdr->src.addr[0], (unsigned)ethhdr->src.addr[1], (unsigned)ethhdr->src.addr[2],
+ (unsigned)ethhdr->src.addr[3], (unsigned)ethhdr->src.addr[4], (unsigned)ethhdr->src.addr[5],
+ lwip_htons(ethhdr->type)));
+
+ type = ethhdr->type;
+#if ETHARP_SUPPORT_VLAN
+ if (type == PP_HTONS(ETHTYPE_VLAN)) {
+ struct eth_vlan_hdr *vlan = (struct eth_vlan_hdr*)(((char*)ethhdr) + SIZEOF_ETH_HDR);
+ if (p->len <= SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) {
+ /* a packet with only an ethernet/vlan header (or less) is not valid for us */
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ MIB2_STATS_NETIF_INC(netif, ifinerrors);
+ goto free_and_return;
+ }
+#if defined(LWIP_HOOK_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) /* if not, allow all VLANs */
+#ifdef LWIP_HOOK_VLAN_CHECK
+ if (!LWIP_HOOK_VLAN_CHECK(netif, ethhdr, vlan)) {
+#elif defined(ETHARP_VLAN_CHECK_FN)
+ if (!ETHARP_VLAN_CHECK_FN(ethhdr, vlan)) {
+#elif defined(ETHARP_VLAN_CHECK)
+ if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) {
+#endif
+ /* silently ignore this packet: not for our VLAN */
+ pbuf_free(p);
+ return ERR_OK;
+ }
+#endif /* defined(LWIP_HOOK_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) */
+ type = vlan->tpid;
+ ip_hdr_offset = SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR;
+ }
+#endif /* ETHARP_SUPPORT_VLAN */
+
+#if LWIP_ARP_FILTER_NETIF
+ netif = LWIP_ARP_FILTER_NETIF_FN(p, netif, lwip_htons(type));
+#endif /* LWIP_ARP_FILTER_NETIF*/
+
+ if (ethhdr->dest.addr[0] & 1) {
+ /* this might be a multicast or broadcast packet */
+ if (ethhdr->dest.addr[0] == LL_IP4_MULTICAST_ADDR_0) {
+#if LWIP_IPV4
+ if ((ethhdr->dest.addr[1] == LL_IP4_MULTICAST_ADDR_1) &&
+ (ethhdr->dest.addr[2] == LL_IP4_MULTICAST_ADDR_2)) {
+ /* mark the pbuf as link-layer multicast */
+ p->flags |= PBUF_FLAG_LLMCAST;
+ }
+#endif /* LWIP_IPV4 */
+ }
+#if LWIP_IPV6
+ else if ((ethhdr->dest.addr[0] == LL_IP6_MULTICAST_ADDR_0) &&
+ (ethhdr->dest.addr[1] == LL_IP6_MULTICAST_ADDR_1)) {
+ /* mark the pbuf as link-layer multicast */
+ p->flags |= PBUF_FLAG_LLMCAST;
+ }
+#endif /* LWIP_IPV6 */
+ else if (eth_addr_cmp(&ethhdr->dest, &ethbroadcast)) {
+ /* mark the pbuf as link-layer broadcast */
+ p->flags |= PBUF_FLAG_LLBCAST;
+ }
+ }
+
+ switch (type) {
+#if LWIP_IPV4 && LWIP_ARP
+ /* IP packet? */
+ case PP_HTONS(ETHTYPE_IP):
+ if (!(netif->flags & NETIF_FLAG_ETHARP)) {
+ goto free_and_return;
+ }
+ /* skip Ethernet header */
+ if ((p->len < ip_hdr_offset) || pbuf_header(p, (s16_t)-ip_hdr_offset)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("ethernet_input: IPv4 packet dropped, too short (%"S16_F"/%"S16_F")\n",
+ p->tot_len, ip_hdr_offset));
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("Can't move over header in packet"));
+ goto free_and_return;
+ } else {
+ /* pass to IP layer */
+ ip4_input(p, netif);
+ }
+ break;
+
+ case PP_HTONS(ETHTYPE_ARP):
+ if (!(netif->flags & NETIF_FLAG_ETHARP)) {
+ goto free_and_return;
+ }
+ /* skip Ethernet header */
+ if ((p->len < ip_hdr_offset) || pbuf_header(p, (s16_t)-ip_hdr_offset)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("ethernet_input: ARP response packet dropped, too short (%"S16_F"/%"S16_F")\n",
+ p->tot_len, ip_hdr_offset));
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("Can't move over header in packet"));
+ ETHARP_STATS_INC(etharp.lenerr);
+ ETHARP_STATS_INC(etharp.drop);
+ goto free_and_return;
+ } else {
+ /* pass p to ARP module */
+ etharp_input(p, netif);
+ }
+ break;
+#endif /* LWIP_IPV4 && LWIP_ARP */
+#if PPPOE_SUPPORT
+ case PP_HTONS(ETHTYPE_PPPOEDISC): /* PPP Over Ethernet Discovery Stage */
+ pppoe_disc_input(netif, p);
+ break;
+
+ case PP_HTONS(ETHTYPE_PPPOE): /* PPP Over Ethernet Session Stage */
+ pppoe_data_input(netif, p);
+ break;
+#endif /* PPPOE_SUPPORT */
+
+#if LWIP_IPV6
+ case PP_HTONS(ETHTYPE_IPV6): /* IPv6 */
+ /* skip Ethernet header */
+ if ((p->len < ip_hdr_offset) || pbuf_header(p, (s16_t)-ip_hdr_offset)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("ethernet_input: IPv6 packet dropped, too short (%"S16_F"/%"S16_F")\n",
+ p->tot_len, ip_hdr_offset));
+ goto free_and_return;
+ } else {
+ /* pass to IPv6 layer */
+ ip6_input(p, netif);
+ }
+ break;
+#endif /* LWIP_IPV6 */
+
+ default:
+#ifdef LWIP_HOOK_UNKNOWN_ETH_PROTOCOL
+ if(LWIP_HOOK_UNKNOWN_ETH_PROTOCOL(p, netif) == ERR_OK) {
+ break;
+ }
+#endif
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ MIB2_STATS_NETIF_INC(netif, ifinunknownprotos);
+ goto free_and_return;
+ }
+
+ /* This means the pbuf is freed or consumed,
+ so the caller doesn't have to free it again */
+ return ERR_OK;
+
+free_and_return:
+ pbuf_free(p);
+ return ERR_OK;
+}
+
+/**
+ * @ingroup ethernet
+ * Send an ethernet packet on the network using netif->linkoutput().
+ * The ethernet header is filled in before sending.
+ *
+ * @see LWIP_HOOK_VLAN_SET
+ *
+ * @param netif the lwIP network interface on which to send the packet
+ * @param p the packet to send. pbuf layer must be @ref PBUF_LINK.
+ * @param src the source MAC address to be copied into the ethernet header
+ * @param dst the destination MAC address to be copied into the ethernet header
+ * @param eth_type ethernet type (@ref eth_type)
+ * @return ERR_OK if the packet was sent, any other err_t on failure
+ */
+err_t
+ethernet_output(struct netif* netif, struct pbuf* p,
+ const struct eth_addr* src, const struct eth_addr* dst,
+ u16_t eth_type)
+{
+ struct eth_hdr* ethhdr;
+ u16_t eth_type_be = lwip_htons(eth_type);
+
+#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
+ s32_t vlan_prio_vid = LWIP_HOOK_VLAN_SET(netif, p, src, dst, eth_type);
+ if (vlan_prio_vid >= 0) {
+ struct eth_vlan_hdr* vlanhdr;
+
+ LWIP_ASSERT("prio_vid must be <= 0xFFFF", vlan_prio_vid <= 0xFFFF);
+
+ if (pbuf_header(p, SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) != 0) {
+ goto pbuf_header_failed;
+ }
+ vlanhdr = (struct eth_vlan_hdr*)(((u8_t*)p->payload) + SIZEOF_ETH_HDR);
+ vlanhdr->tpid = eth_type_be;
+ vlanhdr->prio_vid = lwip_htons((u16_t)vlan_prio_vid);
+
+ eth_type_be = PP_HTONS(ETHTYPE_VLAN);
+ } else
+#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
+ {
+ if (pbuf_header(p, SIZEOF_ETH_HDR) != 0) {
+ goto pbuf_header_failed;
+ }
+ }
+
+ ethhdr = (struct eth_hdr*)p->payload;
+ ethhdr->type = eth_type_be;
+ ETHADDR32_COPY(&ethhdr->dest, dst);
+ ETHADDR16_COPY(&ethhdr->src, src);
+
+ LWIP_ASSERT("netif->hwaddr_len must be 6 for ethernet_output!",
+ (netif->hwaddr_len == ETH_HWADDR_LEN));
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE,
+ ("ethernet_output: sending packet %p\n", (void *)p));
+
+ /* send the packet */
+ return netif->linkoutput(netif, p);
+
+pbuf_header_failed:
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("ethernet_output: could not allocate room for header.\n"));
+ LINK_STATS_INC(link.lenerr);
+ return ERR_BUF;
+}
+
+#endif /* LWIP_ARP || LWIP_ETHERNET */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ethernetif.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ethernetif.c
new file mode 100644
index 0000000..dc8ae6c
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ethernetif.c
@@ -0,0 +1,335 @@
+/**
+ * @file
+ * Ethernet Interface Skeleton
+ *
+ */
+
+/*
+ * 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: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+/*
+ * This file is a skeleton for developing Ethernet network interface
+ * drivers for lwIP. Add code to the low_level functions and do a
+ * search-and-replace for the word "ethernetif" to replace it with
+ * something that better describes your network interface.
+ */
+
+#include "lwip/opt.h"
+
+#if 0 /* don't build, this is only a skeleton, see previous comment */
+
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/pbuf.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+#include "lwip/ethip6.h"
+#include "lwip/etharp.h"
+#include "netif/ppp/pppoe.h"
+
+/* Define those to better describe your network interface. */
+#define IFNAME0 'e'
+#define IFNAME1 'n'
+
+/**
+ * Helper struct to hold private data used to operate your ethernet interface.
+ * Keeping the ethernet address of the MAC in this struct is not necessary
+ * as it is already kept in the struct netif.
+ * But this is only an example, anyway...
+ */
+struct ethernetif {
+ struct eth_addr *ethaddr;
+ /* Add whatever per-interface state that is needed here. */
+};
+
+/* Forward declarations. */
+static void ethernetif_input(struct netif *netif);
+
+/**
+ * In this function, the hardware should be initialized.
+ * Called from ethernetif_init().
+ *
+ * @param netif the already initialized lwip network interface structure
+ * for this ethernetif
+ */
+static void
+low_level_init(struct netif *netif)
+{
+ struct ethernetif *ethernetif = netif->state;
+
+ /* set MAC hardware address length */
+ netif->hwaddr_len = ETHARP_HWADDR_LEN;
+
+ /* set MAC hardware address */
+ netif->hwaddr[0] = ;
+ ...
+ netif->hwaddr[5] = ;
+
+ /* 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_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 */
+
+ /* Do whatever else is needed to initialize interface. */
+}
+
+/**
+ * This function should do the actual transmission of the packet. The packet is
+ * contained in the pbuf that is passed to the function. This pbuf
+ * might be chained.
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
+ * @return ERR_OK if the packet could be sent
+ * an err_t value if the packet couldn't be sent
+ *
+ * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
+ * strange results. You might consider waiting for space in the DMA queue
+ * to become available since the stack doesn't retry to send a packet
+ * dropped because of memory failure (except for the TCP timers).
+ */
+
+static err_t
+low_level_output(struct netif *netif, struct pbuf *p)
+{
+ struct ethernetif *ethernetif = netif->state;
+ struct pbuf *q;
+
+ initiate transfer();
+
+#if ETH_PAD_SIZE
+ pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
+#endif
+
+ for (q = p; q != NULL; q = q->next) {
+ /* Send the data from the pbuf to the interface, one pbuf at a
+ time. The size of the data in each pbuf is kept in the ->len
+ variable. */
+ send data from(q->payload, q->len);
+ }
+
+ signal that packet should be sent();
+
+ 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 */
+
+#if ETH_PAD_SIZE
+ pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
+#endif
+
+ LINK_STATS_INC(link.xmit);
+
+ return ERR_OK;
+}
+
+/**
+ * Should allocate a pbuf and transfer the bytes of the incoming
+ * packet from the interface into the pbuf.
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ * @return a pbuf filled with the received packet (including MAC header)
+ * NULL on memory error
+ */
+static struct pbuf *
+low_level_input(struct netif *netif)
+{
+ struct ethernetif *ethernetif = netif->state;
+ struct pbuf *p, *q;
+ u16_t len;
+
+ /* Obtain the size of the packet and put it into the "len"
+ variable. */
+ len = ;
+
+#if ETH_PAD_SIZE
+ len += ETH_PAD_SIZE; /* allow room for Ethernet padding */
+#endif
+
+ /* We allocate a pbuf chain of pbufs from the pool. */
+ p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
+
+ if (p != NULL) {
+
+#if ETH_PAD_SIZE
+ pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
+#endif
+
+ /* We iterate over the pbuf chain until we have read the entire
+ * packet into the pbuf. */
+ for (q = p; q != NULL; q = q->next) {
+ /* Read enough bytes to fill this pbuf in the chain. The
+ * available data in the pbuf is given by the q->len
+ * variable.
+ * This does not necessarily have to be a memcpy, you can also preallocate
+ * pbufs for a DMA-enabled MAC and after receiving truncate it to the
+ * actually received size. In this case, ensure the tot_len member of the
+ * pbuf is the sum of the chained pbuf len members.
+ */
+ read data into(q->payload, q->len);
+ }
+ acknowledge that packet has been read();
+
+ MIB2_STATS_NETIF_ADD(netif, ifinoctets, p->tot_len);
+ if (((u8_t*)p->payload)[0] & 1) {
+ /* broadcast or multicast packet*/
+ MIB2_STATS_NETIF_INC(netif, ifinnucastpkts);
+ } else {
+ /* unicast packet*/
+ MIB2_STATS_NETIF_INC(netif, ifinucastpkts);
+ }
+#if ETH_PAD_SIZE
+ pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
+#endif
+
+ LINK_STATS_INC(link.recv);
+ } else {
+ drop packet();
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ MIB2_STATS_NETIF_INC(netif, ifindiscards);
+ }
+
+ return p;
+}
+
+/**
+ * This function should be called when a packet is ready to be read
+ * from the interface. It uses the function low_level_input() that
+ * should handle the actual reception of bytes from the network
+ * interface. Then the type of the received packet is determined and
+ * the appropriate input function is called.
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ */
+static void
+ethernetif_input(struct netif *netif)
+{
+ struct ethernetif *ethernetif;
+ struct eth_hdr *ethhdr;
+ struct pbuf *p;
+
+ ethernetif = netif->state;
+
+ /* move received packet into a new pbuf */
+ p = low_level_input(netif);
+ /* if no packet could be read, silently ignore this */
+ if (p != NULL) {
+ /* pass all packets to ethernet_input, which decides what packets it supports */
+ if (netif->input(p, netif) != ERR_OK) {
+ LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
+ pbuf_free(p);
+ p = NULL;
+ }
+ }
+}
+
+/**
+ * Should be called at the beginning of the program to set up the
+ * network interface. It calls the function low_level_init() to do the
+ * actual setup of the hardware.
+ *
+ * This function should be passed as a parameter to netif_add().
+ *
+ * @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
+ethernetif_init(struct netif *netif)
+{
+ struct ethernetif *ethernetif;
+
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+
+ ethernetif = mem_malloc(sizeof(struct ethernetif));
+ if (ethernetif == NULL) {
+ LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n"));
+ 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, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);
+
+ netif->state = ethernetif;
+ 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...) */
+ netif->output = etharp_output;
+#if LWIP_IPV6
+ netif->output_ip6 = ethip6_output;
+#endif /* LWIP_IPV6 */
+ netif->linkoutput = low_level_output;
+
+ ethernetif->ethaddr = (struct eth_addr *)&(netif->hwaddr[0]);
+
+ /* initialize the hardware */
+ low_level_init(netif);
+
+ return ERR_OK;
+}
+
+#endif /* 0 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/lowpan6.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/lowpan6.c
new file mode 100644
index 0000000..9a84cbc
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/lowpan6.c
@@ -0,0 +1,1193 @@
+/**
+ * @file
+ *
+ * 6LowPAN output for IPv6. Uses ND tables for link-layer addressing. Fragments packets to 6LowPAN units.
+ */
+
+/*
+ * Copyright (c) 2015 Inico Technologies 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. 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: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+/**
+ * @defgroup sixlowpan 6LowPAN netif
+ * @ingroup addons
+ * 6LowPAN netif implementation
+ */
+
+#include "netif/lowpan6.h"
+
+#if LWIP_IPV6 && LWIP_6LOWPAN
+
+#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>
+
+struct ieee_802154_addr {
+ u8_t addr_len;
+ u8_t addr[8];
+};
+
+/** This is a helper struct.
+ */
+struct lowpan6_reass_helper {
+ struct pbuf *pbuf;
+ struct lowpan6_reass_helper *next_packet;
+ u8_t timer;
+ struct ieee_802154_addr sender_addr;
+ u16_t datagram_size;
+ u16_t datagram_tag;
+};
+
+static struct lowpan6_reass_helper * reass_list;
+
+#if LWIP_6LOWPAN_NUM_CONTEXTS > 0
+static ip6_addr_t lowpan6_context[LWIP_6LOWPAN_NUM_CONTEXTS];
+#endif
+
+static u16_t ieee_802154_pan_id;
+
+static const struct ieee_802154_addr ieee_802154_broadcast = {2, {0xff, 0xff}};
+
+#if LWIP_6LOWPAN_INFER_SHORT_ADDRESS
+static struct ieee_802154_addr short_mac_addr = {2, {0,0}};
+#endif /* LWIP_6LOWPAN_INFER_SHORT_ADDRESS */
+
+static err_t dequeue_datagram(struct lowpan6_reass_helper *lrh);
+
+/**
+ * Periodic timer for 6LowPAN functions:
+ *
+ * - Remove incomplete/old packets
+ */
+void
+lowpan6_tmr(void)
+{
+ struct lowpan6_reass_helper *lrh, *lrh_temp;
+
+ lrh = reass_list;
+ while (lrh != NULL) {
+ lrh_temp = lrh->next_packet;
+ if ((--lrh->timer) == 0) {
+ dequeue_datagram(lrh);
+ pbuf_free(lrh->pbuf);
+ mem_free(lrh);
+ }
+ lrh = lrh_temp;
+ }
+}
+
+/**
+ * Removes a datagram from the reassembly queue.
+ **/
+static err_t
+dequeue_datagram(struct lowpan6_reass_helper *lrh)
+{
+ struct lowpan6_reass_helper *lrh_temp;
+
+ if (reass_list == lrh) {
+ reass_list = reass_list->next_packet;
+ } else {
+ lrh_temp = reass_list;
+ while (lrh_temp != NULL) {
+ if (lrh_temp->next_packet == lrh) {
+ lrh_temp->next_packet = lrh->next_packet;
+ break;
+ }
+ lrh_temp = lrh_temp->next_packet;
+ }
+ }
+
+ return ERR_OK;
+}
+
+static s8_t
+lowpan6_context_lookup(const ip6_addr_t *ip6addr)
+{
+ s8_t i;
+
+ for (i = 0; i < LWIP_6LOWPAN_NUM_CONTEXTS; i++) {
+ if (ip6_addr_netcmp(&lowpan6_context[i], ip6addr)) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/* Determine compression mode for unicast address. */
+static s8_t
+lowpan6_get_address_mode(const ip6_addr_t *ip6addr, const struct ieee_802154_addr *mac_addr)
+{
+ if (mac_addr->addr_len == 2) {
+ if ((ip6addr->addr[2] == (u32_t)PP_HTONL(0x000000ff)) &&
+ ((ip6addr->addr[3] & PP_HTONL(0xffff0000)) == PP_NTOHL(0xfe000000))) {
+ if ((ip6addr->addr[3] & PP_HTONL(0x0000ffff)) == lwip_ntohl((mac_addr->addr[0] << 8) | mac_addr->addr[1])) {
+ return 3;
+ }
+ }
+ } else if (mac_addr->addr_len == 8) {
+ if ((ip6addr->addr[2] == lwip_ntohl(((mac_addr->addr[0] ^ 2) << 24) | (mac_addr->addr[1] << 16) | mac_addr->addr[2] << 8 | mac_addr->addr[3])) &&
+ (ip6addr->addr[3] == lwip_ntohl((mac_addr->addr[4] << 24) | (mac_addr->addr[5] << 16) | mac_addr->addr[6] << 8 | mac_addr->addr[7]))) {
+ return 3;
+ }
+ }
+
+ if ((ip6addr->addr[2] == PP_HTONL(0x000000ffUL)) &&
+ ((ip6addr->addr[3] & PP_HTONL(0xffff0000)) == PP_NTOHL(0xfe000000UL))) {
+ return 2;
+ }
+
+ return 1;
+}
+
+/* Determine compression mode for multicast address. */
+static s8_t
+lowpan6_get_address_mode_mc(const ip6_addr_t *ip6addr)
+{
+ if ((ip6addr->addr[0] == PP_HTONL(0xff020000)) &&
+ (ip6addr->addr[1] == 0) &&
+ (ip6addr->addr[2] == 0) &&
+ ((ip6addr->addr[3] & PP_HTONL(0xffffff00)) == 0)) {
+ return 3;
+ } else if (((ip6addr->addr[0] & PP_HTONL(0xff00ffff)) == PP_HTONL(0xff000000)) &&
+ (ip6addr->addr[1] == 0)) {
+ if ((ip6addr->addr[2] == 0) &&
+ ((ip6addr->addr[3] & PP_HTONL(0xff000000)) == 0)) {
+ return 2;
+ } else if ((ip6addr->addr[2] & PP_HTONL(0xffffff00)) == 0) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Encapsulates data into IEEE 802.15.4 frames.
+ * Fragments an IPv6 datagram into 6LowPAN units, which fit into IEEE 802.15.4 frames.
+ * If configured, will compress IPv6 and or UDP headers.
+ * */
+static err_t
+lowpan6_frag(struct netif *netif, struct pbuf *p, const struct ieee_802154_addr *src, const struct ieee_802154_addr *dst)
+{
+ struct pbuf * p_frag;
+ u16_t frag_len, remaining_len;
+ u8_t * buffer;
+ u8_t ieee_header_len;
+ u8_t lowpan6_header_len;
+ s8_t i;
+ static u8_t frame_seq_num;
+ static u16_t datagram_tag;
+ u16_t datagram_offset;
+ err_t err = ERR_IF;
+
+ /* We'll use a dedicated pbuf for building 6LowPAN fragments. */
+ p_frag = pbuf_alloc(PBUF_RAW, 127, PBUF_RAM);
+ if (p_frag == NULL) {
+ MIB2_STATS_NETIF_INC(netif, ifoutdiscards);
+ return ERR_MEM;
+ }
+
+ /* Write IEEE 802.15.4 header. */
+ buffer = (u8_t*)p_frag->payload;
+ ieee_header_len = 0;
+ if (dst == &ieee_802154_broadcast) {
+ buffer[ieee_header_len++] = 0x01; /* data packet, no ack required. */
+ } else {
+ buffer[ieee_header_len++] = 0x21; /* data packet, ack required. */
+ }
+ buffer[ieee_header_len] = (0x00 << 4); /* 2003 frame version */
+ buffer[ieee_header_len] |= (dst->addr_len == 2) ? (0x02 << 2) : (0x03 << 2); /* destination addressing mode */
+ buffer[ieee_header_len] |= (src->addr_len == 2) ? (0x02 << 6) : (0x03 << 6); /* source addressing mode */
+ ieee_header_len++;
+ buffer[ieee_header_len++] = frame_seq_num++;
+
+ buffer[ieee_header_len++] = ieee_802154_pan_id & 0xff; /* pan id */
+ buffer[ieee_header_len++] = (ieee_802154_pan_id >> 8) & 0xff; /* pan id */
+ i = dst->addr_len;
+ while (i-- > 0) {
+ buffer[ieee_header_len++] = dst->addr[i];
+ }
+
+ buffer[ieee_header_len++] = ieee_802154_pan_id & 0xff; /* pan id */
+ buffer[ieee_header_len++] = (ieee_802154_pan_id >> 8) & 0xff; /* pan id */
+ i = src->addr_len;
+ while (i-- > 0) {
+ buffer[ieee_header_len++] = src->addr[i];
+ }
+
+#if LWIP_6LOWPAN_IPHC
+ /* Perform 6LowPAN IPv6 header compression according to RFC 6282 */
+ {
+ struct ip6_hdr *ip6hdr;
+
+ /* Point to ip6 header and align copies of src/dest addresses. */
+ ip6hdr = (struct ip6_hdr *)p->payload;
+ ip_addr_copy_from_ip6(ip_data.current_iphdr_dest, ip6hdr->dest);
+ ip_addr_copy_from_ip6(ip_data.current_iphdr_src, ip6hdr->src);
+
+ /* Basic length of 6LowPAN header, set dispatch and clear fields. */
+ lowpan6_header_len = 2;
+ buffer[ieee_header_len] = 0x60;
+ buffer[ieee_header_len + 1] = 0;
+
+ /* Determine whether there will be a Context Identifier Extension byte or not.
+ * If so, set it already. */
+#if LWIP_6LOWPAN_NUM_CONTEXTS > 0
+ buffer[ieee_header_len + 2] = 0;
+
+ i = lowpan6_context_lookup(ip_2_ip6(&ip_data.current_iphdr_src));
+ if (i >= 0) {
+ /* Stateful source address compression. */
+ buffer[ieee_header_len + 1] |= 0x40;
+ buffer[ieee_header_len + 2] |= (i & 0x0f) << 4;
+ }
+
+ i = lowpan6_context_lookup(ip_2_ip6(&ip_data.current_iphdr_dest));
+ if (i >= 0) {
+ /* Stateful destination address compression. */
+ buffer[ieee_header_len + 1] |= 0x04;
+ buffer[ieee_header_len + 2] |= i & 0x0f;
+ }
+
+ if (buffer[ieee_header_len + 2] != 0x00) {
+ /* Context identifier extension byte is appended. */
+ buffer[ieee_header_len + 1] |= 0x80;
+ lowpan6_header_len++;
+ }
+#endif /* LWIP_6LOWPAN_NUM_CONTEXTS > 0 */
+
+ /* Determine TF field: Traffic Class, Flow Label */
+ if (IP6H_FL(ip6hdr) == 0) {
+ /* Flow label is elided. */
+ buffer[ieee_header_len] |= 0x10;
+ if (IP6H_TC(ip6hdr) == 0) {
+ /* Traffic class (ECN+DSCP) elided too. */
+ buffer[ieee_header_len] |= 0x08;
+ } else {
+ /* Traffic class (ECN+DSCP) appended. */
+ buffer[ieee_header_len + lowpan6_header_len++] = IP6H_TC(ip6hdr);
+ }
+ } else {
+ if (((IP6H_TC(ip6hdr) & 0x3f) == 0)) {
+ /* DSCP portion of Traffic Class is elided, ECN and FL are appended (3 bytes) */
+ buffer[ieee_header_len] |= 0x08;
+
+ buffer[ieee_header_len + lowpan6_header_len] = IP6H_TC(ip6hdr) & 0xc0;
+ buffer[ieee_header_len + lowpan6_header_len++] |= (IP6H_FL(ip6hdr) >> 16) & 0x0f;
+ buffer[ieee_header_len + lowpan6_header_len++] = (IP6H_FL(ip6hdr) >> 8) & 0xff;
+ buffer[ieee_header_len + lowpan6_header_len++] = IP6H_FL(ip6hdr) & 0xff;
+ } else {
+ /* Traffic class and flow label are appended (4 bytes) */
+ buffer[ieee_header_len + lowpan6_header_len++] = IP6H_TC(ip6hdr);
+ buffer[ieee_header_len + lowpan6_header_len++] = (IP6H_FL(ip6hdr) >> 16) & 0x0f;
+ buffer[ieee_header_len + lowpan6_header_len++] = (IP6H_FL(ip6hdr) >> 8) & 0xff;
+ buffer[ieee_header_len + lowpan6_header_len++] = IP6H_FL(ip6hdr) & 0xff;
+ }
+ }
+
+ /* Compress NH?
+ * Only if UDP for now. @todo support other NH compression. */
+ if (IP6H_NEXTH(ip6hdr) == IP6_NEXTH_UDP) {
+ buffer[ieee_header_len] |= 0x04;
+ } else {
+ /* append nexth. */
+ buffer[ieee_header_len + lowpan6_header_len++] = IP6H_NEXTH(ip6hdr);
+ }
+
+ /* Compress hop limit? */
+ if (IP6H_HOPLIM(ip6hdr) == 255) {
+ buffer[ieee_header_len] |= 0x03;
+ } else if (IP6H_HOPLIM(ip6hdr) == 64) {
+ buffer[ieee_header_len] |= 0x02;
+ } else if (IP6H_HOPLIM(ip6hdr) == 1) {
+ buffer[ieee_header_len] |= 0x01;
+ } else {
+ /* append hop limit */
+ buffer[ieee_header_len + lowpan6_header_len++] = IP6H_HOPLIM(ip6hdr);
+ }
+
+ /* Compress source address */
+ if (((buffer[ieee_header_len + 1] & 0x40) != 0) ||
+ (ip6_addr_islinklocal(ip_2_ip6(&ip_data.current_iphdr_src)))) {
+ /* Context-based or link-local source address compression. */
+ i = lowpan6_get_address_mode(ip_2_ip6(&ip_data.current_iphdr_src), src);
+ buffer[ieee_header_len + 1] |= (i & 0x03) << 4;
+ if (i == 1) {
+ MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 16, 8);
+ lowpan6_header_len += 8;
+ } else if (i == 2) {
+ MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 22, 2);
+ lowpan6_header_len += 2;
+ }
+ } else if (ip6_addr_isany(ip_2_ip6(&ip_data.current_iphdr_src))) {
+ /* Special case: mark SAC and leave SAM=0 */
+ buffer[ieee_header_len + 1] |= 0x40;
+ } else {
+ /* Append full address. */
+ MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 8, 16);
+ lowpan6_header_len += 16;
+ }
+
+ /* Compress destination address */
+ if (ip6_addr_ismulticast(ip_2_ip6(&ip_data.current_iphdr_dest))) {
+ /* @todo support stateful multicast address compression */
+
+ buffer[ieee_header_len + 1] |= 0x08;
+
+ i = lowpan6_get_address_mode_mc(ip_2_ip6(&ip_data.current_iphdr_dest));
+ buffer[ieee_header_len + 1] |= i & 0x03;
+ if (i == 0) {
+ MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 24, 16);
+ lowpan6_header_len += 16;
+ } else if (i == 1) {
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[25];
+ MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 35, 5);
+ lowpan6_header_len += 5;
+ } else if (i == 2) {
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[25];
+ MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 37, 3);
+ lowpan6_header_len += 3;
+ } else if (i == 3) {
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[39];
+ }
+ } else if (((buffer[ieee_header_len + 1] & 0x04) != 0) ||
+ (ip6_addr_islinklocal(ip_2_ip6(&ip_data.current_iphdr_dest)))) {
+ /* Context-based or link-local destination address compression. */
+ i = lowpan6_get_address_mode(ip_2_ip6(&ip_data.current_iphdr_dest), dst);
+ buffer[ieee_header_len + 1] |= i & 0x03;
+ if (i == 1) {
+ MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 32, 8);
+ lowpan6_header_len += 8;
+ } else if (i == 2) {
+ MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 38, 2);
+ lowpan6_header_len += 2;
+ }
+ } else {
+ /* Append full address. */
+ MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 24, 16);
+ lowpan6_header_len += 16;
+ }
+
+ /* Move to payload. */
+ pbuf_header(p, -IP6_HLEN);
+
+ /* Compress UDP header? */
+ if (IP6H_NEXTH(ip6hdr) == IP6_NEXTH_UDP) {
+ /* @todo support optional checksum compression */
+
+ buffer[ieee_header_len + lowpan6_header_len] = 0xf0;
+
+ /* determine port compression mode. */
+ if ((((u8_t *)p->payload)[0] == 0xf0) && ((((u8_t *)p->payload)[1] & 0xf0) == 0xb0) &&
+ (((u8_t *)p->payload)[2] == 0xf0) && ((((u8_t *)p->payload)[3] & 0xf0) == 0xb0)) {
+ /* Compress source and dest ports. */
+ buffer[ieee_header_len + lowpan6_header_len++] |= 0x03;
+ buffer[ieee_header_len + lowpan6_header_len++] = ((((u8_t *)p->payload)[1] & 0x0f) << 4) | (((u8_t *)p->payload)[3] & 0x0f);
+ } else if (((u8_t *)p->payload)[0] == 0xf0) {
+ /* Compress source port. */
+ buffer[ieee_header_len + lowpan6_header_len++] |= 0x02;
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[1];
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[2];
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[3];
+ } else if (((u8_t *)p->payload)[2] == 0xf0) {
+ /* Compress dest port. */
+ buffer[ieee_header_len + lowpan6_header_len++] |= 0x01;
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[0];
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[1];
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[3];
+ } else {
+ /* append full ports. */
+ lowpan6_header_len++;
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[0];
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[1];
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[2];
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[3];
+ }
+
+ /* elide length and copy checksum */
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[6];
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[7];
+
+ pbuf_header(p, -UDP_HLEN);
+ }
+ }
+
+#else /* LWIP_6LOWPAN_HC */
+ /* Send uncompressed IPv6 header with appropriate dispatch byte. */
+ lowpan6_header_len = 1;
+ buffer[ieee_header_len] = 0x41; /* IPv6 dispatch */
+#endif /* LWIP_6LOWPAN_HC */
+
+ /* Calculate remaining packet length */
+ remaining_len = p->tot_len;
+
+ if (remaining_len > 0x7FF) {
+ MIB2_STATS_NETIF_INC(netif, ifoutdiscards);
+ /* datagram_size must fit into 11 bit */
+ pbuf_free(p_frag);
+ return ERR_VAL;
+ }
+
+ /* Fragment, or 1 packet? */
+ if (remaining_len > (127 - ieee_header_len - lowpan6_header_len - 3)) { /* 127 - header - 1 byte dispatch - 2 bytes CRC */
+ /* We must move the 6LowPAN header to make room for the FRAG header. */
+ i = lowpan6_header_len;
+ while (i-- != 0) {
+ buffer[ieee_header_len + i + 4] = buffer[ieee_header_len + i];
+ }
+
+ /* Now we need to fragment the packet. FRAG1 header first */
+ buffer[ieee_header_len] = 0xc0 | (((p->tot_len + lowpan6_header_len) >> 8) & 0x7);
+ buffer[ieee_header_len + 1] = (p->tot_len + lowpan6_header_len) & 0xff;
+
+ datagram_tag++;
+ buffer[ieee_header_len + 2] = datagram_tag & 0xff;
+ buffer[ieee_header_len + 3] = (datagram_tag >> 8) & 0xff;
+
+ /* Fragment follows. */
+ frag_len = (127 - ieee_header_len - 4 - 2) & 0xf8;
+
+ pbuf_copy_partial(p, buffer + ieee_header_len + lowpan6_header_len + 4, frag_len - lowpan6_header_len, 0);
+ remaining_len -= frag_len - lowpan6_header_len;
+ datagram_offset = frag_len;
+
+ /* 2 bytes CRC */
+#if LWIP_6LOWPAN_HW_CRC
+ /* Leave blank, will be filled by HW. */
+#else /* LWIP_6LOWPAN_HW_CRC */
+ /* @todo calculate CRC */
+#endif /* LWIP_6LOWPAN_HW_CRC */
+
+ /* Calculate frame length */
+ p_frag->len = p_frag->tot_len = ieee_header_len + 4 + frag_len + 2; /* add 2 dummy bytes for crc*/
+
+ /* send the packet */
+ MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p_frag->tot_len);
+ LWIP_DEBUGF(LOWPAN6_DEBUG | LWIP_DBG_TRACE, ("lowpan6_send: sending packet %p\n", (void *)p));
+ err = netif->linkoutput(netif, p_frag);
+
+ while ((remaining_len > 0) && (err == ERR_OK)) {
+ /* new frame, new seq num for ACK */
+ buffer[2] = frame_seq_num++;
+
+ buffer[ieee_header_len] |= 0x20; /* Change FRAG1 to FRAGN */
+
+ buffer[ieee_header_len + 4] = (u8_t)(datagram_offset >> 3); /* datagram offset in FRAGN header (datagram_offset is max. 11 bit) */
+
+ frag_len = (127 - ieee_header_len - 5 - 2) & 0xf8;
+ if (frag_len > remaining_len) {
+ frag_len = remaining_len;
+ }
+
+ pbuf_copy_partial(p, buffer + ieee_header_len + 5, frag_len, p->tot_len - remaining_len);
+ remaining_len -= frag_len;
+ datagram_offset += frag_len;
+
+ /* 2 bytes CRC */
+#if LWIP_6LOWPAN_HW_CRC
+ /* Leave blank, will be filled by HW. */
+#else /* LWIP_6LOWPAN_HW_CRC */
+ /* @todo calculate CRC */
+#endif /* LWIP_6LOWPAN_HW_CRC */
+
+ /* Calculate frame length */
+ p_frag->len = p_frag->tot_len = frag_len + 5 + ieee_header_len + 2;
+
+ /* send the packet */
+ MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p_frag->tot_len);
+ LWIP_DEBUGF(LOWPAN6_DEBUG | LWIP_DBG_TRACE, ("lowpan6_send: sending packet %p\n", (void *)p));
+ err = netif->linkoutput(netif, p_frag);
+ }
+ } else {
+ /* It fits in one frame. */
+ frag_len = remaining_len;
+
+ /* Copy IPv6 packet */
+ pbuf_copy_partial(p, buffer + ieee_header_len + lowpan6_header_len, frag_len, 0);
+ remaining_len = 0;
+
+ /* 2 bytes CRC */
+#if LWIP_6LOWPAN_HW_CRC
+ /* Leave blank, will be filled by HW. */
+#else /* LWIP_6LOWPAN_HW_CRC */
+ /* @todo calculate CRC */
+#endif /* LWIP_6LOWPAN_HW_CRC */
+
+ /* Calculate frame length */
+ p_frag->len = p_frag->tot_len = frag_len + lowpan6_header_len + ieee_header_len + 2;
+
+ /* send the packet */
+ MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p_frag->tot_len);
+ LWIP_DEBUGF(LOWPAN6_DEBUG | LWIP_DBG_TRACE, ("lowpan6_send: sending packet %p\n", (void *)p));
+ err = netif->linkoutput(netif, p_frag);
+ }
+
+ pbuf_free(p_frag);
+
+ return err;
+}
+
+err_t
+lowpan6_set_context(u8_t idx, const ip6_addr_t * context)
+{
+ if (idx >= LWIP_6LOWPAN_NUM_CONTEXTS) {
+ return ERR_ARG;
+ }
+
+ ip6_addr_set(&lowpan6_context[idx], context);
+
+ return ERR_OK;
+}
+
+#if LWIP_6LOWPAN_INFER_SHORT_ADDRESS
+err_t
+lowpan6_set_short_addr(u8_t addr_high, u8_t addr_low)
+{
+ short_mac_addr.addr[0] = addr_high;
+ short_mac_addr.addr[1] = addr_low;
+
+ return ERR_OK;
+}
+#endif /* LWIP_6LOWPAN_INFER_SHORT_ADDRESS */
+
+#if LWIP_IPV4
+err_t
+lowpan4_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr)
+{
+ (void)netif;
+ (void)q;
+ (void)ipaddr;
+
+ return ERR_IF;
+}
+#endif /* LWIP_IPV4 */
+
+/**
+ * Resolve and fill-in IEEE 802.15.4 address header for outgoing IPv6 packet.
+ *
+ * Perform Header Compression and fragment if necessary.
+ *
+ * @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 err_t
+ */
+err_t
+lowpan6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr)
+{
+ err_t result;
+ const u8_t *hwaddr;
+ struct ieee_802154_addr src, dest;
+#if LWIP_6LOWPAN_INFER_SHORT_ADDRESS
+ ip6_addr_t ip6_src;
+ struct ip6_hdr * ip6_hdr;
+#endif /* LWIP_6LOWPAN_INFER_SHORT_ADDRESS */
+
+#if LWIP_6LOWPAN_INFER_SHORT_ADDRESS
+ /* Check if we can compress source address (use aligned copy) */
+ ip6_hdr = (struct ip6_hdr *)q->payload;
+ ip6_addr_set(&ip6_src, &ip6_hdr->src);
+ if (lowpan6_get_address_mode(&ip6_src, &short_mac_addr) == 3) {
+ src.addr_len = 2;
+ src.addr[0] = short_mac_addr.addr[0];
+ src.addr[1] = short_mac_addr.addr[1];
+ } else
+#endif /* LWIP_6LOWPAN_INFER_SHORT_ADDRESS */
+ {
+ src.addr_len = netif->hwaddr_len;
+ SMEMCPY(src.addr, netif->hwaddr, netif->hwaddr_len);
+ }
+
+ /* multicast destination IP address? */
+ if (ip6_addr_ismulticast(ip6addr)) {
+ MIB2_STATS_NETIF_INC(netif, ifoutnucastpkts);
+ /* We need to send to the broadcast address.*/
+ return lowpan6_frag(netif, q, &src, &ieee_802154_broadcast);
+ }
+
+ /* We have a unicast destination IP address */
+ /* @todo anycast? */
+
+#if LWIP_6LOWPAN_INFER_SHORT_ADDRESS
+ if (src.addr_len == 2) {
+ /* If source address was compressable to short_mac_addr, and dest has same subnet and
+ * is also compressable to 2-bytes, assume we can infer dest as a short address too. */
+ dest.addr_len = 2;
+ dest.addr[0] = ((u8_t *)q->payload)[38];
+ dest.addr[1] = ((u8_t *)q->payload)[39];
+ if ((src.addr_len == 2) && (ip6_addr_netcmp(&ip6_hdr->src, &ip6_hdr->dest)) &&
+ (lowpan6_get_address_mode(ip6addr, &dest) == 3)) {
+ MIB2_STATS_NETIF_INC(netif, ifoutucastpkts);
+ return lowpan6_frag(netif, q, &src, &dest);
+ }
+ }
+#endif /* LWIP_6LOWPAN_INFER_SHORT_ADDRESS */
+
+ /* Ask ND6 what to do with the packet. */
+ result = nd6_get_next_hop_addr_or_queue(netif, q, ip6addr, &hwaddr);
+ if (result != ERR_OK) {
+ MIB2_STATS_NETIF_INC(netif, ifoutdiscards);
+ return result;
+ }
+
+ /* If no hardware address is returned, nd6 has queued the packet for later. */
+ if (hwaddr == NULL) {
+ return ERR_OK;
+ }
+
+ /* Send out the packet using the returned hardware address. */
+ dest.addr_len = netif->hwaddr_len;
+ SMEMCPY(dest.addr, hwaddr, netif->hwaddr_len);
+ MIB2_STATS_NETIF_INC(netif, ifoutucastpkts);
+ return lowpan6_frag(netif, q, &src, &dest);
+}
+
+static struct pbuf *
+lowpan6_decompress(struct pbuf * p, struct ieee_802154_addr * src, struct ieee_802154_addr * dest)
+{
+ struct pbuf * q;
+ u8_t * lowpan6_buffer;
+ s8_t lowpan6_offset;
+ struct ip6_hdr *ip6hdr;
+ s8_t i;
+ s8_t ip6_offset = IP6_HLEN;
+
+
+ q = pbuf_alloc(PBUF_IP, p->len + IP6_HLEN + UDP_HLEN, PBUF_POOL);
+ if (q == NULL) {
+ pbuf_free(p);
+ return NULL;
+ }
+
+ lowpan6_buffer = (u8_t *)p->payload;
+ ip6hdr = (struct ip6_hdr *)q->payload;
+
+ lowpan6_offset = 2;
+ if (lowpan6_buffer[1] & 0x80) {
+ lowpan6_offset++;
+ }
+
+ /* Set IPv6 version, traffic class and flow label. */
+ if ((lowpan6_buffer[0] & 0x18) == 0x00) {
+ IP6H_VTCFL_SET(ip6hdr, 6, lowpan6_buffer[lowpan6_offset], ((lowpan6_buffer[lowpan6_offset+1] & 0x0f) << 16) | (lowpan6_buffer[lowpan6_offset + 2] << 8) | lowpan6_buffer[lowpan6_offset+3]);
+ lowpan6_offset += 4;
+ } else if ((lowpan6_buffer[0] & 0x18) == 0x08) {
+ IP6H_VTCFL_SET(ip6hdr, 6, lowpan6_buffer[lowpan6_offset] & 0xc0, ((lowpan6_buffer[lowpan6_offset] & 0x0f) << 16) | (lowpan6_buffer[lowpan6_offset + 1] << 8) | lowpan6_buffer[lowpan6_offset+2]);
+ lowpan6_offset += 3;
+ } else if ((lowpan6_buffer[0] & 0x18) == 0x10) {
+ IP6H_VTCFL_SET(ip6hdr, 6, lowpan6_buffer[lowpan6_offset],0);
+ lowpan6_offset += 1;
+ } else if ((lowpan6_buffer[0] & 0x18) == 0x18) {
+ IP6H_VTCFL_SET(ip6hdr, 6, 0, 0);
+ }
+
+ /* Set Next Header */
+ if ((lowpan6_buffer[0] & 0x04) == 0x00) {
+ IP6H_NEXTH_SET(ip6hdr, lowpan6_buffer[lowpan6_offset++]);
+ } else {
+ /* We should fill this later with NHC decoding */
+ IP6H_NEXTH_SET(ip6hdr, 0);
+ }
+
+ /* Set Hop Limit */
+ if ((lowpan6_buffer[0] & 0x03) == 0x00) {
+ IP6H_HOPLIM_SET(ip6hdr, lowpan6_buffer[lowpan6_offset++]);
+ } else if ((lowpan6_buffer[0] & 0x03) == 0x01) {
+ IP6H_HOPLIM_SET(ip6hdr, 1);
+ } else if ((lowpan6_buffer[0] & 0x03) == 0x02) {
+ IP6H_HOPLIM_SET(ip6hdr, 64);
+ } else if ((lowpan6_buffer[0] & 0x03) == 0x03) {
+ IP6H_HOPLIM_SET(ip6hdr, 255);
+ }
+
+ /* Source address decoding. */
+ if ((lowpan6_buffer[1] & 0x40) == 0x00) {
+ /* Stateless compression */
+ if ((lowpan6_buffer[1] & 0x30) == 0x00) {
+ /* copy full address */
+ MEMCPY(&ip6hdr->src.addr[0], lowpan6_buffer + lowpan6_offset, 16);
+ lowpan6_offset += 16;
+ } else if ((lowpan6_buffer[1] & 0x30) == 0x10) {
+ ip6hdr->src.addr[0] = PP_HTONL(0xfe800000UL);
+ ip6hdr->src.addr[1] = 0;
+ MEMCPY(&ip6hdr->src.addr[2], lowpan6_buffer + lowpan6_offset, 8);
+ lowpan6_offset += 8;
+ } else if ((lowpan6_buffer[1] & 0x30) == 0x20) {
+ ip6hdr->src.addr[0] = PP_HTONL(0xfe800000UL);
+ ip6hdr->src.addr[1] = 0;
+ ip6hdr->src.addr[2] = PP_HTONL(0x000000ffUL);
+ ip6hdr->src.addr[3] = lwip_htonl(0xfe000000UL | (lowpan6_buffer[lowpan6_offset] << 8) |
+ lowpan6_buffer[lowpan6_offset+1]);
+ lowpan6_offset += 2;
+ } else if ((lowpan6_buffer[1] & 0x30) == 0x30) {
+ ip6hdr->src.addr[0] = PP_HTONL(0xfe800000UL);
+ ip6hdr->src.addr[1] = 0;
+ if (src->addr_len == 2) {
+ ip6hdr->src.addr[2] = PP_HTONL(0x000000ffUL);
+ ip6hdr->src.addr[3] = lwip_htonl(0xfe000000UL | (src->addr[0] << 8) | src->addr[1]);
+ } else {
+ ip6hdr->src.addr[2] = lwip_htonl(((src->addr[0] ^ 2) << 24) | (src->addr[1] << 16) |
+ (src->addr[2] << 8) | src->addr[3]);
+ ip6hdr->src.addr[3] = lwip_htonl((src->addr[4] << 24) | (src->addr[5] << 16) |
+ (src->addr[6] << 8) | src->addr[7]);
+ }
+ }
+ } else {
+ /* Stateful compression */
+ if ((lowpan6_buffer[1] & 0x30) == 0x00) {
+ /* ANY address */
+ ip6hdr->src.addr[0] = 0;
+ ip6hdr->src.addr[1] = 0;
+ ip6hdr->src.addr[2] = 0;
+ ip6hdr->src.addr[3] = 0;
+ } else {
+ /* Set prefix from context info */
+ if (lowpan6_buffer[1] & 0x80) {
+ i = (lowpan6_buffer[2] >> 4) & 0x0f;
+ } else {
+ i = 0;
+ }
+ if (i >= LWIP_6LOWPAN_NUM_CONTEXTS) {
+ /* Error */
+ pbuf_free(p);
+ pbuf_free(q);
+ return NULL;
+ }
+
+ ip6hdr->src.addr[0] = lowpan6_context[i].addr[0];
+ ip6hdr->src.addr[1] = lowpan6_context[i].addr[1];
+ }
+
+ if ((lowpan6_buffer[1] & 0x30) == 0x10) {
+ MEMCPY(&ip6hdr->src.addr[2], lowpan6_buffer + lowpan6_offset, 8);
+ lowpan6_offset += 8;
+ } else if ((lowpan6_buffer[1] & 0x30) == 0x20) {
+ ip6hdr->src.addr[2] = PP_HTONL(0x000000ffUL);
+ ip6hdr->src.addr[3] = lwip_htonl(0xfe000000UL | (lowpan6_buffer[lowpan6_offset] << 8) | lowpan6_buffer[lowpan6_offset+1]);
+ lowpan6_offset += 2;
+ } else if ((lowpan6_buffer[1] & 0x30) == 0x30) {
+ if (src->addr_len == 2) {
+ ip6hdr->src.addr[2] = PP_HTONL(0x000000ffUL);
+ ip6hdr->src.addr[3] = lwip_htonl(0xfe000000UL | (src->addr[0] << 8) | src->addr[1]);
+ } else {
+ ip6hdr->src.addr[2] = lwip_htonl(((src->addr[0] ^ 2) << 24) | (src->addr[1] << 16) | (src->addr[2] << 8) | src->addr[3]);
+ ip6hdr->src.addr[3] = lwip_htonl((src->addr[4] << 24) | (src->addr[5] << 16) | (src->addr[6] << 8) | src->addr[7]);
+ }
+ }
+ }
+
+ /* Destination address decoding. */
+ if (lowpan6_buffer[1] & 0x08) {
+ /* Multicast destination */
+ if (lowpan6_buffer[1] & 0x04) {
+ /* @todo support stateful multicast addressing */
+ pbuf_free(p);
+ pbuf_free(q);
+ return NULL;
+ }
+
+ if ((lowpan6_buffer[1] & 0x03) == 0x00) {
+ /* copy full address */
+ MEMCPY(&ip6hdr->dest.addr[0], lowpan6_buffer + lowpan6_offset, 16);
+ lowpan6_offset += 16;
+ } else if ((lowpan6_buffer[1] & 0x03) == 0x01) {
+ ip6hdr->dest.addr[0] = lwip_htonl(0xff000000UL | (lowpan6_buffer[lowpan6_offset++] << 16));
+ ip6hdr->dest.addr[1] = 0;
+ ip6hdr->dest.addr[2] = lwip_htonl(lowpan6_buffer[lowpan6_offset++]);
+ ip6hdr->dest.addr[3] = lwip_htonl((lowpan6_buffer[lowpan6_offset] << 24) | (lowpan6_buffer[lowpan6_offset + 1] << 16) | (lowpan6_buffer[lowpan6_offset + 2] << 8) | lowpan6_buffer[lowpan6_offset + 3]);
+ lowpan6_offset += 4;
+ } else if ((lowpan6_buffer[1] & 0x03) == 0x02) {
+ ip6hdr->dest.addr[0] = lwip_htonl(0xff000000UL | lowpan6_buffer[lowpan6_offset++]);
+ ip6hdr->dest.addr[1] = 0;
+ ip6hdr->dest.addr[2] = 0;
+ ip6hdr->dest.addr[3] = lwip_htonl((lowpan6_buffer[lowpan6_offset] << 16) | (lowpan6_buffer[lowpan6_offset + 1] << 8) | lowpan6_buffer[lowpan6_offset + 2]);
+ lowpan6_offset += 3;
+ } else if ((lowpan6_buffer[1] & 0x03) == 0x03) {
+ ip6hdr->dest.addr[0] = PP_HTONL(0xff020000UL);
+ ip6hdr->dest.addr[1] = 0;
+ ip6hdr->dest.addr[2] = 0;
+ ip6hdr->dest.addr[3] = lwip_htonl(lowpan6_buffer[lowpan6_offset++]);
+ }
+
+ } else {
+ if (lowpan6_buffer[1] & 0x04) {
+ /* Stateful destination compression */
+ /* Set prefix from context info */
+ if (lowpan6_buffer[1] & 0x80) {
+ i = lowpan6_buffer[2] & 0x0f;
+ } else {
+ i = 0;
+ }
+ if (i >= LWIP_6LOWPAN_NUM_CONTEXTS) {
+ /* Error */
+ pbuf_free(p);
+ pbuf_free(q);
+ return NULL;
+ }
+
+ ip6hdr->dest.addr[0] = lowpan6_context[i].addr[0];
+ ip6hdr->dest.addr[1] = lowpan6_context[i].addr[1];
+ } else {
+ /* Link local address compression */
+ ip6hdr->dest.addr[0] = PP_HTONL(0xfe800000UL);
+ ip6hdr->dest.addr[1] = 0;
+ }
+
+ if ((lowpan6_buffer[1] & 0x03) == 0x00) {
+ /* copy full address */
+ MEMCPY(&ip6hdr->dest.addr[0], lowpan6_buffer + lowpan6_offset, 16);
+ lowpan6_offset += 16;
+ } else if ((lowpan6_buffer[1] & 0x03) == 0x01) {
+ MEMCPY(&ip6hdr->dest.addr[2], lowpan6_buffer + lowpan6_offset, 8);
+ lowpan6_offset += 8;
+ } else if ((lowpan6_buffer[1] & 0x03) == 0x02) {
+ ip6hdr->dest.addr[2] = PP_HTONL(0x000000ffUL);
+ ip6hdr->dest.addr[3] = lwip_htonl(0xfe000000UL | (lowpan6_buffer[lowpan6_offset] << 8) | lowpan6_buffer[lowpan6_offset + 1]);
+ lowpan6_offset += 2;
+ } else if ((lowpan6_buffer[1] & 0x03) == 0x03) {
+ if (dest->addr_len == 2) {
+ ip6hdr->dest.addr[2] = PP_HTONL(0x000000ffUL);
+ ip6hdr->dest.addr[3] = lwip_htonl(0xfe000000UL | (dest->addr[0] << 8) | dest->addr[1]);
+ } else {
+ ip6hdr->dest.addr[2] = lwip_htonl(((dest->addr[0] ^ 2) << 24) | (dest->addr[1] << 16) | dest->addr[2] << 8 | dest->addr[3]);
+ ip6hdr->dest.addr[3] = lwip_htonl((dest->addr[4] << 24) | (dest->addr[5] << 16) | dest->addr[6] << 8 | dest->addr[7]);
+ }
+ }
+ }
+
+
+ /* Next Header Compression (NHC) decoding? */
+ if (lowpan6_buffer[0] & 0x04) {
+ if ((lowpan6_buffer[lowpan6_offset] & 0xf8) == 0xf0) {
+ struct udp_hdr *udphdr;
+
+ /* UDP compression */
+ IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_UDP);
+ udphdr = (struct udp_hdr *)((u8_t *)q->payload + ip6_offset);
+
+ if (lowpan6_buffer[lowpan6_offset] & 0x04) {
+ /* @todo support checksum decompress */
+ pbuf_free(p);
+ pbuf_free(q);
+ return NULL;
+ }
+
+ /* Decompress ports */
+ i = lowpan6_buffer[lowpan6_offset++] & 0x03;
+ if (i == 0) {
+ udphdr->src = lwip_htons(lowpan6_buffer[lowpan6_offset] << 8 | lowpan6_buffer[lowpan6_offset + 1]);
+ udphdr->dest = lwip_htons(lowpan6_buffer[lowpan6_offset + 2] << 8 | lowpan6_buffer[lowpan6_offset + 3]);
+ lowpan6_offset += 4;
+ } else if (i == 0x01) {
+ udphdr->src = lwip_htons(lowpan6_buffer[lowpan6_offset] << 8 | lowpan6_buffer[lowpan6_offset + 1]);
+ udphdr->dest = lwip_htons(0xf000 | lowpan6_buffer[lowpan6_offset + 2]);
+ lowpan6_offset += 3;
+ } else if (i == 0x02) {
+ udphdr->src = lwip_htons(0xf000 | lowpan6_buffer[lowpan6_offset]);
+ udphdr->dest = lwip_htons(lowpan6_buffer[lowpan6_offset + 1] << 8 | lowpan6_buffer[lowpan6_offset + 2]);
+ lowpan6_offset += 3;
+ } else if (i == 0x03) {
+ udphdr->src = lwip_htons(0xf0b0 | ((lowpan6_buffer[lowpan6_offset] >> 4) & 0x0f));
+ udphdr->dest = lwip_htons(0xf0b0 | (lowpan6_buffer[lowpan6_offset] & 0x0f));
+ lowpan6_offset += 1;
+ }
+
+ udphdr->chksum = lwip_htons(lowpan6_buffer[lowpan6_offset] << 8 | lowpan6_buffer[lowpan6_offset + 1]);
+ lowpan6_offset += 2;
+ udphdr->len = lwip_htons(p->tot_len - lowpan6_offset + UDP_HLEN);
+
+ ip6_offset += UDP_HLEN;
+ } else {
+ /* @todo support NHC other than UDP */
+ pbuf_free(p);
+ pbuf_free(q);
+ return NULL;
+ }
+ }
+
+ /* Now we copy leftover contents from p to q, so we have all L2 and L3 headers (and L4?) in a single PBUF.
+ * Replace p with q, and free p */
+ pbuf_header(p, -lowpan6_offset);
+ MEMCPY((u8_t*)q->payload + ip6_offset, p->payload, p->len);
+ q->len = q->tot_len = ip6_offset + p->len;
+ if (p->next != NULL) {
+ pbuf_cat(q, p->next);
+ }
+ p->next = NULL;
+ pbuf_free(p);
+
+ /* Infer IPv6 payload length for header */
+ IP6H_PLEN_SET(ip6hdr, q->tot_len - IP6_HLEN);
+
+ /* all done */
+ return q;
+}
+
+err_t
+lowpan6_input(struct pbuf * p, struct netif *netif)
+{
+ u8_t * puc;
+ s8_t i;
+ struct ieee_802154_addr src, dest;
+ u16_t datagram_size, datagram_offset, datagram_tag;
+ struct lowpan6_reass_helper *lrh, *lrh_temp;
+
+ MIB2_STATS_NETIF_ADD(netif, ifinoctets, p->tot_len);
+
+ /* Analyze header. @todo validate. */
+ puc = (u8_t*)p->payload;
+ datagram_offset = 5;
+ if ((puc[1] & 0x0c) == 0x0c) {
+ dest.addr_len = 8;
+ for (i = 0; i < 8; i++) {
+ dest.addr[i] = puc[datagram_offset + 7 - i];
+ }
+ datagram_offset += 8;
+ } else {
+ dest.addr_len = 2;
+ dest.addr[0] = puc[datagram_offset + 1];
+ dest.addr[1] = puc[datagram_offset];
+ datagram_offset += 2;
+ }
+
+ datagram_offset += 2; /* skip PAN ID. */
+
+ if ((puc[1] & 0xc0) == 0xc0) {
+ src.addr_len = 8;
+ for (i = 0; i < 8; i++) {
+ src.addr[i] = puc[datagram_offset + 7 - i];
+ }
+ datagram_offset += 8;
+ } else {
+ src.addr_len = 2;
+ src.addr[0] = puc[datagram_offset + 1];
+ src.addr[1] = puc[datagram_offset];
+ datagram_offset += 2;
+ }
+
+ pbuf_header(p, -datagram_offset); /* hide IEEE802.15.4 header. */
+
+ /* Check dispatch. */
+ puc = (u8_t*)p->payload;
+
+ if ((*puc & 0xf8) == 0xc0) {
+ /* FRAG1 dispatch. add this packet to reassembly list. */
+ datagram_size = ((u16_t)(puc[0] & 0x07) << 8) | (u16_t)puc[1];
+ datagram_tag = ((u16_t)puc[2] << 8) | (u16_t)puc[3];
+
+ /* check for duplicate */
+ lrh = reass_list;
+ while (lrh != NULL) {
+ if ((lrh->sender_addr.addr_len == src.addr_len) &&
+ (memcmp(lrh->sender_addr.addr, src.addr, src.addr_len) == 0)) {
+ /* address match with packet in reassembly. */
+ if ((datagram_tag == lrh->datagram_tag) && (datagram_size == lrh->datagram_size)) {
+ MIB2_STATS_NETIF_INC(netif, ifindiscards);
+ /* duplicate fragment. */
+ pbuf_free(p);
+ return ERR_OK;
+ } else {
+ /* We are receiving the start of a new datagram. Discard old one (incomplete). */
+ lrh_temp = lrh->next_packet;
+ dequeue_datagram(lrh);
+ pbuf_free(lrh->pbuf);
+ mem_free(lrh);
+
+ /* Check next datagram in queue. */
+ lrh = lrh_temp;
+ }
+ } else {
+ /* Check next datagram in queue. */
+ lrh = lrh->next_packet;
+ }
+ }
+
+ pbuf_header(p, -4); /* hide frag1 dispatch */
+
+ lrh = (struct lowpan6_reass_helper *) mem_malloc(sizeof(struct lowpan6_reass_helper));
+ if (lrh == NULL) {
+ MIB2_STATS_NETIF_INC(netif, ifindiscards);
+ pbuf_free(p);
+ return ERR_MEM;
+ }
+
+ lrh->sender_addr.addr_len = src.addr_len;
+ for (i = 0; i < src.addr_len; i++) {
+ lrh->sender_addr.addr[i] = src.addr[i];
+ }
+ lrh->datagram_size = datagram_size;
+ lrh->datagram_tag = datagram_tag;
+ lrh->pbuf = p;
+ lrh->next_packet = reass_list;
+ lrh->timer = 2;
+ reass_list = lrh;
+
+ return ERR_OK;
+ } else if ((*puc & 0xf8) == 0xe0) {
+ /* FRAGN dispatch, find packet being reassembled. */
+ datagram_size = ((u16_t)(puc[0] & 0x07) << 8) | (u16_t)puc[1];
+ datagram_tag = ((u16_t)puc[2] << 8) | (u16_t)puc[3];
+ datagram_offset = (u16_t)puc[4] << 3;
+ pbuf_header(p, -5); /* hide frag1 dispatch */
+
+ for (lrh = reass_list; lrh != NULL; lrh = lrh->next_packet) {
+ if ((lrh->sender_addr.addr_len == src.addr_len) &&
+ (memcmp(lrh->sender_addr.addr, src.addr, src.addr_len) == 0) &&
+ (datagram_tag == lrh->datagram_tag) &&
+ (datagram_size == lrh->datagram_size)) {
+ break;
+ }
+ }
+ if (lrh == NULL) {
+ /* rogue fragment */
+ MIB2_STATS_NETIF_INC(netif, ifindiscards);
+ pbuf_free(p);
+ return ERR_OK;
+ }
+
+ if (lrh->pbuf->tot_len < datagram_offset) {
+ /* duplicate, ignore. */
+ pbuf_free(p);
+ return ERR_OK;
+ } else if (lrh->pbuf->tot_len > datagram_offset) {
+ MIB2_STATS_NETIF_INC(netif, ifindiscards);
+ /* We have missed a fragment. Delete whole reassembly. */
+ dequeue_datagram(lrh);
+ pbuf_free(lrh->pbuf);
+ mem_free(lrh);
+ pbuf_free(p);
+ return ERR_OK;
+ }
+ pbuf_cat(lrh->pbuf, p);
+ p = NULL;
+
+ /* is packet now complete?*/
+ if (lrh->pbuf->tot_len >= lrh->datagram_size) {
+ /* dequeue from reass list. */
+ dequeue_datagram(lrh);
+
+ /* get pbuf */
+ p = lrh->pbuf;
+
+ /* release helper */
+ mem_free(lrh);
+ } else {
+ return ERR_OK;
+ }
+ }
+
+ if (p == NULL) {
+ return ERR_OK;
+ }
+
+ /* We have a complete packet, check dispatch for headers. */
+ puc = (u8_t*)p->payload;
+
+ if (*puc == 0x41) {
+ /* This is a complete IPv6 packet, just skip dispatch byte. */
+ pbuf_header(p, -1); /* hide dispatch byte. */
+ } else if ((*puc & 0xe0 )== 0x60) {
+ /* IPv6 headers are compressed using IPHC. */
+ p = lowpan6_decompress(p, &src, &dest);
+ if (p == NULL) {
+ MIB2_STATS_NETIF_INC(netif, ifindiscards);
+ return ERR_OK;
+ }
+ } else {
+ MIB2_STATS_NETIF_INC(netif, ifindiscards);
+ pbuf_free(p);
+ return ERR_OK;
+ }
+
+ /* @todo: distinguish unicast/multicast */
+ MIB2_STATS_NETIF_INC(netif, ifinucastpkts);
+
+ return ip6_input(p, netif);
+}
+
+err_t
+lowpan6_if_init(struct netif *netif)
+{
+ netif->name[0] = 'L';
+ netif->name[1] = '6';
+#if LWIP_IPV4
+ netif->output = lowpan4_output;
+#endif /* LWIP_IPV4 */
+ netif->output_ip6 = lowpan6_output;
+
+ MIB2_INIT_NETIF(netif, snmp_ifType_other, 0);
+
+ /* maximum transfer unit */
+ netif->mtu = 1280;
+
+ /* broadcast capability */
+ netif->flags = NETIF_FLAG_BROADCAST /* | NETIF_FLAG_LOWPAN6 */;
+
+ return ERR_OK;
+}
+
+err_t
+lowpan6_set_pan_id(u16_t pan_id)
+{
+ ieee_802154_pan_id = pan_id;
+
+ 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
+ */
+err_t
+tcpip_6lowpan_input(struct pbuf *p, struct netif *inp)
+{
+ return tcpip_inpkt(p, inp, lowpan6_input);
+}
+#endif /* !NO_SYS */
+
+#endif /* LWIP_IPV6 && LWIP_6LOWPAN */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/PPPD_FOLLOWUP b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/PPPD_FOLLOWUP
new file mode 100644
index 0000000..c231982
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/PPPD_FOLLOWUP
@@ -0,0 +1,473 @@
+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.
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/auth.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/auth.c
new file mode 100644
index 0000000..c8673ad
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/auth.c
@@ -0,0 +1,2510 @@
+/*
+ * 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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ccp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ccp.c
new file mode 100644
index 0000000..f8519eb
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ccp.c
@@ -0,0 +1,1740 @@
+/*
+ * 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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/chap-md5.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/chap-md5.c
new file mode 100644
index 0000000..88f069f
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/chap-md5.c
@@ -0,0 +1,126 @@
+/*
+ * 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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/chap-new.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/chap-new.c
new file mode 100644
index 0000000..485122d
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/chap-new.c
@@ -0,0 +1,677 @@
+/*
+ * 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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/chap_ms.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/chap_ms.c
new file mode 100644
index 0000000..5a989c9
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/chap_ms.c
@@ -0,0 +1,962 @@
+/*
+ * 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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/demand.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/demand.c
new file mode 100644
index 0000000..26c6c30
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/demand.c
@@ -0,0 +1,465 @@
+/*
+ * 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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/eap.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/eap.c
new file mode 100644
index 0000000..8fb5636
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/eap.c
@@ -0,0 +1,2423 @@
+/*
+ * 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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ecp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ecp.c
new file mode 100644
index 0000000..4d84f60
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ecp.c
@@ -0,0 +1,191 @@
+/*
+ * 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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/eui64.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/eui64.c
new file mode 100644
index 0000000..01493bc
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/eui64.c
@@ -0,0 +1,56 @@
+/*
+ * 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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/fsm.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/fsm.c
new file mode 100644
index 0000000..81eba11
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/fsm.c
@@ -0,0 +1,799 @@
+/*
+ * 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: 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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ipcp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ipcp.c
new file mode 100644
index 0000000..b7c766e
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ipcp.c
@@ -0,0 +1,2418 @@
+/*
+ * 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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ipv6cp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ipv6cp.c
new file mode 100644
index 0000000..11c18df
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ipv6cp.c
@@ -0,0 +1,1533 @@
+/*
+ * 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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/lcp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/lcp.c
new file mode 100644
index 0000000..90ed183
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/lcp.c
@@ -0,0 +1,2790 @@
+/*
+ * 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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/magic.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/magic.c
new file mode 100644
index 0000000..d0d87c5
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/magic.c
@@ -0,0 +1,294 @@
+/*
+ * 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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/mppe.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/mppe.c
new file mode 100644
index 0000000..331039f
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/mppe.c
@@ -0,0 +1,412 @@
+/*
+ * 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_POOL);
+ if (!np) {
+ return ERR_MEM;
+ }
+
+ /* Hide MPPE header + protocol */
+ pbuf_header(np, -(s16_t)(MPPE_OVHD + sizeof(protocol)));
+
+ if ((err = pbuf_copy(np, *pb)) != ERR_OK) {
+ pbuf_free(np);
+ return err;
+ }
+
+ /* Reveal MPPE header + protocol */
+ pbuf_header(np, (s16_t)(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_header(np, -(s16_t)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_header(np, (s16_t)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_header(n0, -(s16_t)(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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/multilink.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/multilink.c
new file mode 100644
index 0000000..62014e8
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/multilink.c
@@ -0,0 +1,609 @@
+/*
+ * 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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/README b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/README
new file mode 100644
index 0000000..3fdf159
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/README
@@ -0,0 +1,22 @@
+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/
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/arc4.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/arc4.c
new file mode 100644
index 0000000..6e17ec4
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/arc4.c
@@ -0,0 +1,101 @@
+/*
+ * 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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/des.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/des.c
new file mode 100644
index 0000000..9a89d00
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/des.c
@@ -0,0 +1,422 @@
+/*
+ * 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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/md4.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/md4.c
new file mode 100644
index 0000000..b1701a0
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/md4.c
@@ -0,0 +1,281 @@
+/*
+ * 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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/md5.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/md5.c
new file mode 100644
index 0000000..1ec4d81
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/md5.c
@@ -0,0 +1,300 @@
+/*
+ * 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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/sha1.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/sha1.c
new file mode 100644
index 0000000..c2192ea
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/sha1.c
@@ -0,0 +1,335 @@
+/*
+ * 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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ppp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ppp.c
new file mode 100644
index 0000000..8b77765
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ppp.c
@@ -0,0 +1,1647 @@
+/*****************************************************************************
+* 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 netif
+ * @ingroup addons
+ * @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 LWIP_IPV4
+static err_t ppp_netif_output_ip4(struct netif *netif, struct pbuf *pb, const ip4_addr_t *ipaddr);
+#endif /* LWIP_IPV4 */
+#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) {
+#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) {
+ 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) {
+ if (pcb->phase != PPP_PHASE_DEAD) {
+ return ERR_ALREADY;
+ }
+
+ PPPDEBUG(LOG_DEBUG, ("ppp_connect[%d]: holdoff=%d\n", pcb->netif->num, holdoff));
+
+ 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) {
+ if (pcb->phase != PPP_PHASE_DEAD) {
+ return ERR_ALREADY;
+ }
+
+ PPPDEBUG(LOG_DEBUG, ("ppp_listen[%d]\n", pcb->netif->num));
+
+ 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)
+{
+ 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;
+ 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)
+{
+ 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 LWIP_IPV4
+ /* FIXME: change that when netif_null_output_ip4() will materialize */
+ netif->output = ppp_netif_output_ip4;
+#endif /* LWIP_IPV4 */
+#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 LWIP_IPV4
+/*
+ * 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);
+#if PPP_IPV4_SUPPORT
+ return ppp_netif_output(netif, pb, PPP_IP);
+#else /* PPP_IPV4_SUPPORT */
+ LWIP_UNUSED_ARG(netif);
+ LWIP_UNUSED_ARG(pb);
+ return ERR_IF;
+#endif /* PPP_IPV4_SUPPORT */
+}
+#endif /* LWIP_IPV4 */
+
+#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_header(pb, -(s16_t)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_header(pb, -(s16_t)1);
+ } else {
+ protocol = (pl[0] << 8) | pl[1];
+ pbuf_header(pb, -(s16_t)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 = ppp_singlebuf(pb);
+ (*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 */
+ pbuf_header(pb, (s16_t)sizeof(protocol));
+ 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);
+}
+
+/* merge a pbuf chain into one pbuf */
+struct pbuf *ppp_singlebuf(struct pbuf *p) {
+ struct pbuf *q, *b;
+ u8_t *pl;
+
+ if(p->tot_len == p->len) {
+ return p;
+ }
+
+ q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+ if(!q) {
+ PPPDEBUG(LOG_ERR,
+ ("ppp_singlebuf: unable to alloc new buf (%d)\n", p->tot_len));
+ return p; /* live dangerously */
+ }
+
+ for(b = p, pl = (u8_t*)q->payload; b != NULL; b = b->next) {
+ MEMCPY(pl, b->payload, b->len);
+ pl += b->len;
+ }
+
+ pbuf_free(p);
+
+ return q;
+}
+
+/*
+ * 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(&ns, ns1);
+ dns_setserver(0, &ns);
+ ip_addr_set_ip4_u32(&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(&nsb, ns1);
+ if (ip_addr_cmp(nsa, &nsb)) {
+ dns_setserver(0, IP_ADDR_ANY);
+ }
+ nsa = dns_getserver(1);
+ ip_addr_set_ip4_u32(&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(pcb->netif, 0, IP6_ADDR_ANY6);
+ netif_ip6_addr_set_state(pcb->netif, 0, IP6_ADDR_INVALID);
+ 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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppapi.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppapi.c
new file mode 100644
index 0000000..947f7ba
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppapi.c
@@ -0,0 +1,427 @@
+/**
+ * @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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppcrypt.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppcrypt.c
new file mode 100644
index 0000000..82d78c1
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppcrypt.c
@@ -0,0 +1,66 @@
+/*
+ * 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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppoe.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppoe.c
new file mode 100644
index 0000000..eabfa4d
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppoe.c
@@ -0,0 +1,1191 @@
+/*****************************************************************************
+* 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;
+ LWIP_UNUSED_ARG(service_name);
+ LWIP_UNUSED_ARG(concentrator_name);
+
+ 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;
+ /* 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_header(p, -(s16_t)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_header(ph, -(s16_t)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_header(pb, -(s16_t)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;
+ }
+ }
+
+#ifdef PPPOE_TODO
+ if (sc->sc_concentrator_name) {
+ mem_free(sc->sc_concentrator_name);
+ }
+ if (sc->sc_service_name) {
+ mem_free(sc->sc_service_name);
+ }
+#endif /* PPPOE_TODO */
+ 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;
+ 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 off, 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 = ppp_singlebuf(pb);
+
+ if (pb->len < sizeof(*ethhdr)) {
+ goto done;
+ }
+ ethhdr = (struct eth_hdr *)pb->payload;
+ off = sizeof(*ethhdr);
+
+ ac_cookie = NULL;
+ ac_cookie_len = 0;
+#ifdef PPPOE_SERVER
+ hunique = NULL;
+ hunique_len = 0;
+#endif
+ session = 0;
+ if (pb->len - off < (u16_t)PPPOE_HEADERLEN) {
+ 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);
+ off += sizeof(*ph);
+
+ if (plen + off > pb->len) {
+ 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) {
+ pb->tot_len = pb->len = (u16_t)off + plen; /* ignore trailing garbage */
+ }
+ 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));
+ }
+ 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_header(pb, -(s16_t)sizeof(struct eth_hdr)) != 0) {
+ /* bail out */
+ PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_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_header(pb, -(s16_t)(PPPOE_HEADERLEN)) != 0) {
+ /* bail out */
+ PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_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_header(pb, (s16_t)(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(&ethhdr->dest.addr, &sc->sc_dest.addr, sizeof(ethhdr->dest.addr));
+ MEMCPY(&ethhdr->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;
+#ifdef PPPOE_TODO
+ int l1 = 0, l2 = 0; /* XXX: gcc */
+#endif /* PPPOE_TODO */
+
+ /* 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 */
+#ifdef PPPOE_TODO
+ 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_TODO */
+ 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);
+#ifdef PPPOE_TODO
+ if (sc->sc_service_name != NULL) {
+ PPPOE_ADD_16(p, l1);
+ MEMCPY(p, sc->sc_service_name, l1);
+ p += l1;
+ } else
+#endif /* PPPOE_TODO */
+ {
+ PPPOE_ADD_16(p, 0);
+ }
+#ifdef PPPOE_TODO
+ 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_TODO */
+ 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));
+ }
+ 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));
+ }
+ 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));
+ }
+ 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;
+#ifdef PPPOE_TODO
+ size_t l1 = 0; /* XXX: gcc */
+#endif /* PPPOE_TODO */
+
+ len = 2 + 2 + 2 + 2 + sizeof(sc); /* service name, host unique */
+#ifdef PPPOE_TODO
+ if (sc->sc_service_name != NULL) { /* service name tag maybe empty */
+ l1 = strlen(sc->sc_service_name);
+ len += l1;
+ }
+#endif /* PPPOE_TODO */
+ 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);
+#ifdef PPPOE_TODO
+ if (sc->sc_service_name != NULL) {
+ PPPOE_ADD_16(p, l1);
+ MEMCPY(p, sc->sc_service_name, l1);
+ p += l1;
+ } else
+#endif /* PPPOE_TODO */
+ {
+ 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);
+
+ pbuf_header(pb, (s16_t)sizeof(struct eth_hdr));
+ ethhdr = (struct eth_hdr *)pb->payload;
+ ethhdr->type = PP_HTONS(ETHTYPE_PPPOEDISC);
+ MEMCPY(&ethhdr->dest.addr, dest, sizeof(ethhdr->dest.addr));
+ MEMCPY(&ethhdr->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_header(pb, (s16_t)(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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppol2tp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppol2tp.c
new file mode 100644
index 0000000..d44471e
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppol2tp.c
@@ -0,0 +1,1131 @@
+/**
+ * @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);
+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_header(ph, -(s16_t)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_header(pb, -(s16_t)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_header(p, -(s16_t)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_header(p, -(s16_t)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 */
+
+ l2tp->peer_nr = nr;
+ l2tp->peer_ns = ns;
+ /* printf("L2TP CTRL INPUT, ns=%d, nr=%d, len=%d\n", ns, nr, p->len); */
+
+ /* Handle the special case of the ICCN acknowledge */
+ if (l2tp->phase == PPPOL2TP_STATE_ICCN_SENT && l2tp->peer_nr > l2tp->our_ns) {
+ l2tp->phase = PPPOL2TP_STATE_DATA;
+ }
+
+ /* ZLB packets */
+ if (p->tot_len == 0) {
+ return;
+ }
+
+ p = ppp_singlebuf(p);
+ 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); /* 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_header(p, -(s16_t)(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));
+ }
+ 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));
+ }
+ 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++;
+ ppp_start(l2tp->ppp); /* notify upper layers */
+ if ((err = pppol2tp_send_iccn(l2tp, l2tp->our_ns)) != 0) {
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send ICCN, error=%d\n", err));
+ }
+ 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);
+ 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));
+ }
+ 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 (l2tp->peer_nr <= l2tp->our_ns -1) { /* 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));
+ 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));
+ }
+ 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));
+ }
+ 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+1, 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+1, 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+1, 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) {
+ 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(l2tp->peer_ns+1, 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+1, 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_header(pb, (s16_t)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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppos.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppos.c
new file mode 100644
index 0000000..fb48df4
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppos.c
@@ -0,0 +1,875 @@
+/**
+ * @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;
+
+ 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. */
+ 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;
+ }
+
+ /* 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. */
+ 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;
+ }
+
+ /* 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.
+ *
+ * @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;
+
+ 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);
+
+ 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_header(inp, -(s16_t)(PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN));
+#endif /* IP_FORWARD || LWIP_IPV6_FORWARD */
+#if PPP_INPROC_IRQ_SAFE
+ if(tcpip_callback_with_block(pppos_input_callback, inp, 0) != 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... */
+ 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
+ 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_header(pb, -(s16_t)sizeof(struct pppos_input_header))) {
+ LWIP_ASSERT("pbuf_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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/upap.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/upap.c
new file mode 100644
index 0000000..d00c2d7
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/upap.c
@@ -0,0 +1,677 @@
+/*
+ * 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 = LWIP_MIN(strlen(user), 0xff);
+ pcb->upap.us_passwd = password;
+ pcb->upap.us_passwdlen = 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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/utils.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/utils.c
new file mode 100644
index 0000000..008c633
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/utils.c
@@ -0,0 +1,959 @@
+/*
+ * 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 <ctype.h> /* isdigit() */
+
+#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 (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 (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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/vj.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/vj.c
new file mode 100644
index 0000000..168c340
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/vj.c
@@ -0,0 +1,695 @@
+/*
+ * 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_alloc(PBUF_RAW, np->tot_len, PBUF_POOL);
+ if (!np) {
+ return (TYPE_IP);
+ }
+
+ if (pbuf_copy(np, *pb) != ERR_OK) {
+ pbuf_free(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_header(np, -(s16_t)hlen)){
+ /* Can we cope with this failing? Just assert for now */
+ LWIP_ASSERT("pbuf_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_header(np, -(s16_t)hlen)) {
+ /* Can we cope with this failing? Just assert for now */
+ LWIP_ASSERT("pbuf_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(((struct tcp_hdr *)&((char *)ip)[hlen])) << 2)
+ > 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));
+ comp->flags |= VJF_TOSS;
+ INCR(vjs_errorin);
+ return -1;
+ }
+ cs = &comp->rstate[comp->last_recv = IPH_PROTO(ip)];
+ comp->flags &=~ VJF_TOSS;
+ IPH_PROTO_SET(ip, IP_PROTO_TCP);
+ MEMCPY(&cs->cs_ip, ip, 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_header(n0, -(s16_t)vjlen)) {
+ /* Can we cope with this failing? Just assert for now */
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ goto bad;
+ }
+
+ if(LWIP_MEM_ALIGN(n0->payload) != n0->payload) {
+ struct pbuf *np, *q;
+ u8_t *bufptr;
+
+#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_header(np, -(s16_t)cs->cs_hlen)) {
+ /* Can we cope with this failing? Just assert for now */
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ goto bad;
+ }
+
+ bufptr = (u8_t*)n0->payload;
+ for(q = np; q != NULL; q = q->next) {
+ MEMCPY(q->payload, bufptr, q->len);
+ bufptr += q->len;
+ }
+
+ if(n0->next) {
+ pbuf_chain(np, n0->next);
+ pbuf_dechain(n0);
+ }
+ pbuf_free(n0);
+ n0 = np;
+ }
+
+ if (pbuf_header(n0, (s16_t)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:
+ comp->flags |= VJF_TOSS;
+ INCR(vjs_errorin);
+ return (-1);
+}
+
+#endif /* PPP_SUPPORT && VJ_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/slipif.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/slipif.c
new file mode 100644
index 0000000..6eb83c3
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/slipif.c
@@ -0,0 +1,555 @@
+/**
+ * @file
+ * SLIP Interface
+ *
+ */
+
+/*
+ * 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. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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.
+ *
+ * This file is built upon the file: src/arch/rtxc/netif/sioslip.c
+ *
+ * Author: Magnus Ivarsson <magnus.ivarsson(at)volvo.com>
+ * Simon Goldschmidt
+ */
+
+
+/**
+ * @defgroup slipif SLIP netif
+ * @ingroup addons
+ *
+ * This is an arch independent SLIP netif. The specific serial hooks must be
+ * provided by another file. They are sio_open, sio_read/sio_tryread and sio_send
+ *
+ * Usage: This netif can be used in three ways:\n
+ * 1) For NO_SYS==0, an RX thread can be used which blocks on sio_read()
+ * until data is received.\n
+ * 2) In your main loop, call slipif_poll() to check for new RX bytes,
+ * completed packets are fed into netif->input().\n
+ * 3) Call slipif_received_byte[s]() from your serial RX ISR and
+ * slipif_process_rxqueue() from your main loop. ISR level decodes
+ * packets and puts completed packets on a queue which is fed into
+ * the stack from the main loop (needs SYS_LIGHTWEIGHT_PROT for
+ * pbuf_alloc to work on ISR level!).
+ *
+ */
+
+#include "netif/slipif.h"
+#include "lwip/opt.h"
+
+#include "lwip/def.h"
+#include "lwip/pbuf.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+#include "lwip/sys.h"
+#include "lwip/sio.h"
+
+#define SLIP_END 0xC0 /* 0300: start and end of every packet */
+#define SLIP_ESC 0xDB /* 0333: escape start (one byte escaped data follows) */
+#define SLIP_ESC_END 0xDC /* 0334: following escape: original byte is 0xC0 (END) */
+#define SLIP_ESC_ESC 0xDD /* 0335: following escape: original byte is 0xDB (ESC) */
+
+/** Maximum packet size that is received by this netif */
+#ifndef SLIP_MAX_SIZE
+#define SLIP_MAX_SIZE 1500
+#endif
+
+/** Define this to the interface speed for SNMP
+ * (sio_fd is the sio_fd_t returned by sio_open).
+ * The default value of zero means 'unknown'.
+ */
+#ifndef SLIP_SIO_SPEED
+#define SLIP_SIO_SPEED(sio_fd) 0
+#endif
+
+enum slipif_recv_state {
+ SLIP_RECV_NORMAL,
+ SLIP_RECV_ESCAPE
+};
+
+struct slipif_priv {
+ sio_fd_t sd;
+ /* q is the whole pbuf chain for a packet, p is the current pbuf in the chain */
+ struct pbuf *p, *q;
+ u8_t state;
+ u16_t i, recved;
+#if SLIP_RX_FROM_ISR
+ struct pbuf *rxpackets;
+#endif
+};
+
+/**
+ * Send a pbuf doing the necessary SLIP encapsulation
+ *
+ * Uses the serial layer's sio_send()
+ *
+ * @param netif the lwip network interface structure for this slipif
+ * @param p the pbuf chain packet to send
+ * @return always returns ERR_OK since the serial layer does not provide return values
+ */
+static err_t
+slipif_output(struct netif *netif, struct pbuf *p)
+{
+ struct slipif_priv *priv;
+ struct pbuf *q;
+ u16_t i;
+ u8_t c;
+
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+ LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
+ LWIP_ASSERT("p != NULL", (p != NULL));
+
+ LWIP_DEBUGF(SLIP_DEBUG, ("slipif_output(%"U16_F"): sending %"U16_F" bytes\n", (u16_t)netif->num, p->tot_len));
+ priv = (struct slipif_priv *)netif->state;
+
+ /* Send pbuf out on the serial I/O device. */
+ /* Start with packet delimiter. */
+ sio_send(SLIP_END, priv->sd);
+
+ for (q = p; q != NULL; q = q->next) {
+ for (i = 0; i < q->len; i++) {
+ c = ((u8_t *)q->payload)[i];
+ switch (c) {
+ case SLIP_END:
+ /* need to escape this byte (0xC0 -> 0xDB, 0xDC) */
+ sio_send(SLIP_ESC, priv->sd);
+ sio_send(SLIP_ESC_END, priv->sd);
+ break;
+ case SLIP_ESC:
+ /* need to escape this byte (0xDB -> 0xDB, 0xDD) */
+ sio_send(SLIP_ESC, priv->sd);
+ sio_send(SLIP_ESC_ESC, priv->sd);
+ break;
+ default:
+ /* normal byte - no need for escaping */
+ sio_send(c, priv->sd);
+ break;
+ }
+ }
+ }
+ /* End with packet delimiter. */
+ sio_send(SLIP_END, priv->sd);
+ return ERR_OK;
+}
+
+#if LWIP_IPV4
+/**
+ * Send a pbuf doing the necessary SLIP encapsulation
+ *
+ * Uses the serial layer's sio_send()
+ *
+ * @param netif the lwip network interface structure for this slipif
+ * @param p the pbuf chain packet to send
+ * @param ipaddr the ip address to send the packet to (not used for slipif)
+ * @return always returns ERR_OK since the serial layer does not provide return values
+ */
+static err_t
+slipif_output_v4(struct netif *netif, struct pbuf *p, const ip4_addr_t *ipaddr)
+{
+ LWIP_UNUSED_ARG(ipaddr);
+ return slipif_output(netif, p);
+}
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+/**
+ * Send a pbuf doing the necessary SLIP encapsulation
+ *
+ * Uses the serial layer's sio_send()
+ *
+ * @param netif the lwip network interface structure for this slipif
+ * @param p the pbuf chain packet to send
+ * @param ipaddr the ip address to send the packet to (not used for slipif)
+ * @return always returns ERR_OK since the serial layer does not provide return values
+ */
+static err_t
+slipif_output_v6(struct netif *netif, struct pbuf *p, const ip6_addr_t *ipaddr)
+{
+ LWIP_UNUSED_ARG(ipaddr);
+ return slipif_output(netif, p);
+}
+#endif /* LWIP_IPV6 */
+
+/**
+ * Handle the incoming SLIP stream character by character
+ *
+ * @param netif the lwip network interface structure for this slipif
+ * @param c received character (multiple calls to this function will
+ * return a complete packet, NULL is returned before - used for polling)
+ * @return The IP packet when SLIP_END is received
+ */
+static struct pbuf*
+slipif_rxbyte(struct netif *netif, u8_t c)
+{
+ struct slipif_priv *priv;
+ struct pbuf *t;
+
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+ LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
+
+ priv = (struct slipif_priv *)netif->state;
+
+ switch (priv->state) {
+ case SLIP_RECV_NORMAL:
+ switch (c) {
+ case SLIP_END:
+ if (priv->recved > 0) {
+ /* Received whole packet. */
+ /* Trim the pbuf to the size of the received packet. */
+ pbuf_realloc(priv->q, priv->recved);
+
+ LINK_STATS_INC(link.recv);
+
+ LWIP_DEBUGF(SLIP_DEBUG, ("slipif: Got packet (%"U16_F" bytes)\n", priv->recved));
+ t = priv->q;
+ priv->p = priv->q = NULL;
+ priv->i = priv->recved = 0;
+ return t;
+ }
+ return NULL;
+ case SLIP_ESC:
+ priv->state = SLIP_RECV_ESCAPE;
+ return NULL;
+ default:
+ break;
+ } /* end switch (c) */
+ break;
+ case SLIP_RECV_ESCAPE:
+ /* un-escape END or ESC bytes, leave other bytes
+ (although that would be a protocol error) */
+ switch (c) {
+ case SLIP_ESC_END:
+ c = SLIP_END;
+ break;
+ case SLIP_ESC_ESC:
+ c = SLIP_ESC;
+ break;
+ default:
+ break;
+ }
+ priv->state = SLIP_RECV_NORMAL;
+ break;
+ default:
+ break;
+ } /* end switch (priv->state) */
+
+ /* byte received, packet not yet completely received */
+ if (priv->p == NULL) {
+ /* allocate a new pbuf */
+ LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: alloc\n"));
+ priv->p = pbuf_alloc(PBUF_LINK, (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN - PBUF_LINK_ENCAPSULATION_HLEN), PBUF_POOL);
+
+ if (priv->p == NULL) {
+ LINK_STATS_INC(link.drop);
+ LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: no new pbuf! (DROP)\n"));
+ /* don't process any further since we got no pbuf to receive to */
+ return NULL;
+ }
+
+ if (priv->q != NULL) {
+ /* 'chain' the pbuf to the existing chain */
+ pbuf_cat(priv->q, priv->p);
+ } else {
+ /* p is the first pbuf in the chain */
+ priv->q = priv->p;
+ }
+ }
+
+ /* this automatically drops bytes if > SLIP_MAX_SIZE */
+ if ((priv->p != NULL) && (priv->recved <= SLIP_MAX_SIZE)) {
+ ((u8_t *)priv->p->payload)[priv->i] = c;
+ priv->recved++;
+ priv->i++;
+ if (priv->i >= priv->p->len) {
+ /* on to the next pbuf */
+ priv->i = 0;
+ if (priv->p->next != NULL && priv->p->next->len > 0) {
+ /* p is a chain, on to the next in the chain */
+ priv->p = priv->p->next;
+ } else {
+ /* p is a single pbuf, set it to NULL so next time a new
+ * pbuf is allocated */
+ priv->p = NULL;
+ }
+ }
+ }
+ return NULL;
+}
+
+/** Like slipif_rxbyte, but passes completed packets to netif->input
+ *
+ * @param netif The lwip network interface structure for this slipif
+ * @param c received character
+ */
+static void
+slipif_rxbyte_input(struct netif *netif, u8_t c)
+{
+ struct pbuf *p;
+ p = slipif_rxbyte(netif, c);
+ if (p != NULL) {
+ if (netif->input(p, netif) != ERR_OK) {
+ pbuf_free(p);
+ }
+ }
+}
+
+#if SLIP_USE_RX_THREAD
+/**
+ * The SLIP input thread.
+ *
+ * Feed the IP layer with incoming packets
+ *
+ * @param nf the lwip network interface structure for this slipif
+ */
+static void
+slipif_loop_thread(void *nf)
+{
+ u8_t c;
+ struct netif *netif = (struct netif *)nf;
+ struct slipif_priv *priv = (struct slipif_priv *)netif->state;
+
+ while (1) {
+ if (sio_read(priv->sd, &c, 1) > 0) {
+ slipif_rxbyte_input(netif, c);
+ }
+ }
+}
+#endif /* SLIP_USE_RX_THREAD */
+
+/**
+ * SLIP netif initialization
+ *
+ * Call the arch specific sio_open and remember
+ * the opened device in the state field of the netif.
+ *
+ * @param netif the lwip network interface structure for this slipif
+ * @return ERR_OK if serial line could be opened,
+ * ERR_MEM if no memory could be allocated,
+ * ERR_IF is serial line couldn't be opened
+ *
+ * @note netif->num must contain the number of the serial port to open
+ * (0 by default). If netif->state is != NULL, it is interpreted as an
+ * u8_t pointer pointing to the serial port number instead of netif->num.
+ *
+ */
+err_t
+slipif_init(struct netif *netif)
+{
+ struct slipif_priv *priv;
+ u8_t sio_num;
+
+ LWIP_DEBUGF(SLIP_DEBUG, ("slipif_init: netif->num=%"U16_F"\n", (u16_t)netif->num));
+
+ /* Allocate private data */
+ priv = (struct slipif_priv *)mem_malloc(sizeof(struct slipif_priv));
+ if (!priv) {
+ return ERR_MEM;
+ }
+
+ netif->name[0] = 's';
+ netif->name[1] = 'l';
+#if LWIP_IPV4
+ netif->output = slipif_output_v4;
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+ netif->output_ip6 = slipif_output_v6;
+#endif /* LWIP_IPV6 */
+ netif->mtu = SLIP_MAX_SIZE;
+
+ /* netif->state or netif->num contain the port number */
+ if (netif->state != NULL) {
+ sio_num = *(u8_t*)netif->state;
+ } else {
+ sio_num = netif->num;
+ }
+ /* Try to open the serial port. */
+ priv->sd = sio_open(sio_num);
+ if (!priv->sd) {
+ /* Opening the serial port failed. */
+ mem_free(priv);
+ return ERR_IF;
+ }
+
+ /* Initialize private data */
+ priv->p = NULL;
+ priv->q = NULL;
+ priv->state = SLIP_RECV_NORMAL;
+ priv->i = 0;
+ priv->recved = 0;
+#if SLIP_RX_FROM_ISR
+ priv->rxpackets = NULL;
+#endif
+
+ netif->state = priv;
+
+ /* initialize the snmp variables and counters inside the struct netif */
+ MIB2_INIT_NETIF(netif, snmp_ifType_slip, SLIP_SIO_SPEED(priv->sd));
+
+#if SLIP_USE_RX_THREAD
+ /* Create a thread to poll the serial line. */
+ sys_thread_new(SLIPIF_THREAD_NAME, slipif_loop_thread, netif,
+ SLIPIF_THREAD_STACKSIZE, SLIPIF_THREAD_PRIO);
+#endif /* SLIP_USE_RX_THREAD */
+ return ERR_OK;
+}
+
+/**
+ * Polls the serial device and feeds the IP layer with incoming packets.
+ *
+ * @param netif The lwip network interface structure for this slipif
+ */
+void
+slipif_poll(struct netif *netif)
+{
+ u8_t c;
+ struct slipif_priv *priv;
+
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+ LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
+
+ priv = (struct slipif_priv *)netif->state;
+
+ while (sio_tryread(priv->sd, &c, 1) > 0) {
+ slipif_rxbyte_input(netif, c);
+ }
+}
+
+#if SLIP_RX_FROM_ISR
+/**
+ * Feeds the IP layer with incoming packets that were receive
+ *
+ * @param netif The lwip network interface structure for this slipif
+ */
+void
+slipif_process_rxqueue(struct netif *netif)
+{
+ struct slipif_priv *priv;
+ SYS_ARCH_DECL_PROTECT(old_level);
+
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+ LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
+
+ priv = (struct slipif_priv *)netif->state;
+
+ SYS_ARCH_PROTECT(old_level);
+ while (priv->rxpackets != NULL) {
+ struct pbuf *p = priv->rxpackets;
+#if SLIP_RX_QUEUE
+ /* dequeue packet */
+ struct pbuf *q = p;
+ while ((q->len != q->tot_len) && (q->next != NULL)) {
+ q = q->next;
+ }
+ priv->rxpackets = q->next;
+ q->next = NULL;
+#else /* SLIP_RX_QUEUE */
+ priv->rxpackets = NULL;
+#endif /* SLIP_RX_QUEUE */
+ SYS_ARCH_UNPROTECT(old_level);
+ if (netif->input(p, netif) != ERR_OK) {
+ pbuf_free(p);
+ }
+ SYS_ARCH_PROTECT(old_level);
+ }
+}
+
+/** Like slipif_rxbyte, but queues completed packets.
+ *
+ * @param netif The lwip network interface structure for this slipif
+ * @param data Received serial byte
+ */
+static void
+slipif_rxbyte_enqueue(struct netif *netif, u8_t data)
+{
+ struct pbuf *p;
+ struct slipif_priv *priv = (struct slipif_priv *)netif->state;
+ SYS_ARCH_DECL_PROTECT(old_level);
+
+ p = slipif_rxbyte(netif, data);
+ if (p != NULL) {
+ SYS_ARCH_PROTECT(old_level);
+ if (priv->rxpackets != NULL) {
+#if SLIP_RX_QUEUE
+ /* queue multiple pbufs */
+ struct pbuf *q = p;
+ while (q->next != NULL) {
+ q = q->next;
+ }
+ q->next = p;
+ } else {
+#else /* SLIP_RX_QUEUE */
+ pbuf_free(priv->rxpackets);
+ }
+ {
+#endif /* SLIP_RX_QUEUE */
+ priv->rxpackets = p;
+ }
+ SYS_ARCH_UNPROTECT(old_level);
+ }
+}
+
+/**
+ * Process a received byte, completed packets are put on a queue that is
+ * fed into IP through slipif_process_rxqueue().
+ *
+ * This function can be called from ISR if SYS_LIGHTWEIGHT_PROT is enabled.
+ *
+ * @param netif The lwip network interface structure for this slipif
+ * @param data received character
+ */
+void
+slipif_received_byte(struct netif *netif, u8_t data)
+{
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+ LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
+ slipif_rxbyte_enqueue(netif, data);
+}
+
+/**
+ * Process multiple received byte, completed packets are put on a queue that is
+ * fed into IP through slipif_process_rxqueue().
+ *
+ * This function can be called from ISR if SYS_LIGHTWEIGHT_PROT is enabled.
+ *
+ * @param netif The lwip network interface structure for this slipif
+ * @param data received character
+ * @param len Number of received characters
+ */
+void
+slipif_received_bytes(struct netif *netif, u8_t *data, u8_t len)
+{
+ u8_t i;
+ u8_t *rxdata = data;
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+ LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
+
+ for (i = 0; i < len; i++, rxdata++) {
+ slipif_rxbyte_enqueue(netif, *rxdata);
+ }
+}
+#endif /* SLIP_RX_FROM_ISR */