From 931fcfd0f5e3f415ee662a425b0f00260d03a013 Mon Sep 17 00:00:00 2001 From: marcbou Date: Thu, 16 Aug 2007 18:12:20 +0000 Subject: [PATCH] Added PPPoE support and various PPP improvements. --- src/api/tcpip.c | 29 + src/include/lwip/opt.h | 44 +- src/include/lwip/tcpip.h | 11 +- src/include/netif/etharp.h | 4 + src/netif/etharp.c | 4 +- src/netif/ethernetif.c | 10 + src/netif/ppp/auth.c | 4 +- src/netif/ppp/chap.c | 18 +- src/netif/ppp/fsm.c | 2 +- src/netif/ppp/lcp.c | 6 + src/netif/ppp/ppp.c | 474 ++++++++++++++-- src/netif/ppp/ppp.h | 21 +- src/netif/ppp/ppp_oe.c | 1092 ++++++++++++++++++++++++++++++++++++ src/netif/ppp/ppp_oe.h | 161 ++++++ 14 files changed, 1805 insertions(+), 75 deletions(-) create mode 100644 src/netif/ppp/ppp_oe.c create mode 100644 src/netif/ppp/ppp_oe.h diff --git a/src/api/tcpip.c b/src/api/tcpip.c index e625da45..38fa6e4a 100644 --- a/src/api/tcpip.c +++ b/src/api/tcpip.c @@ -328,6 +328,14 @@ tcpip_thread(void *arg) msg->msg.cb.f(msg->msg.cb.ctx); memp_free(MEMP_TCPIP_MSG, msg); break; + case TCPIP_MSG_TIMEOUT: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg)); + + if(msg->msg.tmo.msecs != 0xffffffff) + sys_timeout (msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg); + else + sys_untimeout (msg->msg.tmo.h, msg->msg.tmo.arg); + break; default: break; } @@ -420,6 +428,27 @@ tcpip_callback(void (*f)(void *ctx), void *ctx) return ERR_VAL; } +err_t +tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg) +{ + struct tcpip_msg *msg; + + if (mbox != SYS_MBOX_NULL) { + msg = memp_malloc(MEMP_TCPIP_MSG); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_TIMEOUT; + msg->msg.tmo.msecs = msecs; + msg->msg.tmo.h = h; + msg->msg.tmo.arg = arg; + sys_mbox_post(mbox, msg); + return ERR_OK; + } + return ERR_VAL; +} + /** * Call the lower part of a netconn_* function * This function is then running in the thread context diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h index 67f2c120..a85cdcf4 100644 --- a/src/include/lwip/opt.h +++ b/src/include/lwip/opt.h @@ -643,9 +643,19 @@ #define PPP_SUPPORT 0 /* Set for PPP */ #endif +#ifndef PPPOE_SUPPORT +#define PPPOE_SUPPORT 0 /* Set for PPP Over Ethernet */ +#endif + +#ifndef PPPOS_SUPPORT +#define PPPOS_SUPPORT PPP_SUPPORT /* Set for PPP Over Serial */ +#endif + #if PPP_SUPPORT +#ifndef NUM_PPP #define NUM_PPP 1 /* Max PPP sessions. */ +#endif @@ -657,9 +667,15 @@ #define CHAP_SUPPORT 0 /* Set for CHAP. */ #endif +#ifndef MSCHAP_SUPPORT #define MSCHAP_SUPPORT 0 /* Set for MSCHAP (NOT FUNCTIONAL!) */ +#endif +#ifndef CBCP_SUPPORT #define CBCP_SUPPORT 0 /* Set for CBCP (NOT FUNCTIONAL!) */ +#endif +#ifndef CCP_SUPPORT #define CCP_SUPPORT 0 /* Set for CCP (NOT FUNCTIONAL!) */ +#endif #ifndef VJ_SUPPORT #define VJ_SUPPORT 0 /* Set for VJ header compression. */ @@ -673,30 +689,48 @@ /* * Timeouts. */ +#ifndef FSM_DEFTIMEOUT #define FSM_DEFTIMEOUT 6 /* Timeout time in seconds */ +#endif +#ifndef FSM_DEFMAXTERMREQS #define FSM_DEFMAXTERMREQS 2 /* Maximum Terminate-Request transmissions */ +#endif +#ifndef FSM_DEFMAXCONFREQS #define FSM_DEFMAXCONFREQS 10 /* Maximum Configure-Request transmissions */ +#endif +#ifndef FSM_DEFMAXNAKLOOPS #define FSM_DEFMAXNAKLOOPS 5 /* Maximum number of nak loops */ +#endif +#ifndef UPAP_DEFTIMEOUT #define UPAP_DEFTIMEOUT 6 /* Timeout (seconds) for retransmitting req */ +#endif +#ifndef UPAP_DEFREQTIME #define UPAP_DEFREQTIME 30 /* Time to wait for auth-req from peer */ +#endif +#ifndef CHAP_DEFTIMEOUT #define CHAP_DEFTIMEOUT 6 /* Timeout time in seconds */ +#endif +#ifndef CHAP_DEFTRANSMITS #define CHAP_DEFTRANSMITS 10 /* max # times to send challenge */ +#endif /* Interval in seconds between keepalive echo requests, 0 to disable. */ -#if 1 +#ifndef LCP_ECHOINTERVAL #define LCP_ECHOINTERVAL 0 -#else -#define LCP_ECHOINTERVAL 10 #endif /* Number of unanswered echo requests before failure. */ +#ifndef LCP_MAXECHOFAILS #define LCP_MAXECHOFAILS 3 +#endif /* Max Xmit idle time (in jiffies) before resend flag char. */ +#ifndef PPP_MAXIDLEFLAG #define PPP_MAXIDLEFLAG 100 +#endif /* * Packet sizes @@ -707,6 +741,8 @@ * of living in lcp.h) */ #define PPP_MTU 1500 /* Default MTU (size of Info field) */ +#endif +#ifndef PPP_MAXMTU #if 0 #define PPP_MAXMTU 65535 - (PPP_HDRLEN + PPP_FCSLEN) #else @@ -715,7 +751,9 @@ #define PPP_MINMTU 64 #define PPP_MRU 1500 /* default MRU = max length of info field */ #define PPP_MAXMRU 1500 /* Largest MRU we allow */ +#ifndef PPP_DEFMRU #define PPP_DEFMRU 296 /* Try for this */ +#endif #define PPP_MINMRU 128 /* No MRUs below this */ diff --git a/src/include/lwip/tcpip.h b/src/include/lwip/tcpip.h index 449c8fc3..23ae5ab3 100644 --- a/src/include/lwip/tcpip.h +++ b/src/include/lwip/tcpip.h @@ -80,6 +80,8 @@ err_t tcpip_netifapi_lock(struct netifapi_msg *netifapimsg); #endif /* LWIP_NETIF_API */ err_t tcpip_callback(void (*f)(void *ctx), void *ctx); +err_t tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg); +#define tcpip_untimeout(h, arg) tcpip_timeout(0xffffffff, h, arg) enum tcpip_msg_type { TCPIP_MSG_API, @@ -92,7 +94,8 @@ enum tcpip_msg_type { #if LWIP_NETIF_API TCPIP_MSG_NETIFAPI, #endif /* LWIP_NETIF_API */ - TCPIP_MSG_CALLBACK + TCPIP_MSG_CALLBACK, + TCPIP_MSG_TIMEOUT }; struct tcpip_msg { @@ -106,11 +109,17 @@ struct tcpip_msg { struct { struct pbuf *p; struct netif *netif; + err_t (*f)(struct pbuf *, struct netif *); } inp; struct { void (*f)(void *ctx); void *ctx; } cb; + struct { + u32_t msecs; + sys_timeout_handler h; + void *arg; + } tmo; } msg; }; diff --git a/src/include/netif/etharp.h b/src/include/netif/etharp.h index 9fa422d7..52a3e8b9 100644 --- a/src/include/netif/etharp.h +++ b/src/include/netif/etharp.h @@ -121,6 +121,8 @@ PACK_STRUCT_END #define ETHTYPE_ARP 0x0806 #define ETHTYPE_IP 0x0800 +#define ETHTYPE_PPPOEDISC 0x8863 /* PPP Over Ethernet Discovery Stage */ +#define ETHTYPE_PPPOE 0x8864 /* PPP Over Ethernet Session Stage */ /** ARP message types (opcodes) */ #define ARP_REQUEST 1 @@ -162,6 +164,8 @@ err_t etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr, #error ARP_TABLE_SIZE must fit in an s8_t #endif +extern const struct eth_addr ethbroadcast, ethzero; + #ifdef __cplusplus } #endif diff --git a/src/netif/etharp.c b/src/netif/etharp.c index 399c4060..52b2931e 100644 --- a/src/netif/etharp.c +++ b/src/netif/etharp.c @@ -102,8 +102,8 @@ struct etharp_entry { struct netif *netif; }; -static const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}}; -static const struct eth_addr ethzero = {{0,0,0,0,0,0}}; +const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}}; +const struct eth_addr ethzero = {{0,0,0,0,0,0}}; static struct etharp_entry arp_table[ARP_TABLE_SIZE]; #if !LWIP_NETIF_HWADDRHINT static u8_t etharp_cached_entry = 0; diff --git a/src/netif/ethernetif.c b/src/netif/ethernetif.c index 8ad0da19..96ddc0ea 100644 --- a/src/netif/ethernetif.c +++ b/src/netif/ethernetif.c @@ -51,6 +51,7 @@ #include #include "netif/etharp.h" +#include "netif/ppp_oe.h" /* Define those to better describe your network interface. */ #define IFNAME0 'e' @@ -289,6 +290,15 @@ ethernetif_input(struct netif *netif) #endif /* ETHARP_TCPIP_INPUT */ #endif /* ETHARP_TCPIP_ETHINPUT */ +#if PPPOE_SUPPORT > 0 + case ETHTYPE_PPPOEDISC: /* PPP Over Ethernet Discovery Stage */ + pppoe_disc_input(netif, p); + break; + case ETHTYPE_PPPOE: /* PPP Over Ethernet Session Stage */ + pppoe_data_input(netif, p); + break; +#endif + default: pbuf_free(p); p = NULL; diff --git a/src/netif/ppp/auth.c b/src/netif/ppp/auth.c index 00d8b345..640cc457 100644 --- a/src/netif/ppp/auth.c +++ b/src/netif/ppp/auth.c @@ -195,7 +195,7 @@ void link_terminated(int unit) logout(); lcp_phase[unit] = PHASE_DEAD; AUTHDEBUG((LOG_NOTICE, "Connection terminated.\n")); - pppMainWakeup(unit); + pppLinkTerminated(unit); } /* @@ -223,7 +223,7 @@ void link_down(int unit) num_np_up = 0; if (lcp_phase[unit] != PHASE_DEAD) lcp_phase[unit] = PHASE_TERMINATE; - pppMainWakeup(unit); + pppLinkDown(unit); } /* diff --git a/src/netif/ppp/chap.c b/src/netif/ppp/chap.c index 4d1dc0d2..53dbeae0 100644 --- a/src/netif/ppp/chap.c +++ b/src/netif/ppp/chap.c @@ -101,8 +101,10 @@ static void ChapLowerUp (int); static void ChapLowerDown (int); static void ChapInput (int, u_char *, int); static void ChapProtocolReject (int); +#if 0 static int ChapPrintPkt (u_char *, int, void (*) (void *, char *, ...), void *); +#endif static void ChapChallengeTimeout (void *); static void ChapResponseTimeout (void *); @@ -146,15 +148,6 @@ struct protent chap_protent = { -/*****************************/ -/*** LOCAL DATA STRUCTURES ***/ -/*****************************/ -static char *ChapCodenames[] = { - "Challenge", "Response", "Success", "Failure" -}; - - - /***********************************/ /*** PUBLIC FUNCTION DEFINITIONS ***/ /***********************************/ @@ -808,6 +801,10 @@ static void ChapSendResponse(chap_state *cstate) ++cstate->resp_transmits; } +#if 0 +static char *ChapCodenames[] = { + "Challenge", "Response", "Success", "Failure" +}; /* * ChapPrintPkt - print the contents of a CHAP packet. */ @@ -866,7 +863,8 @@ static int ChapPrintPkt( return len + CHAP_HEADERLEN; } - #endif +#endif /* CHAP_SUPPORT > 0 */ + #endif /* PPP_SUPPORT */ diff --git a/src/netif/ppp/fsm.c b/src/netif/ppp/fsm.c index fe8b38a9..6f08c800 100644 --- a/src/netif/ppp/fsm.c +++ b/src/netif/ppp/fsm.c @@ -655,7 +655,7 @@ static void fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len) if (id != f->reqid || f->seen_ack) /* Expected id? */ return; /* Nope, toss... */ proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci; - if (!proc || !(ret = proc(f, inp, len))) { + if (!proc || !((ret = proc(f, inp, len)))) { /* Nak/reject is bad - ignore it */ FSMDEBUG((LOG_INFO, "%s: received bad %s (length %d)\n", PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), len)); diff --git a/src/netif/ppp/lcp.c b/src/netif/ppp/lcp.c index ee90f441..20745d07 100644 --- a/src/netif/ppp/lcp.c +++ b/src/netif/ppp/lcp.c @@ -61,6 +61,12 @@ #include "lcp.h" #include "pppdebug.h" +#if PPPOE_SUPPORT +#include "ppp_oe.h" +#else +#define PPPOE_MAXMTU PPP_MAXMRU +#endif + /*************************/ /*** LOCAL DEFINITIONS ***/ diff --git a/src/netif/ppp/ppp.c b/src/netif/ppp/ppp.c index 3bf7cc03..25d229fb 100644 --- a/src/netif/ppp/ppp.c +++ b/src/netif/ppp/ppp.c @@ -98,6 +98,9 @@ #if VJ_SUPPORT > 0 #include "vj.h" #endif +#if PPPOE_SUPPORT > 0 +#include "ppp_oe.h" +#endif #include "pppdebug.h" @@ -134,24 +137,29 @@ typedef enum { */ typedef struct PPPControl_s { char openFlag; /* True when in use. */ - char oldFrame; /* Old framing character for fd. */ +#if PPPOE_SUPPORT + struct netif *ethif; + struct pppoe_softc *pppoe_sc; +#endif + int if_up; /* True when the interface is up. */ + int errCode; /* Code indicating why interface is down. */ +#if PPPOS_SUPPORT sio_fd_t fd; /* File device ID of port. */ int kill_link; /* Shut the link down. */ int sig_hup; /* Carrier lost. */ - int if_up; /* True when the interface is up. */ - int errCode; /* Code indicating why interface is down. */ struct pbuf *inHead, *inTail; /* The input packet. */ PPPDevStates inState; /* The input process state. */ char inEscaped; /* Escape next character. */ u16_t inProtocol; /* The input protocol code. */ u16_t inFCS; /* Input Frame Check Sequence value. */ +#endif int mtu; /* Peer's mru */ int pcomp; /* Does peer accept protocol compression? */ int accomp; /* Does peer accept addr/ctl compression? */ u_long lastXMit; /* Time of last transmission. */ ext_accm inACCM; /* Async-Ctl-Char-Map for input. */ ext_accm outACCM; /* Async-Ctl-Char-Map for output. */ -#if VJ_SUPPORT > 0 +#if PPPOS_SUPPORT && VJ_SUPPORT > 0 int vjEnabled; /* Flag indicating VJ compression enabled. */ struct vjcompress vjComp; /* Van Jabobsen compression header. */ #endif @@ -180,9 +188,11 @@ struct npioctl { /***********************************/ /*** LOCAL FUNCTION DECLARATIONS ***/ /***********************************/ +#if PPPOS_SUPPORT static void pppMain(void *pd); static void pppDrop(PPPControl *pc); static void pppInProc(int pd, u_char *s, int l); +#endif /******************************/ @@ -220,13 +230,14 @@ struct protent *ppp_protocols[] = { * Buffers for outgoing packets. This must be accessed only from the appropriate * PPP task so that it doesn't need to be protected to avoid collisions. */ -u_char outpacket_buf[NUM_PPP][PPP_MRU+PPP_HDRLEN]; +u_char *outpacket_buf[NUM_PPP]; /*****************************/ /*** LOCAL DATA STRUCTURES ***/ /*****************************/ +#if PPPOS_SUPPORT /* * FCS lookup table as calculated by genfcstab. */ @@ -279,6 +290,86 @@ static u_char pppACCMMask[] = { }; +void +pppMainWakeup(int pd) +{ + PPPDEBUG((LOG_DEBUG, "pppMainWakeup: unit %d\n", pd)); + sio_read_abort(pppControl[pd].fd); +} +#endif /* PPPOS_SUPPORT */ + +void +pppLinkTerminated(int pd) +{ + PPPControl *pc = &pppControl[pd]; + + PPPDEBUG((LOG_DEBUG, "pppLinkTerminated: unit %d\n", pd)); + +#if PPPOE_SUPPORT + if(pc->ethif) { + pppoe_disconnect(pc->pppoe_sc); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT + pppMainWakeup(pd); +#endif /* PPPOS_SUPPORT */ + } +} + +void +pppLinkDown(int pd) +{ + PPPControl *pc = &pppControl[pd]; + + PPPDEBUG((LOG_DEBUG, "pppLinkDown: unit %d\n", pd)); + +#if PPPOE_SUPPORT + if(pc->ethif) { + pppoe_disconnect(pc->pppoe_sc); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT + pppMainWakeup(pd); +#endif /* PPPOS_SUPPORT */ + } +} + +/* these callbacks are necessary because lcp_* functions + must be called in the same context as pppInput(), + namely the tcpip_thread(), essentially because + they manipulate timeouts which are thread-private +*/ + +static void +pppStartCB(void *arg) +{ + int pd = (int)arg; + + PPPDEBUG((LOG_DEBUG, "pppStartCB: unit %d\n", pd)); + lcp_lowerup(pd); + lcp_open(pd); /* Start protocol */ +} + +static void +pppStopCB(void *arg) +{ + int pd = (int)arg; + + PPPDEBUG((LOG_DEBUG, "pppStopCB: unit %d\n", pd)); + lcp_close(pd, "User request"); +} + +static void +pppHupCB(void *arg) +{ + int pd = (int)arg; + + PPPDEBUG((LOG_DEBUG, "pppHupCB: unit %d\n", pd)); + lcp_lowerdown(pd); + link_terminated(pd); +} /***********************************/ /*** PUBLIC FUNCTION DEFINITIONS ***/ /***********************************/ @@ -286,7 +377,8 @@ static u_char pppACCMMask[] = { struct ppp_settings ppp_settings; -void pppInit(void) +err_t +pppInit(void) { struct protent *protp; int i, j; @@ -302,6 +394,10 @@ void pppInit(void) subnetMask = htonl(0xffffff00); + outpacket_buf[i] = mem_malloc(PPP_MRU+PPP_HDRLEN); + if(!outpacket_buf[i]) + return ERR_MEM; + /* * Initialize to the standard option set. */ @@ -313,6 +409,12 @@ void pppInit(void) /* Clear the statistics. */ memset(&lwip_stats.link, 0, sizeof(lwip_stats.link)); #endif + +#if PPPOE_SUPPORT > 0 + pppoe_init(); +#endif + + return ERR_OK; } void pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd) @@ -374,6 +476,7 @@ void pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd) ppp_settings.passwd[0] = '\0'; } +#if PPPOS_SUPPORT /* Open a new PPP connection using the given I/O device. * This initializes the PPP control block but does not * attempt to negotiate the LCP session. If this port @@ -381,7 +484,7 @@ void pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd) * established before calling this. * Return a new PPP connection descriptor on success or * an error code (negative) on failure. */ -int pppOpen(sio_fd_t fd, void (*linkStatusCB)(void *ctx, int errCode, void *arg), void *linkStatusCtx) +int pppOverSerialOpen(sio_fd_t fd, void (*linkStatusCB)(void *ctx, int errCode, void *arg), void *linkStatusCtx) { PPPControl *pc; int pd; @@ -401,6 +504,7 @@ int pppOpen(sio_fd_t fd, void (*linkStatusCB)(void *ctx, int errCode, void *arg) lcp_init(pd); pc = &pppControl[pd]; pc->fd = fd; + pc->ethif= NULL; pc->kill_link = 0; pc->sig_hup = 0; pc->if_up = 0; @@ -444,6 +548,99 @@ int pppOpen(sio_fd_t fd, void (*linkStatusCB)(void *ctx, int errCode, void *arg) } return pd; } +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +static void pppOverEthernetLinkStatusCB(int pd, int up); + +void pppOverEthernetClose(int pd) +{ + PPPControl* pc = &pppControl[pd]; + + // *TJL* There's no lcp_deinit + lcp_close(pd, NULL); + + pppoe_destroy(&pc->netif); +} + +int pppOverEthernetOpen(struct netif *ethif, const char *service_name, const char *concentrator_name, void (*linkStatusCB)(void *ctx, int errCode, void *arg), void *linkStatusCtx) +{ + PPPControl *pc; + int pd; + + /* Find a free PPP session descriptor. Critical region? */ + for (pd = 0; pd < NUM_PPP && pppControl[pd].openFlag != 0; pd++); + if (pd >= NUM_PPP) + pd = PPPERR_OPEN; + else + pppControl[pd].openFlag = !0; + + /* Launch a deamon thread. */ + if (pd >= 0) { + + pppControl[pd].openFlag = 1; + + lcp_init(pd); + + lcp_wantoptions[pd].mru = PPPOE_MAXMTU; + lcp_wantoptions[pd].neg_asyncmap = 0; + lcp_wantoptions[pd].neg_pcompression = 0; + lcp_wantoptions[pd].neg_accompression = 0; + + lcp_allowoptions[pd].mru = PPPOE_MAXMTU; + lcp_allowoptions[pd].neg_asyncmap = 0; + lcp_allowoptions[pd].neg_pcompression = 0; + lcp_allowoptions[pd].neg_accompression = 0; + + pc = &pppControl[pd]; + pc->if_up = 0; + pc->errCode = 0; + pc->lastXMit = 0; +#if PPPOS_SUPPORT + pc->kill_link = 0; + pc->sig_hup = 0; + pc->inState = PDIDLE; + pc->inHead = NULL; + pc->inTail = NULL; + pc->inEscaped = 0; +#if VJ_SUPPORT > 0 + pc->vjEnabled = 0; +#endif +#endif /* PPPOS_SUPPORT */ + pc->ethif= ethif; + + + memset(pc->inACCM, 0, sizeof(ext_accm)); + memset(pc->outACCM, 0, sizeof(ext_accm)); + + pc->linkStatusCB = linkStatusCB; + pc->linkStatusCtx = linkStatusCtx; + + if(pppoe_create(ethif, pd, pppOverEthernetLinkStatusCB, &pc->pppoe_sc) != ERR_OK) { + pc->openFlag = 0; + return PPPERR_OPEN; + } + + pppoe_connect(pc->pppoe_sc); + + if(!linkStatusCB) { + while(pd >= 0 && !pc->if_up) { + sys_msleep(500); + if (lcp_phase[pd] == PHASE_DEAD) { + pppClose(pd); + if (pc->errCode) + pd = pc->errCode; + else + pd = PPPERR_CONNECT; + } + } + } + + } + return pd; +} +#endif /* PPPOE_SUPPORT */ + /* Close a PPP connection and release the descriptor. * Any outstanding packets in the queues are dropped. @@ -454,8 +651,20 @@ int pppClose(int pd) int st = 0; /* Disconnect */ +#if PPPOE_SUPPORT + if(pc->ethif) { + PPPDEBUG((LOG_DEBUG, "pppClose: unit %d kill_link -> pppStopCB\n", pd)); + pc->errCode = PPPERR_USER; + /* This will leave us at PHASE_DEAD. */ + tcpip_callback(pppStopCB, (void*)pd); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT pc->kill_link = !0; pppMainWakeup(pd); +#endif /* PPPOS_SUPPORT */ + } if(!pc->linkStatusCB) { while(st >= 0 && lcp_phase[pd] != PHASE_DEAD) { @@ -471,10 +680,21 @@ void pppSigHUP(int pd) { PPPControl *pc = &pppControl[pd]; +#if PPPOE_SUPPORT + if(pc->ethif) { + PPPDEBUG((LOG_DEBUG, "pppSigHUP: unit %d sig_hup -> pppHupCB\n", pd)); + tcpip_callback(pppHupCB, (void*)pd); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT pc->sig_hup = 1; pppMainWakeup(pd); +#endif /* PPPOS_SUPPORT */ + } } +#if PPPOS_SUPPORT static void nPut(PPPControl *pc, struct pbuf *nb) { struct pbuf *b; @@ -535,6 +755,50 @@ static struct pbuf *pppAppend(u_char c, struct pbuf *nb, ext_accm *outACCM) return tb; } +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +static err_t +pppifOutputOverEthernet(int pd, struct pbuf *p) +{ + PPPControl *pc = &pppControl[pd]; + struct pbuf *pb; + u_short protocol = PPP_IP; + int i=0; + + pb = pbuf_alloc(PBUF_LINK, pppoe_hdrlen + sizeof(protocol), PBUF_RAM); + if(!pb) { +#if LINK_STATS + lwip_stats.link.memerr++; + lwip_stats.link.proterr++; +#endif /* LINK_STATS */ + return ERR_MEM; + } + + pbuf_header(pb, -pppoe_hdrlen); + + pc->lastXMit = sys_jiffies(); + + if (!pc->pcomp || protocol > 0xFF) { + *((u_char*)pb->payload + i++) = (protocol >> 8) & 0xFF; + } + *((u_char*)pb->payload + i) = protocol & 0xFF; + + pbuf_chain(pb, p); + + if(pppoe_xmit(pc->pppoe_sc, pb) != ERR_OK) { +#if LINK_STATS + lwip_stats.link.err++; +#endif /* LINK_STATS */ + return PPPERR_DEVICE; + } + +#if LINK_STATS + lwip_stats.link.xmit++; +#endif /* LINK_STATS */ + return ERR_OK; +} +#endif /* PPPOE_SUPPORT */ /* Send a packet on the given connection. */ static err_t pppifOutput(struct netif *netif, struct pbuf *pb, struct ip_addr *ipaddr) @@ -542,9 +806,11 @@ static err_t pppifOutput(struct netif *netif, struct pbuf *pb, struct ip_addr *i int pd = (int)netif->state; u_short protocol = PPP_IP; PPPControl *pc = &pppControl[pd]; +#if PPPOS_SUPPORT u_int fcsOut = PPP_INITFCS; struct pbuf *headMB = NULL, *tailMB = NULL, *p; u_char c; +#endif /* PPPOS_SUPPORT */ LWIP_UNUSED_ARG(ipaddr); @@ -571,6 +837,13 @@ static err_t pppifOutput(struct netif *netif, struct pbuf *pb, struct ip_addr *i return ERR_RTE; } +#if PPPOE_SUPPORT + if(pc->ethif) { + return pppifOutputOverEthernet(pd, pb); + } +#endif /* PPPOE_SUPPORT */ + +#if PPPOS_SUPPORT /* Grab an output buffer. */ headMB = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); if (headMB == NULL) { @@ -675,6 +948,7 @@ static err_t pppifOutput(struct netif *netif, struct pbuf *pb, struct ip_addr *i PPPDEBUG((LOG_INFO, "pppifOutput[%d]: proto=0x%04X\n", pd, protocol)); nPut(pc, headMB); +#endif /* PPPOS_SUPPORT */ return ERR_OK; } @@ -708,12 +982,14 @@ int pppIOCtl(int pd, int cmd, void *arg) else st = PPPERR_PARAM; break; +#if PPPOS_SUPPORT case PPPCTLG_FD: if (arg) *(sio_fd_t *)arg = pc->fd; else st = PPPERR_PARAM; break; +#endif /* PPPOS_SUPPORT */ default: st = PPPERR_PARAM; break; @@ -740,6 +1016,45 @@ u_int pppMTU(int pd) return st; } +#if PPPOE_SUPPORT +int pppWriteOverEthernet(int pd, const u_char *s, int n) +{ + PPPControl *pc = &pppControl[pd]; + struct pbuf *pb; + + /* skip address & flags */ + s += 2; + n -= 2; + + pb = pbuf_alloc(PBUF_LINK, pppoe_hdrlen + n, PBUF_RAM); + if(!pb) { +#if LINK_STATS + lwip_stats.link.memerr++; + lwip_stats.link.proterr++; +#endif /* LINK_STATS */ + return PPPERR_ALLOC; + } + + pbuf_header(pb, -pppoe_hdrlen); + + pc->lastXMit = sys_jiffies(); + + SMEMCPY(pb->payload, s, n); + + if(pppoe_xmit(pc->pppoe_sc, pb) != ERR_OK) { +#if LINK_STATS + lwip_stats.link.err++; +#endif /* LINK_STATS */ + return PPPERR_DEVICE; + } + +#if LINK_STATS + lwip_stats.link.xmit++; +#endif /* LINK_STATS */ + return PPPERR_NONE; +} +#endif /* PPPOE_SUPPORT */ + /* * Write n characters to a ppp link. * RETURN: >= 0 Number of characters written @@ -748,9 +1063,19 @@ u_int pppMTU(int pd) int pppWrite(int pd, const u_char *s, int n) { PPPControl *pc = &pppControl[pd]; +#if PPPOS_SUPPORT u_char c; - u_int fcsOut = PPP_INITFCS; - struct pbuf *headMB = NULL, *tailMB; + u_int fcsOut; + struct pbuf *headMB, *tailMB; +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT + if(pc->ethif) { + return pppWriteOverEthernet(pd, s, n); + } +#endif /* PPPOE_SUPPORT */ + +#if PPPOS_SUPPORT headMB = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); if (headMB == NULL) { #if LINK_STATS @@ -768,6 +1093,7 @@ int pppWrite(int pd, const u_char *s, int n) tailMB = pppAppend(PPP_FLAG, tailMB, NULL); pc->lastXMit = sys_jiffies(); + fcsOut = PPP_INITFCS; /* Load output buffer. */ while (n-- > 0) { c = *s++; @@ -803,6 +1129,7 @@ int pppWrite(int pd, const u_char *s, int n) PPPDEBUG((LOG_INFO, "pppWrite[%d]: len=%d\n", pd, headMB->len)); /* "pppWrite[%d]: %d:%.*H", pd, headMB->len, LWIP_MIN(headMB->len * 2, 40), headMB->payload)); */ nPut(pc, headMB); +#endif /* PPPOS_SUPPORT */ return PPPERR_NONE; } @@ -967,7 +1294,7 @@ int sifvjcomp( int maxcid ) { -#if VJ_SUPPORT > 0 +#if PPPOS_SUPPORT > 0 && VJ_SUPPORT > 0 PPPControl *pc = &pppControl[pd]; pc->vjEnabled = vjcomp; @@ -1009,6 +1336,8 @@ int sifup(int pd) if (netif_add(&pc->netif, &pc->addrs.our_ipaddr, &pc->addrs.netmask, &pc->addrs.his_ipaddr, (void *)pd, pppifNetifInit, ip_input)) { netif_set_up(&pc->netif); + /* ugly workaround for storing a reference to the ppp related info*/ + pc->netif.dhcp = (struct dhcp *) &pc->addrs; pc->if_up = 1; pc->errCode = PPPERR_NONE; @@ -1154,50 +1483,11 @@ int cifdefaultroute(int pd, u32_t l, u32_t g) return st; } -void -pppMainWakeup(int pd) -{ - PPPDEBUG((LOG_DEBUG, "pppMainWakeup: unit %d\n", pd)); - sio_read_abort(pppControl[pd].fd); -} - -/* these callbacks are necessary because lcp_* functions - must be called in the same context as pppInput(), - namely the tcpip_thread(), essentially because - they manipulate timeouts which are thread-private -*/ - -static void -pppStartCB(void *arg) -{ - int pd = (int)arg; - - PPPDEBUG((LOG_DEBUG, "pppStartCB: unit %d\n", pd)); - lcp_lowerup(pd); - lcp_open(pd); /* Start protocol */ -} - -static void -pppStopCB(void *arg) -{ - int pd = (int)arg; - - PPPDEBUG((LOG_DEBUG, "pppStopCB: unit %d\n", pd)); - lcp_close(pd, "User request"); -} - -static void -pppHupCB(void *arg) -{ - int pd = (int)arg; - - PPPDEBUG((LOG_DEBUG, "pppHupCB: unit %d\n", pd)); - lcp_lowerdown(pd); - link_terminated(pd); -} /**********************************/ /*** LOCAL FUNCTION DEFINITIONS ***/ /**********************************/ + +#if PPPOS_SUPPORT /* The main PPP process function. This implements the state machine according * to section 4 of RFC 1661: The Point-To-Point Protocol. */ static void pppMain(void *arg) @@ -1243,6 +1533,7 @@ static void pppMain(void *arg) } } PPPDEBUG((LOG_INFO, "pppMain: unit %d: PHASE_DEAD\n", pd)); + pppDrop(pc); // bug fix #17726 pbuf_free(p); out: @@ -1252,8 +1543,44 @@ out: pc->openFlag = 0; } +#endif /* PPPOS_SUPPORT */ -static struct pbuf *pppSingleBuf(struct pbuf *p) +#if PPPOE_SUPPORT + +void pppOverEthernetInitFailed(void* arg) +{ + PPPControl* pc; + int pd = (int)arg; + + pppHupCB(arg); + pppStopCB(arg); + + pc = &pppControl[pd]; + pppoe_destroy(&pc->netif); + pc->openFlag = 0; + + if(pc->linkStatusCB) + { + pc->linkStatusCB(pc->linkStatusCtx, pc->errCode ? pc->errCode : PPPERR_PROTOCOL, NULL); + } +} + +static void +pppOverEthernetLinkStatusCB(int pd, int up) +{ + if(up) { + PPPDEBUG((LOG_INFO, "pppMain: unit %d: Connecting\n", pd)); + tcpip_callback(pppStartCB, (void*)pd); + } else { + PPPControl* pc; + pc = &pppControl[pd]; + + tcpip_callback(pppOverEthernetInitFailed, (void*)pd); + } +} +#endif /* PPPOE_SUPPORT */ + +struct pbuf *pppSingleBuf(struct pbuf *p) { struct pbuf *q, *b; u_char *pl; @@ -1412,6 +1739,7 @@ out: } +#if PPPOS_SUPPORT /* * Drop the input packet. */ @@ -1635,5 +1963,47 @@ static void pppInProc(int pd, u_char *s, int l) } avRandomize(); } +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +void pppInProcOverEthernet(int pd, struct pbuf *pb) +{ + struct pppInputHeader *pih; + u16_t inProtocol; + + if(pb->len < sizeof(inProtocol)) { + PPPDEBUG((LOG_ERR, "pppInProcOverEthernet: too small for protocol field\n")); + goto drop; + } + + inProtocol = (((u8_t *)pb->payload)[0] << 8) | ((u8_t*)pb->payload)[1]; + + /* make room for pppInputHeader - should not fail */ + if (pbuf_header(pb, sizeof(*pih) - sizeof(inProtocol)) != 0) { + PPPDEBUG((LOG_ERR, "pppInProcOverEthernet: could not allocate room for header\n")); + goto drop; + } + + pih = pb->payload; + + pih->unit = pd; + pih->proto = inProtocol; + + /* Dispatch the packet thereby consuming it. */ + if(tcpip_callback(pppInput, pb) != ERR_OK) { + PPPDEBUG((LOG_ERR, "pppInProcOverEthernet[%d]: tcpip_callback() failed, dropping packet\n", pd)); + goto drop; + } + + return; + +drop: +#if LINK_STATS + lwip_stats.link.drop++; +#endif + pbuf_free(pb); + return; +} +#endif /* PPPOE_SUPPORT */ #endif /* PPP_SUPPORT */ diff --git a/src/netif/ppp/ppp.h b/src/netif/ppp/ppp.h index b0963f25..d4c550db 100644 --- a/src/netif/ppp/ppp.h +++ b/src/netif/ppp/ppp.h @@ -329,7 +329,7 @@ struct ppp_addrs { *** PUBLIC DATA STRUCTURES *** *****************************/ /* Buffers for outgoing packets. */ -extern u_char outpacket_buf[NUM_PPP][PPP_MRU+PPP_HDRLEN]; +extern u_char *outpacket_buf[NUM_PPP]; extern struct ppp_settings ppp_settings; @@ -341,7 +341,7 @@ extern struct protent *ppp_protocols[];/* Table of pointers to supported protoco ***********************/ /* Initialize the PPP subsystem. */ -void pppInit(void); +err_t pppInit(void); /* Warning: Using PPPAUTHTYPE_ANY might have security consequences. * RFC 1994 says: @@ -372,13 +372,18 @@ enum pppAuthType { void pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd); /* - * Open a new PPP connection using the given I/O device. + * Open a new PPP connection using the given serial I/O device. * This initializes the PPP control block but does not * attempt to negotiate the LCP session. * Return a new PPP connection descriptor on success or * an error code (negative) on failure. */ -int pppOpen(sio_fd_t fd, void (*linkStatusCB)(void *ctx, int errCode, void *arg), void *linkStatusCtx); +int pppOverSerialOpen(sio_fd_t fd, void (*linkStatusCB)(void *ctx, int errCode, void *arg), void *linkStatusCtx); + +/* + * Open a new PPP Over Ethernet (PPPOE) connection. + */ +int pppOverEthernetOpen(struct netif *ethif, const char *service_name, const char *concentrator_name, void (*linkStatusCB)(void *ctx, int errCode, void *arg), void *linkStatusCtx); /* * Close a PPP connection and release the descriptor. @@ -410,6 +415,14 @@ u_int pppMTU(int pd); */ int pppWrite(int pd, const u_char *s, int n); +void pppInProcOverEthernet(int pd, struct pbuf *pb); + +struct pbuf *pppSingleBuf(struct pbuf *p); + +void pppLinkTerminated(int pd); + +void pppLinkDown(int pd); + void pppMainWakeup(int pd); /* Configure i/f transmit parameters */ diff --git a/src/netif/ppp/ppp_oe.c b/src/netif/ppp/ppp_oe.c new file mode 100644 index 00000000..aa4fac62 --- /dev/null +++ b/src/netif/ppp/ppp_oe.c @@ -0,0 +1,1092 @@ +/***************************************************************************** +* ppp_oe.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 +* 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 . + * + * 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 +#include + +#include "ppp.h" +#include "pppdebug.h" + +#include "netif/etharp.h" +#include "queue.h" +#include "ppp_oe.h" +#include "lwip/sys.h" + +#if PPPOE_SUPPORT > 0 + +/* Add a 16 bit unsigned value to a buffer pointed to by PTR */ +#define PPPOE_ADD_16(PTR, VAL) \ + *(PTR)++ = (VAL) / 256; \ + *(PTR)++ = (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 +/* from if_spppsubr.c */ +#define IFF_PASSIVE IFF_LINK0 /* wait passively for connection */ +#endif + +struct pppoe_softc { + LIST_ENTRY(pppoe_softc) sc_list; + struct netif *sc_ethif; /* ethernet interface we are using */ + int sc_pd; /* ppp unit number */ + void (*sc_linkStatusCB)(int pd, int up); + + int sc_state; /* discovery phase or session connected */ + struct eth_addr sc_dest; /* hardware address of concentrator */ + u16_t sc_session; /* PPPoE session id */ + + char *sc_service_name; /* if != NULL: requested name of service */ + char *sc_concentrator_name; /* if != NULL: requested concentrator id */ + u8_t *sc_ac_cookie; /* content of AC cookie we must echo back */ + size_t sc_ac_cookie_len; /* length of cookie data */ +#ifdef PPPOE_SERVER + u8_t *sc_hunique; /* content of host unique we must echo back */ + size_t sc_hunique_len; /* length of host unique */ +#endif + int sc_padi_retried; /* number of PADI retries already done */ + int sc_padr_retried; /* number of PADR retries already done */ +}; + +/* input routines */ +static void pppoe_dispatch_disc_pkt(struct netif *, struct pbuf *); + +/* management routines */ +static int pppoe_do_disconnect(struct pppoe_softc *); +static void pppoe_abort_connect(struct pppoe_softc *); +static void pppoe_clear_softc(struct pppoe_softc *, const char *); + +/* 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 struct pppoe_softc * pppoe_find_softc_by_session(u_int, struct netif *); +static struct pppoe_softc * pppoe_find_softc_by_hunique(u8_t *, size_t, struct netif *); + +static LIST_HEAD(pppoe_softc_head, pppoe_softc) pppoe_softc_list; + +int pppoe_hdrlen; + +void +pppoe_init(void) +{ + pppoe_hdrlen = sizeof(struct eth_hdr) + PPPOE_HEADERLEN; + LIST_INIT(&pppoe_softc_list); +} + +err_t +pppoe_create(struct netif *ethif, int pd, void (*linkStatusCB)(int pd, int up), struct pppoe_softc **scptr) +{ + struct pppoe_softc *sc; + + sc = mem_malloc(sizeof(struct pppoe_softc)); + if(!sc) { + *scptr = NULL; + return ERR_MEM; + } + memset(sc, 0, sizeof(struct pppoe_softc)); + + /* changed to real address later */ + memcpy(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + + sc->sc_pd = pd; + sc->sc_linkStatusCB = linkStatusCB; + sc->sc_ethif = ethif; + + LIST_INSERT_HEAD(&pppoe_softc_list, sc, sc_list); + + *scptr = sc; + + return ERR_OK; +} + +err_t +pppoe_destroy(struct netif *ifp) +{ + struct pppoe_softc * sc; + + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (sc->sc_ethif == ifp) + break; + } + + if(!(sc && (sc->sc_ethif == ifp))) + return ERR_IF; + + tcpip_untimeout(pppoe_timeout, sc); + LIST_REMOVE(sc, sc_list); + + if (sc->sc_concentrator_name) + mem_free(sc->sc_concentrator_name); + if (sc->sc_service_name) + mem_free(sc->sc_service_name); + if (sc->sc_ac_cookie) + mem_free(sc->sc_ac_cookie); + mem_free(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; + + if (session == 0) + return NULL; + + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (sc->sc_state == PPPOE_STATE_SESSION + && sc->sc_session == session) { + if (sc->sc_ethif == rcvif) + return sc; + else + return NULL; + } + } + 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 (LIST_EMPTY(&pppoe_softc_list)) + return NULL; + + if (len != sizeof sc) + return NULL; + memcpy(&t, token, len); + + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) + 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) { + printf("%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) { + printf("%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; +} + +static void +pppoe_linkstatus_up(void *arg) +{ + struct pppoe_softc *sc = (struct pppoe_softc*)arg; + + sc->sc_linkStatusCB(sc->sc_pd, 1); +} + +/* analyze and handle a single received packet while not in session state */ +static void +pppoe_dispatch_disc_pkt(struct netif *netif, struct pbuf *pb) +{ + u16_t tag, len; + u16_t session, plen; + struct pppoe_softc *sc; + const char *err_msg; + char devname[6]; + char *error; + u8_t *ac_cookie; + size_t ac_cookie_len; +#ifdef PPPOE_SERVER + u8_t *hunique; + size_t hunique_len; +#endif + struct pppoehdr *ph; + struct pppoetag pt; + int off = 0, err, errortag; + struct eth_hdr *ethhdr; + + pb = pppSingleBuf(pb); + + strcpy(devname, "pppoe"); /* as long as we don't know which instance */ + err_msg = NULL; + errortag = 0; + 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 <= PPPOE_HEADERLEN) { + printf("pppoe: packet too short: %d\n", pb->len); + goto done; + } + + ph = (struct pppoehdr *) (ethhdr + 1); + if (ph->vertype != PPPOE_VERTYPE) { + printf("pppoe: unknown version/type packet: 0x%x\n", + ph->vertype); + goto done; + } + session = ntohs(ph->session); + plen = ntohs(ph->plen); + off += sizeof(*ph); + + if (plen + off > pb->len) { + printf("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 = 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 = ntohs(pt.tag); + len = ntohs(pt.len); + if (off + sizeof(pt) + len > pb->len) { + printf("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); + if (sc != NULL) + snprintf(devname, sizeof(devname), "%c%c%"U16_F, sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + break; + case PPPOE_TAG_ACCOOKIE: + if (ac_cookie == NULL) { + ac_cookie = (u8_t*)pb->payload + off + sizeof(pt); + ac_cookie_len = len; + } + break; + case PPPOE_TAG_SNAME_ERR: + err_msg = "SERVICE NAME ERROR"; + errortag = 1; + break; + case PPPOE_TAG_ACSYS_ERR: + err_msg = "AC SYSTEM ERROR"; + errortag = 1; + break; + case PPPOE_TAG_GENERIC_ERR: + err_msg = "GENERIC ERROR"; + errortag = 1; + break; + } + if (err_msg) { + error = NULL; + if (errortag && len) { + error = mem_malloc(len+1); + if (error) { + strncpy(error, (char*)pb->payload + off + sizeof(pt), len); + error[len-1] = '\0'; + } + } + if (error) { + printf("%s: %s: %s\n", devname, err_msg, error); + mem_free(error); + } else + printf("%s: %s\n", devname, err_msg); + if (errortag) + goto done; + } + 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) { +/* printf("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 */ + printf("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)) + printf("pppoe: received PADR but could not find request for it\n"); + goto done; + } + if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + printf("%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; + tcpip_timeout (100, pppoe_linkstatus_up, sc); /* 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 (!LIST_EMPTY(&pppoe_softc_list)) + printf("pppoe: received PADO but could not find request for it\n"); + goto done; + } + if (sc->sc_state != PPPOE_STATE_PADI_SENT) { + printf("%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) { + if (sc->sc_ac_cookie) + mem_free(sc->sc_ac_cookie); + sc->sc_ac_cookie = mem_malloc(ac_cookie_len); + if (sc->sc_ac_cookie == NULL) + goto done; + 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)); + tcpip_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)); + } + tcpip_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; + tcpip_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; + tcpip_timeout (100, pppoe_linkstatus_up, sc); /* notify upper layers */ + break; + case PPPOE_CODE_PADT: + if (sc == NULL) + goto done; + pppoe_clear_softc(sc, "received PADT"); + break; + default: + if(sc) { + printf("%c%c%"U16_F": unknown code (0x%04x) session = 0x%04x\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, + ph->code, session); + } else { + printf("pppoe: unknown code (0x%04x) session = 0x%04x\n", ph->code, session); + } + break; + } + +done: + pbuf_free(pb); + return; +} + +void +pppoe_disc_input(struct netif *netif, struct pbuf *p) +{ + /* avoid error messages if there is not a single pppoe instance */ + if (!LIST_EMPTY(&pppoe_softc_list)) { + pppoe_dispatch_disc_pkt(netif, p); + } else + pbuf_free(p); +} + +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, -(int)sizeof(struct eth_hdr)) != 0) { + /* bail out */ + PPPDEBUG((LOG_ERR, "pppoe_data_input: pbuf_header failed\n")); + LINK_STATS_INC(link.lenerr); + goto drop; + } + + pb = pppSingleBuf (pb); + + if (pb->len <= PPPOE_HEADERLEN) { + printf("pppoe (data): dropping too short packet: %d bytes\n", pb->len); + goto drop; + } + + if (pb->len < sizeof(*ph)) { + printf("pppoe_data_input: could not get PPPoE header\n"); + goto drop; + } + ph = (struct pppoehdr *)pb->payload; + + if (ph->vertype != PPPOE_VERTYPE) { + printf("pppoe (data): unknown version/type packet: 0x%x\n", ph->vertype); + goto drop; + } + if (ph->code != 0) + goto drop; + + session = ntohs(ph->session); + sc = pppoe_find_softc_by_session(session, netif); + if (sc == NULL) { +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + printf("pppoe: input for unknown session 0x%x, sending PADT\n", session); + pppoe_send_padt(netif, session, shost); +#endif + goto drop; + } + + plen = ntohs(ph->plen); + + if (pbuf_header(pb, -(int)(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->len < plen) + goto drop; + + pppInProcOverEthernet(sc->sc_pd, 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; + + if (!sc->sc_ethif) { + pbuf_free(pb); + return ERR_IF; + } + + ethhdr = (struct eth_hdr *)pb->payload; + etype = sc->sc_state == PPPOE_STATE_SESSION ? ETHTYPE_PPPOE : ETHTYPE_PPPOEDISC; + ethhdr->type = htons(etype); + memcpy(ethhdr->dest.addr, sc->sc_dest.addr, sizeof(ethhdr->dest.addr)); + memcpy(ethhdr->src.addr, ((struct eth_addr *)sc->sc_ethif->hwaddr)->addr, 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, l1 = 0, l2 = 0; /* XXX: gcc */ + + if (sc->sc_state >PPPOE_STATE_PADI_SENT) + PPPDEBUG((LOG_ERR, "ERROR: pppoe_send_padi in state %d", sc->sc_state)); + + /* 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 */ + if (sc->sc_service_name != NULL) { + l1 = strlen(sc->sc_service_name); + len += l1; + } + if (sc->sc_concentrator_name != NULL) { + l2 = strlen(sc->sc_concentrator_name); + len += 2 + 2 + l2; + } + + /* allocate a buffer */ + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM); + if (!pb) + return ERR_MEM; + + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + /* fill in pkt */ + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADI, 0, 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); + } + 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; + } + 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) +{ + int retry_wait, 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. + */ + + /* initialize for quick retry mode */ + retry_wait = PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried); + + sc->sc_padi_retried++; + if (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; + } + } + 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)); + } + tcpip_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)); + } + tcpip_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)); + } + tcpip_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc); + break; + case PPPOE_STATE_CLOSING: + pppoe_do_disconnect(sc); + break; + default: + return; /* all done, work in peace */ + } +} + +/* Start a connection (i.e. initiate discovery phase) */ +int +pppoe_connect(struct pppoe_softc *sc) +{ + int err; + + if (sc->sc_state != PPPOE_STATE_INITIAL) + return EBUSY; + +#ifdef PPPOE_SERVER + /* wait PADI if IFF_PASSIVE */ + if ((sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) + return 0; +#endif + /* save state, in case we fail to send PADI */ + sc->sc_state = PPPOE_STATE_PADI_SENT; + sc->sc_padr_retried = 0; + err = pppoe_send_padi(sc); + 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)); + tcpip_timeout(PPPOE_DISC_TIMEOUT, pppoe_timeout, sc); + return err; +} + +/* disconnect */ +void +pppoe_disconnect(struct pppoe_softc *sc) +{ + if (sc->sc_state < PPPOE_STATE_SESSION) + return; + /* + * Do not call pppoe_disconnect here, the upper layer state + * machine gets confused by this. We must return from this + * function and defer disconnecting to the timeout handler. + */ + sc->sc_state = PPPOE_STATE_CLOSING; + tcpip_timeout(20, pppoe_timeout, sc); +} + +static int +pppoe_do_disconnect(struct pppoe_softc *sc) +{ + int err; + + if (sc->sc_state < PPPOE_STATE_SESSION) + err = EBUSY; + else { + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": disconnecting\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + err = pppoe_send_padt(sc->sc_ethif, sc->sc_session, (const u8_t *)&sc->sc_dest); + } + + /* cleanup softc */ + sc->sc_state = PPPOE_STATE_INITIAL; + memcpy(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + if (sc->sc_ac_cookie) { + mem_free(sc->sc_ac_cookie); + sc->sc_ac_cookie = NULL; + } + sc->sc_ac_cookie_len = 0; +#ifdef PPPOE_SERVER + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + sc->sc_hunique = NULL; + } + sc->sc_hunique_len = 0; +#endif + sc->sc_session = 0; + + sc->sc_linkStatusCB(sc->sc_pd, 0); /* notify upper layers */ + + return err; +} + +/* Connection attempt aborted */ +static void +pppoe_abort_connect(struct pppoe_softc *sc) +{ + printf("%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_CLOSING; + + sc->sc_linkStatusCB(sc->sc_pd, 0); /* notify upper layers */ + + /* clear connection state */ + memcpy(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + sc->sc_state = PPPOE_STATE_INITIAL; +} + +/* Send a PADR packet */ +static err_t +pppoe_send_padr(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len, l1 = 0; /* XXX: gcc */ + + if (sc->sc_state != PPPOE_STATE_PADR_SENT) + return ERR_CONN; + + len = 2 + 2 + 2 + 2 + sizeof(sc); /* service name, host unique */ + if (sc->sc_service_name != NULL) { /* service name tag maybe empty */ + l1 = strlen(sc->sc_service_name); + len += l1; + } + if (sc->sc_ac_cookie_len > 0) + len += 2 + 2 + sc->sc_ac_cookie_len; /* AC cookie */ + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM); + if (!pb) + return ERR_MEM; + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADR, 0, 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); + } + 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, sizeof(struct eth_hdr) + PPPOE_HEADERLEN, PBUF_RAM); + if (!pb) + return ERR_MEM; + + ethhdr = (struct eth_hdr *)pb->payload; + ethhdr->type = htons(ETHTYPE_PPPOEDISC); + memcpy(ethhdr->dest.addr, dest, sizeof(ethhdr->dest.addr)); + memcpy(ethhdr->src.addr, ((struct eth_addr *)outgoing_if->hwaddr)->addr, 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; + + if (sc->sc_state != PPPOE_STATE_PADO_SENT) + return ERR_CONN; + + /* 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, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM); + if (!pb) + return ERR_MEM; + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + 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 */ + + if (sc->sc_state != PPPOE_STATE_PADO_SENT) + return ERR_CONN; + + 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, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM); + if (!pb) + return ERR_MEM; + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + 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 + +err_t +pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb) +{ + u8_t *p; + size_t len; + + /* are we ready to process data yet? */ + if (sc->sc_state < PPPOE_STATE_SESSION) { + /*sppp_flush(&sc->sc_sppp.pp_if);*/ + pbuf_free(pb); + return ERR_CONN; + } + + len = pb->tot_len; + + /* make room for Ethernet header - should not fail */ + if (pbuf_header(pb, sizeof(struct eth_hdr) + PPPOE_HEADERLEN) != 0) { + /* bail out */ + PPPDEBUG((LOG_ERR, "pppoe: %c%c%"U16_F": pppoe_xmit: could not allocate room for 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 + sizeof(struct eth_hdr); + 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); + printf("%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 + +static void +pppoe_clear_softc(struct pppoe_softc *sc, const char *message) +{ + /* stop timer */ + tcpip_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)); + + /* fix our state */ + sc->sc_state = PPPOE_STATE_INITIAL; + + /* notify upper layers */ + sc->sc_linkStatusCB(sc->sc_pd, 0); + + /* clean up softc */ + memcpy(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + if (sc->sc_ac_cookie) { + mem_free(sc->sc_ac_cookie); + sc->sc_ac_cookie = NULL; + } + sc->sc_ac_cookie_len = 0; + sc->sc_session = 0; +} + +#endif /* PPPOE_SUPPORT > 0 */ diff --git a/src/netif/ppp/ppp_oe.h b/src/netif/ppp/ppp_oe.h new file mode 100644 index 00000000..3aa55aec --- /dev/null +++ b/src/netif/ppp/ppp_oe.h @@ -0,0 +1,161 @@ +/***************************************************************************** +* ppp_oe.h - 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 +* 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 . + * + * 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. + */ +#ifndef PPP_OE_H +#define PPP_OE_H + +#include "lwip/opt.h" + +#if PPPOE_SUPPORT > 0 + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct pppoehdr { + PACK_STRUCT_FIELD(u8_t vertype); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t session); + PACK_STRUCT_FIELD(u16_t plen); +} 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 pppoetag { + PACK_STRUCT_FIELD(u16_t tag); + PACK_STRUCT_FIELD(u16_t len); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + + +#define PPPOE_STATE_INITIAL 0 +#define PPPOE_STATE_PADI_SENT 1 +#define PPPOE_STATE_PADR_SENT 2 +#define PPPOE_STATE_SESSION 3 +#define PPPOE_STATE_CLOSING 4 +/* passive */ +#define PPPOE_STATE_PADO_SENT 1 + +#define PPPOE_HEADERLEN sizeof(struct pppoehdr) +#define PPPOE_VERTYPE 0x11 /* VER=1, TYPE = 1 */ + +#define PPPOE_TAG_EOL 0x0000 /* end of list */ +#define PPPOE_TAG_SNAME 0x0101 /* service name */ +#define PPPOE_TAG_ACNAME 0x0102 /* access concentrator name */ +#define PPPOE_TAG_HUNIQUE 0x0103 /* host unique */ +#define PPPOE_TAG_ACCOOKIE 0x0104 /* AC cookie */ +#define PPPOE_TAG_VENDOR 0x0105 /* vendor specific */ +#define PPPOE_TAG_RELAYSID 0x0110 /* relay session id */ +#define PPPOE_TAG_SNAME_ERR 0x0201 /* service name error */ +#define PPPOE_TAG_ACSYS_ERR 0x0202 /* AC system error */ +#define PPPOE_TAG_GENERIC_ERR 0x0203 /* gerneric error */ + +#define PPPOE_CODE_PADI 0x09 /* Active Discovery Initiation */ +#define PPPOE_CODE_PADO 0x07 /* Active Discovery Offer */ +#define PPPOE_CODE_PADR 0x19 /* Active Discovery Request */ +#define PPPOE_CODE_PADS 0x65 /* Active Discovery Session confirmation */ +#define PPPOE_CODE_PADT 0xA7 /* Active Discovery Terminate */ + +#ifndef ETHERMTU +#define ETHERMTU 1500 +#endif + +/* two byte PPP protocol discriminator, then IP data */ +#define PPPOE_MAXMTU (ETHERMTU-PPPOE_HEADERLEN-2) + +struct pppoe_softc; + + +void pppoe_init(void); + +err_t pppoe_create(struct netif *ethif, int pd, void (*linkStatusCB)(int pd, int up), struct pppoe_softc **scptr); +err_t pppoe_destroy(struct netif *ifp); + +int pppoe_connect(struct pppoe_softc *sc); +void pppoe_disconnect(struct pppoe_softc *sc); + +void pppoe_disc_input(struct netif *netif, struct pbuf *p); +void pppoe_data_input(struct netif *netif, struct pbuf *p); + +err_t pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb); + +extern int pppoe_hdrlen; + +#endif /* PPPOE_SUPPORT */ + +#endif /* PPP_OE_H */