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