--- /dev/null
- pppoe_discovery_SOURCES = pppoe-discovery.c debug.c
+pppd_plugin_LTLIBRARIES = pppoe.la
+pppd_plugindir = $(PPPD_PLUGIN_DIR)
+sbin_PROGRAMS = pppoe-discovery
+dist_man8_MANS = pppoe-discovery.8
+
+noinst_HEADERS = \
+ pppoe.h
+
+pppoe_la_CPPFLAGS = -I${top_srcdir} -I${top_srcdir}/include
+pppoe_la_LDFLAGS = -module -avoid-version
+pppoe_la_SOURCES = plugin.c discovery.c if.c common.c
+
+pppoe_discovery_CPPFLAGS = -I${top_srcdir} -I${top_srcdir}/include
++pppoe_discovery_SOURCES = pppoe-discovery.c discovery.c if.c common.c
static char const RCSID[] =
"$Id: common.c,v 1.3 2008/06/09 08:34:23 paulus Exp $";
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
#define _GNU_SOURCE 1
#include "pppoe.h"
#include "pppd/pppd.h"
size_t elen = strlen(msg);
err.type = htons(TAG_GENERIC_ERROR);
err.length = htons(elen);
- strcpy(err.payload, msg);
+ strcpy((char*) err.payload, msg);
memcpy(cursor, &err, elen + TAG_HDR_SIZE);
cursor += elen + TAG_HDR_SIZE;
plen += elen + TAG_HDR_SIZE;
info("Sent PADT");
}
+ static void
+ pppoe_printpkt_hex(void (*printer)(void *, char *, ...), void *arg, unsigned char const *buf, int len)
+ {
+ int i;
+ int base;
+
+ /* do NOT dump PAP packets */
+ if (len >= 2 && buf[0] == 0xC0 && buf[1] == 0x23) {
+ printer(arg, "(PAP Authentication Frame -- Contents not dumped)\n");
+ return;
+ }
+
+ for (base=0; base<len; base += 16) {
+ for (i=base; i<base+16; i++) {
+ if (i < len) {
+ printer(arg, "%02x ", (unsigned) buf[i]);
+ } else {
+ printer(arg, " ");
+ }
+ }
+ printer(arg, " ");
+ for (i=base; i<base+16; i++) {
+ if (i < len) {
+ if (isprint(buf[i])) {
+ printer(arg, "%c", buf[i]);
+ } else {
+ printer(arg, ".");
+ }
+ } else {
+ break;
+ }
+ }
+ printer(arg, "\n");
+ }
+ }
+
#define EH(x) (x)[0], (x)[1], (x)[2], (x)[3], (x)[4], (x)[5]
/* Print out a PPPOE packet for debugging */
void (*printer)(void *, char *, ...), void *arg)
{
int len = ntohs(packet->length);
- int i, tag, tlen, text;
+ int i, j, tag, tlen, text;
switch (ntohs(packet->ethHdr.h_proto)) {
case ETH_PPPOE_DISCOVERY:
printer(arg, " dst %02x:%02x:%02x:%02x:%02x:%02x ", EH(packet->ethHdr.h_dest));
printer(arg, " src %02x:%02x:%02x:%02x:%02x:%02x\n", EH(packet->ethHdr.h_source));
+ if (pppoe_verbose >= 2)
+ pppoe_printpkt_hex(printer, arg, packet->payload, ntohs(packet->length));
if (ntohs(packet->ethHdr.h_proto) != ETH_PPPOE_DISCOVERY)
return;
}
if (tlen) {
if (text)
- printer(arg, " %.*v", tlen, &packet->payload[i]);
- else if (tlen <= 32)
- printer(arg, " %.*B", tlen, &packet->payload[i]);
- else
- printer(arg, " %.32B... (length %d)",
- &packet->payload[i], tlen);
+ printer(arg, " %.*s", tlen, &packet->payload[i]);
+ else {
+ for (j = 0; j < tlen && j < 32; j++)
+ printer(arg, " %02x", (unsigned) *(&packet->payload[i]+j));
+ if (j < tlen)
+ printer(arg, "... (length %d)", tlen);
+ }
}
printer(arg, "]");
}
static char const RCSID[] =
"$Id: discovery.c,v 1.6 2008/06/15 04:35:50 paulus Exp $";
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
#define _GNU_SOURCE 1
#include "pppoe.h"
#include "pppd/pppd.h"
switch(type) {
case TAG_AC_NAME:
pc->seenACName = 1;
- if (conn->printACNames) {
+ if (pppoe_verbose >= 1) {
info("Access-Concentrator: %.*s", (int) len, data);
}
if (conn->acName && len == strlen(conn->acName) &&
break;
case TAG_SERVICE_NAME:
pc->seenServiceName = 1;
+ if (pppoe_verbose >= 1 && len > 0) {
+ info("Service-Name: %.*s", (int) len, data);
+ }
if (conn->serviceName && len == strlen(conn->serviceName) &&
!strncmp((char *) data, conn->serviceName, len)) {
pc->serviceNameOK = 1;
}
break;
case TAG_AC_COOKIE:
- conn->cookie.type = htons(type);
- conn->cookie.length = htons(len);
- memcpy(conn->cookie.payload, data, len);
+ if (pppoe_verbose >= 1) {
+ char buffer[100];
+ char *ptr = buffer;
+ ptr += sprintf(ptr, "Cookie:");
+ /* Print first 20 bytes of cookie */
+ for (i=0; i<len && i < 20; i++) {
+ ptr += sprintf(ptr, " %02x", (unsigned) data[i]);
+ }
+ if (i < len) ptr += sprintf(ptr, "...");
+ info(buffer);
+ }
+ if (conn->discoveryState != STATE_RECEIVED_PADO) {
+ conn->cookie.type = htons(type);
+ conn->cookie.length = htons(len);
+ memcpy(conn->cookie.payload, data, len);
+ }
break;
case TAG_RELAY_SESSION_ID:
- conn->relayId.type = htons(type);
- conn->relayId.length = htons(len);
- memcpy(conn->relayId.payload, data, len);
+ if (pppoe_verbose >= 1) {
+ char buffer[100];
+ char *ptr = buffer;
+ ptr += sprintf(ptr, "Relay-ID:");
+ /* Print first 20 bytes of relay ID */
+ for (i=0; i<len && i < 20; i++) {
+ ptr += printf(ptr, " %02x", (unsigned) data[i]);
+ }
+ if (i < len) ptr += printf(ptr, "...");
+ info(buffer);
+ }
+ if (conn->discoveryState != STATE_RECEIVED_PADO) {
+ conn->relayId.type = htons(type);
+ conn->relayId.length = htons(len);
+ memcpy(conn->relayId.payload, data, len);
+ }
break;
case TAG_PPP_MAX_PAYLOAD:
if (len == sizeof(mru)) {
memcpy(&mru, data, sizeof(mru));
mru = ntohs(mru);
- if (mru >= ETH_PPPOE_MTU) {
- if (lcp_allowoptions[0].mru > mru)
- lcp_allowoptions[0].mru = mru;
- if (lcp_wantoptions[0].mru > mru)
- lcp_wantoptions[0].mru = mru;
+ info("Max-Payload: %u", (unsigned) mru);
+ if (mru >= ETH_PPPOE_MTU && conn->discoveryState != STATE_RECEIVED_PADO) {
+ if (conn->mtu > mru)
+ conn->mtu = mru;
+ if (conn->mru > mru)
+ conn->mru = mru;
conn->seenMaxPayload = 1;
}
}
UINT16_t mru;
switch(type) {
case TAG_SERVICE_NAME:
- dbglog("PADS: Service-Name: '%.*s'", (int) len, data);
+ if (pppoe_verbose >= 1 && len > 0) {
+ info("PADS: Service-Name: '%.*s'", (int) len, data);
+ }
break;
case TAG_PPP_MAX_PAYLOAD:
if (len == sizeof(mru)) {
memcpy(&mru, data, sizeof(mru));
mru = ntohs(mru);
if (mru >= ETH_PPPOE_MTU) {
- if (lcp_allowoptions[0].mru > mru)
- lcp_allowoptions[0].mru = mru;
- if (lcp_wantoptions[0].mru > mru)
- lcp_wantoptions[0].mru = mru;
+ if (conn->mtu > mru)
+ conn->mtu = mru;
+ if (conn->mru > mru)
+ conn->mru = mru;
conn->seenMaxPayload = 1;
}
}
}
/* Add our maximum MTU/MRU */
- if (MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru) > ETH_PPPOE_MTU) {
+ if (MIN(conn->mtu, conn->mru) > ETH_PPPOE_MTU) {
PPPoETag maxPayload;
- UINT16_t mru = htons(MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru));
+ UINT16_t mru = htons(MIN(conn->mtu, conn->mru));
maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD);
maxPayload.length = htons(sizeof(mru));
memcpy(maxPayload.payload, &mru, sizeof(mru));
pc.seenACName = 0;
pc.seenServiceName = 0;
conn->seenMaxPayload = 0;
- conn->error = 0;
if (get_time(&expire_at) < 0) {
error("get_time (waitForPADO): %m");
return; /* Timed out */
}
+ conn->error = 0;
/* Get the packet */
receivePacket(conn->discoverySocket, &packet, &len);
continue;
}
if (parsePacket(&packet, parsePADOTags, &pc) < 0)
- return;
+ continue;
if (conn->error)
- return;
+ continue;
if (!pc.seenACName) {
error("Ignoring PADO packet with no AC-Name tag");
continue;
error("Ignoring PADO packet with no Service-Name tag");
continue;
}
+ if (pppoe_verbose >= 1) {
+ info("AC-Ethernet-Address: %02x:%02x:%02x:%02x:%02x:%02x",
+ (unsigned) packet.ethHdr.h_source[0],
+ (unsigned) packet.ethHdr.h_source[1],
+ (unsigned) packet.ethHdr.h_source[2],
+ (unsigned) packet.ethHdr.h_source[3],
+ (unsigned) packet.ethHdr.h_source[4],
+ (unsigned) packet.ethHdr.h_source[5]);
+ info("--------------------------------------------------");
+ }
conn->numPADOs++;
- if (pc.acNameOK && pc.serviceNameOK) {
+ if (pc.acNameOK && pc.serviceNameOK && conn->discoveryState != STATE_RECEIVED_PADO) {
memcpy(conn->peerEth, packet.ethHdr.h_source, ETH_ALEN);
conn->discoveryState = STATE_RECEIVED_PADO;
- break;
}
}
- } while (conn->discoveryState != STATE_RECEIVED_PADO);
+ } while (pppoe_verbose >= 1 || conn->discoveryState != STATE_RECEIVED_PADO);
}
/***********************************************************************
}
/* Add our maximum MTU/MRU */
- if (MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru) > ETH_PPPOE_MTU) {
+ if (MIN(conn->mtu, conn->mru) > ETH_PPPOE_MTU) {
PPPoETag maxPayload;
- UINT16_t mru = htons(MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru));
+ UINT16_t mru = htons(MIN(conn->mtu, conn->mru));
maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD);
maxPayload.length = htons(sizeof(mru));
memcpy(maxPayload.payload, &mru, sizeof(mru));
}
/**********************************************************************
- *%FUNCTION: discovery
+ *%FUNCTION: discovery1
*%ARGUMENTS:
* conn -- PPPoE connection info structure
*%RETURNS:
* Nothing
*%DESCRIPTION:
- * Performs the PPPoE discovery phase
+ * Performs the PPPoE discovery phase 1
***********************************************************************/
void
- discovery(PPPoEConnection *conn)
+ discovery1(PPPoEConnection *conn)
{
int padiAttempts = 0;
- int padrAttempts = 0;
int timeout = conn->discoveryTimeout;
do {
timeout *= 2;
} while (conn->discoveryState == STATE_SENT_PADI);
+ }
+
+ /**********************************************************************
+ *%FUNCTION: discovery2
+ *%ARGUMENTS:
+ * conn -- PPPoE connection info structure
+ *%RETURNS:
+ * Nothing
+ *%DESCRIPTION:
+ * Performs the PPPoE discovery phase 2
+ ***********************************************************************/
+ void
+ discovery2(PPPoEConnection *conn)
+ {
+ int padrAttempts = 0;
+ int timeout = conn->discoveryTimeout;
- timeout = conn->discoveryTimeout;
do {
padrAttempts++;
if (got_sigterm || padrAttempts > conn->discoveryAttempts) {
if (!conn->seenMaxPayload) {
/* RFC 4638: MUST limit MTU/MRU to 1492 */
- if (lcp_allowoptions[0].mru > ETH_PPPOE_MTU)
- lcp_allowoptions[0].mru = ETH_PPPOE_MTU;
- if (lcp_wantoptions[0].mru > ETH_PPPOE_MTU)
- lcp_wantoptions[0].mru = ETH_PPPOE_MTU;
+ if (conn->mtu > ETH_PPPOE_MTU)
+ conn->mtu = ETH_PPPOE_MTU;
+ if (conn->mru > ETH_PPPOE_MTU)
+ conn->mru = ETH_PPPOE_MTU;
}
/* We're done. */
static char const RCSID[] =
"$Id: plugin.c,v 1.17 2008/06/15 04:35:50 paulus Exp $";
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
#define _GNU_SOURCE 1
#include "pppoe.h"
char *pppd_pppoe_service = NULL;
static char *acName = NULL;
static char *existingSession = NULL;
- static int printACNames = 0;
+ int pppoe_verbose = 0;
static char *pppoe_reqd_mac = NULL;
unsigned char pppoe_reqd_mac_addr[6];
static char *pppoe_host_uniq;
"Attach to existing session (sessid:macaddr)" },
{ "rp_pppoe_sess", o_string, &existingSession,
"Legacy alias for pppoe-sess", OPT_ALIAS },
- { "pppoe-verbose", o_int, &printACNames,
+ { "pppoe-verbose", o_int, &pppoe_verbose,
"Be verbose about discovered access concentrators" },
- { "rp_pppoe_verbose", o_int, &printACNames,
+ { "rp_pppoe_verbose", o_int, &pppoe_verbose,
"Legacy alias for pppoe-verbose", OPT_ALIAS },
{ "pppoe-mac", o_string, &pppoe_reqd_mac,
"Only connect to specified MAC address" },
conn->ifName = devnam;
conn->discoverySocket = -1;
conn->sessionSocket = -1;
- conn->printACNames = printACNames;
conn->discoveryTimeout = pppoe_padi_timeout;
conn->discoveryAttempts = pppoe_padi_attempts;
return 1;
}
/* Restore configuration */
- lcp_allowoptions[0].mru = conn->mtu;
- lcp_wantoptions[0].mru = conn->mru;
+ lcp_allowoptions[0].mru = conn->mtu = conn->storedmtu;
+ lcp_wantoptions[0].mru = conn->mru = conn->storedmru;
/* Update maximum MRU */
s = socket(AF_INET, SOCK_DGRAM, 0);
close(s);
if (lcp_allowoptions[0].mru > ifr.ifr_mtu - TOTAL_OVERHEAD)
- lcp_allowoptions[0].mru = ifr.ifr_mtu - TOTAL_OVERHEAD;
+ lcp_allowoptions[0].mru = conn->mtu = ifr.ifr_mtu - TOTAL_OVERHEAD;
if (lcp_wantoptions[0].mru > ifr.ifr_mtu - TOTAL_OVERHEAD)
- lcp_wantoptions[0].mru = ifr.ifr_mtu - TOTAL_OVERHEAD;
+ lcp_wantoptions[0].mru = conn->mru = ifr.ifr_mtu - TOTAL_OVERHEAD;
if (pppoe_host_uniq) {
if (!parseHostUniq(pppoe_host_uniq, &conn->hostUniq))
error("Failed to create PPPoE discovery socket: %m");
goto errout;
}
- discovery(conn);
+ discovery1(conn);
+ /* discovery1() may update conn->mtu and conn->mru */
+ lcp_allowoptions[0].mru = conn->mtu;
+ lcp_wantoptions[0].mru = conn->mru;
+ if (conn->discoveryState != STATE_RECEIVED_PADO) {
+ error("Unable to complete PPPoE Discovery phase 1");
+ goto errout;
+ }
+ discovery2(conn);
+ /* discovery2() may update conn->mtu and conn->mru */
+ lcp_allowoptions[0].mru = conn->mtu;
+ lcp_wantoptions[0].mru = conn->mru;
if (conn->discoveryState != STATE_SESSION) {
- error("Unable to complete PPPoE Discovery");
+ error("Unable to complete PPPoE Discovery phase 2");
goto errout;
}
}
lcp_wantoptions[0].mru = MAX_PPPOE_MTU;
/* Save configuration */
- conn->mtu = lcp_allowoptions[0].mru;
- conn->mru = lcp_wantoptions[0].mru;
+ conn->storedmtu = lcp_allowoptions[0].mru;
+ conn->storedmru = lcp_wantoptions[0].mru;
ccp_allowoptions[0].deflate = 0;
ccp_wantoptions[0].deflate = 0;
*
*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>
+ #include <signal.h>
#include "pppoe.h"
#include <net/if_arp.h>
#endif
- char *xstrdup(const char *s);
- void usage(void);
+ int debug;
+ int got_sigterm;
+ int pppoe_verbose;
+ static FILE *debugFile;
- void die(int status)
- {
- exit(status);
- }
-
- void error(char *fmt, ...)
+ void
+ fatal(char *fmt, ...)
{
va_list pvar;
va_start(pvar, fmt);
vfprintf(stderr, fmt, pvar);
va_end(pvar);
+ fputc('\n', stderr);
+ exit(1);
}
- /* Initialize frame types to RFC 2516 values. Some broken peers apparently
- use different frame types... sigh... */
-
- UINT16_t Eth_PPPOE_Discovery = ETH_PPPOE_DISCOVERY;
- UINT16_t Eth_PPPOE_Session = ETH_PPPOE_SESSION;
-
- /**********************************************************************
- *%FUNCTION: etherType
- *%ARGUMENTS:
- * packet -- a received PPPoE packet
- *%RETURNS:
- * ethernet packet type (see /usr/include/net/ethertypes.h)
- *%DESCRIPTION:
- * Checks the ethernet packet header to determine its type.
- * We should only be receveing DISCOVERY and SESSION types if the BPF
- * is set up correctly. Logs an error if an unexpected type is received.
- * Note that the ethernet type names come from "pppoe.h" and the packet
- * packet structure names use the LINUX dialect to maintain consistency
- * with the rest of this file. See the BSD section of "pppoe.h" for
- * translations of the data structure names.
- ***********************************************************************/
- UINT16_t
- etherType(PPPoEPacket *packet)
- {
- UINT16_t type = (UINT16_t) ntohs(packet->ethHdr.h_proto);
- if (type != Eth_PPPOE_Discovery && type != Eth_PPPOE_Session) {
- fprintf(stderr, "Invalid ether type 0x%x\n", type);
- }
- return type;
- }
-
- /**********************************************************************
- *%FUNCTION: openInterface
- *%ARGUMENTS:
- * ifname -- name of interface
- * type -- Ethernet frame type
- * hwaddr -- if non-NULL, set to the hardware address
- *%RETURNS:
- * A raw socket for talking to the Ethernet card. Exits on error.
- *%DESCRIPTION:
- * Opens a raw Ethernet socket
- ***********************************************************************/
- int
- openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr)
+ void
+ error(char *fmt, ...)
{
- int optval=1;
- int fd;
- struct ifreq ifr;
- int domain, stype;
-
- #ifdef HAVE_STRUCT_SOCKADDR_LL
- struct sockaddr_ll sa;
- #else
- struct sockaddr sa;
- #endif
-
- memset(&sa, 0, sizeof(sa));
-
- #ifdef HAVE_STRUCT_SOCKADDR_LL
- domain = PF_PACKET;
- stype = SOCK_RAW;
- #else
- domain = PF_INET;
- stype = SOCK_PACKET;
- #endif
-
- if ((fd = socket(domain, stype, htons(type))) < 0) {
- /* Give a more helpful message for the common error case */
- if (errno == EPERM) {
- fatal("Cannot create raw socket -- pppoe must be run as root.");
- }
- fatalSys("socket");
- }
-
- if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) < 0) {
- fatalSys("setsockopt");
- }
-
- /* Fill in hardware address */
- if (hwaddr) {
- strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
- if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
- fatalSys("ioctl(SIOCGIFHWADDR)");
- }
- memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
- #ifdef ARPHRD_ETHER
- if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
- fatal("Interface %.16s is not Ethernet", ifname);
- }
- #endif
- if (NOT_UNICAST(hwaddr)) {
- fatal("Interface %.16s has broadcast/multicast MAC address??", ifname);
- }
- }
-
- /* Sanity check on MTU */
- strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
- if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) {
- fatalSys("ioctl(SIOCGIFMTU)");
- }
- if (ifr.ifr_mtu < ETH_DATA_LEN) {
- fprintf(stderr, "Interface %.16s has MTU of %d -- should be %d.\n",
- ifname, ifr.ifr_mtu, ETH_DATA_LEN);
- fprintf(stderr, "You may have serious connection problems.\n");
- }
-
- #ifdef HAVE_STRUCT_SOCKADDR_LL
- /* Get interface index */
- sa.sll_family = AF_PACKET;
- sa.sll_protocol = htons(type);
-
- strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
- ifr.ifr_name[IFNAMSIZ - 1] = 0;
- if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
- fatalSys("ioctl(SIOCFIGINDEX): Could not get interface index");
- }
- sa.sll_ifindex = ifr.ifr_ifindex;
-
- #else
- strcpy(sa.sa_data, ifname);
- #endif
-
- /* We're only interested in packets on specified interface */
- if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
- fatalSys("bind");
- }
-
- return fd;
+ va_list pvar;
+ va_start(pvar, fmt);
+ vfprintf(stderr, fmt, pvar);
+ fputc('\n', stderr);
+ va_end(pvar);
}
-
- /***********************************************************************
- *%FUNCTION: sendPacket
- *%ARGUMENTS:
- * sock -- socket to send to
- * pkt -- the packet to transmit
- * size -- size of packet (in bytes)
- *%RETURNS:
- * 0 on success; -1 on failure
- *%DESCRIPTION:
- * Transmits a packet
- ***********************************************************************/
- int
- sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size)
+ void
+ warn(char *fmt, ...)
{
- #if defined(HAVE_STRUCT_SOCKADDR_LL)
- if (send(sock, pkt, size, 0) < 0) {
- fatalSys("send (sendPacket)");
- return -1;
- }
- #else
- struct sockaddr sa;
-
- if (!conn) {
- fatal("relay and server not supported on Linux 2.0 kernels");
- }
- strcpy(sa.sa_data, conn->ifName);
- if (sendto(sock, pkt, size, 0, &sa, sizeof(sa)) < 0) {
- fatalSys("sendto (sendPacket)");
- return -1;
- }
- #endif
- return 0;
+ va_list pvar;
+ va_start(pvar, fmt);
+ vfprintf(stderr, fmt, pvar);
+ fputc('\n', stderr);
+ va_end(pvar);
}
- /***********************************************************************
- *%FUNCTION: receivePacket
- *%ARGUMENTS:
- * sock -- socket to read from
- * pkt -- place to store the received packet
- * size -- set to size of packet in bytes
- *%RETURNS:
- * >= 0 if all OK; < 0 if error
- *%DESCRIPTION:
- * Receives a packet
- ***********************************************************************/
- int
- receivePacket(int sock, PPPoEPacket *pkt, int *size)
+ void
+ info(char *fmt, ...)
{
- if ((*size = recv(sock, pkt, sizeof(PPPoEPacket), 0)) < 0) {
- fatalSys("recv (receivePacket)");
- return -1;
- }
- return 0;
+ va_list pvar;
+ va_start(pvar, fmt);
+ vprintf(fmt, pvar);
+ putchar('\n');
+ va_end(pvar);
}
- /**********************************************************************
- *%FUNCTION: parsePacket
- *%ARGUMENTS:
- * packet -- the PPPoE discovery packet to parse
- * func -- function called for each tag in the packet
- * extra -- an opaque data pointer supplied to parsing function
- *%RETURNS:
- * 0 if everything went well; -1 if there was an error
- *%DESCRIPTION:
- * Parses a PPPoE discovery packet, calling "func" for each tag in the packet.
- * "func" is passed the additional argument "extra".
- ***********************************************************************/
- int
- parsePacket(PPPoEPacket *packet, ParseFunc *func, void *extra)
+ void
+ init_pr_log(const char *prefix, int level)
{
- UINT16_t len = ntohs(packet->length);
- unsigned char *curTag;
- UINT16_t tagType, tagLen;
-
- if (PPPOE_VER(packet->vertype) != 1) {
- fprintf(stderr, "Invalid PPPoE version (%d)\n",
- PPPOE_VER(packet->vertype));
- return -1;
- }
- if (PPPOE_TYPE(packet->vertype) != 1) {
- fprintf(stderr, "Invalid PPPoE type (%d)\n",
- PPPOE_TYPE(packet->vertype));
- return -1;
- }
-
- /* Do some sanity checks on packet */
- if (len > ETH_JUMBO_LEN - PPPOE_OVERHEAD) { /* 6-byte overhead for PPPoE header */
- fprintf(stderr, "Invalid PPPoE packet length (%u)\n", len);
- return -1;
- }
-
- /* Step through the tags */
- curTag = packet->payload;
- while(curTag - packet->payload < len) {
- /* Alignment is not guaranteed, so do this by hand... */
- tagType = (curTag[0] << 8) + curTag[1];
- tagLen = (curTag[2] << 8) + curTag[3];
- if (tagType == TAG_END_OF_LIST) {
- return 0;
- }
- if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) {
- fprintf(stderr, "Invalid PPPoE tag length (%u)\n", tagLen);
- return -1;
- }
- func(tagType, tagLen, curTag+TAG_HDR_SIZE, extra);
- curTag = curTag + TAG_HDR_SIZE + tagLen;
- }
- return 0;
}
- /**********************************************************************
- *%FUNCTION: parseForHostUniq
- *%ARGUMENTS:
- * type -- tag type
- * len -- tag length
- * data -- tag data.
- * extra -- user-supplied pointer. This is assumed to be a pointer to int.
- *%RETURNS:
- * Nothing
- *%DESCRIPTION:
- * If a HostUnique tag is found which matches our PID, sets *extra to 1.
- ***********************************************************************/
void
- parseForHostUniq(UINT16_t type, UINT16_t len, unsigned char *data,
- void *extra)
+ end_pr_log(void)
{
- PPPoETag *tag = extra;
-
- if (type == TAG_HOST_UNIQ && len == ntohs(tag->length))
- tag->length = memcmp(data, tag->payload, len);
+ fflush(debugFile);
}
- /**********************************************************************
- *%FUNCTION: packetIsForMe
- *%ARGUMENTS:
- * conn -- PPPoE connection info
- * packet -- a received PPPoE packet
- *%RETURNS:
- * 1 if packet is for this PPPoE daemon; 0 otherwise.
- *%DESCRIPTION:
- * If we are using the Host-Unique tag, verifies that packet contains
- * our unique identifier.
- ***********************************************************************/
- int
- packetIsForMe(PPPoEConnection *conn, PPPoEPacket *packet)
+ void
+ pr_log(void *arg, char *fmt, ...)
{
- PPPoETag hostUniq = conn->hostUniq;
-
- /* If packet is not directed to our MAC address, forget it */
- if (memcmp(packet->ethHdr.h_dest, conn->myEth, ETH_ALEN)) return 0;
-
- /* If we're not using the Host-Unique tag, then accept the packet */
- if (!conn->hostUniq.length) return 1;
-
- parsePacket(packet, parseForHostUniq, &hostUniq);
- return !hostUniq.length;
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(debugFile, fmt, ap);
+ va_end(ap);
}
- /**********************************************************************
- *%FUNCTION: parsePADOTags
- *%ARGUMENTS:
- * type -- tag type
- * len -- tag length
- * data -- tag data
- * extra -- extra user data. Should point to a PacketCriteria structure
- * which gets filled in according to selected AC name and service
- * name.
- *%RETURNS:
- * Nothing
- *%DESCRIPTION:
- * Picks interesting tags out of a PADO packet
- ***********************************************************************/
- void
- parsePADOTags(UINT16_t type, UINT16_t len, unsigned char *data,
- void *extra)
+ size_t
+ strlcpy(char *dest, const char *src, size_t len)
{
- struct PacketCriteria *pc = (struct PacketCriteria *) extra;
- PPPoEConnection *conn = pc->conn;
- int i;
-
- switch(type) {
- case TAG_AC_NAME:
- pc->seenACName = 1;
- if (conn->printACNames) {
- printf("Access-Concentrator: %.*s\n", (int) len, data);
- }
- if (conn->acName && len == strlen(conn->acName) &&
- !strncmp((char *) data, conn->acName, len)) {
- pc->acNameOK = 1;
- }
- break;
- case TAG_SERVICE_NAME:
- pc->seenServiceName = 1;
- if (conn->printACNames && len > 0) {
- printf(" Service-Name: %.*s\n", (int) len, data);
- }
- if (conn->serviceName && len == strlen(conn->serviceName) &&
- !strncmp((char *) data, conn->serviceName, len)) {
- pc->serviceNameOK = 1;
- }
- break;
- case TAG_AC_COOKIE:
- if (conn->printACNames) {
- printf("Got a cookie:");
- /* Print first 20 bytes of cookie */
- for (i=0; i<len && i < 20; i++) {
- printf(" %02x", (unsigned) data[i]);
- }
- if (i < len) printf("...");
- printf("\n");
- }
- conn->cookie.type = htons(type);
- conn->cookie.length = htons(len);
- memcpy(conn->cookie.payload, data, len);
- break;
- case TAG_RELAY_SESSION_ID:
- if (conn->printACNames) {
- printf("Got a Relay-ID:");
- /* Print first 20 bytes of relay ID */
- for (i=0; i<len && i < 20; i++) {
- printf(" %02x", (unsigned) data[i]);
- }
- if (i < len) printf("...");
- printf("\n");
- }
- conn->relayId.type = htons(type);
- conn->relayId.length = htons(len);
- memcpy(conn->relayId.payload, data, len);
- break;
- case TAG_SERVICE_NAME_ERROR:
- if (conn->printACNames) {
- printf("Got a Service-Name-Error tag: %.*s\n", (int) len, data);
- }
- break;
- case TAG_AC_SYSTEM_ERROR:
- if (conn->printACNames) {
- printf("Got a System-Error tag: %.*s\n", (int) len, data);
+ size_t ret = strlen(src);
+
+ if (len != 0) {
+ if (ret < len)
+ strcpy(dest, src);
+ else {
+ strncpy(dest, src, len - 1);
+ dest[len-1] = 0;
}
- break;
- case TAG_GENERIC_ERROR:
- if (conn->printACNames) {
- printf("Got a Generic-Error tag: %.*s\n", (int) len, data);
- }
- break;
}
+ return ret;
}
- /***********************************************************************
- *%FUNCTION: sendPADI
- *%ARGUMENTS:
- * conn -- PPPoEConnection structure
- *%RETURNS:
- * Nothing
- *%DESCRIPTION:
- * Sends a PADI packet
- ***********************************************************************/
- void
- sendPADI(PPPoEConnection *conn)
+ static char *
+ xstrdup(const char *s)
{
- PPPoEPacket packet;
- unsigned char *cursor = packet.payload;
- PPPoETag *svc = (PPPoETag *) (&packet.payload);
- UINT16_t namelen = 0;
- UINT16_t plen;
-
- if (conn->serviceName) {
- namelen = (UINT16_t) strlen(conn->serviceName);
- }
- plen = TAG_HDR_SIZE + namelen;
- CHECK_ROOM(cursor, packet.payload, plen);
-
- /* Set destination to Ethernet broadcast address */
- memset(packet.ethHdr.h_dest, 0xFF, ETH_ALEN);
- memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN);
-
- packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
- packet.vertype = PPPOE_VER_TYPE(1, 1);
- packet.code = CODE_PADI;
- packet.session = 0;
-
- svc->type = TAG_SERVICE_NAME;
- svc->length = htons(namelen);
- CHECK_ROOM(cursor, packet.payload, namelen+TAG_HDR_SIZE);
-
- if (conn->serviceName) {
- memcpy(svc->payload, conn->serviceName, strlen(conn->serviceName));
- }
- cursor += namelen + TAG_HDR_SIZE;
-
- /* If we're using Host-Uniq, copy it over */
- if (conn->hostUniq.length) {
- int len = ntohs(conn->hostUniq.length);
- CHECK_ROOM(cursor, packet.payload, len + TAG_HDR_SIZE);
- memcpy(cursor, &conn->hostUniq, len + TAG_HDR_SIZE);
- cursor += len + TAG_HDR_SIZE;
- plen += len + TAG_HDR_SIZE;
- }
-
- packet.length = htons(plen);
-
- sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE));
- if (conn->debugFile) {
- dumpPacket(conn->debugFile, &packet, "SENT");
- fprintf(conn->debugFile, "\n");
- fflush(conn->debugFile);
+ char *ret = strdup(s);
+ if (!ret) {
+ perror("strdup");
+ exit(1);
}
+ return ret;
}
- /**********************************************************************
- *%FUNCTION: waitForPADO
- *%ARGUMENTS:
- * conn -- PPPoEConnection structure
- * timeout -- how long to wait (in seconds)
- *%RETURNS:
- * Nothing
- *%DESCRIPTION:
- * Waits for a PADO packet and copies useful information
- ***********************************************************************/
- void
- waitForPADO(PPPoEConnection *conn, int timeout)
+ int
+ get_time(struct timeval *tv)
{
- fd_set readable;
- int r;
- struct timeval tv;
- PPPoEPacket packet;
- int len;
-
- struct PacketCriteria pc;
- pc.conn = conn;
- pc.acNameOK = (conn->acName) ? 0 : 1;
- pc.serviceNameOK = (conn->serviceName) ? 0 : 1;
- pc.seenACName = 0;
- pc.seenServiceName = 0;
- conn->error = 0;
-
- do {
- if (BPF_BUFFER_IS_EMPTY) {
- tv.tv_sec = timeout;
- tv.tv_usec = 0;
-
- FD_ZERO(&readable);
- FD_SET(conn->discoverySocket, &readable);
-
- while(1) {
- r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv);
- if (r >= 0 || errno != EINTR) break;
- }
- if (r < 0) {
- perror("select (waitForPADO)");
- return;
- }
- if (r == 0) return; /* Timed out */
- }
-
- /* Get the packet */
- receivePacket(conn->discoverySocket, &packet, &len);
-
- /* Check length */
- if (ntohs(packet.length) + HDR_SIZE > len) {
- fprintf(stderr, "Bogus PPPoE length field (%u)\n",
- (unsigned int) ntohs(packet.length));
- continue;
- }
-
- #ifdef USE_BPF
- /* If it's not a Discovery packet, loop again */
- if (etherType(&packet) != Eth_PPPOE_Discovery) continue;
- #endif
-
- if (conn->debugFile) {
- dumpPacket(conn->debugFile, &packet, "RCVD");
- fprintf(conn->debugFile, "\n");
- fflush(conn->debugFile);
- }
- /* If it's not for us, loop again */
- if (!packetIsForMe(conn, &packet)) continue;
-
- if (packet.code == CODE_PADO) {
- if (BROADCAST(packet.ethHdr.h_source)) {
- fprintf(stderr, "Ignoring PADO packet from broadcast MAC address\n");
- continue;
- }
- parsePacket(&packet, parsePADOTags, &pc);
- if (conn->error)
- return;
- if (!pc.seenACName) {
- fprintf(stderr, "Ignoring PADO packet with no AC-Name tag\n");
- continue;
- }
- if (!pc.seenServiceName) {
- fprintf(stderr, "Ignoring PADO packet with no Service-Name tag\n");
- continue;
- }
- conn->numPADOs++;
- if (pc.acNameOK && pc.serviceNameOK) {
- memcpy(conn->peerEth, packet.ethHdr.h_source, ETH_ALEN);
- if (conn->printACNames) {
- printf("AC-Ethernet-Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
- (unsigned) conn->peerEth[0],
- (unsigned) conn->peerEth[1],
- (unsigned) conn->peerEth[2],
- (unsigned) conn->peerEth[3],
- (unsigned) conn->peerEth[4],
- (unsigned) conn->peerEth[5]);
- printf("--------------------------------------------------\n");
- continue;
- }
- conn->discoveryState = STATE_RECEIVED_PADO;
- break;
- }
- }
- } while (conn->discoveryState != STATE_RECEIVED_PADO);
+ return gettimeofday(tv, NULL);
}
- /**********************************************************************
- *%FUNCTION: discovery
- *%ARGUMENTS:
- * conn -- PPPoE connection info structure
- *%RETURNS:
- * Nothing
- *%DESCRIPTION:
- * Performs the PPPoE discovery phase
- ***********************************************************************/
- void
- discovery(PPPoEConnection *conn)
+ static void
+ term_handler(int signum)
{
- int padiAttempts = 0;
- int timeout = conn->discoveryTimeout;
-
- conn->discoverySocket =
- openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth);
-
- do {
- padiAttempts++;
- if (padiAttempts > conn->discoveryAttempts) {
- fprintf(stderr, "Timeout waiting for PADO packets\n");
- close(conn->discoverySocket);
- conn->discoverySocket = -1;
- return;
- }
- sendPADI(conn);
- conn->discoveryState = STATE_SENT_PADI;
- waitForPADO(conn, timeout);
- } while (!conn->numPADOs);
+ got_sigterm = 1;
}
+ static void usage(void);
+
int main(int argc, char *argv[])
{
int opt;
PPPoEConnection *conn;
+ signal(SIGINT, term_handler);
+ signal(SIGTERM, term_handler);
+
conn = malloc(sizeof(PPPoEConnection));
- if (!conn)
- fatalSys("malloc");
+ if (!conn) {
+ perror("malloc");
+ exit(1);
+ }
memset(conn, 0, sizeof(PPPoEConnection));
- conn->printACNames = 1;
+ pppoe_verbose = 1;
conn->discoveryTimeout = PADI_TIMEOUT;
conn->discoveryAttempts = MAX_PADI_ATTEMPTS;
}
break;
case 'D':
- conn->debugFile = fopen(optarg, "w");
- if (!conn->debugFile) {
+ pppoe_verbose = 2;
+ debug = 1;
+ debugFile = fopen(optarg, "w");
+ if (!debugFile) {
fprintf(stderr, "Could not open %s: %s\n",
optarg, strerror(errno));
exit(1);
}
- fprintf(conn->debugFile, "pppoe-discovery from pppd %s\n", VERSION);
+ fprintf(debugFile, "pppoe-discovery from pppd %s\n", VERSION);
break;
case 'I':
conn->ifName = xstrdup(optarg);
break;
case 'Q':
- conn->printACNames = 0;
+ pppoe_verbose = 0;
break;
case 'V':
case 'h':
/* default interface name */
if (!conn->ifName)
- conn->ifName = strdup("eth0");
+ conn->ifName = xstrdup("eth0");
- conn->discoverySocket = -1;
conn->sessionSocket = -1;
- discovery(conn);
+ conn->discoverySocket = openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth);
+ if (conn->discoverySocket < 0) {
+ perror("Cannot create PPPoE discovery socket");
+ exit(1);
+ }
+
+ discovery1(conn);
if (!conn->numPADOs)
exit(1);
exit(0);
}
- void fatal(char * fmt, ...)
- {
- va_list ap;
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fputc('\n', stderr);
- exit(1);
- }
-
- void fatalSys(char const *str)
- {
- perror(str);
- exit(1);
- }
-
- char *xstrdup(const char *s)
- {
- register char *ret = strdup(s);
- if (!ret)
- fatalSys("strdup");
- return ret;
- }
-
- void usage(void)
+ static void
+ usage(void)
{
fprintf(stderr, "Usage: pppoe-discovery [options]\n");
fprintf(stderr, "Options:\n");
*
***********************************************************************/
-#include "config.h"
-
#include <stdio.h> /* For FILE */
#include <sys/types.h> /* For pid_t */
#include <ctype.h>
char *acName; /* Desired AC name, if any */
int synchronous; /* Use synchronous PPP */
PPPoETag hostUniq; /* Use Host-Uniq tag */
- int printACNames; /* Just print AC names */
- FILE *debugFile; /* Debug file for dumping packets */
int numPADOs; /* Number of PADO packets received */
PPPoETag cookie; /* We have to send this if we get it */
PPPoETag relayId; /* Ditto */
int error; /* Error packet received */
- int debug; /* Set to log packets sent and received */
int discoveryTimeout; /* Timeout for discovery packets */
int discoveryAttempts; /* Number of discovery attempts */
int seenMaxPayload;
- int mtu; /* Stored MTU */
- int mru; /* Stored MRU */
+ int storedmtu; /* Stored MTU */
+ int storedmru; /* Stored MRU */
+ int mtu;
+ int mru;
} PPPoEConnection;
/* Structure used to determine acceptable PADO or PADS packet */
int openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr);
int sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size);
int receivePacket(int sock, PPPoEPacket *pkt, int *size);
- void fatalSys(char const *str);
- void dumpPacket(FILE *fp, PPPoEPacket *packet, char const *dir);
- void dumpHex(FILE *fp, unsigned char const *buf, int len);
int parsePacket(PPPoEPacket *packet, ParseFunc *func, void *extra);
void parseLogErrs(UINT16_t typ, UINT16_t len, unsigned char *data, void *xtra);
void syncReadFromPPP(PPPoEConnection *conn, PPPoEPacket *packet);
void clampMSS(PPPoEPacket *packet, char const *dir, int clampMss);
UINT16_t computeTCPChecksum(unsigned char *ipHdr, unsigned char *tcpHdr);
UINT16_t pppFCS16(UINT16_t fcs, unsigned char *cp, int len);
- void discovery(PPPoEConnection *conn);
+ void discovery1(PPPoEConnection *conn);
+ void discovery2(PPPoEConnection *conn);
unsigned char *findTag(PPPoEPacket *packet, UINT16_t tagType,
PPPoETag *tag);
+ extern int pppoe_verbose;
void pppoe_printpkt(PPPoEPacket *packet,
void (*printer)(void *, char *, ...), void *arg);
void pppoe_log_packet(const char *prefix, PPPoEPacket *packet);
.I speed
An option that is a decimal number is taken as the desired baud rate
for the serial device. On systems such as
-4.4BSD and NetBSD, any speed can be specified. Other systems
-(e.g. Linux, SunOS) only support the commonly-used baud rates.
+Linux, 4.4BSD and NetBSD, any speed can be specified. Other systems
+(e.g. SunOS) only support the commonly-used baud rates.
.TP
.B asyncmap \fImap
This option sets the Async-Control-Character-Map (ACCM) for this end
value of -1, the route is only added if there is no default route at
all.
.TP
-.B defaultroute6
-Add a default IPv6 route to the system routing tables, using the peer as
-the gateway, when IPv6CP negotiation is successfully completed.
-This entry is removed when the PPP connection is broken. This option
-is privileged if the \fInodefaultroute6\fR option has been specified.
-.TP
.B replacedefaultroute
This option is a flag to the defaultroute option. If defaultroute is
set and this flag is also set, pppd replaces an existing default route
\fIbsdcomp 0\fR to disable BSD-Compress compression entirely.
.TP
.B ca \fIca-file
-(EAP-TLS) Use the file \fIca-file\fR as the X.509 Certificate Authority
+(EAP-TLS, or PEAP) Use the file \fIca-file\fR as the X.509 Certificate Authority
(CA) file (in PEM format), needed for setting up an EAP-TLS connection.
This option is used on the client-side in conjunction with the \fBcert\fR
-and \fBkey\fR options.
+and \fBkey\fR options. Either \fIca\fR, or \fIcapath\fR options are required
+for PEAP. EAP-TLS may also use the entry in eaptls-client or eaptls-server
+for a CA certificate associated with a particular peer.
+.TP
+.B capath \fIpath
+(EAP-TLS, or PEAP) Specify a location that contains public CA certificates.
+Either \fIca\fR, or \fIcapath\fR options are required for PEAP.
.TP
.B cdtrcts
Use a non-standard hardware flow control (i.e. DTR/CTS) to control
or \fBpty\fR option is used.
.TP
.B crl \fIfilename
-(EAP-TLS) Use the file \fIfilename\fR as the Certificate Revocation List
+(EAP-TLS, or PEAP) Use the file \fIfilename\fR as the Certificate Revocation List
to check for the validity of the peer's certificate. This option is not
-mandatory for setting up an EAP-TLS connection. Also see the \fBcrl-dir\fR
+mandatory for setting up a TLS connection. Also see the \fBcrl-dir\fR
option.
.TP
.B crl-dir \fIdirectory
-(EAP-TLS) Use the directory \fIdirectory\fR to scan for CRL files in
+(EAP-TLS, or PEAP) Use the directory \fIdirectory\fR to scan for CRL files in
has format ($hash.r0) to check for the validity of the peer's certificate.
-This option is not mandatory for setting up an EAP-TLS connection.
+This option is not mandatory for setting up a TLS connection.
Also see the \fBcrl\fR option.
.TP
.B debug
pppd will use the default MRU value of 1500 bytes for both the
transmit and receive direction.
.TP
+.B defaultroute6
+Add a default IPv6 route to the system routing tables, using the peer as
+the gateway, when IPv6CP negotiation is successfully completed.
+This entry is removed when the PPP connection is broken. This option
+is privileged if the \fInodefaultroute6\fR option has been specified.
+\fBWARNING: Do not enable this option by default\fR. IPv6 routing tables
+are managed by kernel (as apposite to IPv4) and IPv6 default route is
+configured by kernel automatically too based on ICMPv6 Router Advertisement
+packets. This option may conflict with kernel IPv6 route setup and should
+be used only for broken IPv6 networks.
+.TP
.B deflate \fInr,nt
Request that the peer compress packets that it sends, using the
Deflate scheme, with a maximum window size of \fI2**nr\fR bytes, and
Set the maximum number of IPCP terminate-request transmissions to
\fIn\fR (default 3).
.TP
+.B ipcp\-no\-address
+Disable negotiation of addresses via IP-Address IPCP option.
+.TP
+.B ipcp\-no\-addresses
+Disable negotiation of addresses via old-style deprecated IP-Addresses
+IPCP option. pppd by default try to use new-style IP-Address IPCP option.
+If new-style is not supported by peer or is disabled by \fBipcp\-no\-address\fR
+option then pppd fallbacks to old-style deprecated IP-Addresses IPCP option.
+When both new-style and old-style are disabled by both \fBipcp\-no\-address\fR
+and \fBipcp\-no\-addresses\fR options then negotiation of IP addresses
+is completely disabled.
+.TP
.B ipcp\-restart \fIn
Set the IPCP restart interval (retransmission timeout) to \fIn\fR
seconds (default 3).
Terminate after \fIn\fR consecutive failed connection attempts. A
value of 0 means no limit. The default value is 10.
.TP
+.B max-tls-version \fIstring
+(EAP-TLS, or PEAP) Configures the max allowed TLS version used during
+negotiation with a peer. The default value for this is \fI1.2\fR. Values
+allowed for this option is \fI1.0.\fR, \fI1.1\fR, \fI1.2\fR, \fI1.3\fR.
+.TP
.B modem
Use the modem control lines. This option is the default. With this
option, pppd will wait for the CD (Carrier Detect) signal from the
device routes, but the peer itself cannot be addressed directly for IP
traffic.
.TP
+.B nosendip
+Don't send our local IP address to peer during IP address negotiation.
+.TP
.B notty
Normally, pppd requires a terminal device. With this option, pppd
will allocate itself a pseudo-tty master/slave pair and use the slave
Currently supports Microgate SyncLink adapters
under Linux and FreeBSD 2.2.8 and later.
.TP
+.B tls-verify-method \fIstring
+(EAP-TLS, or PEAP) Match the value specified for \fIremotename\fR to that that
+of the X509 certificates subject name, common name, or suffix of the common
+name. Respective values allowed for this option is: \fInone\fR, \fIsubject\fR,
+\fIname\fR, or \fIsuffix\fR. The default value for this option is \fIname\fR.
+.TP
+.B tls-verify-key-usage
+(EAP-TLS, or PEAP) Enables examination of peer certificate's purpose, and
+extended key usage attributes.
+.TP
.B unit \fInum
Sets the ppp unit number (for a ppp0 or ppp1 etc interface name) for outbound
connections. If the unit is already in use a dynamically allocated number will
/etc/ppp/resolv.conf file containing one or two nameserver lines with
the address(es) supplied by the peer.
.TP
+.B usepeerwins
+Ask the peer for up to 2 WINS server addresses. The addresses supplied
+by the peer (if any) are passed to the /etc/ppp/ip\-up script in the
+environment variables WINS1 and WINS2, and the environment variable
+USEPEERWINS will be set to 1.
+.LP
+Please note that some modems (like the Huawei E220) requires this option in
+order to avoid a race condition that results in the incorrect DNS servers
+being assigned.
+.TP
.B user \fIname
Sets the name used for authenticating the local system to the peer to
\fIname\fR.
\fBrp_pppoe_sess\fP option name is supported.
.TP
.B pppoe-verbose \fIn
- Be verbose about discovered access concentrators. For backward
- compatibility also \fBrp_pppoe_verbose\fP option name is supported.
+ Be verbose about discovered access concentrators. When set to 2 or bigger
+ value then dump also discovery packets. For backward compatibility also
+ \fBrp_pppoe_verbose\fP option name is supported.
.TP
.B pppoe-mac \fImacaddr
Connect to specified MAC address.
The IP address for the remote end of the link. This is only set when
IPCP has come up.
.TP
+.B LLLOCAL
+The Link-Local IPv6 address for the local end of the link. This is only
+set when IPV6CP has come up.
+.TP
+.B LLREMOTE
+The Link-Local IPv6 address for the remote end of the link. This is only
+set when IPV6CP has come up.
+.TP
.B PEERNAME
The authenticated name of the peer. This is only set if the peer
authenticates itself.
If the peer supplies DNS server addresses, this variable is set to the
second DNS server address supplied (whether or not the usepeerdns
option was given).
+.TP
+.B WINS1
+If the peer supplies WINS server addresses, this variable is set to the
+first WINS server address supplied.
+.TP
+.B WINS2
+If the peer supplies WINS server addresses, this variable is set to the
+second WINS server address supplied.
+.P
.P
Pppd invokes the following scripts, if they exist. It is not an error
if they don't exist.