diff options
author | Can Erkin Acar <canacar@cvs.openbsd.org> | 2004-11-28 23:39:46 +0000 |
---|---|---|
committer | Can Erkin Acar <canacar@cvs.openbsd.org> | 2004-11-28 23:39:46 +0000 |
commit | 21bc148fb19bda21c314e0e1f9b34eac9a13cc8a (patch) | |
tree | a94aa3b32a2e9452fd7636a18a0d69f0b47c8525 | |
parent | 9b02e944d8a1968fe39ff654b9f987ed838daed2 (diff) |
In kernel pppoe client, a simple IPv4 only implementation.
Initial porting from NetBSD by David Berghoff.
Modified/simplified to match our sppp implementation.
ok deraadt@
-rw-r--r-- | sbin/ifconfig/ifconfig.8 | 28 | ||||
-rw-r--r-- | sbin/ifconfig/ifconfig.c | 145 | ||||
-rw-r--r-- | sys/conf/GENERIC | 4 | ||||
-rw-r--r-- | sys/conf/files | 6 | ||||
-rw-r--r-- | sys/dev/pci/if_lmc.c | 3 | ||||
-rw-r--r-- | sys/dev/pci/if_san_obsd.c | 3 | ||||
-rw-r--r-- | sys/net/if_ethersubr.c | 41 | ||||
-rw-r--r-- | sys/net/if_pppoe.c | 1434 | ||||
-rw-r--r-- | sys/net/if_pppoe.h | 81 | ||||
-rw-r--r-- | sys/net/if_sppp.h | 9 | ||||
-rw-r--r-- | sys/net/if_spppsubr.c | 163 | ||||
-rw-r--r-- | sys/net/netisr.h | 4 | ||||
-rw-r--r-- | sys/net/netisr_dispatch.h | 6 |
13 files changed, 1855 insertions, 72 deletions
diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8 index 41d209b9941..0321f514a84 100644 --- a/sbin/ifconfig/ifconfig.8 +++ b/sbin/ifconfig/ifconfig.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ifconfig.8,v 1.90 2004/11/06 00:54:19 reyk Exp $ +.\" $OpenBSD: ifconfig.8,v 1.91 2004/11/28 23:39:45 canacar Exp $ .\" $NetBSD: ifconfig.8,v 1.11 1996/01/04 21:27:29 pk Exp $ .\" $FreeBSD: ifconfig.8,v 1.16 1998/02/01 07:03:29 steve Exp $ .\" @@ -88,6 +88,20 @@ .Ar interface .Cm group .Ar group-name +.Nm +.Ar pppoe-interface +.Oo +.Cm pppoedev +.Ar parent-interface +.Oc +.Oo +.Cm pppoesvc +.Ar service +.Oc +.Oo +.Cm pppoeac +.Ar access-concentrator +.Oc .Sh DESCRIPTION The .Nm @@ -555,6 +569,17 @@ Disable 802.11 power saving mode. .It Cm powersavesleep Ar duration (IEEE 802.11 devices only) Set the receiver sleep duration (in milliseconds) for 802.11 power saving mode. +.It Cm pppoedev Ar parent-interface +Set the name of the interface through which the pppoe packets +will be transmitted and received. +.It Cm pppoesvc Ar service +Set the service name of the pppoe interface. +.It Fl pppoesvc +Clear a previously set service name. +.It Cm pppoeac Ar access-concentrator +Set the name of the access-concentrator for pppoe interface. +.It Fl pppoeac +Clear a previously set access-concentrator name. .It Cm prefixlen Ar n (inet and inet6 only) Effect is similar to @@ -756,6 +781,7 @@ tried to alter an interface's configuration. .Xr netintro 4 , .Xr pfsync 4 , .Xr ppp 4 , +.Xr pppoe 4 , .Xr sl 4 , .Xr tun 4 , .Xr vlan 4 , diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c index ffcbb083112..bff8bd86003 100644 --- a/sbin/ifconfig/ifconfig.c +++ b/sbin/ifconfig/ifconfig.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ifconfig.c,v 1.119 2004/11/17 01:47:20 itojun Exp $ */ +/* $OpenBSD: ifconfig.c,v 1.120 2004/11/28 23:39:45 canacar Exp $ */ /* $NetBSD: ifconfig.c,v 1.40 1997/10/01 02:19:43 enami Exp $ */ /* @@ -77,7 +77,7 @@ static const char copyright[] = #if 0 static const char sccsid[] = "@(#)ifconfig.c 8.2 (Berkeley) 2/16/94"; #else -static const char rcsid[] = "$OpenBSD: ifconfig.c,v 1.119 2004/11/17 01:47:20 itojun Exp $"; +static const char rcsid[] = "$OpenBSD: ifconfig.c,v 1.120 2004/11/28 23:39:45 canacar Exp $"; #endif #endif /* not lint */ @@ -100,6 +100,7 @@ static const char rcsid[] = "$OpenBSD: ifconfig.c,v 1.119 2004/11/17 01:47:20 it #include <net80211/ieee80211_ioctl.h> #include <net/pfvar.h> #include <net/if_pfsync.h> +#include <net/if_pppoe.h> #include <netatalk/at.h> @@ -208,6 +209,10 @@ void unsetpfsync_syncif(const char *, int); void setpfsync_syncpeer(const char *, int); void unsetpfsync_syncpeer(const char *, int); void pfsync_status(void); +void setpppoe_dev(const char *,int); +void setpppoe_svc(const char *,int); +void setpppoe_ac(const char *,int); +void pppoe_status(void); int main(int, char *[]); int prefix(void *val, int); @@ -328,6 +333,11 @@ const struct cmd { { "timeslot", NEXTARG, 0, settimeslot }, { "description", NEXTARG, 0, setifdesc }, { "descr", NEXTARG, 0, setifdesc }, + { "pppoedev", NEXTARG, 0, setpppoe_dev }, + { "pppoesvc", NEXTARG, 0, setpppoe_svc }, + { "-pppoesvc", 1, 0, setpppoe_svc }, + { "pppoeac", NEXTARG, 0, setpppoe_ac }, + { "-pppoeac", 1, 0, setpppoe_ac }, { NULL, /*src*/ 0, 0, setifaddr }, { NULL, /*dst*/ 0, 0, setifdstaddr }, { NULL, /*illegal*/0, 0, NULL }, @@ -1938,6 +1948,7 @@ status(int link, struct sockaddr_dl *sdl) carp_status(); pfsync_status(); ieee80211_status(); + pppoe_status(); getifgroups(); (void) memset(&ifmr, 0, sizeof(ifmr)); @@ -2734,6 +2745,7 @@ vlan_status(void) "<none>" : vreq.vlr_parent); } + /* ARGSUSED */ void setvlantag(const char *val, int d) @@ -3112,6 +3124,135 @@ pfsync_status(void) } } + +void +pppoe_status(void) +{ + struct pppoediscparms parms; + struct pppoeconnectionstate state; + struct timeval temp_time; + long diff_time; + unsigned long day, hour, min, sec; + int e; + + day = hour = min = sec = 0; /* XXX make gcc happy */ + + memset(&state, 0, sizeof(state)); + memset(&temp_time, 0, sizeof(temp_time)); + + strlcpy(parms.ifname, name, sizeof(parms.ifname)); + if (ioctl(s, PPPOEGETPARMS, &parms)) + return; + + printf("\tdev: %s ", parms.eth_ifname); + + if (*parms.ac_name) + printf("ac: %s ", parms.ac_name); + if (*parms.service_name) + printf("svc: %s ", parms.service_name); + + strlcpy(state.ifname, name, sizeof(state.ifname)); + if (ioctl(s, PPPOEGETSESSION, &state)) + err(1, "PPPOEGETSESSION"); + + printf("state: "); + switch(state.state) { + case PPPOE_STATE_INITIAL: + printf("initial"); break; + case PPPOE_STATE_PADI_SENT: + printf("PADI sent"); break; + case PPPOE_STATE_PADR_SENT: + printf("PADR sent"); break; + case PPPOE_STATE_SESSION: + printf("session"); break; + case PPPOE_STATE_CLOSING: + printf("closing"); break; + } + printf("\n\tsid: 0x%x", state.session_id); + printf(" PADI retries: %d", state.padi_retry_no); + printf(" PADR retries: %d", state.padr_retry_no); + + if (state.state == PPPOE_STATE_SESSION) { + if (state.session_time.tv_sec != 0) { + gettimeofday(&temp_time, NULL); + diff_time = temp_time.tv_sec - + state.session_time.tv_sec; + + day = diff_time / (60 * 60 * 24); + diff_time %= (60 * 60 * 24); + + hour = diff_time / (60 * 60); + diff_time %= (60 * 60); + + min = diff_time / 60; + diff_time %= 60; + + sec = diff_time; + } + printf(" time: "); + if (day != 0) printf("%ldd ", day); + printf("%ld:%ld:%ld", hour, min, sec); + } + putchar('\n'); +} + + +/* ARGSUSED */ +void +setpppoe_dev(const char *val, int d) +{ + struct pppoediscparms parms; + + strlcpy(parms.ifname, name, sizeof(parms.ifname)); + if (ioctl(s, PPPOEGETPARMS, &parms)) + return; + + strlcpy(parms.eth_ifname, val, sizeof(parms.eth_ifname)); + + if (ioctl(s, PPPOESETPARMS, &parms)) + err(1, "PPPOESETPARMS"); +} + + +/* ARGSUSED */ +void +setpppoe_svc(const char *val, int d) +{ + struct pppoediscparms parms; + + strlcpy(parms.ifname, name, sizeof(parms.ifname)); + if (ioctl(s, PPPOEGETPARMS, &parms)) + return; + + if (d == 0) + strlcpy(parms.service_name, val, sizeof(parms.service_name)); + else + memset(parms.service_name, 0, sizeof(parms.service_name)); + + if (ioctl(s, PPPOESETPARMS, &parms)) + err(1, "PPPOESETPARMS"); +} + +/* ARGSUSED */ +void +setpppoe_ac(const char *val, int d) +{ + struct pppoediscparms parms; + + strlcpy(parms.ifname, name, sizeof(parms.ifname)); + if (ioctl(s, PPPOEGETPARMS, &parms)) + return; + + if (d == 0) + strlcpy(parms.ac_name, val, sizeof(parms.ac_name)); + else + memset(parms.ac_name, 0, sizeof(parms.ac_name)); + + if (ioctl(s, PPPOESETPARMS, &parms)) + err(1, "PPPOESETPARMS"); +} + + #ifdef INET6 char * sec2str(time_t total) diff --git a/sys/conf/GENERIC b/sys/conf/GENERIC index 89938e64601..a97d69965ab 100644 --- a/sys/conf/GENERIC +++ b/sys/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.111 2004/11/24 18:53:15 deraadt Exp $ +# $OpenBSD: GENERIC,v 1.112 2004/11/28 23:39:45 canacar Exp $ # # Machine-independent option; used by all architectures for their # GENERIC kernel @@ -104,4 +104,6 @@ pseudo-device vlan # IEEE 802.1Q VLAN # for IPv6 #pseudo-device faith 1 # IPv[46] tcp relay translation i/f +pseudo-device pppoe 1 # PPP over Ethernet (RFC 2516) + option BOOT_CONFIG # add support for boot -c diff --git a/sys/conf/files b/sys/conf/files index eac913ca9e3..6a74ab53a89 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $OpenBSD: files,v 1.320 2004/11/23 09:39:29 reyk Exp $ +# $OpenBSD: files,v 1.321 2004/11/28 23:39:45 canacar Exp $ # $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -433,6 +433,10 @@ file dev/bio.c bio needs-flag pseudo-device hotplug file dev/hotplug.c hotplug needs-flag +pseudo-device pppoe: ifnet, ether, sppp +file net/if_pppoe.c pppoe needs-flag + + # XXX machine-independent SCSI files should live somewhere here, maybe # kernel sources diff --git a/sys/dev/pci/if_lmc.c b/sys/dev/pci/if_lmc.c index f974de08dc9..6399154f598 100644 --- a/sys/dev/pci/if_lmc.c +++ b/sys/dev/pci/if_lmc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_lmc.c,v 1.17 2004/05/12 06:35:11 tedu Exp $ */ +/* $OpenBSD: if_lmc.c,v 1.18 2004/11/28 23:39:45 canacar Exp $ */ /* $NetBSD: if_lmc.c,v 1.1 1999/03/25 03:32:43 explorer Exp $ */ /*- @@ -1451,6 +1451,7 @@ lmc_attach(lmc_softc_t * const sc) #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) sppp_attach((struct ifnet *)&sc->lmc_sppp); sc->lmc_sppp.pp_flags = PP_CISCO | PP_KEEPALIVE; + sc->lmc_sppp.pp_framebytes = 3; #endif #if defined(__bsdi__) sc->lmc_p2pcom.p2p_mdmctl = lmc_mdmctl; diff --git a/sys/dev/pci/if_san_obsd.c b/sys/dev/pci/if_san_obsd.c index 39f5f467ca9..f7163eed26a 100644 --- a/sys/dev/pci/if_san_obsd.c +++ b/sys/dev/pci/if_san_obsd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_san_obsd.c,v 1.4 2004/07/16 15:11:45 alex Exp $ */ +/* $OpenBSD: if_san_obsd.c,v 1.5 2004/11/28 23:39:45 canacar Exp $ */ /*- * Copyright (c) 2001-2004 Sangoma Technologies (SAN) @@ -126,6 +126,7 @@ wanpipe_generic_register(sdla_t *card, struct ifnet *ifp, char *ifname) ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST; ((struct sppp *)ifp)->pp_flags |= PP_CISCO; ((struct sppp *)ifp)->pp_flags |= PP_KEEPALIVE; + ((struct sppp *)ifp)->pp_framebytes = 3; ifp->if_ioctl = wanpipe_generic_ioctl; /* Will set from new_if() */ ifp->if_start = wanpipe_generic_start; ifp->if_watchdog = wanpipe_generic_watchdog; diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index 612c9fbe627..fa4feeaeef6 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_ethersubr.c,v 1.80 2004/10/09 19:55:29 brad Exp $ */ +/* $OpenBSD: if_ethersubr.c,v 1.81 2004/11/28 23:39:45 canacar Exp $ */ /* $NetBSD: if_ethersubr.c,v 1.19 1996/05/07 02:40:30 thorpej Exp $ */ /* @@ -122,6 +122,11 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>. #include <netinet/ip_carp.h> #endif +#include "pppoe.h" +#if NPPPOE > 0 +#include <net/if_pppoe.h> +#endif + #ifdef INET6 #ifndef INET #include <netinet/in.h> @@ -575,6 +580,9 @@ ether_input(ifp, eh, m) int s, llcfound = 0; struct llc *l; struct arpcom *ac; +#if NPPPOE > 0 + struct ether_header *eh_tmp; +#endif if ((ifp->if_flags & IFF_UP) == 0) { m_freem(m); @@ -717,6 +725,37 @@ decapsulate: aarpinput((struct arpcom *)ifp, m); return; #endif +#if NPPPOE > 0 + case ETHERTYPE_PPPOEDISC: + case ETHERTYPE_PPPOE: + /* XXX we dont have this flag */ + /* + if (m->m_flags & M_PROMISC) { + m_freem(m); + return; + } + */ +#ifndef PPPOE_SERVER + if (m->m_flags & (M_MCAST | M_BCAST)) { + m_freem(m); + return; + } +#endif + M_PREPEND(m, sizeof(*eh), M_DONTWAIT); + if (m == NULL) + return; + + eh_tmp = mtod(m, struct ether_header *); + bcopy(eh, eh_tmp, sizeof(struct ether_header)); + + if (etype == ETHERTYPE_PPPOEDISC) + inq = &ppoediscinq; + else + inq = &ppoeinq; + + schednetisr(NETISR_PPPOE); + break; +#endif /* NPPPOE > 0 */ default: if (llcfound || etype > ETHERMTU) goto dropanyway; diff --git a/sys/net/if_pppoe.c b/sys/net/if_pppoe.c new file mode 100644 index 00000000000..3d4583eeea9 --- /dev/null +++ b/sys/net/if_pppoe.c @@ -0,0 +1,1434 @@ +/* $OpenBSD: if_pppoe.c,v 1.1 2004/11/28 23:39:45 canacar Exp $ */ +/* $NetBSD: if_pppoe.c,v 1.51 2003/11/28 08:56:48 keihan Exp $ */ + +/* + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Martin Husemann <martin@NetBSD.org>. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +/* +__KERNEL_RCSID(0, "$NetBSD: if_pppoe.c,v 1.51 2003/11/28 08:56:48 keihan Exp $"); +*/ + +#include "pppoe.h" +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/types.h> +#include <sys/timeout.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/proc.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <net/if_types.h> +#include <net/if_sppp.h> +#include <net/if_pppoe.h> +#include <net/netisr.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif + +#undef PPPOE_DEBUG /* XXX - remove this or make it an option */ + +#define PPPOEDEBUG(a) ((sc->sc_sppp.pp_if.if_flags & IFF_DEBUG) ? printf a : 0) + +struct pppoehdr { + u_int8_t vertype; + u_int8_t code; + u_int16_t session; + u_int16_t plen; +} __packed; + +struct pppoetag { + u_int16_t tag; + u_int16_t len; +} __packed; + +#define PPPOE_HEADERLEN sizeof(struct pppoehdr) +#define PPPOE_VERTYPE 0x11 /* VER=1, TYPE = 1 */ + +#define PPPOE_TAG_EOL 0x0000 /* end of list */ +#define PPPOE_TAG_SNAME 0x0101 /* service name */ +#define PPPOE_TAG_ACNAME 0x0102 /* access concentrator name */ +#define PPPOE_TAG_HUNIQUE 0x0103 /* host unique */ +#define PPPOE_TAG_ACCOOKIE 0x0104 /* AC cookie */ +#define PPPOE_TAG_VENDOR 0x0105 /* vendor specific */ +#define PPPOE_TAG_RELAYSID 0x0110 /* relay session id */ +#define PPPOE_TAG_SNAME_ERR 0x0201 /* service name error */ +#define PPPOE_TAG_ACSYS_ERR 0x0202 /* AC system error */ +#define PPPOE_TAG_GENERIC_ERR 0x0203 /* gerneric error */ + +#define PPPOE_CODE_PADI 0x09 /* Active Discovery Initiation */ +#define PPPOE_CODE_PADO 0x07 /* Active Discovery Offer */ +#define PPPOE_CODE_PADR 0x19 /* Active Discovery Request */ +#define PPPOE_CODE_PADS 0x65 /* Active Discovery Session confirmation */ +#define PPPOE_CODE_PADT 0xA7 /* Active Discovery Terminate */ + +/* two byte PPP protocol discriminator, then IP data */ +#define PPPOE_MAXMTU (ETHERMTU - PPPOE_HEADERLEN - 2) + +/* Add a 16 bit unsigned value to a buffer pointed to by PTR */ +#define PPPOE_ADD_16(PTR, VAL) \ + *(PTR)++ = (VAL) / 256; \ + *(PTR)++ = (VAL) % 256 + +/* Add a complete PPPoE header to the buffer pointed to by PTR */ +#define PPPOE_ADD_HEADER(PTR, CODE, SESS, LEN) \ + *(PTR)++ = PPPOE_VERTYPE; \ + *(PTR)++ = (CODE); \ + PPPOE_ADD_16(PTR, SESS); \ + PPPOE_ADD_16(PTR, LEN) + +#define PPPOE_DISC_TIMEOUT (hz*5) /* base for quick timeout calculation */ +#define PPPOE_SLOW_RETRY (hz*60) /* persistent retry interval */ +#define PPPOE_DISC_MAXPADI 4 /* retry PADI four times (quickly) */ +#define PPPOE_DISC_MAXPADR 2 /* retry PADR twice */ + +#ifdef PPPOE_SERVER +#define IFF_PASSIVE IFF_LINK0 /* wait passively for connection */ +#endif + +struct pppoe_softc { + struct sppp sc_sppp; /* contains a struct ifnet as first element */ + LIST_ENTRY(pppoe_softc) sc_list; + struct ifnet *sc_eth_if; /* ethernet interface we are using */ + + int sc_state; /* discovery phase or session connected */ + struct ether_addr sc_dest; /* hardware address of concentrator */ + u_int16_t sc_session; /* PPPoE session id */ + + char *sc_service_name; /* if != NULL: requested name of service */ + char *sc_concentrator_name; /* if != NULL: requested concentrator id */ + u_int8_t *sc_ac_cookie; /* content of AC cookie we must echo back */ + size_t sc_ac_cookie_len; /* length of cookie data */ +#ifdef PPPOE_SERVER + u_int8_t *sc_hunique; /* content of host unique we must echo back */ + size_t sc_hunique_len; /* length of host unique */ +#endif + struct timeout sc_timeout; /* timeout while not in session state */ + int sc_padi_retried; /* number of PADI retries already done */ + int sc_padr_retried; /* number of PADR retries already done */ + + struct timeval sc_session_time; /* time the session was established */ +}; + +/* incoming traffic will be queued here */ +struct ifqueue ppoediscinq = { NULL }; +struct ifqueue ppoeinq = { NULL }; + +extern int sppp_ioctl(struct ifnet *, unsigned long, void *); + +/* input routines */ +static void pppoe_disc_input(struct mbuf *); +static void pppoe_dispatch_disc_pkt(struct mbuf *, int); +static void pppoe_data_input(struct mbuf *); + +/* management routines */ +void pppoeattach(int); +static int pppoe_connect(struct pppoe_softc *); +static int pppoe_disconnect(struct pppoe_softc *); +static void pppoe_abort_connect(struct pppoe_softc *); +static int pppoe_ioctl(struct ifnet *, unsigned long, caddr_t); +static void pppoe_tls(struct sppp *); +static void pppoe_tlf(struct sppp *); +static void pppoe_start(struct ifnet *); + +/* internal timeout handling */ +static void pppoe_timeout(void *); + +/* sending actual protocol controll packets */ +static int pppoe_send_padi(struct pppoe_softc *); +static int pppoe_send_padr(struct pppoe_softc *); +#ifdef PPPOE_SERVER +static int pppoe_send_pado(struct pppoe_softc *); +static int pppoe_send_pads(struct pppoe_softc *); +#endif +static int pppoe_send_padt(struct ifnet *, u_int, const u_int8_t *); + +/* raw output */ +static int pppoe_output(struct pppoe_softc *, struct mbuf *); + +/* internal helper functions */ +static struct pppoe_softc *pppoe_find_softc_by_session(u_int, struct ifnet *); +static struct pppoe_softc *pppoe_find_softc_by_hunique(u_int8_t *, size_t, struct ifnet *); +static struct mbuf *pppoe_get_mbuf(size_t len); + +LIST_HEAD(pppoe_softc_head, pppoe_softc) pppoe_softc_list; + +/* interface cloning */ +int pppoe_clone_create(struct if_clone *, int); +int pppoe_clone_destroy(struct ifnet *); + +struct if_clone pppoe_cloner = + IF_CLONE_INITIALIZER("pppoe", pppoe_clone_create, pppoe_clone_destroy); + + +/* ARGSUSED */ +void +pppoeattach(int count) +{ + LIST_INIT(&pppoe_softc_list); + if_clone_attach(&pppoe_cloner); + + ppoediscinq.ifq_maxlen = IFQ_MAXLEN; + ppoeinq.ifq_maxlen = IFQ_MAXLEN; +} + +/* Create a new interface. */ +int +pppoe_clone_create(struct if_clone *ifc, int unit) +{ + struct pppoe_softc *sc; + int s; + + MALLOC(sc, struct pppoe_softc *, sizeof(*sc), M_DEVBUF, M_WAITOK); + if (sc == NULL) + return (ENOMEM); + bzero(sc, sizeof(struct pppoe_softc)); + + snprintf(sc->sc_sppp.pp_if.if_xname, + sizeof(sc->sc_sppp.pp_if.if_xname), + "pppoe%d", unit); + sc->sc_sppp.pp_if.if_softc = sc; + sc->sc_sppp.pp_if.if_mtu = PPPOE_MAXMTU; + sc->sc_sppp.pp_if.if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST; + sc->sc_sppp.pp_if.if_type = IFT_PPP; + sc->sc_sppp.pp_if.if_hdrlen = sizeof(struct ether_header) + PPPOE_HEADERLEN; + sc->sc_sppp.pp_flags |= PP_KEEPALIVE | /* use LCP keepalive */ + PP_NOFRAMING; /* no serial encapsulation */ + sc->sc_sppp.pp_framebytes = PPPOE_HEADERLEN; /* framing added to ppp packets */ + sc->sc_sppp.pp_if.if_ioctl = pppoe_ioctl; + sc->sc_sppp.pp_if.if_start = pppoe_start; + sc->sc_sppp.pp_tls = pppoe_tls; + sc->sc_sppp.pp_tlf = pppoe_tlf; + IFQ_SET_MAXLEN(&sc->sc_sppp.pp_if.if_snd, IFQ_MAXLEN); + IFQ_SET_READY(&sc->sc_sppp.pp_if.if_snd); + + /* changed to real address later */ + memcpy(&sc->sc_dest, etherbroadcastaddr, sizeof(sc->sc_dest)); + + /* init timer for interface watchdog */ + timeout_set(&sc->sc_timeout, pppoe_timeout, sc); + + if_attach(&sc->sc_sppp.pp_if); + if_alloc_sadl(&sc->sc_sppp.pp_if); + sppp_attach(&sc->sc_sppp.pp_if); +#if NBPFILTER > 0 + bpfattach(&sc->sc_sppp.pp_if.if_bpf, &sc->sc_sppp.pp_if, DLT_PPP_ETHER, 0); +#endif + + s = splimp(); + LIST_INSERT_HEAD(&pppoe_softc_list, sc, sc_list); + splx(s); + + return (0); +} + +/* Destroy a given interface. */ +int +pppoe_clone_destroy(struct ifnet *ifp) +{ + struct pppoe_softc *sc = ifp->if_softc; + int s; + + s = splimp(); + LIST_REMOVE(sc, sc_list); + splx(s); + +#if NBPFILTER > 0 + bpfdetach(ifp); +#endif + sppp_detach(&sc->sc_sppp.pp_if); + if_detach(ifp); + + if (sc->sc_concentrator_name) + free(sc->sc_concentrator_name, M_DEVBUF); + if (sc->sc_service_name) + free(sc->sc_service_name, M_DEVBUF); + if (sc->sc_ac_cookie) + free(sc->sc_ac_cookie, M_DEVBUF); + + free(sc, M_DEVBUF); + + return (0); +} + +/* + * Find the interface handling the specified session. + * Note: O(number of sessions open), this is a client-side only, mean + * and lean implementation, so number of open sessions typically should + * be 1. + */ +static struct pppoe_softc * +pppoe_find_softc_by_session(u_int session, struct ifnet *rcvif) +{ + struct pppoe_softc *sc; + + if (session == 0) + return (NULL); + + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (sc->sc_state == PPPOE_STATE_SESSION + && sc->sc_session == session) { + if (sc->sc_eth_if == rcvif) + return (sc); + else + return (NULL); + } + } + return (NULL); +} + +/* + * Check host unique token passed and return appropriate softc pointer, + * or NULL if token is bogus. + */ +static struct pppoe_softc * +pppoe_find_softc_by_hunique(u_int8_t *token, size_t len, struct ifnet *rcvif) +{ + struct pppoe_softc *sc, *t; + + if (LIST_EMPTY(&pppoe_softc_list)) + return (NULL); + + if (len != sizeof(sc)) + return (NULL); + memcpy(&t, token, len); + + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) + if (sc == t) break; + + if (sc == NULL) { + printf("pppoe: alien host unique tag, no session found\n"); + return (NULL); + } + + /* should be safe to access *sc now */ + if (sc->sc_state < PPPOE_STATE_PADI_SENT || sc->sc_state >= PPPOE_STATE_SESSION) { + printf("%s: host unique tag found, but it belongs to a connection in state %d\n", + sc->sc_sppp.pp_if.if_xname, sc->sc_state); + return (NULL); + } + if (sc->sc_eth_if != rcvif) { + printf("%s: wrong interface, not accepting host unique\n", + sc->sc_sppp.pp_if.if_xname); + return (NULL); + } + return (sc); +} + +/* Interface interrupt handler routine. */ +void +pppoeintr(void) +{ + struct pppoe_softc *sc; + struct mbuf *m; + + splassert(IPL_SOFTNET); + + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + for (;;) { + MBUFLOCK(IF_DEQUEUE(&ppoediscinq, m);); + if (m == NULL) break; + pppoe_disc_input(m); + } + + for (;;) { + MBUFLOCK(IF_DEQUEUE(&ppoeinq, m);); + if (m == NULL) break; + pppoe_data_input(m); + } + } +} + +/* Analyze and handle a single received packet while not in session state. */ +static void pppoe_dispatch_disc_pkt(struct mbuf *m, int off) +{ + struct pppoe_softc *sc; + struct pppoehdr *ph; + struct pppoetag *pt; + struct mbuf *n; + struct ether_header *eh; + const char *err_msg, *err_txt; + size_t ac_cookie_len; + int noff, err, errortag; + u_int16_t tag, len; + u_int16_t session, plen; + u_int8_t *ac_cookie; +#ifdef PPPOE_SERVER + u_int8_t *hunique; + size_t hunique_len; +#endif + + err_msg = err_txt = NULL; + errortag = 0; + + if (m->m_len < sizeof(*eh)) { + m = m_pullup(m, sizeof(*eh)); + if (m == NULL) + goto done; + } + eh = mtod(m, struct ether_header *); + off += sizeof(*eh); + + ac_cookie = NULL; + ac_cookie_len = 0; +#ifdef PPPOE_SERVER + hunique = NULL; + hunique_len = 0; +#endif + + session = 0; + if (m->m_pkthdr.len - off <= PPPOE_HEADERLEN) { + printf("pppoe: packet too short: %d\n", m->m_pkthdr.len); + goto done; + } + + n = m_pulldown(m, off, sizeof(*ph), &noff); + if (n == NULL) { + printf("pppoe: could not get PPPoE header\n"); + m = NULL; + goto done; + } + ph = (struct pppoehdr *)(mtod(n, caddr_t) + noff); + if (ph->vertype != PPPOE_VERTYPE) { + printf("pppoe: unknown version/type packet: 0x%x\n", + ph->vertype); + goto done; + } + + session = ntohs(ph->session); + plen = ntohs(ph->plen); + off += sizeof(*ph); + if (plen + off > m->m_pkthdr.len) { + printf("pppoe: packet content does not fit: data available = %d, packet size = %u\n", + m->m_pkthdr.len - off, plen); + goto done; + } + + /* ignore trailing garbage */ + m_adj(m, off + plen - m->m_pkthdr.len); + + tag = 0; + len = 0; + sc = NULL; + while (off + sizeof(*pt) <= m->m_pkthdr.len) { + n = m_pulldown(m, off, sizeof(*pt), &noff); + if (n == NULL) { + printf("%s: parse error\n", + sc ? sc->sc_sppp.pp_if.if_xname : "pppoe"); + m = NULL; + goto done; + } + pt = (struct pppoetag *)(mtod(n, caddr_t) + noff); + tag = ntohs(pt->tag); + len = ntohs(pt->len); + if (off + len > m->m_pkthdr.len) { + printf("pppoe: tag 0x%x len 0x%x is too long\n", + tag, len); + goto done; + } + switch (tag) { + case PPPOE_TAG_EOL: + goto breakbreak; + case PPPOE_TAG_SNAME: + break; /* ignored */ + case PPPOE_TAG_ACNAME: + break; /* ignored */ + case PPPOE_TAG_HUNIQUE: + if (sc != NULL) + break; + n = m_pulldown(m, off + sizeof(*pt), len, &noff); + if (n == NULL) { + m = NULL; + err_msg = "TAG HUNIQUE ERROR"; + break; + } +#ifdef PPPOE_SERVER + hunique = mtod(n, caddr_t) + noff; + hunique_len = len; +#endif + sc = pppoe_find_softc_by_hunique(mtod(n, caddr_t) + noff, + len, m->m_pkthdr.rcvif); + break; + case PPPOE_TAG_ACCOOKIE: + if (ac_cookie == NULL) { + n = m_pulldown(m, off + sizeof(*pt), len, + &noff); + if (n == NULL) { + err_msg = "TAG ACCOOKIE ERROR"; + m = NULL; + break; + } + ac_cookie = mtod(n, caddr_t) + noff; + ac_cookie_len = len; + } + break; + case PPPOE_TAG_SNAME_ERR: + err_msg = "SERVICE NAME ERROR"; + errortag = 1; + break; + case PPPOE_TAG_ACSYS_ERR: + err_msg = "AC SYSTEM ERROR"; + errortag = 1; + break; + case PPPOE_TAG_GENERIC_ERR: + err_msg = "GENERIC ERROR"; + errortag = 1; + break; + } + if (err_msg) { + err_txt = ""; + if (errortag && len) { + n = m_pulldown(m, off + sizeof(*pt), len, + &noff); + if (n) + err_txt = mtod(n, caddr_t) + noff; + } + printf("%s: %s: %*s\n", + sc ? sc->sc_sppp.pp_if.if_xname : "pppoe*", + err_msg, len, err_txt); + if (errortag) + goto done; + } + off += sizeof(*pt) + len; + } +breakbreak: + switch (ph->code) { + case PPPOE_CODE_PADI: +#ifdef PPPOE_SERVER + /* + * Got service name, concentrator name, and/or host unique. + * Ignore if we have no interfaces with IFF_PASSIVE|IFF_UP. + */ + if (LIST_EMPTY(&pppoe_softc_list)) + goto done; + + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (!(sc->sc_sppp.pp_if.if_flags & IFF_UP)) + continue; + if (!(sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) + continue; + if (sc->sc_state == PPPOE_STATE_INITIAL) + break; + } + if (sc == NULL) { + PPPOEDEBUG(("pppoe: free passive interface is not found\n")); + goto done; + } + if (hunique) { + if (sc->sc_hunique) + free(sc->sc_hunique, M_DEVBUF); + sc->sc_hunique = malloc(hunique_len, M_DEVBUF, + M_DONTWAIT); + if (sc->sc_hunique == NULL) + goto done; + sc->sc_hunique_len = hunique_len; + memcpy(sc->sc_hunique, hunique, hunique_len); + } + + memcpy(&sc->sc_dest, eh->ether_shost, sizeof(sc->sc_dest)); + sc->sc_state = PPPOE_STATE_PADO_SENT; + pppoe_send_pado(sc); + + break; +#endif /* PPPOE_SERVER */ + case PPPOE_CODE_PADR: +#ifdef PPPOE_SERVER + /* + * Get sc from ac_cookie if IFF_PASSIVE. + */ + if (ac_cookie == NULL) { + /* be quiet if there is not a single pppoe instance */ + printf("pppoe: received PADR but not includes ac_cookie\n"); + goto done; + } + + sc = pppoe_find_softc_by_hunique(ac_cookie, + ac_cookie_len, + m->m_pkthdr.rcvif); + if (sc == NULL) { + /* be quiet if there is not a single pppoe instance */ + if (!LIST_EMPTY(&pppoe_softc_list)) + printf("pppoe: received PADR but could not find request for it\n"); + goto done; + } + if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + printf("%s: received unexpected PADR\n", + sc->sc_sppp.pp_if.if_xname); + goto done; + } + if (hunique) { + if (sc->sc_hunique) + free(sc->sc_hunique, M_DEVBUF); + sc->sc_hunique = malloc(hunique_len, M_DEVBUF, + M_DONTWAIT); + if (sc->sc_hunique == NULL) + goto done; + sc->sc_hunique_len = hunique_len; + memcpy(sc->sc_hunique, hunique, hunique_len); + } + + pppoe_send_pads(sc); + sc->sc_state = PPPOE_STATE_SESSION; + sc->sc_sppp.pp_up(&sc->sc_sppp); + + break; +#else + /* ignore, we are no access concentrator */ + goto done; +#endif /* PPPOE_SERVER */ + case PPPOE_CODE_PADO: + if (sc == NULL) { + /* be quiet if there is not a single pppoe instance */ + if (!LIST_EMPTY(&pppoe_softc_list)) + printf("pppoe: received PADO but could not find request for it\n"); + goto done; + } + if (sc->sc_state != PPPOE_STATE_PADI_SENT) { + printf("%s: received unexpected PADO\n", + sc->sc_sppp.pp_if.if_xname); + goto done; + } + if (ac_cookie) { + if (sc->sc_ac_cookie) + free(sc->sc_ac_cookie, M_DEVBUF); + sc->sc_ac_cookie = malloc(ac_cookie_len, M_DEVBUF, + M_DONTWAIT); + if (sc->sc_ac_cookie == NULL) + goto done; + sc->sc_ac_cookie_len = ac_cookie_len; + memcpy(sc->sc_ac_cookie, ac_cookie, ac_cookie_len); + } + + memcpy(&sc->sc_dest, eh->ether_shost, sizeof(sc->sc_dest)); + timeout_del(&sc->sc_timeout); + sc->sc_padr_retried = 0; + sc->sc_state = PPPOE_STATE_PADR_SENT; + if ((err = pppoe_send_padr(sc)) != 0) { + PPPOEDEBUG(("%s: failed to send PADR, error=%d\n", + sc->sc_sppp.pp_if.if_xname, err)); + } + timeout_add(&sc->sc_timeout, + PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried)); + + break; + case PPPOE_CODE_PADS: + if (sc == NULL) + goto done; + + sc->sc_session = session; + timeout_del(&sc->sc_timeout); + PPPOEDEBUG(("%s: session 0x%x connected\n", + sc->sc_sppp.pp_if.if_xname, session)); + sc->sc_state = PPPOE_STATE_SESSION; + microtime(&sc->sc_session_time); + sc->sc_sppp.pp_up(&sc->sc_sppp); /* notify upper layers */ + + break; + case PPPOE_CODE_PADT: + if (sc == NULL) + goto done; + + /* stop timer (we might be about to transmit a PADT ourself) */ + timeout_del(&sc->sc_timeout); + PPPOEDEBUG(("%s: session 0x%x terminated, received PADT\n", + sc->sc_sppp.pp_if.if_xname, session)); + + /* clean up softc */ + sc->sc_state = PPPOE_STATE_INITIAL; + memcpy(&sc->sc_dest, etherbroadcastaddr, sizeof(sc->sc_dest)); + if (sc->sc_ac_cookie) { + free(sc->sc_ac_cookie, M_DEVBUF); + sc->sc_ac_cookie = NULL; + } + sc->sc_ac_cookie_len = 0; + sc->sc_session = 0; + sc->sc_session_time.tv_sec = 0; + sc->sc_session_time.tv_usec = 0; + sc->sc_sppp.pp_down(&sc->sc_sppp); /* signal upper layer */ + + break; + default: + printf("%s: unknown code (0x%04x) session = 0x%04x\n", + sc ? sc->sc_sppp.pp_if.if_xname : "pppoe", + ph->code, session); + break; + } + +done: + m_freem(m); + return; +} + +/* Input function for discovery packets. */ +static void +pppoe_disc_input(struct mbuf *m) +{ + /* avoid error messages if there is not a single pppoe instance */ + if (!LIST_EMPTY(&pppoe_softc_list)) { + KASSERT(m->m_flags & M_PKTHDR); + pppoe_dispatch_disc_pkt(m, 0); + } else + m_freem(m); +} + +/* Input function for data packets */ +static void +pppoe_data_input(struct mbuf *m) +{ + struct pppoe_softc *sc; + struct pppoehdr *ph; + u_int16_t session, plen; +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + u_int8_t shost[ETHER_ADDR_LEN]; +#endif + + KASSERT(m->m_flags & M_PKTHDR); + +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + memcpy(shost, mtod(m, struct ether_header*)->ether_shost, ETHER_ADDR_LEN); +#endif + m_adj(m, sizeof(struct ether_header)); + if (m->m_pkthdr.len <= PPPOE_HEADERLEN) { + printf("pppoe (data): dropping too short packet: %d bytes\n", + m->m_pkthdr.len); + goto drop; + } + if (m->m_len < sizeof(*ph)) { + m = m_pullup(m, sizeof(*ph)); + if (m == NULL) { + printf("pppoe (data): could not get PPPoE header\n"); + return; + } + } + ph = mtod(m, struct pppoehdr *); + if (ph->vertype != PPPOE_VERTYPE) { + printf("pppoe (data): unknown version/type packet: 0x%x\n", + ph->vertype); + goto drop; + } + if (ph->code != 0) + goto drop; + + session = ntohs(ph->session); + sc = pppoe_find_softc_by_session(session, m->m_pkthdr.rcvif); + if (sc == NULL) { +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + printf("pppoe (data): input for unknown session 0x%x, sending PADT\n", + session); + pppoe_send_padt(m->m_pkthdr.rcvif, session, shost); +#endif + goto drop; + } + + plen = ntohs(ph->plen); + +#if NBPFILTER > 0 + if(sc->sc_sppp.pp_if.if_bpf) + bpf_mtap(sc->sc_sppp.pp_if.if_bpf, m); +#endif + + m_adj(m, PPPOE_HEADERLEN); + +#ifdef PPPOE_DEBUG + { + struct mbuf *p; + + printf("%s: pkthdr.len=%d, pppoe.len=%d", + sc->sc_sppp.pp_if.if_xname, + m->m_pkthdr.len, plen); + p = m; + while (p) { + printf(" l=%d", p->m_len); + p = p->m_next; + } + printf("\n"); + } +#endif + + if (m->m_pkthdr.len < plen) + goto drop; + + /* fix incoming interface pointer (not the raw ethernet interface anymore) */ + m->m_pkthdr.rcvif = &sc->sc_sppp.pp_if; + + /* pass packet up and account for it */ + sc->sc_sppp.pp_if.if_ipackets++; + sppp_input(&sc->sc_sppp.pp_if, m); + return; + +drop: + m_freem(m); +} + +static int +pppoe_output(struct pppoe_softc *sc, struct mbuf *m) +{ + struct sockaddr dst; + struct ether_header *eh; + u_int16_t etype; + + if (sc->sc_eth_if == NULL) { + m_free(m); + return (EIO); + } + + if ((sc->sc_eth_if->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) + return (ENETDOWN); + + memset(&dst, 0, sizeof dst); + dst.sa_family = AF_UNSPEC; + eh = (struct ether_header*)&dst.sa_data; + etype = sc->sc_state == PPPOE_STATE_SESSION ? ETHERTYPE_PPPOE : ETHERTYPE_PPPOEDISC; + eh->ether_type = htons(etype); + memcpy(&eh->ether_dhost, &sc->sc_dest, sizeof sc->sc_dest); + + PPPOEDEBUG(("%s (%x) state=%d, session=0x%x output -> %s, len=%d\n", + sc->sc_sppp.pp_if.if_xname, etype, + sc->sc_state, sc->sc_session, + ether_sprintf((unsigned char *)&sc->sc_dest), m->m_pkthdr.len)); + + m->m_flags &= ~(M_BCAST|M_MCAST); + sc->sc_sppp.pp_if.if_opackets++; + return (sc->sc_eth_if->if_output(sc->sc_eth_if, m, &dst, NULL)); +} + +/* The ioctl routine. */ +static int +pppoe_ioctl(struct ifnet *ifp, unsigned long cmd, caddr_t data) +{ + struct proc *p = curproc; /* XXX */ + struct pppoe_softc *sc = (struct pppoe_softc *)ifp; + int error = 0; + + switch (cmd) { + case PPPOESETPARMS: + { + struct pppoediscparms *parms = (struct pppoediscparms *)data; + int len; + + if ((error = suser(p, p->p_acflag)) != 0) + return (error); + if (parms->eth_ifname[0] != '\0') { + sc->sc_eth_if = ifunit(parms->eth_ifname); + if (sc->sc_eth_if == NULL) + return (ENXIO); + } + + if (sc->sc_concentrator_name) + free(sc->sc_concentrator_name, M_DEVBUF); + sc->sc_concentrator_name = NULL; + + len = strlen(parms->ac_name); + if (len > 0 && len < sizeof(parms->ac_name)) { + char *p = malloc(len + 1, M_DEVBUF, M_WAITOK); + if (p == NULL) + return (ENOMEM); + strlcpy(p, parms->ac_name, len + 1); + sc->sc_concentrator_name = p; + } + + if (sc->sc_service_name) + free(sc->sc_service_name, M_DEVBUF); + sc->sc_service_name = NULL; + + len = strlen(parms->service_name); + if (len > 0 && len < sizeof(parms->service_name)) { + char *p = malloc(len + 1, M_DEVBUF, M_WAITOK); + if (p == NULL) + return (ENOMEM); + strlcpy(p, parms->service_name, len + 1); + sc->sc_service_name = p; + } + return (0); + } + break; + case PPPOEGETPARMS: + { + struct pppoediscparms *parms = (struct pppoediscparms *)data; + + if (sc->sc_eth_if) + strlcpy(parms->eth_ifname, sc->sc_eth_if->if_xname, + IFNAMSIZ); + else + parms->eth_ifname[0] = '\0'; + + if (sc->sc_concentrator_name) + strlcpy(parms->ac_name, sc->sc_concentrator_name, + sizeof(parms->ac_name)); + else + parms->ac_name[0] = '\0'; + + if (sc->sc_service_name) + strlcpy(parms->service_name, sc->sc_service_name, + sizeof(parms->service_name)); + else + parms->service_name[0] = '\0'; + + return (0); + } + break; + case PPPOEGETSESSION: + { + struct pppoeconnectionstate *state = + (struct pppoeconnectionstate *)data; + state->state = sc->sc_state; + state->session_id = sc->sc_session; + state->padi_retry_no = sc->sc_padi_retried; + state->padr_retry_no = sc->sc_padr_retried; + state->session_time.tv_sec = sc->sc_session_time.tv_sec; + state->session_time.tv_usec = sc->sc_session_time.tv_usec; + return (0); + } + break; + case SIOCSIFFLAGS: + { + struct ifreq *ifr = (struct ifreq *)data; + /* + * Prevent running re-establishment timers overriding + * administrators choice. + */ + if ((ifr->ifr_flags & IFF_UP) == 0 + && sc->sc_state >= PPPOE_STATE_PADI_SENT + && sc->sc_state < PPPOE_STATE_SESSION) { + timeout_del(&sc->sc_timeout); + sc->sc_state = PPPOE_STATE_INITIAL; + sc->sc_padi_retried = 0; + sc->sc_padr_retried = 0; + memcpy(&sc->sc_dest, etherbroadcastaddr, + sizeof(sc->sc_dest)); + } + return (sppp_ioctl(ifp, cmd, data)); + } + case SIOCSIFMTU: + { + struct ifreq *ifr = (struct ifreq *)data; + + if (ifr->ifr_mtu > PPPOE_MAXMTU) + return (EINVAL); + return (sppp_ioctl(ifp, cmd, data)); + } + default: + return (sppp_ioctl(ifp, cmd, data)); + } + return (0); +} + +/* + * Allocate a mbuf/cluster with space to store the given data length + * of payload, leaving space for prepending an ethernet header + * in front. + */ +static struct mbuf * +pppoe_get_mbuf(size_t len) +{ + struct mbuf *m; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return (NULL); + if (len + sizeof(struct ether_header) > MHLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + struct mbuf *n; + MFREE(m, n); + return (NULL); + } + } + m->m_data += sizeof(struct ether_header); + m->m_len = len; + m->m_pkthdr.len = len; + m->m_pkthdr.rcvif = NULL; + + return (m); +} + +/* Send PADI. */ +static int +pppoe_send_padi(struct pppoe_softc *sc) +{ + struct mbuf *m0; + int len, l1 = 0, l2 = 0; /* XXX: gcc */ + u_int8_t *p; + + if (sc->sc_state > PPPOE_STATE_PADI_SENT) + panic("pppoe_send_padi in state %d", sc->sc_state); + + /* calculate length of frame (excluding ethernet header + pppoe header) */ + len = 2 + 2 + 2 + 2 + sizeof(sc); /* service name tag is required, host unique is send too */ + if (sc->sc_service_name != NULL) { + l1 = strlen(sc->sc_service_name); + len += l1; + } + if (sc->sc_concentrator_name != NULL) { + l2 = strlen(sc->sc_concentrator_name); + len += 2 + 2 + l2; + } + + /* allocate a buffer */ + m0 = pppoe_get_mbuf(len + PPPOE_HEADERLEN); /* header len + payload len */ + if (m0 == NULL) + return (ENOBUFS); + + /* fill in pkt */ + p = mtod(m0, u_int8_t *); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADI, 0, len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + memcpy(p, sc->sc_service_name, l1); + p += l1; + } else { + PPPOE_ADD_16(p, 0); + } + if (sc->sc_concentrator_name != NULL) { + PPPOE_ADD_16(p, PPPOE_TAG_ACNAME); + PPPOE_ADD_16(p, l2); + memcpy(p, sc->sc_concentrator_name, l2); + p += l2; + } + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sizeof(sc)); + memcpy(p, &sc, sizeof(sc)); + +#ifdef PPPOE_DEBUG + p += sizeof sc; + if (p - mtod(m0, u_int8_t *) != len + PPPOE_HEADERLEN) + panic("pppoe_send_padi: garbled output len, should be %ld, is %ld", + (long)(len + PPPOE_HEADERLEN), (long)(p - mtod(m0, u_int8_t *))); +#endif + + /* send pkt */ + return (pppoe_output(sc, m0)); +} + +/* Watchdog function. */ +static void +pppoe_timeout(void *arg) +{ + struct pppoe_softc *sc = (struct pppoe_softc *)arg; + int x, retry_wait, err; + + PPPOEDEBUG(("%s: timeout\n", sc->sc_sppp.pp_if.if_xname)); + + switch (sc->sc_state) { + case PPPOE_STATE_PADI_SENT: + /* + * We have two basic ways of retrying: + * - Quick retry mode: try a few times in short sequence + * - Slow retry mode: we already had a connection successfully + * established and will try infinitely (without user + * intervention) + * We only enter slow retry mode if IFF_LINK1 (aka autodial) + * is not set. + */ + + /* initialize for quick retry mode */ + retry_wait = PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried); + + x = splnet(); + sc->sc_padi_retried++; + if (sc->sc_padi_retried >= PPPOE_DISC_MAXPADI) { + if ((sc->sc_sppp.pp_if.if_flags & IFF_LINK1) == 0) { + /* slow retry mode */ + retry_wait = PPPOE_SLOW_RETRY; + } else { + pppoe_abort_connect(sc); + splx(x); + return; + } + } + if ((err = pppoe_send_padi(sc)) != 0) { + sc->sc_padi_retried--; + PPPOEDEBUG(("%s: failed to transmit PADI, error=%d\n", + sc->sc_sppp.pp_if.if_xname, err)); + } + timeout_add(&sc->sc_timeout, retry_wait); + splx(x); + + break; + case PPPOE_STATE_PADR_SENT: + x = splnet(); + sc->sc_padr_retried++; + if (sc->sc_padr_retried >= PPPOE_DISC_MAXPADR) { + memcpy(&sc->sc_dest, etherbroadcastaddr, + sizeof(sc->sc_dest)); + sc->sc_state = PPPOE_STATE_PADI_SENT; + sc->sc_padr_retried = 0; + if ((err = pppoe_send_padi(sc)) != 0) { + PPPOEDEBUG(("%s: failed to send PADI, error=%d\n", + sc->sc_sppp.pp_if.if_xname, err)); + } + timeout_add(&sc->sc_timeout, + PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried)); + splx(x); + return; + } + if ((err = pppoe_send_padr(sc)) != 0) { + sc->sc_padr_retried--; + PPPOEDEBUG(("%s: failed to send PADR, error=%d\n", + sc->sc_sppp.pp_if.if_xname, err)); + } + timeout_add(&sc->sc_timeout, + PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried)); + splx(x); + + break; + case PPPOE_STATE_CLOSING: + pppoe_disconnect(sc); + break; + default: + return; /* all done, work in peace */ + } +} + +/* Start a connection (i.e. initiate discovery phase). */ +static int +pppoe_connect(struct pppoe_softc *sc) +{ + int x, err; + + if (sc->sc_state != PPPOE_STATE_INITIAL) + return (EBUSY); + +#ifdef PPPOE_SERVER + /* wait for PADI if IFF_PASSIVE */ + if ((sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) + return (0); +#endif + x = splnet(); + + /* save state, in case we fail to send PADI */ + sc->sc_state = PPPOE_STATE_PADI_SENT; + sc->sc_padr_retried = 0; + err = pppoe_send_padi(sc); + if (err != 0) + PPPOEDEBUG(("%s: failed to send PADI, error=%d\n", + sc->sc_sppp.pp_if.if_xname, err)); + + timeout_add(&sc->sc_timeout, PPPOE_DISC_TIMEOUT); + splx(x); + + return (err); +} + +/* disconnect */ +static int +pppoe_disconnect(struct pppoe_softc *sc) +{ + int err, x; + + x = splnet(); + + if (sc->sc_state < PPPOE_STATE_SESSION) + err = EBUSY; + else { + PPPOEDEBUG(("%s: disconnecting\n", + sc->sc_sppp.pp_if.if_xname)); + err = pppoe_send_padt(sc->sc_eth_if, sc->sc_session, (const u_int8_t *)&sc->sc_dest); + } + + /* cleanup softc */ + sc->sc_state = PPPOE_STATE_INITIAL; + memcpy(&sc->sc_dest, etherbroadcastaddr, sizeof(sc->sc_dest)); + if (sc->sc_ac_cookie) { + free(sc->sc_ac_cookie, M_DEVBUF); + sc->sc_ac_cookie = NULL; + } + sc->sc_ac_cookie_len = 0; +#ifdef PPPOE_SERVER + if (sc->sc_hunique) { + free(sc->sc_hunique, M_DEVBUF); + sc->sc_hunique = NULL; + } + sc->sc_hunique_len = 0; +#endif + sc->sc_session = 0; + + /* notify upper layer */ + sc->sc_sppp.pp_down(&sc->sc_sppp); + + splx(x); + + return (err); +} + +/* Connection attempt aborted. */ +static void +pppoe_abort_connect(struct pppoe_softc *sc) +{ + printf("%s: could not establish connection\n", + sc->sc_sppp.pp_if.if_xname); + sc->sc_state = PPPOE_STATE_CLOSING; + + /* notify upper layer */ + sc->sc_sppp.pp_down(&sc->sc_sppp); + + /* clear connection state */ + memcpy(&sc->sc_dest, etherbroadcastaddr, sizeof(sc->sc_dest)); + sc->sc_state = PPPOE_STATE_INITIAL; +} + +/* Send a PADR packet */ +static int +pppoe_send_padr(struct pppoe_softc *sc) +{ + struct mbuf *m0; + u_int8_t *p; + size_t len, l1 = 0; /* XXX: gcc */ + + if (sc->sc_state != PPPOE_STATE_PADR_SENT) + return (EIO); + + len = 2 + 2 + 2 + 2 + sizeof(sc); /* service name, host unique */ + if (sc->sc_service_name != NULL) { /* service name tag maybe empty */ + l1 = strlen(sc->sc_service_name); + len += l1; + } + if (sc->sc_ac_cookie_len > 0) + len += 2 + 2 + sc->sc_ac_cookie_len; /* AC cookie */ + + m0 = pppoe_get_mbuf(len + PPPOE_HEADERLEN); + if (m0 == NULL) + return (ENOBUFS); + + p = mtod(m0, u_int8_t *); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADR, 0, len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); + + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + memcpy(p, sc->sc_service_name, l1); + p += l1; + } else { + PPPOE_ADD_16(p, 0); + } + if (sc->sc_ac_cookie_len > 0) { + PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE); + PPPOE_ADD_16(p, sc->sc_ac_cookie_len); + memcpy(p, sc->sc_ac_cookie, sc->sc_ac_cookie_len); + p += sc->sc_ac_cookie_len; + } + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sizeof(sc)); + memcpy(p, &sc, sizeof(sc)); + +#ifdef PPPOE_DEBUG + p += sizeof(sc); + if (p - mtod(m0, u_int8_t *) != len + PPPOE_HEADERLEN) + panic("pppoe_send_padr: garbled output len, should be %ld, is %ld", + (long)(len + PPPOE_HEADERLEN), (long)(p - mtod(m0, u_int8_t *))); +#endif + + return (pppoe_output(sc, m0)); +} + +/* Send a PADT packet. */ +static int +pppoe_send_padt(struct ifnet *outgoing_if, u_int session, const u_int8_t *dest) +{ + struct ether_header *eh; + struct sockaddr dst; + struct mbuf *m0; + u_int8_t *p; + + m0 = pppoe_get_mbuf(PPPOE_HEADERLEN); + if (m0 == NULL) + return (ENOBUFS); + + p = mtod(m0, u_int8_t *); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADT, session, 0); + + memset(&dst, 0, sizeof(dst)); + dst.sa_family = AF_UNSPEC; + eh = (struct ether_header *)&dst.sa_data; + eh->ether_type = htons(ETHERTYPE_PPPOEDISC); + memcpy(&eh->ether_dhost, dest, ETHER_ADDR_LEN); + + m0->m_flags &= ~(M_BCAST|M_MCAST); + return (outgoing_if->if_output(outgoing_if, m0, &dst, NULL)); +} + +#ifdef PPPOE_SERVER +/* Send a PADO packet. */ +static int +pppoe_send_pado(struct pppoe_softc *sc) +{ + struct mbuf *m0; + size_t len; + u_int8_t *p; + + if (sc->sc_state != PPPOE_STATE_PADO_SENT) + return (EIO); + + /* calc length */ + len = 0; + /* include ac_cookie */ + len += 2 + 2 + sizeof(sc); + /* include hunique */ + len += 2 + 2 + sc->sc_hunique_len; + + m0 = pppoe_get_mbuf(len + PPPOE_HEADERLEN); + if (m0 == NULL) + return (ENOBUFS); + + p = mtod(m0, u_int8_t *); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADO, 0, len); + PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE); + PPPOE_ADD_16(p, sizeof(sc)); + memcpy(p, &sc, sizeof(sc)); + p += sizeof(sc); + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sc->sc_hunique_len); + memcpy(p, sc->sc_hunique, sc->sc_hunique_len); + + return (pppoe_output(sc, m0)); +} + +/* Send a PADS packet. */ +static int +pppoe_send_pads(struct pppoe_softc *sc) +{ + struct mbuf *m0; + size_t len, l1; + u_int8_t *p; + + if (sc->sc_state != PPPOE_STATE_PADO_SENT) + return (EIO); + + sc->sc_session = mono_time.tv_sec % 0xff + 1; + + /* calc length */ + len = 0; + /* include hunique */ + len += 2 + 2 + 2 + 2 + sc->sc_hunique_len; /* service name, host unique */ + if (sc->sc_service_name != NULL) { /* service name tag maybe empty */ + l1 = strlen(sc->sc_service_name); + len += l1; + } + + m0 = pppoe_get_mbuf(len + PPPOE_HEADERLEN); + if (m0 == NULL) + return (ENOBUFS); + + p = mtod(m0, u_int8_t *); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADS, sc->sc_session, len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + memcpy(p, sc->sc_service_name, l1); + p += l1; + } else { + PPPOE_ADD_16(p, 0); + } + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sc->sc_hunique_len); + memcpy(p, sc->sc_hunique, sc->sc_hunique_len); + + return (pppoe_output(sc, m0)); +} +#endif + +/* this-layer-start function */ +static void +pppoe_tls(struct sppp *sp) +{ + struct pppoe_softc *sc = (void *)sp; + + if (sc->sc_state != PPPOE_STATE_INITIAL) + return; + pppoe_connect(sc); +} + +/* this-layer-finish function */ +static void +pppoe_tlf(struct sppp *sp) +{ + struct pppoe_softc *sc = (void *)sp; + + if (sc->sc_state < PPPOE_STATE_SESSION) + return; + /* + * Do not call pppoe_disconnect here, the upper layer state + * machine gets confused by this. We must return from this + * function and defer disconnecting to the timeout handler. + */ + sc->sc_state = PPPOE_STATE_CLOSING; + timeout_add(&sc->sc_timeout, hz / 50); +} + +static void +pppoe_start(struct ifnet *ifp) +{ + struct pppoe_softc *sc = (void *)ifp; + struct mbuf *m; + size_t len; + u_int8_t *p; + + if (sppp_isempty(ifp)) + return; + + /* are we ready to process data yet? */ + if (sc->sc_state < PPPOE_STATE_SESSION) { + sppp_flush(&sc->sc_sppp.pp_if); + return; + } + + while ((m = sppp_dequeue(ifp)) != NULL) { + len = m->m_pkthdr.len; + M_PREPEND(m, PPPOE_HEADERLEN, M_DONTWAIT); + if (m == NULL) { + ifp->if_oerrors++; + continue; + } + p = mtod(m, u_int8_t *); + PPPOE_ADD_HEADER(p, 0, sc->sc_session, len); + +#if NBPFILTER > 0 + if(sc->sc_sppp.pp_if.if_bpf) + bpf_mtap(sc->sc_sppp.pp_if.if_bpf, m); +#endif + + pppoe_output(sc, m); + } +} diff --git a/sys/net/if_pppoe.h b/sys/net/if_pppoe.h new file mode 100644 index 00000000000..c96f7f179e7 --- /dev/null +++ b/sys/net/if_pppoe.h @@ -0,0 +1,81 @@ +/* $NetBSD: if_pppoe.h,v 1.5 2003/11/28 08:56:48 keihan Exp $ */ + +/* + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Martin Husemann <martin@NetBSD.org>. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _NET_IF_PPPOE_H_ +#define _NET_IF_PPPOE_H_ + +#define PPPOE_NAMELEN 512 /* should be enough */ +struct pppoediscparms { + char ifname[IFNAMSIZ]; /* pppoe interface name */ + char eth_ifname[IFNAMSIZ]; /* external ethernet interface name */ + char ac_name[PPPOE_NAMELEN]; /* access concentrator name */ + char service_name[PPPOE_NAMELEN]; /* service name */ +}; + +#define PPPOESETPARMS _IOW('i', 110, struct pppoediscparms) +#define PPPOEGETPARMS _IOWR('i', 111, struct pppoediscparms) + +#define PPPOE_STATE_INITIAL 0 +#define PPPOE_STATE_PADI_SENT 1 +#define PPPOE_STATE_PADR_SENT 2 +#define PPPOE_STATE_SESSION 3 +#define PPPOE_STATE_CLOSING 4 +/* passive */ +#define PPPOE_STATE_PADO_SENT 1 + +struct pppoeconnectionstate { + char ifname[IFNAMSIZ]; /* pppoe interface name */ + u_int state; /* one of the PPPOE_STATE_ states above */ + u_int session_id; /* if state == PPPOE_STATE_SESSION */ + u_int padi_retry_no; /* number of retries already sent */ + u_int padr_retry_no; + + struct timeval session_time; /* time the session was established */ +}; + +#define PPPOEGETSESSION _IOWR('i', 112, struct pppoeconnectionstate) + +#ifdef _KERNEL + +extern struct ifqueue ppoediscinq; +extern struct ifqueue ppoeinq; + +void pppoeintr(void); + +#endif /* _KERNEL */ +#endif _NET_IF_PPPOE_H_ diff --git a/sys/net/if_sppp.h b/sys/net/if_sppp.h index dbe38fd2f44..171012a7d9b 100644 --- a/sys/net/if_sppp.h +++ b/sys/net/if_sppp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_sppp.h,v 1.6 2002/09/26 20:43:54 chris Exp $ */ +/* $OpenBSD: if_sppp.h,v 1.7 2004/11/28 23:39:45 canacar Exp $ */ /* $NetBSD: if_sppp.h,v 1.2.2.1 1999/04/04 06:57:39 explorer Exp $ */ /* @@ -104,7 +104,8 @@ struct sppp { struct ifqueue pp_cpq; /* PPP control protocol queue */ struct sppp *pp_next; /* next interface in keepalive list */ u_int pp_flags; /* use Cisco protocol instead of PPP */ - u_short pp_alivecnt; /* keepalive packets counter */ + u_int pp_framebytes; /* number of bytes added by hardware framing */ + u_short pp_alivecnt; /* keepalive packets counter */ u_short pp_loopcnt; /* loopback detection counter */ u_long pp_seq; /* local sequence number */ u_long pp_rseq; /* remote sequence number */ @@ -153,7 +154,9 @@ struct sppp { /* 0x04 was PP_TIMO */ #define PP_CALLIN 0x08 /* we are being called */ #define PP_NEEDAUTH 0x10 /* remote requested authentication */ - +#define PP_NOFRAMING 0x20 /* do not add/expect encapsulation + around PPP frames (i.e. the serial + HDLC like encapsulation, RFC1662) */ #define PP_MTU 1500 /* default/minimal MRU */ #define PP_MAX_MRU 2048 /* maximal MRU we want to negotiate */ diff --git a/sys/net/if_spppsubr.c b/sys/net/if_spppsubr.c index 44e9b912818..de6732d79c9 100644 --- a/sys/net/if_spppsubr.c +++ b/sys/net/if_spppsubr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_spppsubr.c,v 1.24 2004/07/16 15:01:09 henning Exp $ */ +/* $OpenBSD: if_spppsubr.c,v 1.25 2004/11/28 23:39:45 canacar Exp $ */ /* * Synchronous PPP/Cisco link level subroutines. * Keepalive protocol implemented in both Cisco and PPP modes. @@ -456,15 +456,15 @@ spppattach(struct ifnet *ifp) void sppp_input(struct ifnet *ifp, struct mbuf *m) { - struct ppp_header *h; + struct ppp_header *h, ht; struct ifqueue *inq = 0; int s; struct sppp *sp = (struct sppp *)ifp; int debug = ifp->if_flags & IFF_DEBUG; if (ifp->if_flags & IFF_UP) - /* Count received bytes, add FCS and one flag */ - ifp->if_ibytes += m->m_pkthdr.len + 3; + /* Count received bytes, add hardware framing */ + ifp->if_ibytes += m->m_pkthdr.len + sp->pp_framebytes; if (m->m_pkthdr.len <= PPP_HEADER_LEN) { /* Too small packet, drop it. */ @@ -479,9 +479,17 @@ sppp_input(struct ifnet *ifp, struct mbuf *m) return; } - /* Get PPP header. */ - h = mtod (m, struct ppp_header*); - m_adj (m, PPP_HEADER_LEN); + if (sp->pp_flags & PP_NOFRAMING) { + memcpy(&ht.protocol, mtod(m, void *), 2); + m_adj(m, 2); + ht.control = PPP_UI; + ht.address = PPP_ALLSTATIONS; + h = &ht; + } else { + /* Get PPP header. */ + h = mtod (m, struct ppp_header*); + m_adj (m, PPP_HEADER_LEN); + } switch (h->address) { case PPP_ALLSTATIONS: @@ -639,6 +647,7 @@ sppp_output(struct ifnet *ifp, struct mbuf *m, struct ppp_header *h; struct ifqueue *ifq = NULL; int s, len, rv = 0; + u_int16_t protocol; s = splimp(); @@ -666,10 +675,18 @@ sppp_output(struct ifnet *ifp, struct mbuf *m, * in front of the queue. */ if (dst->sa_family == AF_INET) { - /* XXX Check mbuf length here? */ - struct ip *ip = mtod (m, struct ip*); - struct tcphdr *tcp = (struct tcphdr*) ((long*)ip + ip->ip_hl); - + struct ip *ip = NULL; + struct tcphdr *th = NULL; + + if (m->m_len >= sizeof(struct ip)) { + ip = mtod(m, struct ip *); + if (ip->ip_p == IPPROTO_TCP && + m->m_len >= sizeof(struct ip) + (ip->ip_hl << 2) + + sizeof(struct tcphdr)) { + th = (struct tcphdr *) + ((caddr_t)ip + (ip->ip_hl << 2)); + } + } /* * When using dynamic local IP address assignment by using * 0.0.0.0 as a local address, the first TCP session will @@ -681,32 +698,32 @@ sppp_output(struct ifnet *ifp, struct mbuf *m, * - we flag TCP packets with src ip 0 as an error */ - if(ip->ip_src.s_addr == INADDR_ANY) /* -hm */ - { + if(ip && ip->ip_src.s_addr == INADDR_ANY) { + u_int8_t proto = ip->ip_p; + m_freem(m); splx(s); - if(ip->ip_p == IPPROTO_TCP) + if(proto == IPPROTO_TCP) return (EADDRNOTAVAIL); else return (0); } - - if (! IF_QFULL (&sp->pp_fastq) && - ((ip->ip_tos & IPTOS_LOWDELAY) || - (ip->ip_p == IPPROTO_TCP && - m->m_len >= sizeof (struct ip) + sizeof (struct tcphdr) && - (INTERACTIVE (ntohs (tcp->th_sport)) || - INTERACTIVE (ntohs (tcp->th_dport)))))) + if (!IF_QFULL(&sp->pp_fastq) && + ((ip && (ip->ip_tos & IPTOS_LOWDELAY)) || + (th && (INTERACTIVE(ntohs(th->th_sport)) || + INTERACTIVE(ntohs(th->th_dport)))))) ifq = &sp->pp_fastq; } #endif + if (sp->pp_flags & PP_NOFRAMING) + goto skip_header; /* * Prepend general data packet PPP header. For now, IP only. */ M_PREPEND (m, PPP_HEADER_LEN, M_DONTWAIT); - if (! m) { + if (!m) { if (ifp->if_flags & IFF_DEBUG) log(LOG_DEBUG, SPP_FMT "no memory for transmit header\n", SPP_ARGS(ifp)); @@ -727,11 +744,12 @@ sppp_output(struct ifnet *ifp, struct mbuf *m, h->control = PPP_UI; /* Unnumbered Info */ } + skip_header: switch (dst->sa_family) { #ifdef INET case AF_INET: /* Internet Protocol */ if (sp->pp_flags & PP_CISCO) - h->protocol = htons (ETHERTYPE_IP); + protocol = htons (ETHERTYPE_IP); else { /* * Don't choke with an ENETDOWN early. It's @@ -742,7 +760,7 @@ sppp_output(struct ifnet *ifp, struct mbuf *m, * not ready to carry IP packets, and return * ENETDOWN, as opposed to ENOBUFS. */ - h->protocol = htons(PPP_IP); + protocol = htons(PPP_IP); if (sp->state[IDX_IPCP] != STATE_OPENED) rv = ENETDOWN; } @@ -750,23 +768,38 @@ sppp_output(struct ifnet *ifp, struct mbuf *m, #endif #ifdef NS case AF_NS: /* Xerox NS Protocol */ - h->protocol = htons ((sp->pp_flags & PP_CISCO) ? + protocol = htons ((sp->pp_flags & PP_CISCO) ? ETHERTYPE_NS : PPP_XNS); break; #endif #ifdef IPX case AF_IPX: /* Novell IPX Protocol */ - h->protocol = htons ((sp->pp_flags & PP_CISCO) ? + protocol = htons ((sp->pp_flags & PP_CISCO) ? ETHERTYPE_IPX : PPP_IPX); break; #endif default: - m_freem (m); + m_freem(m); ++ifp->if_oerrors; - splx (s); + splx(s); return (EAFNOSUPPORT); } + if (sp->pp_flags & PP_NOFRAMING) { + M_PREPEND(m, 2, M_DONTWAIT); + if (m == NULL) { + if (ifp->if_flags & IFF_DEBUG) + log(LOG_DEBUG, SPP_FMT + "no memory for transmit header\n", + SPP_ARGS(ifp)); + ++ifp->if_oerrors; + splx(s); + return (ENOBUFS); + } + *mtod(m, u_int16_t *) = protocol; + } else + h->protocol = protocol; + /* * Queue message on interface, and start output if interface * not yet active. @@ -782,17 +815,18 @@ sppp_output(struct ifnet *ifp, struct mbuf *m, m_freem (m); if (rv == 0) rv = ENOBUFS; - } - IF_ENQUEUE (ifq, m); + } else + IF_ENQUEUE (ifq, m); } else IFQ_ENQUEUE(&ifp->if_snd, m, NULL, rv); + if (rv != 0) { ++ifp->if_oerrors; splx (s); return (rv); } - if (! (ifp->if_flags & IFF_OACTIVE)) + if (!(ifp->if_flags & IFF_OACTIVE)) (*ifp->if_start) (ifp); /* @@ -800,7 +834,7 @@ sppp_output(struct ifnet *ifp, struct mbuf *m, * The packet length includes header, FCS and 1 flag, * according to RFC 1333. */ - ifp->if_obytes += len + 3; + ifp->if_obytes += len + sp->pp_framebytes; splx (s); return (0); } @@ -811,13 +845,14 @@ sppp_attach(struct ifnet *ifp) struct sppp *sp = (struct sppp*) ifp; /* Initialize keepalive handler. */ - if (! spppq) + if (! spppq) { #if defined (__FreeBSD__) keepalive_ch = timeout(sppp_keepalive, 0, hz * 10); #elif defined(__OpenBSD__) timeout_set(&keepalive_ch, sppp_keepalive, NULL); timeout_add(&keepalive_ch, hz * 10); #endif + } /* Insert new entry into the keepalive list. */ sp->pp_next = spppq; @@ -1163,7 +1198,7 @@ sppp_cisco_send(struct sppp *sp, int type, long par1, long par2) if (! (ifp->if_flags & IFF_OACTIVE)) (*ifp->if_start) (ifp); if (m != NULL) - ifp->if_obytes += m->m_pkthdr.len + 3; + ifp->if_obytes += m->m_pkthdr.len + sp->pp_framebytes; } /* @@ -1181,21 +1216,28 @@ sppp_cp_send(struct sppp *sp, u_short proto, u_char type, struct ppp_header *h; struct lcp_header *lh; struct mbuf *m; + size_t pkthdrlen; + + pkthdrlen = (sp->pp_flags & PP_NOFRAMING) ? 2 : PPP_HEADER_LEN; - if (len > MHLEN - PPP_HEADER_LEN - LCP_HEADER_LEN) - len = MHLEN - PPP_HEADER_LEN - LCP_HEADER_LEN; + if (len > MHLEN - pkthdrlen - LCP_HEADER_LEN) + len = MHLEN - pkthdrlen - LCP_HEADER_LEN; MGETHDR (m, M_DONTWAIT, MT_DATA); if (! m) return; - m->m_pkthdr.len = m->m_len = PPP_HEADER_LEN + LCP_HEADER_LEN + len; + m->m_pkthdr.len = m->m_len = pkthdrlen + LCP_HEADER_LEN + len; m->m_pkthdr.rcvif = 0; - h = mtod (m, struct ppp_header*); - h->address = PPP_ALLSTATIONS; /* broadcast address */ - h->control = PPP_UI; /* Unnumbered Info */ - h->protocol = htons (proto); /* Link Control Protocol */ - - lh = (struct lcp_header*) (h + 1); + if (sp->pp_flags & PP_NOFRAMING) { + *mtod(m, u_int16_t *) = htons(proto); + lh = (struct lcp_header *)(mtod(m, u_int8_t *) + 2); + } else { + h = mtod (m, struct ppp_header*); + h->address = PPP_ALLSTATIONS; /* broadcast address */ + h->control = PPP_UI; /* Unnumbered Info */ + h->protocol = htons (proto); /* Link Control Protocol */ + lh = (struct lcp_header*) (h + 1); + } lh->type = type; lh->ident = ident; lh->len = htons (LCP_HEADER_LEN + len); @@ -1220,10 +1262,10 @@ sppp_cp_send(struct sppp *sp, u_short proto, u_char type, m = NULL; } else IF_ENQUEUE (&sp->pp_cpq, m); - if (! (ifp->if_flags & IFF_OACTIVE)) + if (!(ifp->if_flags & IFF_OACTIVE)) (*ifp->if_start) (ifp); if (m != NULL) - ifp->if_obytes += m->m_pkthdr.len + 3; + ifp->if_obytes += m->m_pkthdr.len + sp->pp_framebytes; } /* @@ -2241,11 +2283,7 @@ sppp_lcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len) if (magic == ~sp->lcp.magic) { if (debug) addlog("magic glitch "); -#if defined (__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) - sp->lcp.magic = random(); -#else - sp->lcp.magic = time.tv_sec + time.tv_usec; -#endif + sp->lcp.magic = arc4random(); } else { sp->lcp.magic = magic; if (debug) @@ -3708,7 +3746,7 @@ sppp_auth_send(const struct cp *cp, struct sppp *sp, u_char type, u_char id, struct mbuf *m; u_char *p; int len; - size_t mlen; + size_t mlen, pkthdrlen; const char *msg; va_list ap; @@ -3717,12 +3755,19 @@ sppp_auth_send(const struct cp *cp, struct sppp *sp, u_char type, u_char id, return; m->m_pkthdr.rcvif = 0; - h = mtod (m, struct ppp_header*); - h->address = PPP_ALLSTATIONS; /* broadcast address */ - h->control = PPP_UI; /* Unnumbered Info */ - h->protocol = htons(cp->proto); + if (sp->pp_flags & PP_NOFRAMING) { + *mtod(m, u_int16_t *) = htons(cp->proto); + pkthdrlen = 2; + lh = (struct lcp_header *)(mtod(m, u_int8_t *) + 2); + } else { + h = mtod (m, struct ppp_header*); + h->address = PPP_ALLSTATIONS; /* broadcast address */ + h->control = PPP_UI; /* Unnumbered Info */ + h->protocol = htons(cp->proto); + pkthdrlen = PPP_HEADER_LEN; + lh = (struct lcp_header*)(h + 1); + } - lh = (struct lcp_header*)(h + 1); lh->type = type; lh->ident = id; p = (u_char*) (lh+1); @@ -3733,7 +3778,7 @@ sppp_auth_send(const struct cp *cp, struct sppp *sp, u_char type, u_char id, while ((mlen = va_arg(ap, size_t)) != 0) { msg = va_arg(ap, const char *); len += mlen; - if (len > MHLEN - PPP_HEADER_LEN - LCP_HEADER_LEN) { + if (len > MHLEN - pkthdrlen - LCP_HEADER_LEN) { va_end(ap); m_freem(m); return; @@ -3744,7 +3789,7 @@ sppp_auth_send(const struct cp *cp, struct sppp *sp, u_char type, u_char id, } va_end(ap); - m->m_pkthdr.len = m->m_len = PPP_HEADER_LEN + LCP_HEADER_LEN + len; + m->m_pkthdr.len = m->m_len = pkthdrlen + LCP_HEADER_LEN + len; lh->len = htons (LCP_HEADER_LEN + len); if (debug) { @@ -3767,7 +3812,7 @@ sppp_auth_send(const struct cp *cp, struct sppp *sp, u_char type, u_char id, if (! (ifp->if_flags & IFF_OACTIVE)) (*ifp->if_start) (ifp); if (m != NULL) - ifp->if_obytes += m->m_pkthdr.len + 3; + ifp->if_obytes += m->m_pkthdr.len + sp->pp_framebytes; } /* diff --git a/sys/net/netisr.h b/sys/net/netisr.h index 383f90069b6..dd73562477d 100644 --- a/sys/net/netisr.h +++ b/sys/net/netisr.h @@ -1,4 +1,4 @@ -/* $OpenBSD: netisr.h,v 1.19 2003/06/02 23:28:12 millert Exp $ */ +/* $OpenBSD: netisr.h,v 1.20 2004/11/28 23:39:45 canacar Exp $ */ /* $NetBSD: netisr.h,v 1.12 1995/08/12 23:59:24 mycroft Exp $ */ /* @@ -65,6 +65,7 @@ #define NETISR_NATM 27 /* same as AF_ATM */ #define NETISR_PPP 28 /* for PPP processing */ #define NETISR_BRIDGE 29 /* for bridge processing */ +#define NETISR_PPPOE 30 /* for pppoe processing */ #ifndef _LOCORE #ifdef _KERNEL @@ -81,6 +82,7 @@ void natmintr(void); void pppintr(void); void ccittintr(void); void bridgeintr(void); +void pppoeintr(void); #include <dev/rndvar.h> #define schednetisr(anisr) \ diff --git a/sys/net/netisr_dispatch.h b/sys/net/netisr_dispatch.h index 6e34c89180d..3be10f991fe 100644 --- a/sys/net/netisr_dispatch.h +++ b/sys/net/netisr_dispatch.h @@ -1,4 +1,4 @@ -/* $OpenBSD: netisr_dispatch.h,v 1.6 2004/10/17 20:17:30 grange Exp $ */ +/* $OpenBSD: netisr_dispatch.h,v 1.7 2004/11/28 23:39:45 canacar Exp $ */ /* $NetBSD: netisr_dispatch.h,v 1.2 2000/07/02 04:40:47 cgd Exp $ */ /* @@ -25,6 +25,7 @@ #include "ether.h" #include "ppp.h" #include "bridge.h" +#include "pppoe.h" #endif /* @@ -65,3 +66,6 @@ #if NBRIDGE > 0 DONETISR(NETISR_BRIDGE,bridgeintr); #endif +#if NPPPOE > 0 + DONETISR(NETISR_PPPOE,pppoeintr); +#endif |