summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCan Erkin Acar <canacar@cvs.openbsd.org>2004-11-28 23:39:46 +0000
committerCan Erkin Acar <canacar@cvs.openbsd.org>2004-11-28 23:39:46 +0000
commit21bc148fb19bda21c314e0e1f9b34eac9a13cc8a (patch)
treea94aa3b32a2e9452fd7636a18a0d69f0b47c8525
parent9b02e944d8a1968fe39ff654b9f987ed838daed2 (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.828
-rw-r--r--sbin/ifconfig/ifconfig.c145
-rw-r--r--sys/conf/GENERIC4
-rw-r--r--sys/conf/files6
-rw-r--r--sys/dev/pci/if_lmc.c3
-rw-r--r--sys/dev/pci/if_san_obsd.c3
-rw-r--r--sys/net/if_ethersubr.c41
-rw-r--r--sys/net/if_pppoe.c1434
-rw-r--r--sys/net/if_pppoe.h81
-rw-r--r--sys/net/if_sppp.h9
-rw-r--r--sys/net/if_spppsubr.c163
-rw-r--r--sys/net/netisr.h4
-rw-r--r--sys/net/netisr_dispatch.h6
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