xref: /openbsd/sys/net/if_pppoe.c (revision ff335594)
1*ff335594Smvs /* $OpenBSD: if_pppoe.c,v 1.83 2022/07/14 11:03:15 mvs Exp $ */
221a78fbcScanacar /* $NetBSD: if_pppoe.c,v 1.51 2003/11/28 08:56:48 keihan Exp $ */
321a78fbcScanacar 
421a78fbcScanacar /*
521a78fbcScanacar  * Copyright (c) 2002 The NetBSD Foundation, Inc.
621a78fbcScanacar  * All rights reserved.
721a78fbcScanacar  *
821a78fbcScanacar  * This code is derived from software contributed to The NetBSD Foundation
921a78fbcScanacar  * by Martin Husemann <martin@NetBSD.org>.
1021a78fbcScanacar  *
1121a78fbcScanacar  * Redistribution and use in source and binary forms, with or without
1221a78fbcScanacar  * modification, are permitted provided that the following conditions
1321a78fbcScanacar  * are met:
1421a78fbcScanacar  * 1. Redistributions of source code must retain the above copyright
1521a78fbcScanacar  *    notice, this list of conditions and the following disclaimer.
1621a78fbcScanacar  * 2. Redistributions in binary form must reproduce the above copyright
1721a78fbcScanacar  *    notice, this list of conditions and the following disclaimer in the
1821a78fbcScanacar  *    documentation and/or other materials provided with the distribution.
1921a78fbcScanacar  *
2021a78fbcScanacar  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2121a78fbcScanacar  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2221a78fbcScanacar  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2321a78fbcScanacar  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2421a78fbcScanacar  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2521a78fbcScanacar  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2621a78fbcScanacar  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2721a78fbcScanacar  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2821a78fbcScanacar  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2921a78fbcScanacar  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3021a78fbcScanacar  * POSSIBILITY OF SUCH DAMAGE.
3121a78fbcScanacar  */
3221a78fbcScanacar 
3321a78fbcScanacar #include "pppoe.h"
3421a78fbcScanacar #include "bpfilter.h"
3521a78fbcScanacar 
3621a78fbcScanacar #include <sys/param.h>
3721a78fbcScanacar #include <sys/systm.h>
3821a78fbcScanacar #include <sys/kernel.h>
3921a78fbcScanacar #include <sys/timeout.h>
4021a78fbcScanacar #include <sys/malloc.h>
4121a78fbcScanacar #include <sys/mbuf.h>
4221a78fbcScanacar #include <sys/socket.h>
434d5253fbScanacar #include <sys/syslog.h>
4421a78fbcScanacar #include <sys/ioctl.h>
4521a78fbcScanacar #include <net/if.h>
460deb6685Smpi #include <net/if_var.h>
4721a78fbcScanacar #include <net/if_types.h>
4821a78fbcScanacar #include <net/if_sppp.h>
4921a78fbcScanacar #include <net/if_pppoe.h>
5098a920fdSdlg #include <net/netisr.h>
5121a78fbcScanacar #include <netinet/in.h>
5221a78fbcScanacar #include <netinet/if_ether.h>
5321a78fbcScanacar 
5421a78fbcScanacar #if NBPFILTER > 0
5521a78fbcScanacar #include <net/bpf.h>
5621a78fbcScanacar #endif
5721a78fbcScanacar 
5821a78fbcScanacar #undef PPPOE_DEBUG	/* XXX - remove this or make it an option */
5921a78fbcScanacar 
6021a78fbcScanacar #define PPPOEDEBUG(a)	((sc->sc_sppp.pp_if.if_flags & IFF_DEBUG) ? printf a : 0)
6121a78fbcScanacar 
6221a78fbcScanacar struct pppoehdr {
6321a78fbcScanacar 	u_int8_t vertype;
6421a78fbcScanacar 	u_int8_t code;
6521a78fbcScanacar 	u_int16_t session;
6621a78fbcScanacar 	u_int16_t plen;
6721a78fbcScanacar } __packed;
6821a78fbcScanacar 
6921a78fbcScanacar struct pppoetag {
7021a78fbcScanacar 	u_int16_t tag;
7121a78fbcScanacar 	u_int16_t len;
7221a78fbcScanacar } __packed;
7321a78fbcScanacar 
7421a78fbcScanacar #define	PPPOE_HEADERLEN		sizeof(struct pppoehdr)
751a2f7223Sbrad #define	PPPOE_OVERHEAD		(PPPOE_HEADERLEN + 2)
7621a78fbcScanacar #define	PPPOE_VERTYPE		0x11		/* VER=1, TYPE = 1 */
7721a78fbcScanacar 
7821a78fbcScanacar #define	PPPOE_TAG_EOL		0x0000		/* end of list */
7921a78fbcScanacar #define	PPPOE_TAG_SNAME		0x0101		/* service name */
8021a78fbcScanacar #define	PPPOE_TAG_ACNAME	0x0102		/* access concentrator name */
8121a78fbcScanacar #define	PPPOE_TAG_HUNIQUE	0x0103		/* host unique */
8221a78fbcScanacar #define	PPPOE_TAG_ACCOOKIE	0x0104		/* AC cookie */
8321a78fbcScanacar #define	PPPOE_TAG_VENDOR	0x0105		/* vendor specific */
8421a78fbcScanacar #define	PPPOE_TAG_RELAYSID	0x0110		/* relay session id */
857ab35c70Ssthen #define	PPPOE_TAG_MAX_PAYLOAD	0x0120		/* RFC 4638 max payload */
8621a78fbcScanacar #define	PPPOE_TAG_SNAME_ERR	0x0201		/* service name error */
8721a78fbcScanacar #define	PPPOE_TAG_ACSYS_ERR	0x0202		/* AC system error */
887ab35c70Ssthen #define	PPPOE_TAG_GENERIC_ERR	0x0203		/* generic error */
8921a78fbcScanacar 
9021a78fbcScanacar #define	PPPOE_CODE_PADI		0x09		/* Active Discovery Initiation */
9121a78fbcScanacar #define	PPPOE_CODE_PADO		0x07		/* Active Discovery Offer */
9221a78fbcScanacar #define	PPPOE_CODE_PADR		0x19		/* Active Discovery Request */
9321a78fbcScanacar #define	PPPOE_CODE_PADS		0x65		/* Active Discovery Session confirmation */
9421a78fbcScanacar #define	PPPOE_CODE_PADT		0xA7		/* Active Discovery Terminate */
9521a78fbcScanacar 
9621a78fbcScanacar /* two byte PPP protocol discriminator, then IP data */
977ab35c70Ssthen #define	PPPOE_MTU	(ETHERMTU - PPPOE_OVERHEAD)
987ab35c70Ssthen #define	PPPOE_MAXMTU	PP_MAX_MRU
9921a78fbcScanacar 
10021a78fbcScanacar /* Add a 16 bit unsigned value to a buffer pointed to by PTR */
10121a78fbcScanacar #define	PPPOE_ADD_16(PTR, VAL)			\
10221a78fbcScanacar 		*(PTR)++ = (VAL) / 256;		\
10321a78fbcScanacar 		*(PTR)++ = (VAL) % 256
10421a78fbcScanacar 
10521a78fbcScanacar /* Add a complete PPPoE header to the buffer pointed to by PTR */
10621a78fbcScanacar #define	PPPOE_ADD_HEADER(PTR, CODE, SESS, LEN)	\
10721a78fbcScanacar 		*(PTR)++ = PPPOE_VERTYPE;	\
10821a78fbcScanacar 		*(PTR)++ = (CODE);		\
10921a78fbcScanacar 		PPPOE_ADD_16(PTR, SESS);	\
11021a78fbcScanacar 		PPPOE_ADD_16(PTR, LEN)
11121a78fbcScanacar 
112bd031ea9Skn #define	PPPOE_DISC_TIMEOUT	5	/* base for quick timeout calculation (seconds) */
113bd031ea9Skn #define	PPPOE_SLOW_RETRY	60	/* persistent retry interval (seconds) */
11421a78fbcScanacar #define	PPPOE_DISC_MAXPADI	4	/* retry PADI four times (quickly) */
11521a78fbcScanacar #define	PPPOE_DISC_MAXPADR	2	/* retry PADR twice */
11621a78fbcScanacar 
117333d623aSkn /*
118333d623aSkn  * Locks used to protect struct members and global data
119333d623aSkn  *       I       immutable after creation
120*ff335594Smvs  *       K       kernel lock
121333d623aSkn  */
122333d623aSkn 
12321a78fbcScanacar struct pppoe_softc {
12421a78fbcScanacar 	struct sppp sc_sppp;		/* contains a struct ifnet as first element */
125*ff335594Smvs 	LIST_ENTRY(pppoe_softc) sc_list;/* [K] */
126*ff335594Smvs 	unsigned int sc_eth_ifidx;	/* [K] */
12721a78fbcScanacar 
128*ff335594Smvs 	int sc_state;			/* [K] discovery phase or session connected */
129*ff335594Smvs 	struct ether_addr sc_dest;	/* [K] hardware address of concentrator */
130*ff335594Smvs 	u_int16_t sc_session;		/* [K] PPPoE session id */
13121a78fbcScanacar 
132*ff335594Smvs 	char *sc_service_name;		/* [K] if != NULL: requested name of service */
133*ff335594Smvs 	char *sc_concentrator_name;	/* [K] if != NULL: requested concentrator id */
134*ff335594Smvs 	u_int8_t *sc_ac_cookie;		/* [K] content of AC cookie we must echo back */
135*ff335594Smvs 	size_t sc_ac_cookie_len;	/* [K] length of cookie data */
136*ff335594Smvs 	u_int8_t *sc_relay_sid;		/* [K] content of relay SID we must echo back */
137*ff335594Smvs 	size_t sc_relay_sid_len;	/* [K] length of relay SID data */
138333d623aSkn 	u_int32_t sc_unique;		/* [I] our unique id */
139*ff335594Smvs 	struct timeout sc_timeout;	/* [K] timeout while not in session state */
140*ff335594Smvs 	int sc_padi_retried;		/* [K] number of PADI retries already done */
141*ff335594Smvs 	int sc_padr_retried;		/* [K] number of PADR retries already done */
14221a78fbcScanacar 
143*ff335594Smvs 	struct timeval sc_session_time;	/* [K] time the session was established */
14421a78fbcScanacar };
14521a78fbcScanacar 
14621a78fbcScanacar /* input routines */
1472c8b151eSmvs void pppoe_disc_input(struct mbuf *);
1482c8b151eSmvs void pppoe_data_input(struct mbuf *);
1499830ba85Smvs static void pppoe_dispatch_disc_pkt(struct mbuf *);
15021a78fbcScanacar 
15121a78fbcScanacar /* management routines */
15221a78fbcScanacar void pppoeattach(int);
15321a78fbcScanacar static int  pppoe_connect(struct pppoe_softc *);
15421a78fbcScanacar static int  pppoe_disconnect(struct pppoe_softc *);
15521a78fbcScanacar static void pppoe_abort_connect(struct pppoe_softc *);
15621a78fbcScanacar static int  pppoe_ioctl(struct ifnet *, unsigned long, caddr_t);
15721a78fbcScanacar static void pppoe_tls(struct sppp *);
15821a78fbcScanacar static void pppoe_tlf(struct sppp *);
15921a78fbcScanacar static void pppoe_start(struct ifnet *);
16021a78fbcScanacar 
16121a78fbcScanacar /* internal timeout handling */
16221a78fbcScanacar static void pppoe_timeout(void *);
16321a78fbcScanacar 
164bdc883b9Smiod /* sending actual protocol control packets */
16521a78fbcScanacar static int pppoe_send_padi(struct pppoe_softc *);
16621a78fbcScanacar static int pppoe_send_padr(struct pppoe_softc *);
1674c6ae08dSsthen static int pppoe_send_padt(unsigned int, u_int, const u_int8_t *, u_int8_t);
16821a78fbcScanacar 
16921a78fbcScanacar /* raw output */
17021a78fbcScanacar static int pppoe_output(struct pppoe_softc *, struct mbuf *);
17121a78fbcScanacar 
17221a78fbcScanacar /* internal helper functions */
173fb492c37Smpi static struct pppoe_softc *pppoe_find_softc_by_session(u_int, u_int);
174fb492c37Smpi static struct pppoe_softc *pppoe_find_softc_by_hunique(u_int8_t *, size_t, u_int);
17521a78fbcScanacar static struct mbuf	  *pppoe_get_mbuf(size_t len);
17621a78fbcScanacar 
17721a78fbcScanacar LIST_HEAD(pppoe_softc_head, pppoe_softc) pppoe_softc_list;
17821a78fbcScanacar 
17921a78fbcScanacar /* interface cloning */
18021a78fbcScanacar int pppoe_clone_create(struct if_clone *, int);
18121a78fbcScanacar int pppoe_clone_destroy(struct ifnet *);
18221a78fbcScanacar 
18321a78fbcScanacar struct if_clone pppoe_cloner =
18421a78fbcScanacar     IF_CLONE_INITIALIZER("pppoe", pppoe_clone_create, pppoe_clone_destroy);
18521a78fbcScanacar 
1862c8b151eSmvs struct mbuf_queue pppoediscinq = MBUF_QUEUE_INITIALIZER(
1872c8b151eSmvs 	IFQ_MAXLEN, IPL_SOFTNET);
1882c8b151eSmvs struct mbuf_queue pppoeinq = MBUF_QUEUE_INITIALIZER(
1892c8b151eSmvs 	IFQ_MAXLEN, IPL_SOFTNET);
1902c8b151eSmvs 
pppoeintr(void)1912c8b151eSmvs void pppoeintr(void)
1922c8b151eSmvs {
1932c8b151eSmvs 	struct mbuf_list ml;
1942c8b151eSmvs 	struct mbuf *m;
1952c8b151eSmvs 
1962c8b151eSmvs 	NET_ASSERT_LOCKED();
1972c8b151eSmvs 
1982c8b151eSmvs 	mq_delist(&pppoediscinq, &ml);
1992c8b151eSmvs 	while ((m = ml_dequeue(&ml)) != NULL)
2002c8b151eSmvs 		pppoe_disc_input(m);
2012c8b151eSmvs 
2022c8b151eSmvs 	mq_delist(&pppoeinq, &ml);
2032c8b151eSmvs 	while ((m = ml_dequeue(&ml)) != NULL)
2042c8b151eSmvs 		pppoe_data_input(m);
2052c8b151eSmvs }
20621a78fbcScanacar 
20721a78fbcScanacar void
pppoeattach(int count)20821a78fbcScanacar pppoeattach(int count)
20921a78fbcScanacar {
21021a78fbcScanacar 	LIST_INIT(&pppoe_softc_list);
21121a78fbcScanacar 	if_clone_attach(&pppoe_cloner);
21221a78fbcScanacar }
21321a78fbcScanacar 
21421a78fbcScanacar /* Create a new interface. */
21521a78fbcScanacar int
pppoe_clone_create(struct if_clone * ifc,int unit)21621a78fbcScanacar pppoe_clone_create(struct if_clone *ifc, int unit)
21721a78fbcScanacar {
218bdc883b9Smiod 	struct pppoe_softc *sc, *tmpsc;
219bdc883b9Smiod 	u_int32_t unique;
22021a78fbcScanacar 
221809d3a3eSbluhm 	sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
22221a78fbcScanacar 	snprintf(sc->sc_sppp.pp_if.if_xname,
22321a78fbcScanacar 		 sizeof(sc->sc_sppp.pp_if.if_xname),
22421a78fbcScanacar 		 "pppoe%d", unit);
22521a78fbcScanacar 	sc->sc_sppp.pp_if.if_softc = sc;
2267ab35c70Ssthen 	sc->sc_sppp.pp_if.if_mtu = PPPOE_MTU;
22721a78fbcScanacar 	sc->sc_sppp.pp_if.if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST;
22821a78fbcScanacar 	sc->sc_sppp.pp_if.if_type = IFT_PPP;
22921a78fbcScanacar 	sc->sc_sppp.pp_if.if_hdrlen = sizeof(struct ether_header) + PPPOE_HEADERLEN;
2306673d99eSsthen 	sc->sc_sppp.pp_flags |= PP_KEEPALIVE;		/* use LCP keepalive */
23121a78fbcScanacar 	sc->sc_sppp.pp_framebytes = PPPOE_HEADERLEN;	/* framing added to ppp packets */
23221a78fbcScanacar 	sc->sc_sppp.pp_if.if_ioctl = pppoe_ioctl;
23321a78fbcScanacar 	sc->sc_sppp.pp_if.if_start = pppoe_start;
234dcb17c31Smpi 	sc->sc_sppp.pp_if.if_rtrequest = p2p_rtrequest;
235b5aed6b3Smvs 	sc->sc_sppp.pp_if.if_xflags = IFXF_CLONED;
23621a78fbcScanacar 	sc->sc_sppp.pp_tls = pppoe_tls;
23721a78fbcScanacar 	sc->sc_sppp.pp_tlf = pppoe_tlf;
23821a78fbcScanacar 
23921a78fbcScanacar 	/* changed to real address later */
24021a78fbcScanacar 	memcpy(&sc->sc_dest, etherbroadcastaddr, sizeof(sc->sc_dest));
24121a78fbcScanacar 
24221a78fbcScanacar 	/* init timer for interface watchdog */
24356983f85Smpi 	timeout_set_proc(&sc->sc_timeout, pppoe_timeout, sc);
24421a78fbcScanacar 
24521a78fbcScanacar 	if_attach(&sc->sc_sppp.pp_if);
24621a78fbcScanacar 	if_alloc_sadl(&sc->sc_sppp.pp_if);
24721a78fbcScanacar 	sppp_attach(&sc->sc_sppp.pp_if);
24821a78fbcScanacar #if NBPFILTER > 0
24921a78fbcScanacar 	bpfattach(&sc->sc_sppp.pp_if.if_bpf, &sc->sc_sppp.pp_if, DLT_PPP_ETHER, 0);
25021a78fbcScanacar #endif
25121a78fbcScanacar 
252aa28b9a6Smpi 	NET_LOCK();
253bdc883b9Smiod retry:
254bdc883b9Smiod 	unique = arc4random();
255bdc883b9Smiod 	LIST_FOREACH(tmpsc, &pppoe_softc_list, sc_list)
256bdc883b9Smiod 		if (tmpsc->sc_unique == unique)
257bdc883b9Smiod 			goto retry;
258bdc883b9Smiod 	sc->sc_unique = unique;
25921a78fbcScanacar 	LIST_INSERT_HEAD(&pppoe_softc_list, sc, sc_list);
260aa28b9a6Smpi 	NET_UNLOCK();
26121a78fbcScanacar 
26221a78fbcScanacar 	return (0);
26321a78fbcScanacar }
26421a78fbcScanacar 
26521a78fbcScanacar /* Destroy a given interface. */
26621a78fbcScanacar int
pppoe_clone_destroy(struct ifnet * ifp)26721a78fbcScanacar pppoe_clone_destroy(struct ifnet *ifp)
26821a78fbcScanacar {
26921a78fbcScanacar 	struct pppoe_softc *sc = ifp->if_softc;
27021a78fbcScanacar 
271aa28b9a6Smpi 	NET_LOCK();
27221a78fbcScanacar 	LIST_REMOVE(sc, sc_list);
273aa28b9a6Smpi 	NET_UNLOCK();
274a5bac811Smpi 
275c19ff3acScanacar 	timeout_del(&sc->sc_timeout);
27621a78fbcScanacar 
27721a78fbcScanacar 	sppp_detach(&sc->sc_sppp.pp_if);
27821a78fbcScanacar 	if_detach(ifp);
27921a78fbcScanacar 
28021a78fbcScanacar 	if (sc->sc_concentrator_name)
2814de75e7cSkn 		free(sc->sc_concentrator_name, M_DEVBUF,
2824de75e7cSkn 		    strlen(sc->sc_concentrator_name) + 1);
28321a78fbcScanacar 	if (sc->sc_service_name)
2844de75e7cSkn 		free(sc->sc_service_name, M_DEVBUF,
2854de75e7cSkn 		    strlen(sc->sc_service_name) + 1);
28621a78fbcScanacar 	if (sc->sc_ac_cookie)
2874de75e7cSkn 		free(sc->sc_ac_cookie, M_DEVBUF, sc->sc_ac_cookie_len);
2888d5ba128Scanacar 	if (sc->sc_relay_sid)
2894de75e7cSkn 		free(sc->sc_relay_sid, M_DEVBUF, sc->sc_relay_sid_len);
29021a78fbcScanacar 
2914de75e7cSkn 	free(sc, M_DEVBUF, sizeof(*sc));
29221a78fbcScanacar 
29321a78fbcScanacar 	return (0);
29421a78fbcScanacar }
29521a78fbcScanacar 
29621a78fbcScanacar /*
29721a78fbcScanacar  * Find the interface handling the specified session.
29821a78fbcScanacar  * Note: O(number of sessions open), this is a client-side only, mean
29921a78fbcScanacar  * and lean implementation, so number of open sessions typically should
30021a78fbcScanacar  * be 1.
30121a78fbcScanacar  */
30221a78fbcScanacar static struct pppoe_softc *
pppoe_find_softc_by_session(u_int session,u_int ifidx)303fb492c37Smpi pppoe_find_softc_by_session(u_int session, u_int ifidx)
30421a78fbcScanacar {
30521a78fbcScanacar 	struct pppoe_softc *sc;
30621a78fbcScanacar 
30721a78fbcScanacar 	if (session == 0)
30821a78fbcScanacar 		return (NULL);
30921a78fbcScanacar 
31021a78fbcScanacar 	LIST_FOREACH(sc, &pppoe_softc_list, sc_list) {
31121a78fbcScanacar 		if (sc->sc_state == PPPOE_STATE_SESSION
3124ccb8dd6Scanacar 		    && sc->sc_session == session
3136d7c3328Sreyk 		    && sc->sc_eth_ifidx == ifidx) {
31421a78fbcScanacar 			return (sc);
31521a78fbcScanacar 		}
31621a78fbcScanacar 	}
31721a78fbcScanacar 	return (NULL);
31821a78fbcScanacar }
31921a78fbcScanacar 
32021a78fbcScanacar /*
32121a78fbcScanacar  * Check host unique token passed and return appropriate softc pointer,
32221a78fbcScanacar  * or NULL if token is bogus.
32321a78fbcScanacar  */
32421a78fbcScanacar static struct pppoe_softc *
pppoe_find_softc_by_hunique(u_int8_t * token,size_t len,u_int ifidx)325fb492c37Smpi pppoe_find_softc_by_hunique(u_int8_t *token, size_t len, u_int ifidx)
32621a78fbcScanacar {
3272a01ab2fScanacar 	struct pppoe_softc *sc;
3282a01ab2fScanacar 	u_int32_t hunique;
32921a78fbcScanacar 
33021a78fbcScanacar 	if (LIST_EMPTY(&pppoe_softc_list))
33121a78fbcScanacar 		return (NULL);
33221a78fbcScanacar 
3332a01ab2fScanacar 	if (len != sizeof(hunique))
33421a78fbcScanacar 		return (NULL);
3352a01ab2fScanacar 	memcpy(&hunique, token, len);
33621a78fbcScanacar 
33721a78fbcScanacar 	LIST_FOREACH(sc, &pppoe_softc_list, sc_list)
3382a01ab2fScanacar 		if (sc->sc_unique == hunique)
3392a01ab2fScanacar 			break;
34021a78fbcScanacar 
34121a78fbcScanacar 	if (sc == NULL) {
34221a78fbcScanacar 		printf("pppoe: alien host unique tag, no session found\n");
34321a78fbcScanacar 		return (NULL);
34421a78fbcScanacar 	}
34521a78fbcScanacar 
34621a78fbcScanacar 	/* should be safe to access *sc now */
34721a78fbcScanacar 	if (sc->sc_state < PPPOE_STATE_PADI_SENT || sc->sc_state >= PPPOE_STATE_SESSION) {
34821a78fbcScanacar 		printf("%s: host unique tag found, but it belongs to a connection in state %d\n",
34921a78fbcScanacar 			sc->sc_sppp.pp_if.if_xname, sc->sc_state);
35021a78fbcScanacar 		return (NULL);
35121a78fbcScanacar 	}
3526d7c3328Sreyk 	if (sc->sc_eth_ifidx != ifidx) {
35321a78fbcScanacar 		printf("%s: wrong interface, not accepting host unique\n",
35421a78fbcScanacar 			sc->sc_sppp.pp_if.if_xname);
35521a78fbcScanacar 		return (NULL);
35621a78fbcScanacar 	}
35721a78fbcScanacar 	return (sc);
35821a78fbcScanacar }
35921a78fbcScanacar 
36021a78fbcScanacar /* Analyze and handle a single received packet while not in session state. */
3619566a16bSmvs static void
pppoe_dispatch_disc_pkt(struct mbuf * m)3629566a16bSmvs pppoe_dispatch_disc_pkt(struct mbuf *m)
36321a78fbcScanacar {
36421a78fbcScanacar 	struct pppoe_softc *sc;
36521a78fbcScanacar 	struct pppoehdr *ph;
36621a78fbcScanacar 	struct pppoetag *pt;
36721a78fbcScanacar 	struct mbuf *n;
36821a78fbcScanacar 	struct ether_header *eh;
3694d5253fbScanacar 	const char *err_msg, *devname;
37021a78fbcScanacar 	size_t ac_cookie_len;
3718d5ba128Scanacar 	size_t relay_sid_len;
3728ee934e7Sstsp 	int off, noff, err, errortag, max_payloadtag;
3738ee934e7Sstsp 	u_int16_t max_payload;
37421a78fbcScanacar 	u_int16_t tag, len;
37521a78fbcScanacar 	u_int16_t session, plen;
37621a78fbcScanacar 	u_int8_t *ac_cookie;
3778d5ba128Scanacar 	u_int8_t *relay_sid;
378a04f326cSclaudio 	u_int8_t code;
37921a78fbcScanacar 
3804d5253fbScanacar 	err_msg = NULL;
3814d5253fbScanacar 	devname = "pppoe";
3829830ba85Smvs 	off = 0;
38321a78fbcScanacar 	errortag = 0;
3848ee934e7Sstsp 	max_payloadtag = 0;
38521a78fbcScanacar 
38621a78fbcScanacar 	if (m->m_len < sizeof(*eh)) {
38721a78fbcScanacar 		m = m_pullup(m, sizeof(*eh));
38821a78fbcScanacar 		if (m == NULL)
38921a78fbcScanacar 			goto done;
39021a78fbcScanacar 	}
39121a78fbcScanacar 	eh = mtod(m, struct ether_header *);
39221a78fbcScanacar 	off += sizeof(*eh);
39321a78fbcScanacar 
39421a78fbcScanacar 	ac_cookie = NULL;
39521a78fbcScanacar 	ac_cookie_len = 0;
3968d5ba128Scanacar 	relay_sid = NULL;
3978d5ba128Scanacar 	relay_sid_len = 0;
3988ee934e7Sstsp 	max_payload = 0;
39921a78fbcScanacar 
40021a78fbcScanacar 	session = 0;
40121a78fbcScanacar 	if (m->m_pkthdr.len - off <= PPPOE_HEADERLEN) {
40221a78fbcScanacar 		printf("pppoe: packet too short: %d\n", m->m_pkthdr.len);
40321a78fbcScanacar 		goto done;
40421a78fbcScanacar 	}
40521a78fbcScanacar 
40621a78fbcScanacar 	n = m_pulldown(m, off, sizeof(*ph), &noff);
40721a78fbcScanacar 	if (n == NULL) {
40821a78fbcScanacar 		printf("pppoe: could not get PPPoE header\n");
40921a78fbcScanacar 		m = NULL;
41021a78fbcScanacar 		goto done;
41121a78fbcScanacar 	}
41221a78fbcScanacar 	ph = (struct pppoehdr *)(mtod(n, caddr_t) + noff);
41321a78fbcScanacar 	if (ph->vertype != PPPOE_VERTYPE) {
41421a78fbcScanacar 		printf("pppoe: unknown version/type packet: 0x%x\n",
41521a78fbcScanacar 		    ph->vertype);
41621a78fbcScanacar 		goto done;
41721a78fbcScanacar 	}
41821a78fbcScanacar 
41921a78fbcScanacar 	session = ntohs(ph->session);
42021a78fbcScanacar 	plen = ntohs(ph->plen);
421a04f326cSclaudio 	code = ph->code;
42221a78fbcScanacar 	off += sizeof(*ph);
42321a78fbcScanacar 	if (plen + off > m->m_pkthdr.len) {
42421a78fbcScanacar 		printf("pppoe: packet content does not fit: data available = %d, packet size = %u\n",
42521a78fbcScanacar 		    m->m_pkthdr.len - off, plen);
42621a78fbcScanacar 		goto done;
42721a78fbcScanacar 	}
42821a78fbcScanacar 
42921a78fbcScanacar 	/* ignore trailing garbage */
43021a78fbcScanacar 	m_adj(m, off + plen - m->m_pkthdr.len);
43121a78fbcScanacar 
43221a78fbcScanacar 	tag = 0;
43321a78fbcScanacar 	len = 0;
43421a78fbcScanacar 	sc = NULL;
43521a78fbcScanacar 	while (off + sizeof(*pt) <= m->m_pkthdr.len) {
43621a78fbcScanacar 		n = m_pulldown(m, off, sizeof(*pt), &noff);
43721a78fbcScanacar 		if (n == NULL) {
4384d5253fbScanacar 			printf("%s: parse error\n", devname);
43921a78fbcScanacar 			m = NULL;
44021a78fbcScanacar 			goto done;
44121a78fbcScanacar 		}
44221a78fbcScanacar 		pt = (struct pppoetag *)(mtod(n, caddr_t) + noff);
44321a78fbcScanacar 		tag = ntohs(pt->tag);
44421a78fbcScanacar 		len = ntohs(pt->len);
4452aa349e1Sbrad 		off += sizeof(*pt);
44621a78fbcScanacar 		if (off + len > m->m_pkthdr.len) {
4474d5253fbScanacar 			printf("%s: tag 0x%x len 0x%x is too long\n",
4484d5253fbScanacar 			    devname, tag, len);
44921a78fbcScanacar 			goto done;
45021a78fbcScanacar 		}
45121a78fbcScanacar 		switch (tag) {
45221a78fbcScanacar 		case PPPOE_TAG_EOL:
45321a78fbcScanacar 			goto breakbreak;
45421a78fbcScanacar 		case PPPOE_TAG_SNAME:
45521a78fbcScanacar 			break;	/* ignored */
45621a78fbcScanacar 		case PPPOE_TAG_ACNAME:
45721a78fbcScanacar 			break;	/* ignored */
45821a78fbcScanacar 		case PPPOE_TAG_HUNIQUE:
45921a78fbcScanacar 			if (sc != NULL)
46021a78fbcScanacar 				break;
4612aa349e1Sbrad 			n = m_pulldown(m, off, len, &noff);
46221a78fbcScanacar 			if (n == NULL) {
46321a78fbcScanacar 				m = NULL;
46421a78fbcScanacar 				err_msg = "TAG HUNIQUE ERROR";
46521a78fbcScanacar 				break;
46621a78fbcScanacar 			}
46721a78fbcScanacar 			sc = pppoe_find_softc_by_hunique(mtod(n, caddr_t) + noff,
468fb492c37Smpi 			    len, m->m_pkthdr.ph_ifidx);
4694d5253fbScanacar 			if (sc != NULL)
4704d5253fbScanacar 				devname = sc->sc_sppp.pp_if.if_xname;
47121a78fbcScanacar 			break;
47221a78fbcScanacar 		case PPPOE_TAG_ACCOOKIE:
47321a78fbcScanacar 			if (ac_cookie == NULL) {
4742aa349e1Sbrad 				n = m_pulldown(m, off, len,
47521a78fbcScanacar 				    &noff);
47621a78fbcScanacar 				if (n == NULL) {
47721a78fbcScanacar 					err_msg = "TAG ACCOOKIE ERROR";
47821a78fbcScanacar 					m = NULL;
47921a78fbcScanacar 					break;
48021a78fbcScanacar 				}
48121a78fbcScanacar 				ac_cookie = mtod(n, caddr_t) + noff;
48221a78fbcScanacar 				ac_cookie_len = len;
48321a78fbcScanacar 			}
48421a78fbcScanacar 			break;
4858d5ba128Scanacar 		case PPPOE_TAG_RELAYSID:
4868d5ba128Scanacar 			if (relay_sid == NULL) {
4872aa349e1Sbrad 				n = m_pulldown(m, off, len,
4888d5ba128Scanacar 				    &noff);
4898d5ba128Scanacar 				if (n == NULL) {
4908d5ba128Scanacar 					err_msg = "TAG RELAYSID ERROR";
4918d5ba128Scanacar 					m = NULL;
4928d5ba128Scanacar 					break;
4938d5ba128Scanacar 				}
4948d5ba128Scanacar 				relay_sid = mtod(n, caddr_t) + noff;
4958d5ba128Scanacar 				relay_sid_len = len;
4968d5ba128Scanacar 			}
4978d5ba128Scanacar 			break;
4987ab35c70Ssthen 		case PPPOE_TAG_MAX_PAYLOAD:
4998ee934e7Sstsp 			if (!max_payloadtag) {
5007ab35c70Ssthen 				n = m_pulldown(m, off, len,
5017ab35c70Ssthen 				    &noff);
5028ee934e7Sstsp 				if (n == NULL || len != sizeof(max_payload)) {
5037ab35c70Ssthen 					err_msg = "TAG MAX_PAYLOAD ERROR";
5047ab35c70Ssthen 					m = NULL;
5057ab35c70Ssthen 					break;
5067ab35c70Ssthen 				}
5078ee934e7Sstsp 				memcpy(&max_payload, mtod(n, caddr_t) + noff,
5088ee934e7Sstsp 				    sizeof(max_payload));
5098ee934e7Sstsp 				max_payloadtag = 1;
5107ab35c70Ssthen 			}
5117ab35c70Ssthen 			break;
51221a78fbcScanacar 		case PPPOE_TAG_SNAME_ERR:
51321a78fbcScanacar 			err_msg = "SERVICE NAME ERROR";
51421a78fbcScanacar 			errortag = 1;
51521a78fbcScanacar 			break;
51621a78fbcScanacar 		case PPPOE_TAG_ACSYS_ERR:
51721a78fbcScanacar 			err_msg = "AC SYSTEM ERROR";
51821a78fbcScanacar 			errortag = 1;
51921a78fbcScanacar 			break;
52021a78fbcScanacar 		case PPPOE_TAG_GENERIC_ERR:
52121a78fbcScanacar 			err_msg = "GENERIC ERROR";
52221a78fbcScanacar 			errortag = 1;
52321a78fbcScanacar 			break;
52421a78fbcScanacar 		}
52521a78fbcScanacar 		if (err_msg) {
5264d5253fbScanacar 			log(LOG_INFO, "%s: %s: ", devname, err_msg);
52721a78fbcScanacar 			if (errortag && len) {
5282aa349e1Sbrad 				n = m_pulldown(m, off, len,
52921a78fbcScanacar 				    &noff);
530b3af46a9Sclaudio 				if (n == NULL) {
531b3af46a9Sclaudio 					m = NULL;
532b3af46a9Sclaudio 				} else {
5334d5253fbScanacar 					u_int8_t *et = mtod(n, caddr_t) + noff;
5344d5253fbScanacar 					while (len--)
5354d5253fbScanacar 						addlog("%c", *et++);
53621a78fbcScanacar 				}
5374d5253fbScanacar 			}
5384d5253fbScanacar 			addlog("\n");
53921a78fbcScanacar 			goto done;
54021a78fbcScanacar 		}
5412aa349e1Sbrad 		off += len;
54221a78fbcScanacar 	}
54321a78fbcScanacar breakbreak:
544a04f326cSclaudio 	switch (code) {
54521a78fbcScanacar 	case PPPOE_CODE_PADI:
54621a78fbcScanacar 	case PPPOE_CODE_PADR:
54721a78fbcScanacar 		/* ignore, we are no access concentrator */
54821a78fbcScanacar 		goto done;
54921a78fbcScanacar 	case PPPOE_CODE_PADO:
55021a78fbcScanacar 		if (sc == NULL) {
55121a78fbcScanacar 			/* be quiet if there is not a single pppoe instance */
55221a78fbcScanacar 			if (!LIST_EMPTY(&pppoe_softc_list))
55321a78fbcScanacar 				printf("pppoe: received PADO but could not find request for it\n");
55421a78fbcScanacar 			goto done;
55521a78fbcScanacar 		}
55621a78fbcScanacar 		if (sc->sc_state != PPPOE_STATE_PADI_SENT) {
55721a78fbcScanacar 			printf("%s: received unexpected PADO\n",
55821a78fbcScanacar 			    sc->sc_sppp.pp_if.if_xname);
55921a78fbcScanacar 			goto done;
56021a78fbcScanacar 		}
56121a78fbcScanacar 		if (ac_cookie) {
56221a78fbcScanacar 			if (sc->sc_ac_cookie)
5634de75e7cSkn 				free(sc->sc_ac_cookie, M_DEVBUF,
5644de75e7cSkn 				    sc->sc_ac_cookie_len);
56521a78fbcScanacar 			sc->sc_ac_cookie = malloc(ac_cookie_len, M_DEVBUF,
56621a78fbcScanacar 			    M_DONTWAIT);
567d20143d6Stobhe 			if (sc->sc_ac_cookie == NULL) {
568d20143d6Stobhe 				sc->sc_ac_cookie_len = 0;
56921a78fbcScanacar 				goto done;
570d20143d6Stobhe 			}
57121a78fbcScanacar 			sc->sc_ac_cookie_len = ac_cookie_len;
57221a78fbcScanacar 			memcpy(sc->sc_ac_cookie, ac_cookie, ac_cookie_len);
573d20143d6Stobhe 		} else if (sc->sc_ac_cookie) {
574d20143d6Stobhe 			free(sc->sc_ac_cookie, M_DEVBUF, sc->sc_ac_cookie_len);
575d20143d6Stobhe 			sc->sc_ac_cookie = NULL;
576d20143d6Stobhe 			sc->sc_ac_cookie_len = 0;
57721a78fbcScanacar 		}
5788d5ba128Scanacar 		if (relay_sid) {
5798d5ba128Scanacar 			if (sc->sc_relay_sid)
5804de75e7cSkn 				free(sc->sc_relay_sid, M_DEVBUF,
5814de75e7cSkn 				    sc->sc_relay_sid_len);
5828d5ba128Scanacar 			sc->sc_relay_sid = malloc(relay_sid_len, M_DEVBUF,
5838d5ba128Scanacar 			    M_DONTWAIT);
584d20143d6Stobhe 			if (sc->sc_relay_sid == NULL) {
585d20143d6Stobhe 				sc->sc_relay_sid_len = 0;
5868d5ba128Scanacar 				goto done;
587d20143d6Stobhe 			}
5888d5ba128Scanacar 			sc->sc_relay_sid_len = relay_sid_len;
5898d5ba128Scanacar 			memcpy(sc->sc_relay_sid, relay_sid, relay_sid_len);
590d20143d6Stobhe 		} else if (sc->sc_relay_sid) {
591d20143d6Stobhe 			free(sc->sc_relay_sid, M_DEVBUF, sc->sc_relay_sid_len);
592d20143d6Stobhe 			sc->sc_relay_sid = NULL;
593d20143d6Stobhe 			sc->sc_relay_sid_len = 0;
5948d5ba128Scanacar 		}
5957ab35c70Ssthen 		if (sc->sc_sppp.pp_if.if_mtu > PPPOE_MTU &&
5968ee934e7Sstsp 		    (!max_payloadtag ||
5978ee934e7Sstsp 		     ntohs(max_payload) != sc->sc_sppp.pp_if.if_mtu)) {
5987ab35c70Ssthen 			printf("%s: No valid PPP-Max-Payload tag received in PADO\n",
5997ab35c70Ssthen 			    sc->sc_sppp.pp_if.if_xname);
6007ab35c70Ssthen 			sc->sc_sppp.pp_if.if_mtu = PPPOE_MTU;
6017ab35c70Ssthen 		}
60221a78fbcScanacar 
60321a78fbcScanacar 		memcpy(&sc->sc_dest, eh->ether_shost, sizeof(sc->sc_dest));
60421a78fbcScanacar 		sc->sc_padr_retried = 0;
60521a78fbcScanacar 		sc->sc_state = PPPOE_STATE_PADR_SENT;
60621a78fbcScanacar 		if ((err = pppoe_send_padr(sc)) != 0) {
60721a78fbcScanacar 			PPPOEDEBUG(("%s: failed to send PADR, error=%d\n",
60821a78fbcScanacar 			    sc->sc_sppp.pp_if.if_xname, err));
60921a78fbcScanacar 		}
610bd031ea9Skn 		timeout_add_sec(&sc->sc_timeout,
61121a78fbcScanacar 		    PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried));
61221a78fbcScanacar 
61321a78fbcScanacar 		break;
61421a78fbcScanacar 	case PPPOE_CODE_PADS:
61521a78fbcScanacar 		if (sc == NULL)
61621a78fbcScanacar 			goto done;
61721a78fbcScanacar 
61821a78fbcScanacar 		sc->sc_session = session;
61921a78fbcScanacar 		timeout_del(&sc->sc_timeout);
62021a78fbcScanacar 		PPPOEDEBUG(("%s: session 0x%x connected\n",
62121a78fbcScanacar 		    sc->sc_sppp.pp_if.if_xname, session));
62221a78fbcScanacar 		sc->sc_state = PPPOE_STATE_SESSION;
623e0790d6dSkn 		getmicrouptime(&sc->sc_session_time);
62421a78fbcScanacar 		sc->sc_sppp.pp_up(&sc->sc_sppp);	/* notify upper layers */
62521a78fbcScanacar 
62621a78fbcScanacar 		break;
62721a78fbcScanacar 	case PPPOE_CODE_PADT:
62821a78fbcScanacar 		if (sc == NULL)
62921a78fbcScanacar 			goto done;
63021a78fbcScanacar 
63121a78fbcScanacar 		/* stop timer (we might be about to transmit a PADT ourself) */
63221a78fbcScanacar 		timeout_del(&sc->sc_timeout);
63321a78fbcScanacar 		PPPOEDEBUG(("%s: session 0x%x terminated, received PADT\n",
63421a78fbcScanacar 		    sc->sc_sppp.pp_if.if_xname, session));
63521a78fbcScanacar 
63621a78fbcScanacar 		/* clean up softc */
63721a78fbcScanacar 		sc->sc_state = PPPOE_STATE_INITIAL;
63821a78fbcScanacar 		memcpy(&sc->sc_dest, etherbroadcastaddr, sizeof(sc->sc_dest));
63921a78fbcScanacar 		if (sc->sc_ac_cookie) {
6404de75e7cSkn 			free(sc->sc_ac_cookie, M_DEVBUF,
6414de75e7cSkn 			    sc->sc_ac_cookie_len);
64221a78fbcScanacar 			sc->sc_ac_cookie = NULL;
64321a78fbcScanacar 		}
6448d5ba128Scanacar 		if (sc->sc_relay_sid) {
6454de75e7cSkn 			free(sc->sc_relay_sid, M_DEVBUF, sc->sc_relay_sid_len);
6468d5ba128Scanacar 			sc->sc_relay_sid = NULL;
6478d5ba128Scanacar 		}
64821a78fbcScanacar 		sc->sc_ac_cookie_len = 0;
6498d5ba128Scanacar 		sc->sc_relay_sid_len = 0;
65021a78fbcScanacar 		sc->sc_session = 0;
65121a78fbcScanacar 		sc->sc_session_time.tv_sec = 0;
65221a78fbcScanacar 		sc->sc_session_time.tv_usec = 0;
65321a78fbcScanacar 		sc->sc_sppp.pp_down(&sc->sc_sppp);	/* signal upper layer */
65421a78fbcScanacar 
65521a78fbcScanacar 		break;
65621a78fbcScanacar 	default:
65721a78fbcScanacar 		printf("%s: unknown code (0x%04x) session = 0x%04x\n",
65821a78fbcScanacar 		    sc ? sc->sc_sppp.pp_if.if_xname : "pppoe",
659a04f326cSclaudio 		    code, session);
66021a78fbcScanacar 		break;
66121a78fbcScanacar 	}
66221a78fbcScanacar 
66321a78fbcScanacar done:
66421a78fbcScanacar 	m_freem(m);
66521a78fbcScanacar }
66621a78fbcScanacar 
66721a78fbcScanacar /* Input function for discovery packets. */
668bdda9a87Skn void
pppoe_disc_input(struct mbuf * m)66921a78fbcScanacar pppoe_disc_input(struct mbuf *m)
67021a78fbcScanacar {
67121a78fbcScanacar 	/* avoid error messages if there is not a single pppoe instance */
67221a78fbcScanacar 	if (!LIST_EMPTY(&pppoe_softc_list)) {
67321a78fbcScanacar 		KASSERT(m->m_flags & M_PKTHDR);
6749830ba85Smvs 		pppoe_dispatch_disc_pkt(m);
67521a78fbcScanacar 	} else
67621a78fbcScanacar 		m_freem(m);
67721a78fbcScanacar }
67821a78fbcScanacar 
67921a78fbcScanacar /* Input function for data packets */
680bdda9a87Skn void
pppoe_data_input(struct mbuf * m)68121a78fbcScanacar pppoe_data_input(struct mbuf *m)
68221a78fbcScanacar {
68321a78fbcScanacar 	struct pppoe_softc *sc;
68421a78fbcScanacar 	struct pppoehdr *ph;
68521a78fbcScanacar 	u_int16_t session, plen;
68621a78fbcScanacar #ifdef PPPOE_TERM_UNKNOWN_SESSIONS
68721a78fbcScanacar 	u_int8_t shost[ETHER_ADDR_LEN];
68821a78fbcScanacar #endif
6893b608204Sbrad 	if (LIST_EMPTY(&pppoe_softc_list))
6903b608204Sbrad 		goto drop;
69121a78fbcScanacar 
69221a78fbcScanacar 	KASSERT(m->m_flags & M_PKTHDR);
69321a78fbcScanacar 
69421a78fbcScanacar #ifdef PPPOE_TERM_UNKNOWN_SESSIONS
69521a78fbcScanacar 	memcpy(shost, mtod(m, struct ether_header*)->ether_shost, ETHER_ADDR_LEN);
69621a78fbcScanacar #endif
69721a78fbcScanacar 	m_adj(m, sizeof(struct ether_header));
69821a78fbcScanacar 	if (m->m_pkthdr.len <= PPPOE_HEADERLEN) {
69921a78fbcScanacar 		printf("pppoe (data): dropping too short packet: %d bytes\n",
70021a78fbcScanacar 		    m->m_pkthdr.len);
70121a78fbcScanacar 		goto drop;
70221a78fbcScanacar 	}
70321a78fbcScanacar 	if (m->m_len < sizeof(*ph)) {
70421a78fbcScanacar 		m = m_pullup(m, sizeof(*ph));
70521a78fbcScanacar 		if (m == NULL) {
70621a78fbcScanacar 			printf("pppoe (data): could not get PPPoE header\n");
70721a78fbcScanacar 			return;
70821a78fbcScanacar 		}
70921a78fbcScanacar 	}
71021a78fbcScanacar 	ph = mtod(m, struct pppoehdr *);
71121a78fbcScanacar 	if (ph->vertype != PPPOE_VERTYPE) {
71221a78fbcScanacar 		printf("pppoe (data): unknown version/type packet: 0x%x\n",
71321a78fbcScanacar 		    ph->vertype);
71421a78fbcScanacar 		goto drop;
71521a78fbcScanacar 	}
71621a78fbcScanacar 	if (ph->code != 0)
71721a78fbcScanacar 		goto drop;
71821a78fbcScanacar 
71921a78fbcScanacar 	session = ntohs(ph->session);
720fb492c37Smpi 	sc = pppoe_find_softc_by_session(session, m->m_pkthdr.ph_ifidx);
72121a78fbcScanacar 	if (sc == NULL) {
72221a78fbcScanacar #ifdef PPPOE_TERM_UNKNOWN_SESSIONS
72321a78fbcScanacar 		printf("pppoe (data): input for unknown session 0x%x, sending PADT\n",
72421a78fbcScanacar 		    session);
7254c6ae08dSsthen 		pppoe_send_padt(m->m_pkthdr.ph_ifidx, session, shost, 0);
72621a78fbcScanacar #endif
72721a78fbcScanacar 		goto drop;
72821a78fbcScanacar 	}
72921a78fbcScanacar 
73021a78fbcScanacar 	plen = ntohs(ph->plen);
73121a78fbcScanacar 
73221a78fbcScanacar #if NBPFILTER > 0
73321a78fbcScanacar 	if(sc->sc_sppp.pp_if.if_bpf)
734c4acdf64Sdjm 		bpf_mtap(sc->sc_sppp.pp_if.if_bpf, m, BPF_DIRECTION_IN);
73521a78fbcScanacar #endif
73621a78fbcScanacar 
73721a78fbcScanacar 	m_adj(m, PPPOE_HEADERLEN);
73821a78fbcScanacar 
73921a78fbcScanacar #ifdef PPPOE_DEBUG
74021a78fbcScanacar 	{
74121a78fbcScanacar 		struct mbuf *p;
74221a78fbcScanacar 
74321a78fbcScanacar 		printf("%s: pkthdr.len=%d, pppoe.len=%d",
74421a78fbcScanacar 			sc->sc_sppp.pp_if.if_xname,
74521a78fbcScanacar 			m->m_pkthdr.len, plen);
74621a78fbcScanacar 		p = m;
74721a78fbcScanacar 		while (p) {
74821a78fbcScanacar 			printf(" l=%d", p->m_len);
74921a78fbcScanacar 			p = p->m_next;
75021a78fbcScanacar 		}
75121a78fbcScanacar 		printf("\n");
75221a78fbcScanacar 	}
75321a78fbcScanacar #endif
75421a78fbcScanacar 
75521a78fbcScanacar 	if (m->m_pkthdr.len < plen)
75621a78fbcScanacar 		goto drop;
75721a78fbcScanacar 
75821a78fbcScanacar 	/* fix incoming interface pointer (not the raw ethernet interface anymore) */
759fb492c37Smpi 	m->m_pkthdr.ph_ifidx = sc->sc_sppp.pp_if.if_index;
76021a78fbcScanacar 
76121a78fbcScanacar 	/* pass packet up and account for it */
76221a78fbcScanacar 	sc->sc_sppp.pp_if.if_ipackets++;
76321a78fbcScanacar 	sppp_input(&sc->sc_sppp.pp_if, m);
76421a78fbcScanacar 	return;
76521a78fbcScanacar 
76621a78fbcScanacar drop:
76721a78fbcScanacar 	m_freem(m);
76821a78fbcScanacar }
76921a78fbcScanacar 
77021a78fbcScanacar static int
pppoe_output(struct pppoe_softc * sc,struct mbuf * m)77121a78fbcScanacar pppoe_output(struct pppoe_softc *sc, struct mbuf *m)
77221a78fbcScanacar {
77321a78fbcScanacar 	struct sockaddr dst;
77421a78fbcScanacar 	struct ether_header *eh;
7756d7c3328Sreyk 	struct ifnet *eth_if;
77621a78fbcScanacar 	u_int16_t etype;
7776d7c3328Sreyk 	int ret;
77821a78fbcScanacar 
7796d7c3328Sreyk 	if ((eth_if = if_get(sc->sc_eth_ifidx)) == NULL) {
780c7b46a35Spat 		m_freem(m);
78121a78fbcScanacar 		return (EIO);
78221a78fbcScanacar 	}
78321a78fbcScanacar 
7846d7c3328Sreyk 	if ((eth_if->if_flags & (IFF_UP|IFF_RUNNING))
785c7b46a35Spat 	    != (IFF_UP|IFF_RUNNING)) {
7866d7c3328Sreyk 		if_put(eth_if);
787c7b46a35Spat 		m_freem(m);
78821a78fbcScanacar 		return (ENETDOWN);
789c7b46a35Spat 	}
79021a78fbcScanacar 
79121a78fbcScanacar 	memset(&dst, 0, sizeof dst);
79221a78fbcScanacar 	dst.sa_family = AF_UNSPEC;
79321a78fbcScanacar 	eh = (struct ether_header*)&dst.sa_data;
79421a78fbcScanacar 	etype = sc->sc_state == PPPOE_STATE_SESSION ? ETHERTYPE_PPPOE : ETHERTYPE_PPPOEDISC;
79521a78fbcScanacar 	eh->ether_type = htons(etype);
79621a78fbcScanacar 	memcpy(&eh->ether_dhost, &sc->sc_dest, sizeof sc->sc_dest);
79721a78fbcScanacar 
79821a78fbcScanacar 	PPPOEDEBUG(("%s (%x) state=%d, session=0x%x output -> %s, len=%d\n",
79921a78fbcScanacar 	    sc->sc_sppp.pp_if.if_xname, etype,
80021a78fbcScanacar 	    sc->sc_state, sc->sc_session,
80121a78fbcScanacar 	    ether_sprintf((unsigned char *)&sc->sc_dest), m->m_pkthdr.len));
80221a78fbcScanacar 
80321a78fbcScanacar 	m->m_flags &= ~(M_BCAST|M_MCAST);
8044deb5574Sclaudio 	/* encapsulated packet is forced into rdomain of physical interface */
8056d7c3328Sreyk 	m->m_pkthdr.ph_rtableid = eth_if->if_rdomain;
8064deb5574Sclaudio 
8076d7c3328Sreyk 	ret = eth_if->if_output(eth_if, m, &dst, NULL);
8086d7c3328Sreyk 	if_put(eth_if);
8096d7c3328Sreyk 
8106d7c3328Sreyk 	return (ret);
81121a78fbcScanacar }
81221a78fbcScanacar 
81321a78fbcScanacar /* The ioctl routine. */
81421a78fbcScanacar static int
pppoe_ioctl(struct ifnet * ifp,unsigned long cmd,caddr_t data)81521a78fbcScanacar pppoe_ioctl(struct ifnet *ifp, unsigned long cmd, caddr_t data)
81621a78fbcScanacar {
81721a78fbcScanacar 	struct proc *p = curproc;	/* XXX */
81821a78fbcScanacar 	struct pppoe_softc *sc = (struct pppoe_softc *)ifp;
8196d7c3328Sreyk 	struct ifnet *eth_if;
820a5bac811Smpi 	int error = 0;
82121a78fbcScanacar 
82221a78fbcScanacar 	switch (cmd) {
82321a78fbcScanacar 	case PPPOESETPARMS:
82421a78fbcScanacar 	{
82521a78fbcScanacar 		struct pppoediscparms *parms = (struct pppoediscparms *)data;
82621a78fbcScanacar 		int len;
82721a78fbcScanacar 
8283e676399Smpi 		if ((error = suser(p)) != 0)
82921a78fbcScanacar 			return (error);
83021a78fbcScanacar 		if (parms->eth_ifname[0] != '\0') {
8311a2f7223Sbrad 			struct ifnet	*eth_if;
8321a2f7223Sbrad 
8330f1fc83dSmvs 			eth_if = if_unit(parms->eth_ifname);
834caf20d9bShenning 			if (eth_if == NULL || eth_if->if_type != IFT_ETHER) {
8350f1fc83dSmvs 				if_put(eth_if);
8366d7c3328Sreyk 				sc->sc_eth_ifidx = 0;
83721a78fbcScanacar 				return (ENXIO);
83821a78fbcScanacar 			}
8391a2f7223Sbrad 
8401a2f7223Sbrad 			if (sc->sc_sppp.pp_if.if_mtu >
8411a2f7223Sbrad 			    eth_if->if_mtu - PPPOE_OVERHEAD) {
8421a2f7223Sbrad 				sc->sc_sppp.pp_if.if_mtu = eth_if->if_mtu -
8431a2f7223Sbrad 				    PPPOE_OVERHEAD;
8441a2f7223Sbrad 			}
8456d7c3328Sreyk 			sc->sc_eth_ifidx = eth_if->if_index;
8460f1fc83dSmvs 			if_put(eth_if);
847b575845aSbrad 		}
84821a78fbcScanacar 
84921a78fbcScanacar 		if (sc->sc_concentrator_name)
8504de75e7cSkn 			free(sc->sc_concentrator_name, M_DEVBUF,
8514de75e7cSkn 			    strlen(sc->sc_concentrator_name) + 1);
85221a78fbcScanacar 		sc->sc_concentrator_name = NULL;
85321a78fbcScanacar 
85421a78fbcScanacar 		len = strlen(parms->ac_name);
85521a78fbcScanacar 		if (len > 0 && len < sizeof(parms->ac_name)) {
856bdc883b9Smiod 			char *p = malloc(len + 1, M_DEVBUF, M_WAITOK|M_CANFAIL);
85721a78fbcScanacar 			if (p == NULL)
85821a78fbcScanacar 				return (ENOMEM);
85921a78fbcScanacar 			strlcpy(p, parms->ac_name, len + 1);
86021a78fbcScanacar 			sc->sc_concentrator_name = p;
86121a78fbcScanacar 		}
86221a78fbcScanacar 
86321a78fbcScanacar 		if (sc->sc_service_name)
8644de75e7cSkn 			free(sc->sc_service_name, M_DEVBUF,
8654de75e7cSkn 			    strlen(sc->sc_service_name) + 1);
86621a78fbcScanacar 		sc->sc_service_name = NULL;
86721a78fbcScanacar 
86821a78fbcScanacar 		len = strlen(parms->service_name);
86921a78fbcScanacar 		if (len > 0 && len < sizeof(parms->service_name)) {
870bdc883b9Smiod 			char *p = malloc(len + 1, M_DEVBUF, M_WAITOK|M_CANFAIL);
87121a78fbcScanacar 			if (p == NULL)
87221a78fbcScanacar 				return (ENOMEM);
87321a78fbcScanacar 			strlcpy(p, parms->service_name, len + 1);
87421a78fbcScanacar 			sc->sc_service_name = p;
87521a78fbcScanacar 		}
87621a78fbcScanacar 		return (0);
87721a78fbcScanacar 	}
87821a78fbcScanacar 	break;
87921a78fbcScanacar 	case PPPOEGETPARMS:
88021a78fbcScanacar 	{
88121a78fbcScanacar 		struct pppoediscparms *parms = (struct pppoediscparms *)data;
88221a78fbcScanacar 
8836d7c3328Sreyk 		if ((eth_if = if_get(sc->sc_eth_ifidx)) != NULL) {
8846d7c3328Sreyk 			strlcpy(parms->eth_ifname, eth_if->if_xname,
88521a78fbcScanacar 			    IFNAMSIZ);
8866d7c3328Sreyk 			if_put(eth_if);
8876d7c3328Sreyk 		} else
88821a78fbcScanacar 			parms->eth_ifname[0] = '\0';
88921a78fbcScanacar 
89021a78fbcScanacar 		if (sc->sc_concentrator_name)
89121a78fbcScanacar 			strlcpy(parms->ac_name, sc->sc_concentrator_name,
89221a78fbcScanacar 			    sizeof(parms->ac_name));
89321a78fbcScanacar 		else
89421a78fbcScanacar 			parms->ac_name[0] = '\0';
89521a78fbcScanacar 
89621a78fbcScanacar 		if (sc->sc_service_name)
89721a78fbcScanacar 			strlcpy(parms->service_name, sc->sc_service_name,
89821a78fbcScanacar 			    sizeof(parms->service_name));
89921a78fbcScanacar 		else
90021a78fbcScanacar 			parms->service_name[0] = '\0';
90121a78fbcScanacar 
90221a78fbcScanacar 		return (0);
90321a78fbcScanacar 	}
90421a78fbcScanacar 	break;
90521a78fbcScanacar 	case PPPOEGETSESSION:
90621a78fbcScanacar 	{
90721a78fbcScanacar 		struct pppoeconnectionstate *state =
90821a78fbcScanacar 		    (struct pppoeconnectionstate *)data;
90921a78fbcScanacar 		state->state = sc->sc_state;
91021a78fbcScanacar 		state->session_id = sc->sc_session;
91121a78fbcScanacar 		state->padi_retry_no = sc->sc_padi_retried;
91221a78fbcScanacar 		state->padr_retry_no = sc->sc_padr_retried;
91321a78fbcScanacar 		state->session_time.tv_sec = sc->sc_session_time.tv_sec;
91421a78fbcScanacar 		state->session_time.tv_usec = sc->sc_session_time.tv_usec;
91521a78fbcScanacar 		return (0);
91621a78fbcScanacar 	}
91721a78fbcScanacar 	break;
91821a78fbcScanacar 	case SIOCSIFFLAGS:
91921a78fbcScanacar 	{
92021a78fbcScanacar 		struct ifreq *ifr = (struct ifreq *)data;
92121a78fbcScanacar 		/*
92221a78fbcScanacar 		 * Prevent running re-establishment timers overriding
92321a78fbcScanacar 		 * administrators choice.
92421a78fbcScanacar 		 */
92521a78fbcScanacar 		if ((ifr->ifr_flags & IFF_UP) == 0
92621a78fbcScanacar 		     && sc->sc_state >= PPPOE_STATE_PADI_SENT
92721a78fbcScanacar 		     && sc->sc_state < PPPOE_STATE_SESSION) {
92821a78fbcScanacar 			timeout_del(&sc->sc_timeout);
92921a78fbcScanacar 			sc->sc_state = PPPOE_STATE_INITIAL;
93021a78fbcScanacar 			sc->sc_padi_retried = 0;
93121a78fbcScanacar 			sc->sc_padr_retried = 0;
93221a78fbcScanacar 			memcpy(&sc->sc_dest, etherbroadcastaddr,
93321a78fbcScanacar 			    sizeof(sc->sc_dest));
93421a78fbcScanacar 		}
93521a78fbcScanacar 		return (sppp_ioctl(ifp, cmd, data));
93621a78fbcScanacar 	}
93721a78fbcScanacar 	case SIOCSIFMTU:
93821a78fbcScanacar 	{
93921a78fbcScanacar 		struct ifreq *ifr = (struct ifreq *)data;
94021a78fbcScanacar 
9416d7c3328Sreyk 		eth_if = if_get(sc->sc_eth_ifidx);
9426d7c3328Sreyk 
9437ab35c70Ssthen 		if (ifr->ifr_mtu > MIN(PPPOE_MAXMTU,
9446d7c3328Sreyk 		    (eth_if == NULL ? PPPOE_MAXMTU :
9456d7c3328Sreyk 		    (eth_if->if_mtu - PPPOE_OVERHEAD))))
9466d7c3328Sreyk 			error = EINVAL;
9476d7c3328Sreyk 		else
9486d7c3328Sreyk 			error = 0;
9496d7c3328Sreyk 
9506d7c3328Sreyk 		if_put(eth_if);
9516d7c3328Sreyk 
952f6281ee2Smvs 		if (error != 0)
953f6281ee2Smvs 			return (error);
954f6281ee2Smvs 
95521a78fbcScanacar 		return (sppp_ioctl(ifp, cmd, data));
95621a78fbcScanacar 	}
95721a78fbcScanacar 	default:
9580eba87f8Smikeb 		error = sppp_ioctl(ifp, cmd, data);
9590eba87f8Smikeb 		if (error == ENETRESET) {
9600eba87f8Smikeb 			error = 0;
9610eba87f8Smikeb 			if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
9620eba87f8Smikeb 			    (IFF_UP | IFF_RUNNING)) {
9630eba87f8Smikeb 				if_down(ifp);
9640eba87f8Smikeb 				if (sc->sc_state >= PPPOE_STATE_PADI_SENT &&
9650eba87f8Smikeb 				    sc->sc_state < PPPOE_STATE_SESSION) {
9660eba87f8Smikeb 					timeout_del(&sc->sc_timeout);
9670eba87f8Smikeb 					sc->sc_state = PPPOE_STATE_INITIAL;
9680eba87f8Smikeb 					sc->sc_padi_retried = 0;
9690eba87f8Smikeb 					sc->sc_padr_retried = 0;
9700eba87f8Smikeb 					memcpy(&sc->sc_dest,
9710eba87f8Smikeb 					    etherbroadcastaddr,
9720eba87f8Smikeb 					    sizeof(sc->sc_dest));
9730eba87f8Smikeb 				}
9740eba87f8Smikeb 				error = sppp_ioctl(ifp, SIOCSIFFLAGS, NULL);
9750eba87f8Smikeb 				if (error)
9760eba87f8Smikeb 					return (error);
9770eba87f8Smikeb 				if_up(ifp);
9780eba87f8Smikeb 				return (sppp_ioctl(ifp, SIOCSIFFLAGS, NULL));
9790eba87f8Smikeb 			}
9800eba87f8Smikeb 		}
9810eba87f8Smikeb 		return (error);
98221a78fbcScanacar 	}
98321a78fbcScanacar 	return (0);
98421a78fbcScanacar }
98521a78fbcScanacar 
98621a78fbcScanacar /*
98721a78fbcScanacar  * Allocate a mbuf/cluster with space to store the given data length
98821a78fbcScanacar  * of payload, leaving space for prepending an ethernet header
98921a78fbcScanacar  * in front.
99021a78fbcScanacar  */
99121a78fbcScanacar static struct mbuf *
pppoe_get_mbuf(size_t len)99221a78fbcScanacar pppoe_get_mbuf(size_t len)
99321a78fbcScanacar {
99421a78fbcScanacar 	struct mbuf *m;
99521a78fbcScanacar 
996d20143d6Stobhe 	if (len + sizeof(struct ether_header) > MCLBYTES)
997d20143d6Stobhe 		return NULL;
998d20143d6Stobhe 
99921a78fbcScanacar 	MGETHDR(m, M_DONTWAIT, MT_DATA);
100021a78fbcScanacar 	if (m == NULL)
100121a78fbcScanacar 		return (NULL);
100221a78fbcScanacar 	if (len + sizeof(struct ether_header) > MHLEN) {
100321a78fbcScanacar 		MCLGET(m, M_DONTWAIT);
100421a78fbcScanacar 		if ((m->m_flags & M_EXT) == 0) {
1005db12bc82Sthib 			m_free(m);
100621a78fbcScanacar 			return (NULL);
100721a78fbcScanacar 		}
100821a78fbcScanacar 	}
100921a78fbcScanacar 	m->m_data += sizeof(struct ether_header);
101021a78fbcScanacar 	m->m_len = len;
101121a78fbcScanacar 	m->m_pkthdr.len = len;
1012fb492c37Smpi 	m->m_pkthdr.ph_ifidx = 0;
101321a78fbcScanacar 
101421a78fbcScanacar 	return (m);
101521a78fbcScanacar }
101621a78fbcScanacar 
101721a78fbcScanacar /* Send PADI. */
101821a78fbcScanacar static int
pppoe_send_padi(struct pppoe_softc * sc)101921a78fbcScanacar pppoe_send_padi(struct pppoe_softc *sc)
102021a78fbcScanacar {
102121a78fbcScanacar 	struct mbuf *m0;
102221a78fbcScanacar 	int len, l1 = 0, l2 = 0; /* XXX: gcc */
102321a78fbcScanacar 	u_int8_t *p;
102421a78fbcScanacar 
102521a78fbcScanacar 	if (sc->sc_state > PPPOE_STATE_PADI_SENT)
102621a78fbcScanacar 		panic("pppoe_send_padi in state %d", sc->sc_state);
102721a78fbcScanacar 
102821a78fbcScanacar 	/* calculate length of frame (excluding ethernet header + pppoe header) */
1029bdc883b9Smiod 	len = 2 + 2 + 2 + 2 + sizeof(sc->sc_unique); /* service name tag is required, host unique is sent too */
103021a78fbcScanacar 	if (sc->sc_service_name != NULL) {
103121a78fbcScanacar 		l1 = strlen(sc->sc_service_name);
103221a78fbcScanacar 		len += l1;
103321a78fbcScanacar 	}
103421a78fbcScanacar 	if (sc->sc_concentrator_name != NULL) {
103521a78fbcScanacar 		l2 = strlen(sc->sc_concentrator_name);
103621a78fbcScanacar 		len += 2 + 2 + l2;
103721a78fbcScanacar 	}
10387ab35c70Ssthen 	if (sc->sc_sppp.pp_if.if_mtu > PPPOE_MTU)
10397ab35c70Ssthen 		len += 2 + 2 + 2;
104021a78fbcScanacar 
104121a78fbcScanacar 	/* allocate a buffer */
104221a78fbcScanacar 	m0 = pppoe_get_mbuf(len + PPPOE_HEADERLEN);	/* header len + payload len */
104321a78fbcScanacar 	if (m0 == NULL)
104421a78fbcScanacar 		return (ENOBUFS);
10454c6ae08dSsthen 	m0->m_pkthdr.pf.prio = sc->sc_sppp.pp_if.if_llprio;
104621a78fbcScanacar 
104721a78fbcScanacar 	/* fill in pkt */
104821a78fbcScanacar 	p = mtod(m0, u_int8_t *);
104921a78fbcScanacar 	PPPOE_ADD_HEADER(p, PPPOE_CODE_PADI, 0, len);
105021a78fbcScanacar 	PPPOE_ADD_16(p, PPPOE_TAG_SNAME);
105121a78fbcScanacar 	if (sc->sc_service_name != NULL) {
105221a78fbcScanacar 		PPPOE_ADD_16(p, l1);
105321a78fbcScanacar 		memcpy(p, sc->sc_service_name, l1);
105421a78fbcScanacar 		p += l1;
105521a78fbcScanacar 	} else {
105621a78fbcScanacar 		PPPOE_ADD_16(p, 0);
105721a78fbcScanacar 	}
105821a78fbcScanacar 	if (sc->sc_concentrator_name != NULL) {
105921a78fbcScanacar 		PPPOE_ADD_16(p, PPPOE_TAG_ACNAME);
106021a78fbcScanacar 		PPPOE_ADD_16(p, l2);
106121a78fbcScanacar 		memcpy(p, sc->sc_concentrator_name, l2);
106221a78fbcScanacar 		p += l2;
106321a78fbcScanacar 	}
106421a78fbcScanacar 	PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE);
10652a01ab2fScanacar 	PPPOE_ADD_16(p, sizeof(sc->sc_unique));
10662a01ab2fScanacar 	memcpy(p, &sc->sc_unique, sizeof(sc->sc_unique));
10677ab35c70Ssthen 	p += sizeof(sc->sc_unique);
10687ab35c70Ssthen 
10697ab35c70Ssthen 	if (sc->sc_sppp.pp_if.if_mtu > PPPOE_MTU) {
10707ab35c70Ssthen 		PPPOE_ADD_16(p, PPPOE_TAG_MAX_PAYLOAD);
10717ab35c70Ssthen 		PPPOE_ADD_16(p, 2);
10727ab35c70Ssthen 		PPPOE_ADD_16(p, (u_int16_t)sc->sc_sppp.pp_if.if_mtu);
10737ab35c70Ssthen 	}
107421a78fbcScanacar 
107521a78fbcScanacar #ifdef PPPOE_DEBUG
107621a78fbcScanacar 	if (p - mtod(m0, u_int8_t *) != len + PPPOE_HEADERLEN)
107721a78fbcScanacar 		panic("pppoe_send_padi: garbled output len, should be %ld, is %ld",
107821a78fbcScanacar 		    (long)(len + PPPOE_HEADERLEN), (long)(p - mtod(m0, u_int8_t *)));
107921a78fbcScanacar #endif
108021a78fbcScanacar 
108121a78fbcScanacar 	/* send pkt */
108221a78fbcScanacar 	return (pppoe_output(sc, m0));
108321a78fbcScanacar }
108421a78fbcScanacar 
108521a78fbcScanacar /* Watchdog function. */
108621a78fbcScanacar static void
pppoe_timeout(void * arg)108721a78fbcScanacar pppoe_timeout(void *arg)
108821a78fbcScanacar {
108921a78fbcScanacar 	struct pppoe_softc *sc = (struct pppoe_softc *)arg;
1090aa28b9a6Smpi 	int x, retry_wait, err;
109121a78fbcScanacar 
109221a78fbcScanacar 	PPPOEDEBUG(("%s: timeout\n", sc->sc_sppp.pp_if.if_xname));
109321a78fbcScanacar 
1094aa28b9a6Smpi 	NET_LOCK();
109556983f85Smpi 
109621a78fbcScanacar 	switch (sc->sc_state) {
109721a78fbcScanacar 	case PPPOE_STATE_PADI_SENT:
109821a78fbcScanacar 		/*
109921a78fbcScanacar 		 * We have two basic ways of retrying:
110021a78fbcScanacar 		 *  - Quick retry mode: try a few times in short sequence
110121a78fbcScanacar 		 *  - Slow retry mode: we already had a connection successfully
110221a78fbcScanacar 		 *    established and will try infinitely (without user
110321a78fbcScanacar 		 *    intervention)
110421a78fbcScanacar 		 * We only enter slow retry mode if IFF_LINK1 (aka autodial)
110521a78fbcScanacar 		 * is not set.
110621a78fbcScanacar 		 */
110721a78fbcScanacar 
110821a78fbcScanacar 		/* initialize for quick retry mode */
110921a78fbcScanacar 		retry_wait = PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried);
111021a78fbcScanacar 
111121a78fbcScanacar 		x = splnet();
111221a78fbcScanacar 		sc->sc_padi_retried++;
111321a78fbcScanacar 		if (sc->sc_padi_retried >= PPPOE_DISC_MAXPADI) {
111421a78fbcScanacar 			if ((sc->sc_sppp.pp_if.if_flags & IFF_LINK1) == 0) {
111521a78fbcScanacar 				/* slow retry mode */
111621a78fbcScanacar 				retry_wait = PPPOE_SLOW_RETRY;
111721a78fbcScanacar 			} else {
111821a78fbcScanacar 				pppoe_abort_connect(sc);
111921a78fbcScanacar 				splx(x);
112056983f85Smpi 				break;
112121a78fbcScanacar 			}
112221a78fbcScanacar 		}
112321a78fbcScanacar 		if ((err = pppoe_send_padi(sc)) != 0) {
112421a78fbcScanacar 			sc->sc_padi_retried--;
112521a78fbcScanacar 			PPPOEDEBUG(("%s: failed to transmit PADI, error=%d\n",
112621a78fbcScanacar 			    sc->sc_sppp.pp_if.if_xname, err));
112721a78fbcScanacar 		}
1128bd031ea9Skn 		timeout_add_sec(&sc->sc_timeout, retry_wait);
112921a78fbcScanacar 		splx(x);
113021a78fbcScanacar 
113121a78fbcScanacar 		break;
113221a78fbcScanacar 	case PPPOE_STATE_PADR_SENT:
113321a78fbcScanacar 		x = splnet();
113421a78fbcScanacar 		sc->sc_padr_retried++;
113521a78fbcScanacar 		if (sc->sc_padr_retried >= PPPOE_DISC_MAXPADR) {
113621a78fbcScanacar 			memcpy(&sc->sc_dest, etherbroadcastaddr,
113721a78fbcScanacar 			    sizeof(sc->sc_dest));
113821a78fbcScanacar 			sc->sc_state = PPPOE_STATE_PADI_SENT;
113921a78fbcScanacar 			sc->sc_padr_retried = 0;
114021a78fbcScanacar 			if ((err = pppoe_send_padi(sc)) != 0) {
114121a78fbcScanacar 				PPPOEDEBUG(("%s: failed to send PADI, error=%d\n",
114221a78fbcScanacar 				    sc->sc_sppp.pp_if.if_xname, err));
114321a78fbcScanacar 			}
1144bd031ea9Skn 			timeout_add_sec(&sc->sc_timeout,
114521a78fbcScanacar 			    PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried));
114621a78fbcScanacar 			splx(x);
114756983f85Smpi 			break;
114821a78fbcScanacar 		}
114921a78fbcScanacar 		if ((err = pppoe_send_padr(sc)) != 0) {
115021a78fbcScanacar 			sc->sc_padr_retried--;
115121a78fbcScanacar 			PPPOEDEBUG(("%s: failed to send PADR, error=%d\n",
115221a78fbcScanacar 			    sc->sc_sppp.pp_if.if_xname, err));
115321a78fbcScanacar 		}
1154bd031ea9Skn 		timeout_add_sec(&sc->sc_timeout,
115521a78fbcScanacar 		    PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried));
115621a78fbcScanacar 		splx(x);
115721a78fbcScanacar 
115821a78fbcScanacar 		break;
115921a78fbcScanacar 	case PPPOE_STATE_CLOSING:
116021a78fbcScanacar 		pppoe_disconnect(sc);
116121a78fbcScanacar 		break;
116221a78fbcScanacar 	default:
116356983f85Smpi 		break;	/* all done, work in peace */
116421a78fbcScanacar 	}
116556983f85Smpi 
1166aa28b9a6Smpi 	NET_UNLOCK();
116721a78fbcScanacar }
116821a78fbcScanacar 
116921a78fbcScanacar /* Start a connection (i.e. initiate discovery phase). */
117021a78fbcScanacar static int
pppoe_connect(struct pppoe_softc * sc)117121a78fbcScanacar pppoe_connect(struct pppoe_softc *sc)
117221a78fbcScanacar {
117321a78fbcScanacar 	int x, err;
117421a78fbcScanacar 
117521a78fbcScanacar 	if (sc->sc_state != PPPOE_STATE_INITIAL)
117621a78fbcScanacar 		return (EBUSY);
117721a78fbcScanacar 
117821a78fbcScanacar 	x = splnet();
117921a78fbcScanacar 
118021a78fbcScanacar 	/* save state, in case we fail to send PADI */
118121a78fbcScanacar 	sc->sc_state = PPPOE_STATE_PADI_SENT;
118221a78fbcScanacar 	sc->sc_padr_retried = 0;
118321a78fbcScanacar 	err = pppoe_send_padi(sc);
118421a78fbcScanacar 	if (err != 0)
118521a78fbcScanacar 		PPPOEDEBUG(("%s: failed to send PADI, error=%d\n",
118621a78fbcScanacar 		    sc->sc_sppp.pp_if.if_xname, err));
118721a78fbcScanacar 
1188bd031ea9Skn 	timeout_add_sec(&sc->sc_timeout, PPPOE_DISC_TIMEOUT);
118921a78fbcScanacar 	splx(x);
119021a78fbcScanacar 
119121a78fbcScanacar 	return (err);
119221a78fbcScanacar }
119321a78fbcScanacar 
119421a78fbcScanacar /* disconnect */
119521a78fbcScanacar static int
pppoe_disconnect(struct pppoe_softc * sc)119621a78fbcScanacar pppoe_disconnect(struct pppoe_softc *sc)
119721a78fbcScanacar {
119821a78fbcScanacar 	int err, x;
119921a78fbcScanacar 
120021a78fbcScanacar 	x = splnet();
120121a78fbcScanacar 
120221a78fbcScanacar 	if (sc->sc_state < PPPOE_STATE_SESSION)
120321a78fbcScanacar 		err = EBUSY;
120421a78fbcScanacar 	else {
120521a78fbcScanacar 		PPPOEDEBUG(("%s: disconnecting\n",
120621a78fbcScanacar 		    sc->sc_sppp.pp_if.if_xname));
12076d7c3328Sreyk 		err = pppoe_send_padt(sc->sc_eth_ifidx,
12084c6ae08dSsthen 		    sc->sc_session, (const u_int8_t *)&sc->sc_dest,
12094c6ae08dSsthen 		    sc->sc_sppp.pp_if.if_llprio);
121021a78fbcScanacar 	}
121121a78fbcScanacar 
121221a78fbcScanacar 	/* cleanup softc */
121321a78fbcScanacar 	sc->sc_state = PPPOE_STATE_INITIAL;
121421a78fbcScanacar 	memcpy(&sc->sc_dest, etherbroadcastaddr, sizeof(sc->sc_dest));
121521a78fbcScanacar 	if (sc->sc_ac_cookie) {
12164de75e7cSkn 		free(sc->sc_ac_cookie, M_DEVBUF, sc->sc_ac_cookie_len);
121721a78fbcScanacar 		sc->sc_ac_cookie = NULL;
121821a78fbcScanacar 	}
121921a78fbcScanacar 	sc->sc_ac_cookie_len = 0;
12208d5ba128Scanacar 	if (sc->sc_relay_sid) {
12214de75e7cSkn 		free(sc->sc_relay_sid, M_DEVBUF, sc->sc_relay_sid_len);
12228d5ba128Scanacar 		sc->sc_relay_sid = NULL;
12238d5ba128Scanacar 	}
12248d5ba128Scanacar 	sc->sc_relay_sid_len = 0;
122521a78fbcScanacar 	sc->sc_session = 0;
122621a78fbcScanacar 
122721a78fbcScanacar 	/* notify upper layer */
122821a78fbcScanacar 	sc->sc_sppp.pp_down(&sc->sc_sppp);
122921a78fbcScanacar 
123021a78fbcScanacar 	splx(x);
123121a78fbcScanacar 
123221a78fbcScanacar 	return (err);
123321a78fbcScanacar }
123421a78fbcScanacar 
123521a78fbcScanacar /* Connection attempt aborted. */
123621a78fbcScanacar static void
pppoe_abort_connect(struct pppoe_softc * sc)123721a78fbcScanacar pppoe_abort_connect(struct pppoe_softc *sc)
123821a78fbcScanacar {
123921a78fbcScanacar 	printf("%s: could not establish connection\n",
124021a78fbcScanacar 		sc->sc_sppp.pp_if.if_xname);
124121a78fbcScanacar 	sc->sc_state = PPPOE_STATE_CLOSING;
124221a78fbcScanacar 
124321a78fbcScanacar 	/* notify upper layer */
124421a78fbcScanacar 	sc->sc_sppp.pp_down(&sc->sc_sppp);
124521a78fbcScanacar 
124621a78fbcScanacar 	/* clear connection state */
124721a78fbcScanacar 	memcpy(&sc->sc_dest, etherbroadcastaddr, sizeof(sc->sc_dest));
124821a78fbcScanacar 	sc->sc_state = PPPOE_STATE_INITIAL;
124921a78fbcScanacar }
125021a78fbcScanacar 
125121a78fbcScanacar /* Send a PADR packet */
125221a78fbcScanacar static int
pppoe_send_padr(struct pppoe_softc * sc)125321a78fbcScanacar pppoe_send_padr(struct pppoe_softc *sc)
125421a78fbcScanacar {
125521a78fbcScanacar 	struct mbuf *m0;
125621a78fbcScanacar 	u_int8_t *p;
125721a78fbcScanacar 	size_t len, l1 = 0; /* XXX: gcc */
125821a78fbcScanacar 
125921a78fbcScanacar 	if (sc->sc_state != PPPOE_STATE_PADR_SENT)
126021a78fbcScanacar 		return (EIO);
126121a78fbcScanacar 
12622a01ab2fScanacar 	len = 2 + 2 + 2 + 2 + sizeof(sc->sc_unique);	/* service name, host unique */
126321a78fbcScanacar 	if (sc->sc_service_name != NULL) {		/* service name tag maybe empty */
126421a78fbcScanacar 		l1 = strlen(sc->sc_service_name);
126521a78fbcScanacar 		len += l1;
126621a78fbcScanacar 	}
126721a78fbcScanacar 	if (sc->sc_ac_cookie_len > 0)
126821a78fbcScanacar 		len += 2 + 2 + sc->sc_ac_cookie_len;	/* AC cookie */
12698d5ba128Scanacar 	if (sc->sc_relay_sid_len > 0)
12708d5ba128Scanacar 		len += 2 + 2 + sc->sc_relay_sid_len;	/* Relay SID */
12717ab35c70Ssthen 	if (sc->sc_sppp.pp_if.if_mtu > PPPOE_MTU)
12727ab35c70Ssthen 		len += 2 + 2 + 2;
127321a78fbcScanacar 
127421a78fbcScanacar 	m0 = pppoe_get_mbuf(len + PPPOE_HEADERLEN);
127521a78fbcScanacar 	if (m0 == NULL)
127621a78fbcScanacar 		return (ENOBUFS);
12774c6ae08dSsthen 	m0->m_pkthdr.pf.prio = sc->sc_sppp.pp_if.if_llprio;
127821a78fbcScanacar 
127921a78fbcScanacar 	p = mtod(m0, u_int8_t *);
128021a78fbcScanacar 	PPPOE_ADD_HEADER(p, PPPOE_CODE_PADR, 0, len);
128121a78fbcScanacar 	PPPOE_ADD_16(p, PPPOE_TAG_SNAME);
128221a78fbcScanacar 
128321a78fbcScanacar 	if (sc->sc_service_name != NULL) {
128421a78fbcScanacar 		PPPOE_ADD_16(p, l1);
128521a78fbcScanacar 		memcpy(p, sc->sc_service_name, l1);
128621a78fbcScanacar 		p += l1;
128721a78fbcScanacar 	} else {
128821a78fbcScanacar 		PPPOE_ADD_16(p, 0);
128921a78fbcScanacar 	}
129021a78fbcScanacar 	if (sc->sc_ac_cookie_len > 0) {
129121a78fbcScanacar 		PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE);
129221a78fbcScanacar 		PPPOE_ADD_16(p, sc->sc_ac_cookie_len);
129321a78fbcScanacar 		memcpy(p, sc->sc_ac_cookie, sc->sc_ac_cookie_len);
129421a78fbcScanacar 		p += sc->sc_ac_cookie_len;
129521a78fbcScanacar 	}
12968d5ba128Scanacar 	if (sc->sc_relay_sid_len > 0) {
12978d5ba128Scanacar 		PPPOE_ADD_16(p, PPPOE_TAG_RELAYSID);
12988d5ba128Scanacar 		PPPOE_ADD_16(p, sc->sc_relay_sid_len);
12998d5ba128Scanacar 		memcpy(p, sc->sc_relay_sid, sc->sc_relay_sid_len);
13008d5ba128Scanacar 		p += sc->sc_relay_sid_len;
13018d5ba128Scanacar 	}
130221a78fbcScanacar 	PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE);
13032a01ab2fScanacar 	PPPOE_ADD_16(p, sizeof(sc->sc_unique));
13042a01ab2fScanacar 	memcpy(p, &sc->sc_unique, sizeof(sc->sc_unique));
13057ab35c70Ssthen 	p += sizeof(sc->sc_unique);
13067ab35c70Ssthen 
13077ab35c70Ssthen 	if (sc->sc_sppp.pp_if.if_mtu > PPPOE_MTU) {
13087ab35c70Ssthen 		PPPOE_ADD_16(p, PPPOE_TAG_MAX_PAYLOAD);
13097ab35c70Ssthen 		PPPOE_ADD_16(p, 2);
13107ab35c70Ssthen 		PPPOE_ADD_16(p, (u_int16_t)sc->sc_sppp.pp_if.if_mtu);
13117ab35c70Ssthen 	}
131221a78fbcScanacar 
131321a78fbcScanacar #ifdef PPPOE_DEBUG
131421a78fbcScanacar 	if (p - mtod(m0, u_int8_t *) != len + PPPOE_HEADERLEN)
131521a78fbcScanacar 		panic("pppoe_send_padr: garbled output len, should be %ld, is %ld",
131621a78fbcScanacar 			(long)(len + PPPOE_HEADERLEN), (long)(p - mtod(m0, u_int8_t *)));
131721a78fbcScanacar #endif
131821a78fbcScanacar 
131921a78fbcScanacar 	return (pppoe_output(sc, m0));
132021a78fbcScanacar }
132121a78fbcScanacar 
132221a78fbcScanacar /* Send a PADT packet. */
132321a78fbcScanacar static int
pppoe_send_padt(unsigned int ifidx,u_int session,const u_int8_t * dest,u_int8_t prio)13244c6ae08dSsthen pppoe_send_padt(unsigned int ifidx, u_int session, const u_int8_t *dest, u_int8_t prio)
132521a78fbcScanacar {
132621a78fbcScanacar 	struct ether_header *eh;
132721a78fbcScanacar 	struct sockaddr dst;
13286d7c3328Sreyk 	struct ifnet *eth_if;
132921a78fbcScanacar 	struct mbuf *m0;
133021a78fbcScanacar 	u_int8_t *p;
13316d7c3328Sreyk 	int ret;
13326d7c3328Sreyk 
13336d7c3328Sreyk 	if ((eth_if = if_get(ifidx)) == NULL)
13346d7c3328Sreyk 		return (EINVAL);
133521a78fbcScanacar 
133621a78fbcScanacar 	m0 = pppoe_get_mbuf(PPPOE_HEADERLEN);
13376d7c3328Sreyk 	if (m0 == NULL) {
13386d7c3328Sreyk 		if_put(eth_if);
133921a78fbcScanacar 		return (ENOBUFS);
13406d7c3328Sreyk 	}
13414c6ae08dSsthen 	m0->m_pkthdr.pf.prio = prio;
134221a78fbcScanacar 
134321a78fbcScanacar 	p = mtod(m0, u_int8_t *);
134421a78fbcScanacar 	PPPOE_ADD_HEADER(p, PPPOE_CODE_PADT, session, 0);
134521a78fbcScanacar 
134621a78fbcScanacar 	memset(&dst, 0, sizeof(dst));
134721a78fbcScanacar 	dst.sa_family = AF_UNSPEC;
134821a78fbcScanacar 	eh = (struct ether_header *)&dst.sa_data;
134921a78fbcScanacar 	eh->ether_type = htons(ETHERTYPE_PPPOEDISC);
135021a78fbcScanacar 	memcpy(&eh->ether_dhost, dest, ETHER_ADDR_LEN);
135121a78fbcScanacar 
135221a78fbcScanacar 	m0->m_flags &= ~(M_BCAST|M_MCAST);
135343ac8a5aSmpi 	/* encapsulated packet is forced into rdomain of physical interface */
13546d7c3328Sreyk 	m0->m_pkthdr.ph_rtableid = eth_if->if_rdomain;
135543ac8a5aSmpi 
13566d7c3328Sreyk 	ret = eth_if->if_output(eth_if, m0, &dst, NULL);
13576d7c3328Sreyk 	if_put(eth_if);
13586d7c3328Sreyk 
13596d7c3328Sreyk 	return (ret);
136021a78fbcScanacar }
136121a78fbcScanacar 
136221a78fbcScanacar 
136321a78fbcScanacar /* this-layer-start function */
136421a78fbcScanacar static void
pppoe_tls(struct sppp * sp)136521a78fbcScanacar pppoe_tls(struct sppp *sp)
136621a78fbcScanacar {
136721a78fbcScanacar 	struct pppoe_softc *sc = (void *)sp;
136821a78fbcScanacar 
136921a78fbcScanacar 	if (sc->sc_state != PPPOE_STATE_INITIAL)
137021a78fbcScanacar 		return;
137121a78fbcScanacar 	pppoe_connect(sc);
137221a78fbcScanacar }
137321a78fbcScanacar 
137421a78fbcScanacar /* this-layer-finish function */
137521a78fbcScanacar static void
pppoe_tlf(struct sppp * sp)137621a78fbcScanacar pppoe_tlf(struct sppp *sp)
137721a78fbcScanacar {
137821a78fbcScanacar 	struct pppoe_softc *sc = (void *)sp;
137921a78fbcScanacar 
138021a78fbcScanacar 	if (sc->sc_state < PPPOE_STATE_SESSION)
138121a78fbcScanacar 		return;
138221a78fbcScanacar 	/*
138321a78fbcScanacar 	 * Do not call pppoe_disconnect here, the upper layer state
138421a78fbcScanacar 	 * machine gets confused by this. We must return from this
138521a78fbcScanacar 	 * function and defer disconnecting to the timeout handler.
138621a78fbcScanacar 	 */
138721a78fbcScanacar 	sc->sc_state = PPPOE_STATE_CLOSING;
1388bd031ea9Skn 	timeout_add_msec(&sc->sc_timeout, 20);
138921a78fbcScanacar }
139021a78fbcScanacar 
139121a78fbcScanacar static void
pppoe_start(struct ifnet * ifp)139221a78fbcScanacar pppoe_start(struct ifnet *ifp)
139321a78fbcScanacar {
139421a78fbcScanacar 	struct pppoe_softc *sc = (void *)ifp;
139521a78fbcScanacar 	struct mbuf *m;
139621a78fbcScanacar 	size_t len;
139721a78fbcScanacar 	u_int8_t *p;
139821a78fbcScanacar 
139921a78fbcScanacar 	if (sppp_isempty(ifp))
140021a78fbcScanacar 		return;
140121a78fbcScanacar 
140221a78fbcScanacar 	/* are we ready to process data yet? */
140321a78fbcScanacar 	if (sc->sc_state < PPPOE_STATE_SESSION) {
140421a78fbcScanacar 		sppp_flush(&sc->sc_sppp.pp_if);
140521a78fbcScanacar 		return;
140621a78fbcScanacar 	}
140721a78fbcScanacar 
140821a78fbcScanacar 	while ((m = sppp_dequeue(ifp)) != NULL) {
140921a78fbcScanacar 		len = m->m_pkthdr.len;
141021a78fbcScanacar 		M_PREPEND(m, PPPOE_HEADERLEN, M_DONTWAIT);
141121a78fbcScanacar 		if (m == NULL) {
141221a78fbcScanacar 			ifp->if_oerrors++;
141321a78fbcScanacar 			continue;
141421a78fbcScanacar 		}
141521a78fbcScanacar 		p = mtod(m, u_int8_t *);
141621a78fbcScanacar 		PPPOE_ADD_HEADER(p, 0, sc->sc_session, len);
141721a78fbcScanacar 
141821a78fbcScanacar #if NBPFILTER > 0
141921a78fbcScanacar 		if(sc->sc_sppp.pp_if.if_bpf)
1420c4acdf64Sdjm 			bpf_mtap(sc->sc_sppp.pp_if.if_bpf, m,
1421c4acdf64Sdjm 			    BPF_DIRECTION_OUT);
142221a78fbcScanacar #endif
142321a78fbcScanacar 
142421a78fbcScanacar 		pppoe_output(sc, m);
142521a78fbcScanacar 	}
142621a78fbcScanacar }
1427