1195972f6Sopenharmony_ci/*****************************************************************************
2195972f6Sopenharmony_ci* pppoe.c - PPP Over Ethernet implementation for lwIP.
3195972f6Sopenharmony_ci*
4195972f6Sopenharmony_ci* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc.
5195972f6Sopenharmony_ci*
6195972f6Sopenharmony_ci* The authors hereby grant permission to use, copy, modify, distribute,
7195972f6Sopenharmony_ci* and license this software and its documentation for any purpose, provided
8195972f6Sopenharmony_ci* that existing copyright notices are retained in all copies and that this
9195972f6Sopenharmony_ci* notice and the following disclaimer are included verbatim in any
10195972f6Sopenharmony_ci* distributions. No written agreement, license, or royalty fee is required
11195972f6Sopenharmony_ci* for any of the authorized uses.
12195972f6Sopenharmony_ci*
13195972f6Sopenharmony_ci* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
14195972f6Sopenharmony_ci* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15195972f6Sopenharmony_ci* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16195972f6Sopenharmony_ci* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
17195972f6Sopenharmony_ci* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18195972f6Sopenharmony_ci* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19195972f6Sopenharmony_ci* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20195972f6Sopenharmony_ci* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21195972f6Sopenharmony_ci* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22195972f6Sopenharmony_ci* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23195972f6Sopenharmony_ci*
24195972f6Sopenharmony_ci******************************************************************************
25195972f6Sopenharmony_ci* REVISION HISTORY
26195972f6Sopenharmony_ci*
27195972f6Sopenharmony_ci* 06-01-01 Marc Boucher <marc@mbsi.ca>
28195972f6Sopenharmony_ci*   Ported to lwIP.
29195972f6Sopenharmony_ci*****************************************************************************/
30195972f6Sopenharmony_ci
31195972f6Sopenharmony_ci
32195972f6Sopenharmony_ci
33195972f6Sopenharmony_ci/* based on NetBSD: if_pppoe.c,v 1.64 2006/01/31 23:50:15 martin Exp */
34195972f6Sopenharmony_ci
35195972f6Sopenharmony_ci/*-
36195972f6Sopenharmony_ci * Copyright (c) 2002 The NetBSD Foundation, Inc.
37195972f6Sopenharmony_ci * All rights reserved.
38195972f6Sopenharmony_ci *
39195972f6Sopenharmony_ci * This code is derived from software contributed to The NetBSD Foundation
40195972f6Sopenharmony_ci * by Martin Husemann <martin@NetBSD.org>.
41195972f6Sopenharmony_ci *
42195972f6Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
43195972f6Sopenharmony_ci * modification, are permitted provided that the following conditions
44195972f6Sopenharmony_ci * are met:
45195972f6Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright
46195972f6Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
47195972f6Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
48195972f6Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
49195972f6Sopenharmony_ci *    documentation and/or other materials provided with the distribution.
50195972f6Sopenharmony_ci * 3. All advertising materials mentioning features or use of this software
51195972f6Sopenharmony_ci *    must display the following acknowledgement:
52195972f6Sopenharmony_ci *        This product includes software developed by the NetBSD
53195972f6Sopenharmony_ci *        Foundation, Inc. and its contributors.
54195972f6Sopenharmony_ci * 4. Neither the name of The NetBSD Foundation nor the names of its
55195972f6Sopenharmony_ci *    contributors may be used to endorse or promote products derived
56195972f6Sopenharmony_ci *    from this software without specific prior written permission.
57195972f6Sopenharmony_ci *
58195972f6Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
59195972f6Sopenharmony_ci * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
60195972f6Sopenharmony_ci * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
61195972f6Sopenharmony_ci * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
62195972f6Sopenharmony_ci * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
63195972f6Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
64195972f6Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
65195972f6Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
66195972f6Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
67195972f6Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
68195972f6Sopenharmony_ci * POSSIBILITY OF SUCH DAMAGE.
69195972f6Sopenharmony_ci */
70195972f6Sopenharmony_ci
71195972f6Sopenharmony_ci#include "netif/ppp/ppp_opts.h"
72195972f6Sopenharmony_ci#if PPP_SUPPORT && PPPOE_SUPPORT /* don't build if not configured for use in lwipopts.h */
73195972f6Sopenharmony_ci
74195972f6Sopenharmony_ci#if 0 /* UNUSED */
75195972f6Sopenharmony_ci#include <string.h>
76195972f6Sopenharmony_ci#include <stdio.h>
77195972f6Sopenharmony_ci#endif /* UNUSED */
78195972f6Sopenharmony_ci
79195972f6Sopenharmony_ci#include "lwip/timeouts.h"
80195972f6Sopenharmony_ci#include "lwip/memp.h"
81195972f6Sopenharmony_ci#include "lwip/stats.h"
82195972f6Sopenharmony_ci#include "lwip/snmp.h"
83195972f6Sopenharmony_ci
84195972f6Sopenharmony_ci#include "netif/ethernet.h"
85195972f6Sopenharmony_ci#include "netif/ppp/ppp_impl.h"
86195972f6Sopenharmony_ci#include "netif/ppp/lcp.h"
87195972f6Sopenharmony_ci#include "netif/ppp/ipcp.h"
88195972f6Sopenharmony_ci#include "netif/ppp/pppoe.h"
89195972f6Sopenharmony_ci
90195972f6Sopenharmony_ci/* Memory pool */
91195972f6Sopenharmony_ciLWIP_MEMPOOL_DECLARE(PPPOE_IF, MEMP_NUM_PPPOE_INTERFACES, sizeof(struct pppoe_softc), "PPPOE_IF")
92195972f6Sopenharmony_ci
93195972f6Sopenharmony_ci/* Add a 16 bit unsigned value to a buffer pointed to by PTR */
94195972f6Sopenharmony_ci#define PPPOE_ADD_16(PTR, VAL) \
95195972f6Sopenharmony_ci    *(PTR)++ = (u8_t)((VAL) / 256);    \
96195972f6Sopenharmony_ci    *(PTR)++ = (u8_t)((VAL) % 256)
97195972f6Sopenharmony_ci
98195972f6Sopenharmony_ci/* Add a complete PPPoE header to the buffer pointed to by PTR */
99195972f6Sopenharmony_ci#define PPPOE_ADD_HEADER(PTR, CODE, SESS, LEN)  \
100195972f6Sopenharmony_ci    *(PTR)++ = PPPOE_VERTYPE;  \
101195972f6Sopenharmony_ci    *(PTR)++ = (CODE);         \
102195972f6Sopenharmony_ci    PPPOE_ADD_16(PTR, SESS);   \
103195972f6Sopenharmony_ci    PPPOE_ADD_16(PTR, LEN)
104195972f6Sopenharmony_ci
105195972f6Sopenharmony_ci#define PPPOE_DISC_TIMEOUT (5*1000)  /* base for quick timeout calculation */
106195972f6Sopenharmony_ci#define PPPOE_SLOW_RETRY   (60*1000) /* persistent retry interval */
107195972f6Sopenharmony_ci#define PPPOE_DISC_MAXPADI  4        /* retry PADI four times (quickly) */
108195972f6Sopenharmony_ci#define PPPOE_DISC_MAXPADR  2        /* retry PADR twice */
109195972f6Sopenharmony_ci
110195972f6Sopenharmony_ci#ifdef PPPOE_SERVER
111195972f6Sopenharmony_ci#error "PPPOE_SERVER is not yet supported under lwIP!"
112195972f6Sopenharmony_ci/* from if_spppsubr.c */
113195972f6Sopenharmony_ci#define IFF_PASSIVE IFF_LINK0 /* wait passively for connection */
114195972f6Sopenharmony_ci#endif
115195972f6Sopenharmony_ci
116195972f6Sopenharmony_ci#define PPPOE_ERRORSTRING_LEN     64
117195972f6Sopenharmony_ci
118195972f6Sopenharmony_ci
119195972f6Sopenharmony_ci/* callbacks called from PPP core */
120195972f6Sopenharmony_cistatic err_t pppoe_write(ppp_pcb *ppp, void *ctx, struct pbuf *p);
121195972f6Sopenharmony_cistatic err_t pppoe_netif_output(ppp_pcb *ppp, void *ctx, struct pbuf *p, u_short protocol);
122195972f6Sopenharmony_cistatic void pppoe_connect(ppp_pcb *ppp, void *ctx);
123195972f6Sopenharmony_cistatic void pppoe_disconnect(ppp_pcb *ppp, void *ctx);
124195972f6Sopenharmony_cistatic err_t pppoe_destroy(ppp_pcb *ppp, void *ctx);
125195972f6Sopenharmony_ci
126195972f6Sopenharmony_ci/* management routines */
127195972f6Sopenharmony_cistatic void pppoe_abort_connect(struct pppoe_softc *);
128195972f6Sopenharmony_ci#if 0 /* UNUSED */
129195972f6Sopenharmony_cistatic void pppoe_clear_softc(struct pppoe_softc *, const char *);
130195972f6Sopenharmony_ci#endif /* UNUSED */
131195972f6Sopenharmony_ci
132195972f6Sopenharmony_ci/* internal timeout handling */
133195972f6Sopenharmony_cistatic void pppoe_timeout(void *);
134195972f6Sopenharmony_ci
135195972f6Sopenharmony_ci/* sending actual protocol controll packets */
136195972f6Sopenharmony_cistatic err_t pppoe_send_padi(struct pppoe_softc *);
137195972f6Sopenharmony_cistatic err_t pppoe_send_padr(struct pppoe_softc *);
138195972f6Sopenharmony_ci#ifdef PPPOE_SERVER
139195972f6Sopenharmony_cistatic err_t pppoe_send_pado(struct pppoe_softc *);
140195972f6Sopenharmony_cistatic err_t pppoe_send_pads(struct pppoe_softc *);
141195972f6Sopenharmony_ci#endif
142195972f6Sopenharmony_cistatic err_t pppoe_send_padt(struct netif *, u_int, const u8_t *);
143195972f6Sopenharmony_ci
144195972f6Sopenharmony_ci/* internal helper functions */
145195972f6Sopenharmony_cistatic err_t pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb);
146195972f6Sopenharmony_cistatic struct pppoe_softc* pppoe_find_softc_by_session(u_int session, struct netif *rcvif);
147195972f6Sopenharmony_cistatic struct pppoe_softc* pppoe_find_softc_by_hunique(u8_t *token, size_t len, struct netif *rcvif);
148195972f6Sopenharmony_ci
149195972f6Sopenharmony_ci/** linked list of created pppoe interfaces */
150195972f6Sopenharmony_cistatic struct pppoe_softc *pppoe_softc_list;
151195972f6Sopenharmony_ci
152195972f6Sopenharmony_ci/* Callbacks structure for PPP core */
153195972f6Sopenharmony_cistatic const struct link_callbacks pppoe_callbacks = {
154195972f6Sopenharmony_ci  pppoe_connect,
155195972f6Sopenharmony_ci#if PPP_SERVER
156195972f6Sopenharmony_ci  NULL,
157195972f6Sopenharmony_ci#endif /* PPP_SERVER */
158195972f6Sopenharmony_ci  pppoe_disconnect,
159195972f6Sopenharmony_ci  pppoe_destroy,
160195972f6Sopenharmony_ci  pppoe_write,
161195972f6Sopenharmony_ci  pppoe_netif_output,
162195972f6Sopenharmony_ci  NULL,
163195972f6Sopenharmony_ci  NULL
164195972f6Sopenharmony_ci};
165195972f6Sopenharmony_ci
166195972f6Sopenharmony_ci/*
167195972f6Sopenharmony_ci * Create a new PPP Over Ethernet (PPPoE) connection.
168195972f6Sopenharmony_ci *
169195972f6Sopenharmony_ci * Return 0 on success, an error code on failure.
170195972f6Sopenharmony_ci */
171195972f6Sopenharmony_cippp_pcb *pppoe_create(struct netif *pppif,
172195972f6Sopenharmony_ci       struct netif *ethif,
173195972f6Sopenharmony_ci       const char *service_name, const char *concentrator_name,
174195972f6Sopenharmony_ci       ppp_link_status_cb_fn link_status_cb, void *ctx_cb)
175195972f6Sopenharmony_ci{
176195972f6Sopenharmony_ci  ppp_pcb *ppp;
177195972f6Sopenharmony_ci  struct pppoe_softc *sc;
178195972f6Sopenharmony_ci#if !PPPOE_SCNAME_SUPPORT
179195972f6Sopenharmony_ci  LWIP_UNUSED_ARG(service_name);
180195972f6Sopenharmony_ci  LWIP_UNUSED_ARG(concentrator_name);
181195972f6Sopenharmony_ci#endif /* !PPPOE_SCNAME_SUPPORT */
182195972f6Sopenharmony_ci  LWIP_ASSERT_CORE_LOCKED();
183195972f6Sopenharmony_ci
184195972f6Sopenharmony_ci  sc = (struct pppoe_softc *)LWIP_MEMPOOL_ALLOC(PPPOE_IF);
185195972f6Sopenharmony_ci  if (sc == NULL) {
186195972f6Sopenharmony_ci    return NULL;
187195972f6Sopenharmony_ci  }
188195972f6Sopenharmony_ci
189195972f6Sopenharmony_ci  ppp = ppp_new(pppif, &pppoe_callbacks, sc, link_status_cb, ctx_cb);
190195972f6Sopenharmony_ci  if (ppp == NULL) {
191195972f6Sopenharmony_ci    LWIP_MEMPOOL_FREE(PPPOE_IF, sc);
192195972f6Sopenharmony_ci    return NULL;
193195972f6Sopenharmony_ci  }
194195972f6Sopenharmony_ci
195195972f6Sopenharmony_ci  memset(sc, 0, sizeof(struct pppoe_softc));
196195972f6Sopenharmony_ci  sc->pcb = ppp;
197195972f6Sopenharmony_ci  sc->sc_ethif = ethif;
198195972f6Sopenharmony_ci#if PPPOE_SCNAME_SUPPORT
199195972f6Sopenharmony_ci  sc->sc_service_name = service_name;
200195972f6Sopenharmony_ci  sc->sc_concentrator_name = concentrator_name;
201195972f6Sopenharmony_ci#endif /* PPPOE_SCNAME_SUPPORT */
202195972f6Sopenharmony_ci  /* put the new interface at the head of the list */
203195972f6Sopenharmony_ci  sc->next = pppoe_softc_list;
204195972f6Sopenharmony_ci  pppoe_softc_list = sc;
205195972f6Sopenharmony_ci  return ppp;
206195972f6Sopenharmony_ci}
207195972f6Sopenharmony_ci
208195972f6Sopenharmony_ci/* Called by PPP core */
209195972f6Sopenharmony_cistatic err_t pppoe_write(ppp_pcb *ppp, void *ctx, struct pbuf *p) {
210195972f6Sopenharmony_ci  struct pppoe_softc *sc = (struct pppoe_softc *)ctx;
211195972f6Sopenharmony_ci  struct pbuf *ph; /* Ethernet + PPPoE header */
212195972f6Sopenharmony_ci  err_t ret;
213195972f6Sopenharmony_ci#if MIB2_STATS
214195972f6Sopenharmony_ci  u16_t tot_len;
215195972f6Sopenharmony_ci#else /* MIB2_STATS */
216195972f6Sopenharmony_ci  LWIP_UNUSED_ARG(ppp);
217195972f6Sopenharmony_ci#endif /* MIB2_STATS */
218195972f6Sopenharmony_ci
219195972f6Sopenharmony_ci  /* skip address & flags */
220195972f6Sopenharmony_ci  pbuf_remove_header(p, 2);
221195972f6Sopenharmony_ci
222195972f6Sopenharmony_ci  ph = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN), PBUF_RAM);
223195972f6Sopenharmony_ci  if(!ph) {
224195972f6Sopenharmony_ci    LINK_STATS_INC(link.memerr);
225195972f6Sopenharmony_ci    LINK_STATS_INC(link.proterr);
226195972f6Sopenharmony_ci    MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
227195972f6Sopenharmony_ci    pbuf_free(p);
228195972f6Sopenharmony_ci    return ERR_MEM;
229195972f6Sopenharmony_ci  }
230195972f6Sopenharmony_ci
231195972f6Sopenharmony_ci  pbuf_remove_header(ph, PPPOE_HEADERLEN); /* hide PPPoE header */
232195972f6Sopenharmony_ci  pbuf_cat(ph, p);
233195972f6Sopenharmony_ci#if MIB2_STATS
234195972f6Sopenharmony_ci  tot_len = ph->tot_len;
235195972f6Sopenharmony_ci#endif /* MIB2_STATS */
236195972f6Sopenharmony_ci
237195972f6Sopenharmony_ci  ret = pppoe_xmit(sc, ph);
238195972f6Sopenharmony_ci  if (ret != ERR_OK) {
239195972f6Sopenharmony_ci    LINK_STATS_INC(link.err);
240195972f6Sopenharmony_ci    MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
241195972f6Sopenharmony_ci    return ret;
242195972f6Sopenharmony_ci  }
243195972f6Sopenharmony_ci
244195972f6Sopenharmony_ci  MIB2_STATS_NETIF_ADD(ppp->netif, ifoutoctets, (u16_t)tot_len);
245195972f6Sopenharmony_ci  MIB2_STATS_NETIF_INC(ppp->netif, ifoutucastpkts);
246195972f6Sopenharmony_ci  LINK_STATS_INC(link.xmit);
247195972f6Sopenharmony_ci  return ERR_OK;
248195972f6Sopenharmony_ci}
249195972f6Sopenharmony_ci
250195972f6Sopenharmony_ci/* Called by PPP core */
251195972f6Sopenharmony_cistatic err_t pppoe_netif_output(ppp_pcb *ppp, void *ctx, struct pbuf *p, u_short protocol) {
252195972f6Sopenharmony_ci  struct pppoe_softc *sc = (struct pppoe_softc *)ctx;
253195972f6Sopenharmony_ci  struct pbuf *pb;
254195972f6Sopenharmony_ci  u8_t *pl;
255195972f6Sopenharmony_ci  err_t err;
256195972f6Sopenharmony_ci#if MIB2_STATS
257195972f6Sopenharmony_ci  u16_t tot_len;
258195972f6Sopenharmony_ci#else /* MIB2_STATS */
259195972f6Sopenharmony_ci  LWIP_UNUSED_ARG(ppp);
260195972f6Sopenharmony_ci#endif /* MIB2_STATS */
261195972f6Sopenharmony_ci
262195972f6Sopenharmony_ci  /* @todo: try to use pbuf_header() here! */
263195972f6Sopenharmony_ci  pb = pbuf_alloc(PBUF_LINK, PPPOE_HEADERLEN + sizeof(protocol), PBUF_RAM);
264195972f6Sopenharmony_ci  if(!pb) {
265195972f6Sopenharmony_ci    LINK_STATS_INC(link.memerr);
266195972f6Sopenharmony_ci    LINK_STATS_INC(link.proterr);
267195972f6Sopenharmony_ci    MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
268195972f6Sopenharmony_ci    return ERR_MEM;
269195972f6Sopenharmony_ci  }
270195972f6Sopenharmony_ci
271195972f6Sopenharmony_ci  pbuf_remove_header(pb, PPPOE_HEADERLEN);
272195972f6Sopenharmony_ci
273195972f6Sopenharmony_ci  pl = (u8_t*)pb->payload;
274195972f6Sopenharmony_ci  PUTSHORT(protocol, pl);
275195972f6Sopenharmony_ci
276195972f6Sopenharmony_ci  pbuf_chain(pb, p);
277195972f6Sopenharmony_ci#if MIB2_STATS
278195972f6Sopenharmony_ci  tot_len = pb->tot_len;
279195972f6Sopenharmony_ci#endif /* MIB2_STATS */
280195972f6Sopenharmony_ci
281195972f6Sopenharmony_ci  if( (err = pppoe_xmit(sc, pb)) != ERR_OK) {
282195972f6Sopenharmony_ci    LINK_STATS_INC(link.err);
283195972f6Sopenharmony_ci    MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
284195972f6Sopenharmony_ci    return err;
285195972f6Sopenharmony_ci  }
286195972f6Sopenharmony_ci
287195972f6Sopenharmony_ci  MIB2_STATS_NETIF_ADD(ppp->netif, ifoutoctets, tot_len);
288195972f6Sopenharmony_ci  MIB2_STATS_NETIF_INC(ppp->netif, ifoutucastpkts);
289195972f6Sopenharmony_ci  LINK_STATS_INC(link.xmit);
290195972f6Sopenharmony_ci  return ERR_OK;
291195972f6Sopenharmony_ci}
292195972f6Sopenharmony_ci
293195972f6Sopenharmony_cistatic err_t
294195972f6Sopenharmony_cipppoe_destroy(ppp_pcb *ppp, void *ctx)
295195972f6Sopenharmony_ci{
296195972f6Sopenharmony_ci  struct pppoe_softc *sc = (struct pppoe_softc *)ctx;
297195972f6Sopenharmony_ci  struct pppoe_softc **copp, *freep;
298195972f6Sopenharmony_ci  LWIP_UNUSED_ARG(ppp);
299195972f6Sopenharmony_ci
300195972f6Sopenharmony_ci  sys_untimeout(pppoe_timeout, sc);
301195972f6Sopenharmony_ci
302195972f6Sopenharmony_ci  /* remove interface from list */
303195972f6Sopenharmony_ci  for (copp = &pppoe_softc_list; (freep = *copp); copp = &freep->next) {
304195972f6Sopenharmony_ci    if (freep == sc) {
305195972f6Sopenharmony_ci       *copp = freep->next;
306195972f6Sopenharmony_ci       break;
307195972f6Sopenharmony_ci    }
308195972f6Sopenharmony_ci  }
309195972f6Sopenharmony_ci  LWIP_MEMPOOL_FREE(PPPOE_IF, sc);
310195972f6Sopenharmony_ci
311195972f6Sopenharmony_ci  return ERR_OK;
312195972f6Sopenharmony_ci}
313195972f6Sopenharmony_ci
314195972f6Sopenharmony_ci/*
315195972f6Sopenharmony_ci * Find the interface handling the specified session.
316195972f6Sopenharmony_ci * Note: O(number of sessions open), this is a client-side only, mean
317195972f6Sopenharmony_ci * and lean implementation, so number of open sessions typically should
318195972f6Sopenharmony_ci * be 1.
319195972f6Sopenharmony_ci */
320195972f6Sopenharmony_cistatic struct pppoe_softc* pppoe_find_softc_by_session(u_int session, struct netif *rcvif) {
321195972f6Sopenharmony_ci  struct pppoe_softc *sc;
322195972f6Sopenharmony_ci
323195972f6Sopenharmony_ci  for (sc = pppoe_softc_list; sc != NULL; sc = sc->next) {
324195972f6Sopenharmony_ci    if (sc->sc_state == PPPOE_STATE_SESSION
325195972f6Sopenharmony_ci        && sc->sc_session == session
326195972f6Sopenharmony_ci         && sc->sc_ethif == rcvif) {
327195972f6Sopenharmony_ci           return sc;
328195972f6Sopenharmony_ci      }
329195972f6Sopenharmony_ci  }
330195972f6Sopenharmony_ci  return NULL;
331195972f6Sopenharmony_ci}
332195972f6Sopenharmony_ci
333195972f6Sopenharmony_ci/* Check host unique token passed and return appropriate softc pointer,
334195972f6Sopenharmony_ci * or NULL if token is bogus. */
335195972f6Sopenharmony_cistatic struct pppoe_softc* pppoe_find_softc_by_hunique(u8_t *token, size_t len, struct netif *rcvif) {
336195972f6Sopenharmony_ci  struct pppoe_softc *sc, *t;
337195972f6Sopenharmony_ci
338195972f6Sopenharmony_ci  if (len != sizeof sc) {
339195972f6Sopenharmony_ci    return NULL;
340195972f6Sopenharmony_ci  }
341195972f6Sopenharmony_ci  MEMCPY(&t, token, len);
342195972f6Sopenharmony_ci
343195972f6Sopenharmony_ci  for (sc = pppoe_softc_list; sc != NULL; sc = sc->next) {
344195972f6Sopenharmony_ci    if (sc == t) {
345195972f6Sopenharmony_ci      break;
346195972f6Sopenharmony_ci    }
347195972f6Sopenharmony_ci  }
348195972f6Sopenharmony_ci
349195972f6Sopenharmony_ci  if (sc == NULL) {
350195972f6Sopenharmony_ci    PPPDEBUG(LOG_DEBUG, ("pppoe: alien host unique tag, no session found\n"));
351195972f6Sopenharmony_ci    return NULL;
352195972f6Sopenharmony_ci  }
353195972f6Sopenharmony_ci
354195972f6Sopenharmony_ci  /* should be safe to access *sc now */
355195972f6Sopenharmony_ci  if (sc->sc_state < PPPOE_STATE_PADI_SENT || sc->sc_state >= PPPOE_STATE_SESSION) {
356195972f6Sopenharmony_ci    PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": host unique tag found, but it belongs to a connection in state %d\n",
357195972f6Sopenharmony_ci      sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_state));
358195972f6Sopenharmony_ci    return NULL;
359195972f6Sopenharmony_ci  }
360195972f6Sopenharmony_ci  if (sc->sc_ethif != rcvif) {
361195972f6Sopenharmony_ci    PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": wrong interface, not accepting host unique\n",
362195972f6Sopenharmony_ci      sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
363195972f6Sopenharmony_ci    return NULL;
364195972f6Sopenharmony_ci  }
365195972f6Sopenharmony_ci  return sc;
366195972f6Sopenharmony_ci}
367195972f6Sopenharmony_ci
368195972f6Sopenharmony_ci/* analyze and handle a single received packet while not in session state */
369195972f6Sopenharmony_civoid
370195972f6Sopenharmony_cipppoe_disc_input(struct netif *netif, struct pbuf *pb)
371195972f6Sopenharmony_ci{
372195972f6Sopenharmony_ci  u16_t tag, len, off;
373195972f6Sopenharmony_ci  u16_t session, plen;
374195972f6Sopenharmony_ci  struct pppoe_softc *sc;
375195972f6Sopenharmony_ci#if PPP_DEBUG
376195972f6Sopenharmony_ci  const char *err_msg = NULL;
377195972f6Sopenharmony_ci#endif /* PPP_DEBUG */
378195972f6Sopenharmony_ci  u8_t *ac_cookie;
379195972f6Sopenharmony_ci  u16_t ac_cookie_len;
380195972f6Sopenharmony_ci#ifdef PPPOE_SERVER
381195972f6Sopenharmony_ci  u8_t *hunique;
382195972f6Sopenharmony_ci  size_t hunique_len;
383195972f6Sopenharmony_ci#endif
384195972f6Sopenharmony_ci  struct pppoehdr *ph;
385195972f6Sopenharmony_ci  struct pppoetag pt;
386195972f6Sopenharmony_ci  int err;
387195972f6Sopenharmony_ci  struct eth_hdr *ethhdr;
388195972f6Sopenharmony_ci
389195972f6Sopenharmony_ci  /* don't do anything if there is not a single PPPoE instance */
390195972f6Sopenharmony_ci  if (pppoe_softc_list == NULL) {
391195972f6Sopenharmony_ci    pbuf_free(pb);
392195972f6Sopenharmony_ci    return;
393195972f6Sopenharmony_ci  }
394195972f6Sopenharmony_ci
395195972f6Sopenharmony_ci  pb = pbuf_coalesce(pb, PBUF_RAW);
396195972f6Sopenharmony_ci
397195972f6Sopenharmony_ci  ethhdr = (struct eth_hdr *)pb->payload;
398195972f6Sopenharmony_ci
399195972f6Sopenharmony_ci  ac_cookie = NULL;
400195972f6Sopenharmony_ci  ac_cookie_len = 0;
401195972f6Sopenharmony_ci#ifdef PPPOE_SERVER
402195972f6Sopenharmony_ci  hunique = NULL;
403195972f6Sopenharmony_ci  hunique_len = 0;
404195972f6Sopenharmony_ci#endif
405195972f6Sopenharmony_ci  session = 0;
406195972f6Sopenharmony_ci  off = sizeof(struct eth_hdr) + sizeof(struct pppoehdr);
407195972f6Sopenharmony_ci  if (pb->len < off) {
408195972f6Sopenharmony_ci    PPPDEBUG(LOG_DEBUG, ("pppoe: packet too short: %d\n", pb->len));
409195972f6Sopenharmony_ci    goto done;
410195972f6Sopenharmony_ci  }
411195972f6Sopenharmony_ci
412195972f6Sopenharmony_ci  ph = (struct pppoehdr *) (ethhdr + 1);
413195972f6Sopenharmony_ci  if (ph->vertype != PPPOE_VERTYPE) {
414195972f6Sopenharmony_ci    PPPDEBUG(LOG_DEBUG, ("pppoe: unknown version/type packet: 0x%x\n", ph->vertype));
415195972f6Sopenharmony_ci    goto done;
416195972f6Sopenharmony_ci  }
417195972f6Sopenharmony_ci  session = lwip_ntohs(ph->session);
418195972f6Sopenharmony_ci  plen = lwip_ntohs(ph->plen);
419195972f6Sopenharmony_ci
420195972f6Sopenharmony_ci  if (plen > (pb->len - off)) {
421195972f6Sopenharmony_ci    PPPDEBUG(LOG_DEBUG, ("pppoe: packet content does not fit: data available = %d, packet size = %u\n",
422195972f6Sopenharmony_ci        pb->len - off, plen));
423195972f6Sopenharmony_ci    goto done;
424195972f6Sopenharmony_ci  }
425195972f6Sopenharmony_ci  if(pb->tot_len == pb->len) {
426195972f6Sopenharmony_ci    u16_t framelen = off + plen;
427195972f6Sopenharmony_ci    if (framelen < pb->len) {
428195972f6Sopenharmony_ci      /* ignore trailing garbage */
429195972f6Sopenharmony_ci      pb->tot_len = pb->len = framelen;
430195972f6Sopenharmony_ci    }
431195972f6Sopenharmony_ci  }
432195972f6Sopenharmony_ci  tag = 0;
433195972f6Sopenharmony_ci  len = 0;
434195972f6Sopenharmony_ci  sc = NULL;
435195972f6Sopenharmony_ci  while (off + sizeof(pt) <= pb->len) {
436195972f6Sopenharmony_ci    MEMCPY(&pt, (u8_t*)pb->payload + off, sizeof(pt));
437195972f6Sopenharmony_ci    tag = lwip_ntohs(pt.tag);
438195972f6Sopenharmony_ci    len = lwip_ntohs(pt.len);
439195972f6Sopenharmony_ci    if (off + sizeof(pt) + len > pb->len) {
440195972f6Sopenharmony_ci      PPPDEBUG(LOG_DEBUG, ("pppoe: tag 0x%x len 0x%x is too long\n", tag, len));
441195972f6Sopenharmony_ci      goto done;
442195972f6Sopenharmony_ci    }
443195972f6Sopenharmony_ci    switch (tag) {
444195972f6Sopenharmony_ci      case PPPOE_TAG_EOL:
445195972f6Sopenharmony_ci        goto breakbreak;
446195972f6Sopenharmony_ci      case PPPOE_TAG_SNAME:
447195972f6Sopenharmony_ci        break;  /* ignored */
448195972f6Sopenharmony_ci      case PPPOE_TAG_ACNAME:
449195972f6Sopenharmony_ci        break;  /* ignored */
450195972f6Sopenharmony_ci      case PPPOE_TAG_HUNIQUE:
451195972f6Sopenharmony_ci        if (sc != NULL) {
452195972f6Sopenharmony_ci          break;
453195972f6Sopenharmony_ci        }
454195972f6Sopenharmony_ci#ifdef PPPOE_SERVER
455195972f6Sopenharmony_ci        hunique = (u8_t*)pb->payload + off + sizeof(pt);
456195972f6Sopenharmony_ci        hunique_len = len;
457195972f6Sopenharmony_ci#endif
458195972f6Sopenharmony_ci        sc = pppoe_find_softc_by_hunique((u8_t*)pb->payload + off + sizeof(pt), len, netif);
459195972f6Sopenharmony_ci        break;
460195972f6Sopenharmony_ci      case PPPOE_TAG_ACCOOKIE:
461195972f6Sopenharmony_ci        if (ac_cookie == NULL) {
462195972f6Sopenharmony_ci          if (len > PPPOE_MAX_AC_COOKIE_LEN) {
463195972f6Sopenharmony_ci            PPPDEBUG(LOG_DEBUG, ("pppoe: AC cookie is too long: len = %d, max = %d\n", len, PPPOE_MAX_AC_COOKIE_LEN));
464195972f6Sopenharmony_ci            goto done;
465195972f6Sopenharmony_ci          }
466195972f6Sopenharmony_ci          ac_cookie = (u8_t*)pb->payload + off + sizeof(pt);
467195972f6Sopenharmony_ci          ac_cookie_len = len;
468195972f6Sopenharmony_ci        }
469195972f6Sopenharmony_ci        break;
470195972f6Sopenharmony_ci#if PPP_DEBUG
471195972f6Sopenharmony_ci      case PPPOE_TAG_SNAME_ERR:
472195972f6Sopenharmony_ci        err_msg = "SERVICE NAME ERROR";
473195972f6Sopenharmony_ci        break;
474195972f6Sopenharmony_ci      case PPPOE_TAG_ACSYS_ERR:
475195972f6Sopenharmony_ci        err_msg = "AC SYSTEM ERROR";
476195972f6Sopenharmony_ci        break;
477195972f6Sopenharmony_ci      case PPPOE_TAG_GENERIC_ERR:
478195972f6Sopenharmony_ci        err_msg = "GENERIC ERROR";
479195972f6Sopenharmony_ci        break;
480195972f6Sopenharmony_ci#endif /* PPP_DEBUG */
481195972f6Sopenharmony_ci      default:
482195972f6Sopenharmony_ci        break;
483195972f6Sopenharmony_ci    }
484195972f6Sopenharmony_ci#if PPP_DEBUG
485195972f6Sopenharmony_ci    if (err_msg != NULL) {
486195972f6Sopenharmony_ci      char error_tmp[PPPOE_ERRORSTRING_LEN];
487195972f6Sopenharmony_ci      u16_t error_len = LWIP_MIN(len, sizeof(error_tmp)-1);
488195972f6Sopenharmony_ci      strncpy(error_tmp, (char*)pb->payload + off + sizeof(pt), error_len);
489195972f6Sopenharmony_ci      error_tmp[error_len] = '\0';
490195972f6Sopenharmony_ci      if (sc) {
491195972f6Sopenharmony_ci        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));
492195972f6Sopenharmony_ci      } else {
493195972f6Sopenharmony_ci        PPPDEBUG(LOG_DEBUG, ("pppoe: %s: %s\n", err_msg, error_tmp));
494195972f6Sopenharmony_ci      }
495195972f6Sopenharmony_ci    }
496195972f6Sopenharmony_ci#endif /* PPP_DEBUG */
497195972f6Sopenharmony_ci    off += sizeof(pt) + len;
498195972f6Sopenharmony_ci  }
499195972f6Sopenharmony_ci
500195972f6Sopenharmony_cibreakbreak:;
501195972f6Sopenharmony_ci  switch (ph->code) {
502195972f6Sopenharmony_ci    case PPPOE_CODE_PADI:
503195972f6Sopenharmony_ci#ifdef PPPOE_SERVER
504195972f6Sopenharmony_ci      /*
505195972f6Sopenharmony_ci       * got service name, concentrator name, and/or host unique.
506195972f6Sopenharmony_ci       * ignore if we have no interfaces with IFF_PASSIVE|IFF_UP.
507195972f6Sopenharmony_ci       */
508195972f6Sopenharmony_ci      if (LIST_EMPTY(&pppoe_softc_list)) {
509195972f6Sopenharmony_ci        goto done;
510195972f6Sopenharmony_ci      }
511195972f6Sopenharmony_ci      LIST_FOREACH(sc, &pppoe_softc_list, sc_list) {
512195972f6Sopenharmony_ci        if (!(sc->sc_sppp.pp_if.if_flags & IFF_UP)) {
513195972f6Sopenharmony_ci          continue;
514195972f6Sopenharmony_ci        }
515195972f6Sopenharmony_ci        if (!(sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) {
516195972f6Sopenharmony_ci          continue;
517195972f6Sopenharmony_ci        }
518195972f6Sopenharmony_ci        if (sc->sc_state == PPPOE_STATE_INITIAL) {
519195972f6Sopenharmony_ci          break;
520195972f6Sopenharmony_ci        }
521195972f6Sopenharmony_ci      }
522195972f6Sopenharmony_ci      if (sc == NULL) {
523195972f6Sopenharmony_ci        /* PPPDEBUG(LOG_DEBUG, ("pppoe: free passive interface is not found\n")); */
524195972f6Sopenharmony_ci        goto done;
525195972f6Sopenharmony_ci      }
526195972f6Sopenharmony_ci      if (hunique) {
527195972f6Sopenharmony_ci        if (sc->sc_hunique) {
528195972f6Sopenharmony_ci          mem_free(sc->sc_hunique);
529195972f6Sopenharmony_ci        }
530195972f6Sopenharmony_ci        sc->sc_hunique = mem_malloc(hunique_len);
531195972f6Sopenharmony_ci        if (sc->sc_hunique == NULL) {
532195972f6Sopenharmony_ci          goto done;
533195972f6Sopenharmony_ci        }
534195972f6Sopenharmony_ci        sc->sc_hunique_len = hunique_len;
535195972f6Sopenharmony_ci        MEMCPY(sc->sc_hunique, hunique, hunique_len);
536195972f6Sopenharmony_ci      }
537195972f6Sopenharmony_ci      MEMCPY(&sc->sc_dest, eh->ether_shost, sizeof sc->sc_dest);
538195972f6Sopenharmony_ci      sc->sc_state = PPPOE_STATE_PADO_SENT;
539195972f6Sopenharmony_ci      pppoe_send_pado(sc);
540195972f6Sopenharmony_ci      break;
541195972f6Sopenharmony_ci#endif /* PPPOE_SERVER */
542195972f6Sopenharmony_ci    case PPPOE_CODE_PADR:
543195972f6Sopenharmony_ci#ifdef PPPOE_SERVER
544195972f6Sopenharmony_ci      /*
545195972f6Sopenharmony_ci       * get sc from ac_cookie if IFF_PASSIVE
546195972f6Sopenharmony_ci       */
547195972f6Sopenharmony_ci      if (ac_cookie == NULL) {
548195972f6Sopenharmony_ci        /* be quiet if there is not a single pppoe instance */
549195972f6Sopenharmony_ci        PPPDEBUG(LOG_DEBUG, ("pppoe: received PADR but not includes ac_cookie\n"));
550195972f6Sopenharmony_ci        goto done;
551195972f6Sopenharmony_ci      }
552195972f6Sopenharmony_ci      sc = pppoe_find_softc_by_hunique(ac_cookie, ac_cookie_len, netif);
553195972f6Sopenharmony_ci      if (sc == NULL) {
554195972f6Sopenharmony_ci        /* be quiet if there is not a single pppoe instance */
555195972f6Sopenharmony_ci        if (!LIST_EMPTY(&pppoe_softc_list)) {
556195972f6Sopenharmony_ci          PPPDEBUG(LOG_DEBUG, ("pppoe: received PADR but could not find request for it\n"));
557195972f6Sopenharmony_ci        }
558195972f6Sopenharmony_ci        goto done;
559195972f6Sopenharmony_ci      }
560195972f6Sopenharmony_ci      if (sc->sc_state != PPPOE_STATE_PADO_SENT) {
561195972f6Sopenharmony_ci        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));
562195972f6Sopenharmony_ci        goto done;
563195972f6Sopenharmony_ci      }
564195972f6Sopenharmony_ci      if (hunique) {
565195972f6Sopenharmony_ci        if (sc->sc_hunique) {
566195972f6Sopenharmony_ci          mem_free(sc->sc_hunique);
567195972f6Sopenharmony_ci        }
568195972f6Sopenharmony_ci        sc->sc_hunique = mem_malloc(hunique_len);
569195972f6Sopenharmony_ci        if (sc->sc_hunique == NULL) {
570195972f6Sopenharmony_ci          goto done;
571195972f6Sopenharmony_ci        }
572195972f6Sopenharmony_ci        sc->sc_hunique_len = hunique_len;
573195972f6Sopenharmony_ci        MEMCPY(sc->sc_hunique, hunique, hunique_len);
574195972f6Sopenharmony_ci      }
575195972f6Sopenharmony_ci      pppoe_send_pads(sc);
576195972f6Sopenharmony_ci      sc->sc_state = PPPOE_STATE_SESSION;
577195972f6Sopenharmony_ci      ppp_start(sc->pcb); /* notify upper layers */
578195972f6Sopenharmony_ci      break;
579195972f6Sopenharmony_ci#else
580195972f6Sopenharmony_ci      /* ignore, we are no access concentrator */
581195972f6Sopenharmony_ci      goto done;
582195972f6Sopenharmony_ci#endif /* PPPOE_SERVER */
583195972f6Sopenharmony_ci    case PPPOE_CODE_PADO:
584195972f6Sopenharmony_ci      if (sc == NULL) {
585195972f6Sopenharmony_ci        /* be quiet if there is not a single pppoe instance */
586195972f6Sopenharmony_ci        if (pppoe_softc_list != NULL) {
587195972f6Sopenharmony_ci          PPPDEBUG(LOG_DEBUG, ("pppoe: received PADO but could not find request for it\n"));
588195972f6Sopenharmony_ci        }
589195972f6Sopenharmony_ci        goto done;
590195972f6Sopenharmony_ci      }
591195972f6Sopenharmony_ci      if (sc->sc_state != PPPOE_STATE_PADI_SENT) {
592195972f6Sopenharmony_ci        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));
593195972f6Sopenharmony_ci        goto done;
594195972f6Sopenharmony_ci      }
595195972f6Sopenharmony_ci      if (ac_cookie) {
596195972f6Sopenharmony_ci        sc->sc_ac_cookie_len = ac_cookie_len;
597195972f6Sopenharmony_ci        MEMCPY(sc->sc_ac_cookie, ac_cookie, ac_cookie_len);
598195972f6Sopenharmony_ci      }
599195972f6Sopenharmony_ci      MEMCPY(&sc->sc_dest, ethhdr->src.addr, sizeof(sc->sc_dest.addr));
600195972f6Sopenharmony_ci      sys_untimeout(pppoe_timeout, sc);
601195972f6Sopenharmony_ci      sc->sc_padr_retried = 0;
602195972f6Sopenharmony_ci      sc->sc_state = PPPOE_STATE_PADR_SENT;
603195972f6Sopenharmony_ci      if ((err = pppoe_send_padr(sc)) != 0) {
604195972f6Sopenharmony_ci        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));
605195972f6Sopenharmony_ci        LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
606195972f6Sopenharmony_ci      }
607195972f6Sopenharmony_ci      sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc);
608195972f6Sopenharmony_ci      break;
609195972f6Sopenharmony_ci    case PPPOE_CODE_PADS:
610195972f6Sopenharmony_ci      if (sc == NULL) {
611195972f6Sopenharmony_ci        goto done;
612195972f6Sopenharmony_ci      }
613195972f6Sopenharmony_ci      sc->sc_session = session;
614195972f6Sopenharmony_ci      sys_untimeout(pppoe_timeout, sc);
615195972f6Sopenharmony_ci      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));
616195972f6Sopenharmony_ci      sc->sc_state = PPPOE_STATE_SESSION;
617195972f6Sopenharmony_ci      ppp_start(sc->pcb); /* notify upper layers */
618195972f6Sopenharmony_ci      break;
619195972f6Sopenharmony_ci    case PPPOE_CODE_PADT:
620195972f6Sopenharmony_ci      /* Don't disconnect here, we let the LCP Echo/Reply find the fact
621195972f6Sopenharmony_ci       * that PPP session is down. Asking the PPP stack to end the session
622195972f6Sopenharmony_ci       * require strict checking about the PPP phase to prevent endless
623195972f6Sopenharmony_ci       * disconnection loops.
624195972f6Sopenharmony_ci       */
625195972f6Sopenharmony_ci#if 0 /* UNUSED */
626195972f6Sopenharmony_ci      if (sc == NULL) { /* PADT frames are rarely sent with a hunique tag, this is actually almost always true */
627195972f6Sopenharmony_ci        goto done;
628195972f6Sopenharmony_ci      }
629195972f6Sopenharmony_ci      pppoe_clear_softc(sc, "received PADT");
630195972f6Sopenharmony_ci#endif /* UNUSED */
631195972f6Sopenharmony_ci      break;
632195972f6Sopenharmony_ci    default:
633195972f6Sopenharmony_ci      if(sc) {
634195972f6Sopenharmony_ci        PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": unknown code (0x%"X16_F") session = 0x%"X16_F"\n",
635195972f6Sopenharmony_ci            sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num,
636195972f6Sopenharmony_ci            (u16_t)ph->code, session));
637195972f6Sopenharmony_ci      } else {
638195972f6Sopenharmony_ci        PPPDEBUG(LOG_DEBUG, ("pppoe: unknown code (0x%"X16_F") session = 0x%"X16_F"\n", (u16_t)ph->code, session));
639195972f6Sopenharmony_ci      }
640195972f6Sopenharmony_ci      break;
641195972f6Sopenharmony_ci  }
642195972f6Sopenharmony_ci
643195972f6Sopenharmony_cidone:
644195972f6Sopenharmony_ci  pbuf_free(pb);
645195972f6Sopenharmony_ci  return;
646195972f6Sopenharmony_ci}
647195972f6Sopenharmony_ci
648195972f6Sopenharmony_civoid
649195972f6Sopenharmony_cipppoe_data_input(struct netif *netif, struct pbuf *pb)
650195972f6Sopenharmony_ci{
651195972f6Sopenharmony_ci  u16_t session, plen;
652195972f6Sopenharmony_ci  struct pppoe_softc *sc;
653195972f6Sopenharmony_ci  struct pppoehdr *ph;
654195972f6Sopenharmony_ci#ifdef PPPOE_TERM_UNKNOWN_SESSIONS
655195972f6Sopenharmony_ci  u8_t shost[ETHER_ADDR_LEN];
656195972f6Sopenharmony_ci#endif
657195972f6Sopenharmony_ci
658195972f6Sopenharmony_ci#ifdef PPPOE_TERM_UNKNOWN_SESSIONS
659195972f6Sopenharmony_ci  MEMCPY(shost, ((struct eth_hdr *)pb->payload)->src.addr, sizeof(shost));
660195972f6Sopenharmony_ci#endif
661195972f6Sopenharmony_ci  if (pbuf_remove_header(pb, sizeof(struct eth_hdr)) != 0) {
662195972f6Sopenharmony_ci    /* bail out */
663195972f6Sopenharmony_ci    PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_remove_header failed\n"));
664195972f6Sopenharmony_ci    LINK_STATS_INC(link.lenerr);
665195972f6Sopenharmony_ci    goto drop;
666195972f6Sopenharmony_ci  }
667195972f6Sopenharmony_ci
668195972f6Sopenharmony_ci  if (pb->len < sizeof(*ph)) {
669195972f6Sopenharmony_ci    PPPDEBUG(LOG_DEBUG, ("pppoe_data_input: could not get PPPoE header\n"));
670195972f6Sopenharmony_ci    goto drop;
671195972f6Sopenharmony_ci  }
672195972f6Sopenharmony_ci  ph = (struct pppoehdr *)pb->payload;
673195972f6Sopenharmony_ci
674195972f6Sopenharmony_ci  if (ph->vertype != PPPOE_VERTYPE) {
675195972f6Sopenharmony_ci    PPPDEBUG(LOG_DEBUG, ("pppoe (data): unknown version/type packet: 0x%x\n", ph->vertype));
676195972f6Sopenharmony_ci    goto drop;
677195972f6Sopenharmony_ci  }
678195972f6Sopenharmony_ci  if (ph->code != 0) {
679195972f6Sopenharmony_ci    goto drop;
680195972f6Sopenharmony_ci  }
681195972f6Sopenharmony_ci
682195972f6Sopenharmony_ci  session = lwip_ntohs(ph->session);
683195972f6Sopenharmony_ci  sc = pppoe_find_softc_by_session(session, netif);
684195972f6Sopenharmony_ci  if (sc == NULL) {
685195972f6Sopenharmony_ci#ifdef PPPOE_TERM_UNKNOWN_SESSIONS
686195972f6Sopenharmony_ci    PPPDEBUG(LOG_DEBUG, ("pppoe: input for unknown session 0x%x, sending PADT\n", session));
687195972f6Sopenharmony_ci    pppoe_send_padt(netif, session, shost);
688195972f6Sopenharmony_ci#endif
689195972f6Sopenharmony_ci    goto drop;
690195972f6Sopenharmony_ci  }
691195972f6Sopenharmony_ci
692195972f6Sopenharmony_ci  plen = lwip_ntohs(ph->plen);
693195972f6Sopenharmony_ci
694195972f6Sopenharmony_ci  if (pbuf_remove_header(pb, PPPOE_HEADERLEN) != 0) {
695195972f6Sopenharmony_ci    /* bail out */
696195972f6Sopenharmony_ci    PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_remove_header PPPOE_HEADERLEN failed\n"));
697195972f6Sopenharmony_ci    LINK_STATS_INC(link.lenerr);
698195972f6Sopenharmony_ci    goto drop;
699195972f6Sopenharmony_ci  }
700195972f6Sopenharmony_ci
701195972f6Sopenharmony_ci  PPPDEBUG(LOG_DEBUG, ("pppoe_data_input: %c%c%"U16_F": pkthdr.len=%d, pppoe.len=%d\n",
702195972f6Sopenharmony_ci        sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num,
703195972f6Sopenharmony_ci        pb->len, plen));
704195972f6Sopenharmony_ci
705195972f6Sopenharmony_ci  if (pb->tot_len < plen) {
706195972f6Sopenharmony_ci    goto drop;
707195972f6Sopenharmony_ci  }
708195972f6Sopenharmony_ci
709195972f6Sopenharmony_ci  /* Dispatch the packet thereby consuming it. */
710195972f6Sopenharmony_ci  ppp_input(sc->pcb, pb);
711195972f6Sopenharmony_ci  return;
712195972f6Sopenharmony_ci
713195972f6Sopenharmony_cidrop:
714195972f6Sopenharmony_ci  pbuf_free(pb);
715195972f6Sopenharmony_ci}
716195972f6Sopenharmony_ci
717195972f6Sopenharmony_cistatic err_t
718195972f6Sopenharmony_cipppoe_output(struct pppoe_softc *sc, struct pbuf *pb)
719195972f6Sopenharmony_ci{
720195972f6Sopenharmony_ci  struct eth_hdr *ethhdr;
721195972f6Sopenharmony_ci  u16_t etype;
722195972f6Sopenharmony_ci  err_t res;
723195972f6Sopenharmony_ci
724195972f6Sopenharmony_ci  /* make room for Ethernet header - should not fail */
725195972f6Sopenharmony_ci  if (pbuf_add_header(pb, sizeof(struct eth_hdr)) != 0) {
726195972f6Sopenharmony_ci    /* bail out */
727195972f6Sopenharmony_ci    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));
728195972f6Sopenharmony_ci    LINK_STATS_INC(link.lenerr);
729195972f6Sopenharmony_ci    pbuf_free(pb);
730195972f6Sopenharmony_ci    return ERR_BUF;
731195972f6Sopenharmony_ci  }
732195972f6Sopenharmony_ci  ethhdr = (struct eth_hdr *)pb->payload;
733195972f6Sopenharmony_ci  etype = sc->sc_state == PPPOE_STATE_SESSION ? ETHTYPE_PPPOE : ETHTYPE_PPPOEDISC;
734195972f6Sopenharmony_ci  ethhdr->type = lwip_htons(etype);
735195972f6Sopenharmony_ci  MEMCPY(&ethhdr->dest.addr, &sc->sc_dest.addr, sizeof(ethhdr->dest.addr));
736195972f6Sopenharmony_ci  MEMCPY(&ethhdr->src.addr, &sc->sc_ethif->hwaddr, sizeof(ethhdr->src.addr));
737195972f6Sopenharmony_ci
738195972f6Sopenharmony_ci  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",
739195972f6Sopenharmony_ci      sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, etype,
740195972f6Sopenharmony_ci      sc->sc_state, sc->sc_session,
741195972f6Sopenharmony_ci      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],
742195972f6Sopenharmony_ci      pb->tot_len));
743195972f6Sopenharmony_ci
744195972f6Sopenharmony_ci  res = sc->sc_ethif->linkoutput(sc->sc_ethif, pb);
745195972f6Sopenharmony_ci
746195972f6Sopenharmony_ci  pbuf_free(pb);
747195972f6Sopenharmony_ci
748195972f6Sopenharmony_ci  return res;
749195972f6Sopenharmony_ci}
750195972f6Sopenharmony_ci
751195972f6Sopenharmony_cistatic err_t
752195972f6Sopenharmony_cipppoe_send_padi(struct pppoe_softc *sc)
753195972f6Sopenharmony_ci{
754195972f6Sopenharmony_ci  struct pbuf *pb;
755195972f6Sopenharmony_ci  u8_t *p;
756195972f6Sopenharmony_ci  int len;
757195972f6Sopenharmony_ci#if PPPOE_SCNAME_SUPPORT
758195972f6Sopenharmony_ci  int l1 = 0, l2 = 0; /* XXX: gcc */
759195972f6Sopenharmony_ci#endif /* PPPOE_SCNAME_SUPPORT */
760195972f6Sopenharmony_ci
761195972f6Sopenharmony_ci  /* calculate length of frame (excluding ethernet header + pppoe header) */
762195972f6Sopenharmony_ci  len = 2 + 2 + 2 + 2 + sizeof sc;  /* service name tag is required, host unique is send too */
763195972f6Sopenharmony_ci#if PPPOE_SCNAME_SUPPORT
764195972f6Sopenharmony_ci  if (sc->sc_service_name != NULL) {
765195972f6Sopenharmony_ci    l1 = (int)strlen(sc->sc_service_name);
766195972f6Sopenharmony_ci    len += l1;
767195972f6Sopenharmony_ci  }
768195972f6Sopenharmony_ci  if (sc->sc_concentrator_name != NULL) {
769195972f6Sopenharmony_ci    l2 = (int)strlen(sc->sc_concentrator_name);
770195972f6Sopenharmony_ci    len += 2 + 2 + l2;
771195972f6Sopenharmony_ci  }
772195972f6Sopenharmony_ci#endif /* PPPOE_SCNAME_SUPPORT */
773195972f6Sopenharmony_ci  LWIP_ASSERT("sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff",
774195972f6Sopenharmony_ci    sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff);
775195972f6Sopenharmony_ci
776195972f6Sopenharmony_ci  /* allocate a buffer */
777195972f6Sopenharmony_ci  pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN + len), PBUF_RAM);
778195972f6Sopenharmony_ci  if (!pb) {
779195972f6Sopenharmony_ci    return ERR_MEM;
780195972f6Sopenharmony_ci  }
781195972f6Sopenharmony_ci  LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
782195972f6Sopenharmony_ci
783195972f6Sopenharmony_ci  p = (u8_t*)pb->payload;
784195972f6Sopenharmony_ci  /* fill in pkt */
785195972f6Sopenharmony_ci  PPPOE_ADD_HEADER(p, PPPOE_CODE_PADI, 0, (u16_t)len);
786195972f6Sopenharmony_ci  PPPOE_ADD_16(p, PPPOE_TAG_SNAME);
787195972f6Sopenharmony_ci#if PPPOE_SCNAME_SUPPORT
788195972f6Sopenharmony_ci  if (sc->sc_service_name != NULL) {
789195972f6Sopenharmony_ci    PPPOE_ADD_16(p, l1);
790195972f6Sopenharmony_ci    MEMCPY(p, sc->sc_service_name, l1);
791195972f6Sopenharmony_ci    p += l1;
792195972f6Sopenharmony_ci  } else
793195972f6Sopenharmony_ci#endif /* PPPOE_SCNAME_SUPPORT */
794195972f6Sopenharmony_ci  {
795195972f6Sopenharmony_ci    PPPOE_ADD_16(p, 0);
796195972f6Sopenharmony_ci  }
797195972f6Sopenharmony_ci#if PPPOE_SCNAME_SUPPORT
798195972f6Sopenharmony_ci  if (sc->sc_concentrator_name != NULL) {
799195972f6Sopenharmony_ci    PPPOE_ADD_16(p, PPPOE_TAG_ACNAME);
800195972f6Sopenharmony_ci    PPPOE_ADD_16(p, l2);
801195972f6Sopenharmony_ci    MEMCPY(p, sc->sc_concentrator_name, l2);
802195972f6Sopenharmony_ci    p += l2;
803195972f6Sopenharmony_ci  }
804195972f6Sopenharmony_ci#endif /* PPPOE_SCNAME_SUPPORT */
805195972f6Sopenharmony_ci  PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE);
806195972f6Sopenharmony_ci  PPPOE_ADD_16(p, sizeof(sc));
807195972f6Sopenharmony_ci  MEMCPY(p, &sc, sizeof sc);
808195972f6Sopenharmony_ci
809195972f6Sopenharmony_ci  /* send pkt */
810195972f6Sopenharmony_ci  return pppoe_output(sc, pb);
811195972f6Sopenharmony_ci}
812195972f6Sopenharmony_ci
813195972f6Sopenharmony_cistatic void
814195972f6Sopenharmony_cipppoe_timeout(void *arg)
815195972f6Sopenharmony_ci{
816195972f6Sopenharmony_ci  u32_t retry_wait;
817195972f6Sopenharmony_ci  int err;
818195972f6Sopenharmony_ci  struct pppoe_softc *sc = (struct pppoe_softc*)arg;
819195972f6Sopenharmony_ci
820195972f6Sopenharmony_ci  PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": timeout\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
821195972f6Sopenharmony_ci
822195972f6Sopenharmony_ci  switch (sc->sc_state) {
823195972f6Sopenharmony_ci    case PPPOE_STATE_PADI_SENT:
824195972f6Sopenharmony_ci      /*
825195972f6Sopenharmony_ci       * We have two basic ways of retrying:
826195972f6Sopenharmony_ci       *  - Quick retry mode: try a few times in short sequence
827195972f6Sopenharmony_ci       *  - Slow retry mode: we already had a connection successfully
828195972f6Sopenharmony_ci       *    established and will try infinitely (without user
829195972f6Sopenharmony_ci       *    intervention)
830195972f6Sopenharmony_ci       * We only enter slow retry mode if IFF_LINK1 (aka autodial)
831195972f6Sopenharmony_ci       * is not set.
832195972f6Sopenharmony_ci       */
833195972f6Sopenharmony_ci      if (sc->sc_padi_retried < 0xff) {
834195972f6Sopenharmony_ci        sc->sc_padi_retried++;
835195972f6Sopenharmony_ci      }
836195972f6Sopenharmony_ci      if (!sc->pcb->settings.persist && sc->sc_padi_retried >= PPPOE_DISC_MAXPADI) {
837195972f6Sopenharmony_ci#if 0
838195972f6Sopenharmony_ci        if ((sc->sc_sppp.pp_if.if_flags & IFF_LINK1) == 0) {
839195972f6Sopenharmony_ci          /* slow retry mode */
840195972f6Sopenharmony_ci          retry_wait = PPPOE_SLOW_RETRY;
841195972f6Sopenharmony_ci        } else
842195972f6Sopenharmony_ci#endif
843195972f6Sopenharmony_ci        {
844195972f6Sopenharmony_ci          pppoe_abort_connect(sc);
845195972f6Sopenharmony_ci          return;
846195972f6Sopenharmony_ci        }
847195972f6Sopenharmony_ci      }
848195972f6Sopenharmony_ci      /* initialize for quick retry mode */
849195972f6Sopenharmony_ci      retry_wait = LWIP_MIN(PPPOE_DISC_TIMEOUT * sc->sc_padi_retried, PPPOE_SLOW_RETRY);
850195972f6Sopenharmony_ci      if ((err = pppoe_send_padi(sc)) != 0) {
851195972f6Sopenharmony_ci        sc->sc_padi_retried--;
852195972f6Sopenharmony_ci        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));
853195972f6Sopenharmony_ci        LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
854195972f6Sopenharmony_ci      }
855195972f6Sopenharmony_ci      sys_timeout(retry_wait, pppoe_timeout, sc);
856195972f6Sopenharmony_ci      break;
857195972f6Sopenharmony_ci
858195972f6Sopenharmony_ci    case PPPOE_STATE_PADR_SENT:
859195972f6Sopenharmony_ci      sc->sc_padr_retried++;
860195972f6Sopenharmony_ci      if (sc->sc_padr_retried >= PPPOE_DISC_MAXPADR) {
861195972f6Sopenharmony_ci        MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest));
862195972f6Sopenharmony_ci        sc->sc_state = PPPOE_STATE_PADI_SENT;
863195972f6Sopenharmony_ci        sc->sc_padr_retried = 0;
864195972f6Sopenharmony_ci        if ((err = pppoe_send_padi(sc)) != 0) {
865195972f6Sopenharmony_ci          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));
866195972f6Sopenharmony_ci          LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
867195972f6Sopenharmony_ci        }
868195972f6Sopenharmony_ci        sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried), pppoe_timeout, sc);
869195972f6Sopenharmony_ci        return;
870195972f6Sopenharmony_ci      }
871195972f6Sopenharmony_ci      if ((err = pppoe_send_padr(sc)) != 0) {
872195972f6Sopenharmony_ci        sc->sc_padr_retried--;
873195972f6Sopenharmony_ci        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));
874195972f6Sopenharmony_ci        LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
875195972f6Sopenharmony_ci      }
876195972f6Sopenharmony_ci      sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc);
877195972f6Sopenharmony_ci      break;
878195972f6Sopenharmony_ci    default:
879195972f6Sopenharmony_ci      return;  /* all done, work in peace */
880195972f6Sopenharmony_ci  }
881195972f6Sopenharmony_ci}
882195972f6Sopenharmony_ci
883195972f6Sopenharmony_ci/* Start a connection (i.e. initiate discovery phase) */
884195972f6Sopenharmony_cistatic void
885195972f6Sopenharmony_cipppoe_connect(ppp_pcb *ppp, void *ctx)
886195972f6Sopenharmony_ci{
887195972f6Sopenharmony_ci  err_t err;
888195972f6Sopenharmony_ci  struct pppoe_softc *sc = (struct pppoe_softc *)ctx;
889195972f6Sopenharmony_ci  lcp_options *lcp_wo;
890195972f6Sopenharmony_ci  lcp_options *lcp_ao;
891195972f6Sopenharmony_ci#if PPP_IPV4_SUPPORT && VJ_SUPPORT
892195972f6Sopenharmony_ci  ipcp_options *ipcp_wo;
893195972f6Sopenharmony_ci  ipcp_options *ipcp_ao;
894195972f6Sopenharmony_ci#endif /* PPP_IPV4_SUPPORT && VJ_SUPPORT */
895195972f6Sopenharmony_ci
896195972f6Sopenharmony_ci  sc->sc_session = 0;
897195972f6Sopenharmony_ci  sc->sc_ac_cookie_len = 0;
898195972f6Sopenharmony_ci  sc->sc_padi_retried = 0;
899195972f6Sopenharmony_ci  sc->sc_padr_retried = 0;
900195972f6Sopenharmony_ci  /* changed to real address later */
901195972f6Sopenharmony_ci  MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest));
902195972f6Sopenharmony_ci#ifdef PPPOE_SERVER
903195972f6Sopenharmony_ci  /* wait PADI if IFF_PASSIVE */
904195972f6Sopenharmony_ci  if ((sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) {
905195972f6Sopenharmony_ci    return 0;
906195972f6Sopenharmony_ci  }
907195972f6Sopenharmony_ci#endif
908195972f6Sopenharmony_ci
909195972f6Sopenharmony_ci  lcp_wo = &ppp->lcp_wantoptions;
910195972f6Sopenharmony_ci  lcp_wo->mru = sc->sc_ethif->mtu-PPPOE_HEADERLEN-2; /* two byte PPP protocol discriminator, then IP data */
911195972f6Sopenharmony_ci  lcp_wo->neg_asyncmap = 0;
912195972f6Sopenharmony_ci  lcp_wo->neg_pcompression = 0;
913195972f6Sopenharmony_ci  lcp_wo->neg_accompression = 0;
914195972f6Sopenharmony_ci  lcp_wo->passive = 0;
915195972f6Sopenharmony_ci  lcp_wo->silent = 0;
916195972f6Sopenharmony_ci
917195972f6Sopenharmony_ci  lcp_ao = &ppp->lcp_allowoptions;
918195972f6Sopenharmony_ci  lcp_ao->mru = sc->sc_ethif->mtu-PPPOE_HEADERLEN-2; /* two byte PPP protocol discriminator, then IP data */
919195972f6Sopenharmony_ci  lcp_ao->neg_asyncmap = 0;
920195972f6Sopenharmony_ci  lcp_ao->neg_pcompression = 0;
921195972f6Sopenharmony_ci  lcp_ao->neg_accompression = 0;
922195972f6Sopenharmony_ci
923195972f6Sopenharmony_ci#if PPP_IPV4_SUPPORT && VJ_SUPPORT
924195972f6Sopenharmony_ci  ipcp_wo = &ppp->ipcp_wantoptions;
925195972f6Sopenharmony_ci  ipcp_wo->neg_vj = 0;
926195972f6Sopenharmony_ci  ipcp_wo->old_vj = 0;
927195972f6Sopenharmony_ci
928195972f6Sopenharmony_ci  ipcp_ao = &ppp->ipcp_allowoptions;
929195972f6Sopenharmony_ci  ipcp_ao->neg_vj = 0;
930195972f6Sopenharmony_ci  ipcp_ao->old_vj = 0;
931195972f6Sopenharmony_ci#endif /* PPP_IPV4_SUPPORT && VJ_SUPPORT */
932195972f6Sopenharmony_ci
933195972f6Sopenharmony_ci  /* save state, in case we fail to send PADI */
934195972f6Sopenharmony_ci  sc->sc_state = PPPOE_STATE_PADI_SENT;
935195972f6Sopenharmony_ci  if ((err = pppoe_send_padi(sc)) != 0) {
936195972f6Sopenharmony_ci    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));
937195972f6Sopenharmony_ci  }
938195972f6Sopenharmony_ci  sys_timeout(PPPOE_DISC_TIMEOUT, pppoe_timeout, sc);
939195972f6Sopenharmony_ci}
940195972f6Sopenharmony_ci
941195972f6Sopenharmony_ci/* disconnect */
942195972f6Sopenharmony_cistatic void
943195972f6Sopenharmony_cipppoe_disconnect(ppp_pcb *ppp, void *ctx)
944195972f6Sopenharmony_ci{
945195972f6Sopenharmony_ci  struct pppoe_softc *sc = (struct pppoe_softc *)ctx;
946195972f6Sopenharmony_ci
947195972f6Sopenharmony_ci  PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": disconnecting\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
948195972f6Sopenharmony_ci  if (sc->sc_state == PPPOE_STATE_SESSION) {
949195972f6Sopenharmony_ci    pppoe_send_padt(sc->sc_ethif, sc->sc_session, (const u8_t *)&sc->sc_dest);
950195972f6Sopenharmony_ci  }
951195972f6Sopenharmony_ci
952195972f6Sopenharmony_ci  /* stop any timer, disconnect can be called while initiating is in progress */
953195972f6Sopenharmony_ci  sys_untimeout(pppoe_timeout, sc);
954195972f6Sopenharmony_ci  sc->sc_state = PPPOE_STATE_INITIAL;
955195972f6Sopenharmony_ci#ifdef PPPOE_SERVER
956195972f6Sopenharmony_ci  if (sc->sc_hunique) {
957195972f6Sopenharmony_ci    mem_free(sc->sc_hunique);
958195972f6Sopenharmony_ci    sc->sc_hunique = NULL; /* probably not necessary, if state is initial we shouldn't have to access hunique anyway  */
959195972f6Sopenharmony_ci  }
960195972f6Sopenharmony_ci  sc->sc_hunique_len = 0; /* probably not necessary, if state is initial we shouldn't have to access hunique anyway  */
961195972f6Sopenharmony_ci#endif
962195972f6Sopenharmony_ci  ppp_link_end(ppp); /* notify upper layers */
963195972f6Sopenharmony_ci  return;
964195972f6Sopenharmony_ci}
965195972f6Sopenharmony_ci
966195972f6Sopenharmony_ci/* Connection attempt aborted */
967195972f6Sopenharmony_cistatic void
968195972f6Sopenharmony_cipppoe_abort_connect(struct pppoe_softc *sc)
969195972f6Sopenharmony_ci{
970195972f6Sopenharmony_ci  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));
971195972f6Sopenharmony_ci  sc->sc_state = PPPOE_STATE_INITIAL;
972195972f6Sopenharmony_ci  ppp_link_failed(sc->pcb); /* notify upper layers */
973195972f6Sopenharmony_ci}
974195972f6Sopenharmony_ci
975195972f6Sopenharmony_ci/* Send a PADR packet */
976195972f6Sopenharmony_cistatic err_t
977195972f6Sopenharmony_cipppoe_send_padr(struct pppoe_softc *sc)
978195972f6Sopenharmony_ci{
979195972f6Sopenharmony_ci  struct pbuf *pb;
980195972f6Sopenharmony_ci  u8_t *p;
981195972f6Sopenharmony_ci  size_t len;
982195972f6Sopenharmony_ci#if PPPOE_SCNAME_SUPPORT
983195972f6Sopenharmony_ci  size_t l1 = 0; /* XXX: gcc */
984195972f6Sopenharmony_ci#endif /* PPPOE_SCNAME_SUPPORT */
985195972f6Sopenharmony_ci
986195972f6Sopenharmony_ci  len = 2 + 2 + 2 + 2 + sizeof(sc);    /* service name, host unique */
987195972f6Sopenharmony_ci#if PPPOE_SCNAME_SUPPORT
988195972f6Sopenharmony_ci  if (sc->sc_service_name != NULL) {    /* service name tag maybe empty */
989195972f6Sopenharmony_ci    l1 = strlen(sc->sc_service_name);
990195972f6Sopenharmony_ci    len += l1;
991195972f6Sopenharmony_ci  }
992195972f6Sopenharmony_ci#endif /* PPPOE_SCNAME_SUPPORT */
993195972f6Sopenharmony_ci  if (sc->sc_ac_cookie_len > 0) {
994195972f6Sopenharmony_ci    len += 2 + 2 + sc->sc_ac_cookie_len;  /* AC cookie */
995195972f6Sopenharmony_ci  }
996195972f6Sopenharmony_ci  LWIP_ASSERT("sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff",
997195972f6Sopenharmony_ci    sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff);
998195972f6Sopenharmony_ci  pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN + len), PBUF_RAM);
999195972f6Sopenharmony_ci  if (!pb) {
1000195972f6Sopenharmony_ci    return ERR_MEM;
1001195972f6Sopenharmony_ci  }
1002195972f6Sopenharmony_ci  LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
1003195972f6Sopenharmony_ci  p = (u8_t*)pb->payload;
1004195972f6Sopenharmony_ci  PPPOE_ADD_HEADER(p, PPPOE_CODE_PADR, 0, len);
1005195972f6Sopenharmony_ci  PPPOE_ADD_16(p, PPPOE_TAG_SNAME);
1006195972f6Sopenharmony_ci#if PPPOE_SCNAME_SUPPORT
1007195972f6Sopenharmony_ci  if (sc->sc_service_name != NULL) {
1008195972f6Sopenharmony_ci    PPPOE_ADD_16(p, l1);
1009195972f6Sopenharmony_ci    MEMCPY(p, sc->sc_service_name, l1);
1010195972f6Sopenharmony_ci    p += l1;
1011195972f6Sopenharmony_ci  } else
1012195972f6Sopenharmony_ci#endif /* PPPOE_SCNAME_SUPPORT */
1013195972f6Sopenharmony_ci  {
1014195972f6Sopenharmony_ci    PPPOE_ADD_16(p, 0);
1015195972f6Sopenharmony_ci  }
1016195972f6Sopenharmony_ci  if (sc->sc_ac_cookie_len > 0) {
1017195972f6Sopenharmony_ci    PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE);
1018195972f6Sopenharmony_ci    PPPOE_ADD_16(p, sc->sc_ac_cookie_len);
1019195972f6Sopenharmony_ci    MEMCPY(p, sc->sc_ac_cookie, sc->sc_ac_cookie_len);
1020195972f6Sopenharmony_ci    p += sc->sc_ac_cookie_len;
1021195972f6Sopenharmony_ci  }
1022195972f6Sopenharmony_ci  PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE);
1023195972f6Sopenharmony_ci  PPPOE_ADD_16(p, sizeof(sc));
1024195972f6Sopenharmony_ci  MEMCPY(p, &sc, sizeof sc);
1025195972f6Sopenharmony_ci
1026195972f6Sopenharmony_ci  return pppoe_output(sc, pb);
1027195972f6Sopenharmony_ci}
1028195972f6Sopenharmony_ci
1029195972f6Sopenharmony_ci/* send a PADT packet */
1030195972f6Sopenharmony_cistatic err_t
1031195972f6Sopenharmony_cipppoe_send_padt(struct netif *outgoing_if, u_int session, const u8_t *dest)
1032195972f6Sopenharmony_ci{
1033195972f6Sopenharmony_ci  struct pbuf *pb;
1034195972f6Sopenharmony_ci  struct eth_hdr *ethhdr;
1035195972f6Sopenharmony_ci  err_t res;
1036195972f6Sopenharmony_ci  u8_t *p;
1037195972f6Sopenharmony_ci
1038195972f6Sopenharmony_ci  pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN), PBUF_RAM);
1039195972f6Sopenharmony_ci  if (!pb) {
1040195972f6Sopenharmony_ci    return ERR_MEM;
1041195972f6Sopenharmony_ci  }
1042195972f6Sopenharmony_ci  LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
1043195972f6Sopenharmony_ci
1044195972f6Sopenharmony_ci  if (pbuf_add_header(pb, sizeof(struct eth_hdr))) {
1045195972f6Sopenharmony_ci    PPPDEBUG(LOG_ERR, ("pppoe: pppoe_send_padt: could not allocate room for PPPoE header\n"));
1046195972f6Sopenharmony_ci    LINK_STATS_INC(link.lenerr);
1047195972f6Sopenharmony_ci    pbuf_free(pb);
1048195972f6Sopenharmony_ci    return ERR_BUF;
1049195972f6Sopenharmony_ci  }
1050195972f6Sopenharmony_ci  ethhdr = (struct eth_hdr *)pb->payload;
1051195972f6Sopenharmony_ci  ethhdr->type = PP_HTONS(ETHTYPE_PPPOEDISC);
1052195972f6Sopenharmony_ci  MEMCPY(&ethhdr->dest.addr, dest, sizeof(ethhdr->dest.addr));
1053195972f6Sopenharmony_ci  MEMCPY(&ethhdr->src.addr, &outgoing_if->hwaddr, sizeof(ethhdr->src.addr));
1054195972f6Sopenharmony_ci
1055195972f6Sopenharmony_ci  p = (u8_t*)(ethhdr + 1);
1056195972f6Sopenharmony_ci  PPPOE_ADD_HEADER(p, PPPOE_CODE_PADT, session, 0);
1057195972f6Sopenharmony_ci
1058195972f6Sopenharmony_ci  res = outgoing_if->linkoutput(outgoing_if, pb);
1059195972f6Sopenharmony_ci
1060195972f6Sopenharmony_ci  pbuf_free(pb);
1061195972f6Sopenharmony_ci
1062195972f6Sopenharmony_ci  return res;
1063195972f6Sopenharmony_ci}
1064195972f6Sopenharmony_ci
1065195972f6Sopenharmony_ci#ifdef PPPOE_SERVER
1066195972f6Sopenharmony_cistatic err_t
1067195972f6Sopenharmony_cipppoe_send_pado(struct pppoe_softc *sc)
1068195972f6Sopenharmony_ci{
1069195972f6Sopenharmony_ci  struct pbuf *pb;
1070195972f6Sopenharmony_ci  u8_t *p;
1071195972f6Sopenharmony_ci  size_t len;
1072195972f6Sopenharmony_ci
1073195972f6Sopenharmony_ci  /* calc length */
1074195972f6Sopenharmony_ci  len = 0;
1075195972f6Sopenharmony_ci  /* include ac_cookie */
1076195972f6Sopenharmony_ci  len += 2 + 2 + sizeof(sc);
1077195972f6Sopenharmony_ci  /* include hunique */
1078195972f6Sopenharmony_ci  len += 2 + 2 + sc->sc_hunique_len;
1079195972f6Sopenharmony_ci  pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN + len), PBUF_RAM);
1080195972f6Sopenharmony_ci  if (!pb) {
1081195972f6Sopenharmony_ci    return ERR_MEM;
1082195972f6Sopenharmony_ci  }
1083195972f6Sopenharmony_ci  LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
1084195972f6Sopenharmony_ci  p = (u8_t*)pb->payload;
1085195972f6Sopenharmony_ci  PPPOE_ADD_HEADER(p, PPPOE_CODE_PADO, 0, len);
1086195972f6Sopenharmony_ci  PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE);
1087195972f6Sopenharmony_ci  PPPOE_ADD_16(p, sizeof(sc));
1088195972f6Sopenharmony_ci  MEMCPY(p, &sc, sizeof(sc));
1089195972f6Sopenharmony_ci  p += sizeof(sc);
1090195972f6Sopenharmony_ci  PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE);
1091195972f6Sopenharmony_ci  PPPOE_ADD_16(p, sc->sc_hunique_len);
1092195972f6Sopenharmony_ci  MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len);
1093195972f6Sopenharmony_ci  return pppoe_output(sc, pb);
1094195972f6Sopenharmony_ci}
1095195972f6Sopenharmony_ci
1096195972f6Sopenharmony_cistatic err_t
1097195972f6Sopenharmony_cipppoe_send_pads(struct pppoe_softc *sc)
1098195972f6Sopenharmony_ci{
1099195972f6Sopenharmony_ci  struct pbuf *pb;
1100195972f6Sopenharmony_ci  u8_t *p;
1101195972f6Sopenharmony_ci  size_t len, l1 = 0;  /* XXX: gcc */
1102195972f6Sopenharmony_ci
1103195972f6Sopenharmony_ci  sc->sc_session = mono_time.tv_sec % 0xff + 1;
1104195972f6Sopenharmony_ci  /* calc length */
1105195972f6Sopenharmony_ci  len = 0;
1106195972f6Sopenharmony_ci  /* include hunique */
1107195972f6Sopenharmony_ci  len += 2 + 2 + 2 + 2 + sc->sc_hunique_len;  /* service name, host unique*/
1108195972f6Sopenharmony_ci  if (sc->sc_service_name != NULL) {    /* service name tag maybe empty */
1109195972f6Sopenharmony_ci    l1 = strlen(sc->sc_service_name);
1110195972f6Sopenharmony_ci    len += l1;
1111195972f6Sopenharmony_ci  }
1112195972f6Sopenharmony_ci  pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN + len), PBUF_RAM);
1113195972f6Sopenharmony_ci  if (!pb) {
1114195972f6Sopenharmony_ci    return ERR_MEM;
1115195972f6Sopenharmony_ci  }
1116195972f6Sopenharmony_ci  LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
1117195972f6Sopenharmony_ci  p = (u8_t*)pb->payload;
1118195972f6Sopenharmony_ci  PPPOE_ADD_HEADER(p, PPPOE_CODE_PADS, sc->sc_session, len);
1119195972f6Sopenharmony_ci  PPPOE_ADD_16(p, PPPOE_TAG_SNAME);
1120195972f6Sopenharmony_ci  if (sc->sc_service_name != NULL) {
1121195972f6Sopenharmony_ci    PPPOE_ADD_16(p, l1);
1122195972f6Sopenharmony_ci    MEMCPY(p, sc->sc_service_name, l1);
1123195972f6Sopenharmony_ci    p += l1;
1124195972f6Sopenharmony_ci  } else {
1125195972f6Sopenharmony_ci    PPPOE_ADD_16(p, 0);
1126195972f6Sopenharmony_ci  }
1127195972f6Sopenharmony_ci  PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE);
1128195972f6Sopenharmony_ci  PPPOE_ADD_16(p, sc->sc_hunique_len);
1129195972f6Sopenharmony_ci  MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len);
1130195972f6Sopenharmony_ci  return pppoe_output(sc, pb);
1131195972f6Sopenharmony_ci}
1132195972f6Sopenharmony_ci#endif
1133195972f6Sopenharmony_ci
1134195972f6Sopenharmony_cistatic err_t
1135195972f6Sopenharmony_cipppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb)
1136195972f6Sopenharmony_ci{
1137195972f6Sopenharmony_ci  u8_t *p;
1138195972f6Sopenharmony_ci  size_t len;
1139195972f6Sopenharmony_ci
1140195972f6Sopenharmony_ci  len = pb->tot_len;
1141195972f6Sopenharmony_ci
1142195972f6Sopenharmony_ci  /* make room for PPPoE header - should not fail */
1143195972f6Sopenharmony_ci  if (pbuf_add_header(pb, PPPOE_HEADERLEN) != 0) {
1144195972f6Sopenharmony_ci    /* bail out */
1145195972f6Sopenharmony_ci    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));
1146195972f6Sopenharmony_ci    LINK_STATS_INC(link.lenerr);
1147195972f6Sopenharmony_ci    pbuf_free(pb);
1148195972f6Sopenharmony_ci    return ERR_BUF;
1149195972f6Sopenharmony_ci  }
1150195972f6Sopenharmony_ci
1151195972f6Sopenharmony_ci  p = (u8_t*)pb->payload;
1152195972f6Sopenharmony_ci  PPPOE_ADD_HEADER(p, 0, sc->sc_session, len);
1153195972f6Sopenharmony_ci
1154195972f6Sopenharmony_ci  return pppoe_output(sc, pb);
1155195972f6Sopenharmony_ci}
1156195972f6Sopenharmony_ci
1157195972f6Sopenharmony_ci#if 0 /*def PFIL_HOOKS*/
1158195972f6Sopenharmony_cistatic int
1159195972f6Sopenharmony_cipppoe_ifattach_hook(void *arg, struct pbuf **mp, struct netif *ifp, int dir)
1160195972f6Sopenharmony_ci{
1161195972f6Sopenharmony_ci  struct pppoe_softc *sc;
1162195972f6Sopenharmony_ci  int s;
1163195972f6Sopenharmony_ci
1164195972f6Sopenharmony_ci  if (mp != (struct pbuf **)PFIL_IFNET_DETACH) {
1165195972f6Sopenharmony_ci    return 0;
1166195972f6Sopenharmony_ci  }
1167195972f6Sopenharmony_ci
1168195972f6Sopenharmony_ci  LIST_FOREACH(sc, &pppoe_softc_list, sc_list) {
1169195972f6Sopenharmony_ci    if (sc->sc_ethif != ifp) {
1170195972f6Sopenharmony_ci      continue;
1171195972f6Sopenharmony_ci    }
1172195972f6Sopenharmony_ci    if (sc->sc_sppp.pp_if.if_flags & IFF_UP) {
1173195972f6Sopenharmony_ci      sc->sc_sppp.pp_if.if_flags &= ~(IFF_UP|IFF_RUNNING);
1174195972f6Sopenharmony_ci      PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": ethernet interface detached, going down\n",
1175195972f6Sopenharmony_ci          sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
1176195972f6Sopenharmony_ci    }
1177195972f6Sopenharmony_ci    sc->sc_ethif = NULL;
1178195972f6Sopenharmony_ci    pppoe_clear_softc(sc, "ethernet interface detached");
1179195972f6Sopenharmony_ci  }
1180195972f6Sopenharmony_ci
1181195972f6Sopenharmony_ci  return 0;
1182195972f6Sopenharmony_ci}
1183195972f6Sopenharmony_ci#endif
1184195972f6Sopenharmony_ci
1185195972f6Sopenharmony_ci#if 0 /* UNUSED */
1186195972f6Sopenharmony_cistatic void
1187195972f6Sopenharmony_cipppoe_clear_softc(struct pppoe_softc *sc, const char *message)
1188195972f6Sopenharmony_ci{
1189195972f6Sopenharmony_ci  LWIP_UNUSED_ARG(message);
1190195972f6Sopenharmony_ci
1191195972f6Sopenharmony_ci  /* stop timer */
1192195972f6Sopenharmony_ci  sys_untimeout(pppoe_timeout, sc);
1193195972f6Sopenharmony_ci  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));
1194195972f6Sopenharmony_ci  sc->sc_state = PPPOE_STATE_INITIAL;
1195195972f6Sopenharmony_ci  ppp_link_end(sc->pcb);  /* notify upper layers - /!\ dangerous /!\ - see pppoe_disc_input() */
1196195972f6Sopenharmony_ci}
1197195972f6Sopenharmony_ci#endif /* UNUSED */
1198195972f6Sopenharmony_ci#endif /* PPP_SUPPORT && PPPOE_SUPPORT */
1199