From ob@hq.seicom.net Sun Sep 19 16:24:30 1999
Return-Path: <ob@hq.seicom.net>
Received: from hq.seicom.net (hq.seicom.net [194.97.200.30])
	by hub.freebsd.org (Postfix) with ESMTP id 5B2FF14CB7
	for <FreeBSD-gnats-submit@freebsd.org>; Sun, 19 Sep 1999 16:24:22 -0700 (PDT)
	(envelope-from ob@hq.seicom.net)
Received: by hq.seicom.net (8.9.3/8.9.3) id BAA01033; Mon, 20 Sep 1999 01:24:21 +0200 (CEST)
Message-Id: <199909192324.BAA01033@hq.seicom.net>
Date: Mon, 20 Sep 1999 01:24:21 +0200 (CEST)
From: ob@seicom.net
Sender: ob@hq.seicom.net
Reply-To: ob@seicom.net
To: FreeBSD-gnats-submit@freebsd.org
Subject: i4b Failure in V3.1, 3.2, (3.3 ?)
X-Send-Pr-Version: 3.2

>Number:         13841
>Category:       kern
>Synopsis:       ISDN i4b does not hang up
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    hm
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sun Sep 19 16:30:02 PDT 1999
>Closed-Date:    Thu Dec 30 00:36:40 PST 1999
>Last-Modified:  Thu Dec 30 00:45:42 PST 1999
>Originator:     Oliver Breuninger
>Release:        FreeBSD 3.2-RELEASE i386
>Organization:
>Environment:

FreeBSD 3.1, 3.2 with ISDN-card e.g. AVM Fritz/A1

>Description:

PPP-Links transmits every 10 seconds a Keepalive packet, in FreeBSD 3.x
release distributions is the available fix no done !

>How-To-Repeat:

Configure i4b to a idle-timeout larger the 10 seconds, connect to your provider,
i4b does not disconnect the call.

>Fix:

Fixed by Paul Herman <pherman@element-5.de>

Fixed for FreeBSD 3.1 & 3.2, 3.3 not tested yet.
	
# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	.
#	./usr
#	./usr/include
#	./usr/include/net
#	./usr/include/net/if_sppp.h
#	./usr/include/net/if_sppp.h.orig
#	./usr/include/net/if_sppp.h.patch
#	./usr/src
#	./usr/src/sys
#	./usr/src/sys/net
#	./usr/src/sys/net/if_sppp.h
#	./usr/src/sys/net/if_sppp.h.orig
#	./usr/src/sys/net/if_sppp.h.patch
#	./usr/src/sys/net/if_spppsubr.c
#	./usr/src/sys/net/if_spppsubr.c.patch
#	./usr/src/sys/net/if_spppsubr.c.orig
#	./usr/src/sys/i4b
#	./usr/src/sys/i4b/driver
#	./usr/src/sys/i4b/driver/i4b_isppp.c
#	./usr/src/sys/i4b/driver/i4b_isppp.c.patch
#	./usr/src/sys/i4b/driver/i4b_isppp.c.orig
#	./usr/src/sys/i4b/layer4
#	./usr/src/sys/i4b/layer4/i4b_l4.c
#	./usr/src/sys/i4b/layer4/i4b_l4.c.patch
#	./usr/src/sys/i4b/layer4/i4b_l4.c.orig
#	./usr/src/sbin
#	./usr/src/sbin/spppcontrol
#	./usr/src/sbin/spppcontrol/Makefile
#	./usr/src/sbin/spppcontrol/spppcontrol.1
#	./usr/src/sbin/spppcontrol/spppcontrol.c
#	./usr/src/sbin/spppcontrol/spppcontrol.c.patch
#	./usr/src/sbin/spppcontrol/spppcontrol.c.orig
#
echo c - .
mkdir -p . > /dev/null 2>&1
echo c - ./usr
mkdir -p ./usr > /dev/null 2>&1
echo c - ./usr/include
mkdir -p ./usr/include > /dev/null 2>&1
echo c - ./usr/include/net
mkdir -p ./usr/include/net > /dev/null 2>&1
echo x - ./usr/include/net/if_sppp.h
sed 's/^X//' >./usr/include/net/if_sppp.h << 'END-of-./usr/include/net/if_sppp.h'
X/*
X * Defines for synchronous PPP/Cisco link level subroutines.
X *
X * Copyright (C) 1994 Cronyx Ltd.
X * Author: Serge Vakulenko, <vak@cronyx.ru>
X *
X * Heavily revamped to conform to RFC 1661.
X * Copyright (C) 1997, Joerg Wunsch.
X *
X * This software is distributed with NO WARRANTIES, not even the implied
X * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
X *
X * Authors grant any other persons or organizations permission to use
X * or modify this software as long as this message is kept with the software,
X * all derivative works or modified versions.
X *
X * From: Version 2.0, Fri Oct  6 20:39:21 MSK 1995
X *
X * $Id: if_sppp.h,v 1.13 1998/12/27 21:30:44 phk Exp $
X */
X
X#ifndef _NET_IF_SPPP_H_
X#define _NET_IF_SPPP_H_ 1
X
X#define IDX_LCP 0		/* idx into state table */
X
Xstruct slcp {
X	u_long	opts;		/* LCP options to send (bitfield) */
X	u_long  magic;          /* local magic number */
X	u_long	mru;		/* our max receive unit */
X	u_long	their_mru;	/* their max receive unit */
X	u_long	protos;		/* bitmask of protos that are started */
X	u_char  echoid;         /* id of last keepalive echo request */
X	/* restart max values, see RFC 1661 */
X	int	timeout;
X	int	max_terminate;
X	int	max_configure;
X	int	max_failure;
X};
X
X#define IDX_IPCP 1		/* idx into state table */
X
Xstruct sipcp {
X	u_long	opts;		/* IPCP options to send (bitfield) */
X	u_int	flags;
X#define IPCP_HISADDR_SEEN 1	/* have seen his address already */
X#define IPCP_MYADDR_DYN   2	/* my address is dynamically assigned */
X#define IPCP_MYADDR_SEEN  4	/* have seen his address already */
X};
X
X#define AUTHNAMELEN	32
X#define AUTHKEYLEN	16
X
Xstruct sauth {
X	u_short	proto;			/* authentication protocol to use */
X	u_short	flags;
X#define AUTHFLAG_NOCALLOUT	1	/* do not require authentication on */
X					/* callouts */
X#define AUTHFLAG_NORECHALLENGE	2	/* do not re-challenge CHAP */
X	u_char	name[AUTHNAMELEN];	/* system identification name */
X	u_char	secret[AUTHKEYLEN];	/* secret password */
X	u_char	challenge[AUTHKEYLEN];	/* random challenge */
X};
X
X#define IDX_PAP		2
X#define IDX_CHAP	3
X
X#define IDX_COUNT (IDX_CHAP + 1) /* bump this when adding cp's! */
X
X/*
X * Don't change the order of this.  Ordering the phases this way allows
X * for a comparision of ``pp_phase >= PHASE_AUTHENTICATE'' in order to
X * know whether LCP is up.
X */
Xenum ppp_phase {
X	PHASE_DEAD, PHASE_ESTABLISH, PHASE_TERMINATE,
X	PHASE_AUTHENTICATE, PHASE_NETWORK
X};
X
Xstruct sppp {
X	/* NB: pp_if _must_ be first */
X	struct  ifnet pp_if;    /* network interface data */
X	struct  ifqueue pp_fastq; /* fast output queue */
X	struct	ifqueue pp_cpq;	/* PPP control protocol queue */
X	struct  sppp *pp_next;  /* next interface in keepalive list */
X	u_int   pp_flags;       /* use Cisco protocol instead of PPP */
X	u_short pp_alivecnt;    /* keepalive packets counter */
X	u_short pp_loopcnt;     /* loopback detection counter */
X	u_long  pp_seq;         /* local sequence number */
X	u_long  pp_rseq;        /* remote sequence number */
X	time_t  pp_last_sent;
X	time_t  pp_last_recv;
X	enum ppp_phase pp_phase;	/* phase we're currently in */
X	int	state[IDX_COUNT];	/* state machine */
X	u_char  confid[IDX_COUNT];	/* id of last configuration request */
X	int	rst_counter[IDX_COUNT];	/* restart counter */
X	int	fail_counter[IDX_COUNT]; /* negotiation failure counter */
X	struct callout_handle ch[IDX_COUNT]; /* per-proto and if callouts */
X	struct callout_handle pap_my_to_ch; /* PAP needs one more... */
X	struct slcp lcp;		/* LCP params */
X	struct sipcp ipcp;		/* IPCP params */
X	struct sauth myauth;		/* auth params, i'm peer */
X	struct sauth hisauth;		/* auth params, i'm authenticator */
X	/*
X	 * These functions are filled in by sppp_attach(), and are
X	 * expected to be used by the lower layer (hardware) drivers
X	 * in order to communicate the (un)availability of the
X	 * communication link.  Lower layer drivers that are always
X	 * ready to communicate (like hardware HDLC) can shortcut
X	 * pp_up from pp_tls, and pp_down from pp_tlf.
X	 */
X	void	(*pp_up)(struct sppp *sp);
X	void	(*pp_down)(struct sppp *sp);
X	/*
X	 * These functions need to be filled in by the lower layer
X	 * (hardware) drivers if they request notification from the
X	 * PPP layer whether the link is actually required.  They
X	 * correspond to the tls and tlf actions.
X	 */
X	void	(*pp_tls)(struct sppp *sp);
X	void	(*pp_tlf)(struct sppp *sp);
X	/*
X	 * These (optional) functions may be filled by the hardware
X	 * driver if any notification of established connections
X	 * (currently: IPCP up) is desired (pp_con) or any internal
X	 * state change of the interface state machine should be
X	 * signaled for monitoring purposes (pp_chg).
X	 */
X	void	(*pp_con)(struct sppp *sp);
X	void	(*pp_chg)(struct sppp *sp, int new_state);
X	/* These two fields are for use by the lower layer */
X	void    *pp_lowerp;
X	int     pp_loweri;
X};
X
X#define PP_KEEPALIVE    0x01    /* use keepalive protocol */
X#define PP_CISCO        0x02    /* use Cisco protocol instead of PPP */
X				/* 0x04 was PP_TIMO */
X#define PP_CALLIN	0x08	/* we are being called */
X#define PP_NEEDAUTH	0x10	/* remote requested authentication */
X
X
X#define PP_MTU          1500    /* default/minimal MRU */
X#define PP_MAX_MRU	2048	/* maximal MRU we want to negotiate */
X
X/*
X * Definitions to pass struct sppp data down into the kernel using the
X * SIOC[SG]IFGENERIC ioctl interface.
X *
X * In order to use this, create a struct spppreq, fill in the cmd
X * field with SPPPIOGDEFS, and put the address of this structure into
X * the ifr_data portion of a struct ifreq.  Pass this struct to a
X * SIOCGIFGENERIC ioctl.  Then replace the cmd field by SPPPIOCDEFS,
X * modify the defs field as desired, and pass the struct ifreq now
X * to a SIOCSIFGENERIC ioctl.
X */
X
X#define SPPPIOGDEFS  ((caddr_t)(('S' << 24) + (1 << 16) + sizeof(struct sppp)))
X#define SPPPIOSDEFS  ((caddr_t)(('S' << 24) + (2 << 16) + sizeof(struct sppp)))
X
Xstruct spppreq {
X	int	cmd;
X	struct sppp defs;
X};
X
X#ifdef KERNEL
Xvoid sppp_attach (struct ifnet *ifp);
Xvoid sppp_detach (struct ifnet *ifp);
Xvoid sppp_input (struct ifnet *ifp, struct mbuf *m);
Xint sppp_ioctl (struct ifnet *ifp, u_long cmd, void *data);
Xstruct mbuf *sppp_dequeue (struct ifnet *ifp);
Xstruct mbuf *sppp_pick(struct ifnet *ifp);
Xint sppp_isempty (struct ifnet *ifp);
Xvoid sppp_flush (struct ifnet *ifp);
X#endif
X
X#endif /* _NET_IF_SPPP_H_ */
END-of-./usr/include/net/if_sppp.h
echo x - ./usr/include/net/if_sppp.h.orig
sed 's/^X//' >./usr/include/net/if_sppp.h.orig << 'END-of-./usr/include/net/if_sppp.h.orig'
X/*
X * Defines for synchronous PPP/Cisco link level subroutines.
X *
X * Copyright (C) 1994 Cronyx Ltd.
X * Author: Serge Vakulenko, <vak@cronyx.ru>
X *
X * Heavily revamped to conform to RFC 1661.
X * Copyright (C) 1997, Joerg Wunsch.
X *
X * This software is distributed with NO WARRANTIES, not even the implied
X * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
X *
X * Authors grant any other persons or organizations permission to use
X * or modify this software as long as this message is kept with the software,
X * all derivative works or modified versions.
X *
X * From: Version 2.0, Fri Oct  6 20:39:21 MSK 1995
X *
X * $Id: if_sppp.h,v 1.13 1998/12/27 21:30:44 phk Exp $
X */
X
X#ifndef _NET_IF_SPPP_H_
X#define _NET_IF_SPPP_H_ 1
X
X#define IDX_LCP 0		/* idx into state table */
X
Xstruct slcp {
X	u_long	opts;		/* LCP options to send (bitfield) */
X	u_long  magic;          /* local magic number */
X	u_long	mru;		/* our max receive unit */
X	u_long	their_mru;	/* their max receive unit */
X	u_long	protos;		/* bitmask of protos that are started */
X	u_char  echoid;         /* id of last keepalive echo request */
X	/* restart max values, see RFC 1661 */
X	int	timeout;
X	int	max_terminate;
X	int	max_configure;
X	int	max_failure;
X};
X
X#define IDX_IPCP 1		/* idx into state table */
X
Xstruct sipcp {
X	u_long	opts;		/* IPCP options to send (bitfield) */
X	u_int	flags;
X#define IPCP_HISADDR_SEEN 1	/* have seen his address already */
X#define IPCP_MYADDR_DYN   2	/* my address is dynamically assigned */
X#define IPCP_MYADDR_SEEN  4	/* have seen his address already */
X};
X
X#define AUTHNAMELEN	32
X#define AUTHKEYLEN	16
X
Xstruct sauth {
X	u_short	proto;			/* authentication protocol to use */
X	u_short	flags;
X#define AUTHFLAG_NOCALLOUT	1	/* do not require authentication on */
X					/* callouts */
X#define AUTHFLAG_NORECHALLENGE	2	/* do not re-challenge CHAP */
X	u_char	name[AUTHNAMELEN];	/* system identification name */
X	u_char	secret[AUTHKEYLEN];	/* secret password */
X	u_char	challenge[AUTHKEYLEN];	/* random challenge */
X};
X
X#define IDX_PAP		2
X#define IDX_CHAP	3
X
X#define IDX_COUNT (IDX_CHAP + 1) /* bump this when adding cp's! */
X
X/*
X * Don't change the order of this.  Ordering the phases this way allows
X * for a comparision of ``pp_phase >= PHASE_AUTHENTICATE'' in order to
X * know whether LCP is up.
X */
Xenum ppp_phase {
X	PHASE_DEAD, PHASE_ESTABLISH, PHASE_TERMINATE,
X	PHASE_AUTHENTICATE, PHASE_NETWORK
X};
X
Xstruct sppp {
X	/* NB: pp_if _must_ be first */
X	struct  ifnet pp_if;    /* network interface data */
X	struct  ifqueue pp_fastq; /* fast output queue */
X	struct	ifqueue pp_cpq;	/* PPP control protocol queue */
X	struct  sppp *pp_next;  /* next interface in keepalive list */
X	u_int   pp_flags;       /* use Cisco protocol instead of PPP */
X	u_short pp_alivecnt;    /* keepalive packets counter */
X	u_short pp_loopcnt;     /* loopback detection counter */
X	u_long  pp_seq;         /* local sequence number */
X	u_long  pp_rseq;        /* remote sequence number */
X	enum ppp_phase pp_phase;	/* phase we're currently in */
X	int	state[IDX_COUNT];	/* state machine */
X	u_char  confid[IDX_COUNT];	/* id of last configuration request */
X	int	rst_counter[IDX_COUNT];	/* restart counter */
X	int	fail_counter[IDX_COUNT]; /* negotiation failure counter */
X	struct callout_handle ch[IDX_COUNT]; /* per-proto and if callouts */
X	struct callout_handle pap_my_to_ch; /* PAP needs one more... */
X	struct slcp lcp;		/* LCP params */
X	struct sipcp ipcp;		/* IPCP params */
X	struct sauth myauth;		/* auth params, i'm peer */
X	struct sauth hisauth;		/* auth params, i'm authenticator */
X	/*
X	 * These functions are filled in by sppp_attach(), and are
X	 * expected to be used by the lower layer (hardware) drivers
X	 * in order to communicate the (un)availability of the
X	 * communication link.  Lower layer drivers that are always
X	 * ready to communicate (like hardware HDLC) can shortcut
X	 * pp_up from pp_tls, and pp_down from pp_tlf.
X	 */
X	void	(*pp_up)(struct sppp *sp);
X	void	(*pp_down)(struct sppp *sp);
X	/*
X	 * These functions need to be filled in by the lower layer
X	 * (hardware) drivers if they request notification from the
X	 * PPP layer whether the link is actually required.  They
X	 * correspond to the tls and tlf actions.
X	 */
X	void	(*pp_tls)(struct sppp *sp);
X	void	(*pp_tlf)(struct sppp *sp);
X	/*
X	 * These (optional) functions may be filled by the hardware
X	 * driver if any notification of established connections
X	 * (currently: IPCP up) is desired (pp_con) or any internal
X	 * state change of the interface state machine should be
X	 * signaled for monitoring purposes (pp_chg).
X	 */
X	void	(*pp_con)(struct sppp *sp);
X	void	(*pp_chg)(struct sppp *sp, int new_state);
X	/* These two fields are for use by the lower layer */
X	void    *pp_lowerp;
X	int     pp_loweri;
X};
X
X#define PP_KEEPALIVE    0x01    /* use keepalive protocol */
X#define PP_CISCO        0x02    /* use Cisco protocol instead of PPP */
X				/* 0x04 was PP_TIMO */
X#define PP_CALLIN	0x08	/* we are being called */
X#define PP_NEEDAUTH	0x10	/* remote requested authentication */
X
X
X#define PP_MTU          1500    /* default/minimal MRU */
X#define PP_MAX_MRU	2048	/* maximal MRU we want to negotiate */
X
X/*
X * Definitions to pass struct sppp data down into the kernel using the
X * SIOC[SG]IFGENERIC ioctl interface.
X *
X * In order to use this, create a struct spppreq, fill in the cmd
X * field with SPPPIOGDEFS, and put the address of this structure into
X * the ifr_data portion of a struct ifreq.  Pass this struct to a
X * SIOCGIFGENERIC ioctl.  Then replace the cmd field by SPPPIOCDEFS,
X * modify the defs field as desired, and pass the struct ifreq now
X * to a SIOCSIFGENERIC ioctl.
X */
X
X#define SPPPIOGDEFS  ((caddr_t)(('S' << 24) + (1 << 16) + sizeof(struct sppp)))
X#define SPPPIOSDEFS  ((caddr_t)(('S' << 24) + (2 << 16) + sizeof(struct sppp)))
X
Xstruct spppreq {
X	int	cmd;
X	struct sppp defs;
X};
X
X#ifdef KERNEL
Xvoid sppp_attach (struct ifnet *ifp);
Xvoid sppp_detach (struct ifnet *ifp);
Xvoid sppp_input (struct ifnet *ifp, struct mbuf *m);
Xint sppp_ioctl (struct ifnet *ifp, u_long cmd, void *data);
Xstruct mbuf *sppp_dequeue (struct ifnet *ifp);
Xstruct mbuf *sppp_pick(struct ifnet *ifp);
Xint sppp_isempty (struct ifnet *ifp);
Xvoid sppp_flush (struct ifnet *ifp);
X#endif
X
X#endif /* _NET_IF_SPPP_H_ */
END-of-./usr/include/net/if_sppp.h.orig
echo x - ./usr/include/net/if_sppp.h.patch
sed 's/^X//' >./usr/include/net/if_sppp.h.patch << 'END-of-./usr/include/net/if_sppp.h.patch'
X*** if_sppp.h		Tue Dec  1 21:20:19 1998
X--- if_sppp.h.new	Tue Mar 16 15:54:06 1999
X***************
X*** 90,95 ****
X--- 90,97 ----
X  	u_short pp_loopcnt;     /* loopback detection counter */
X  	u_long  pp_seq;         /* local sequence number */
X  	u_long  pp_rseq;        /* remote sequence number */
X+ 	time_t  pp_last_sent;
X+ 	time_t  pp_last_recv;
X  	enum ppp_phase pp_phase;	/* phase we're currently in */
X  	int	state[IDX_COUNT];	/* state machine */
X  	u_char  confid[IDX_COUNT];	/* id of last configuration request */
END-of-./usr/include/net/if_sppp.h.patch
echo c - ./usr/src
mkdir -p ./usr/src > /dev/null 2>&1
echo c - ./usr/src/sys
mkdir -p ./usr/src/sys > /dev/null 2>&1
echo c - ./usr/src/sys/net
mkdir -p ./usr/src/sys/net > /dev/null 2>&1
echo x - ./usr/src/sys/net/if_sppp.h
sed 's/^X//' >./usr/src/sys/net/if_sppp.h << 'END-of-./usr/src/sys/net/if_sppp.h'
X/*
X * Defines for synchronous PPP/Cisco link level subroutines.
X *
X * Copyright (C) 1994 Cronyx Ltd.
X * Author: Serge Vakulenko, <vak@cronyx.ru>
X *
X * Heavily revamped to conform to RFC 1661.
X * Copyright (C) 1997, Joerg Wunsch.
X *
X * This software is distributed with NO WARRANTIES, not even the implied
X * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
X *
X * Authors grant any other persons or organizations permission to use
X * or modify this software as long as this message is kept with the software,
X * all derivative works or modified versions.
X *
X * From: Version 2.0, Fri Oct  6 20:39:21 MSK 1995
X *
X * $Id: if_sppp.h,v 1.13 1998/12/27 21:30:44 phk Exp $
X */
X
X#ifndef _NET_IF_SPPP_H_
X#define _NET_IF_SPPP_H_ 1
X
X#define IDX_LCP 0		/* idx into state table */
X
Xstruct slcp {
X	u_long	opts;		/* LCP options to send (bitfield) */
X	u_long  magic;          /* local magic number */
X	u_long	mru;		/* our max receive unit */
X	u_long	their_mru;	/* their max receive unit */
X	u_long	protos;		/* bitmask of protos that are started */
X	u_char  echoid;         /* id of last keepalive echo request */
X	/* restart max values, see RFC 1661 */
X	int	timeout;
X	int	max_terminate;
X	int	max_configure;
X	int	max_failure;
X};
X
X#define IDX_IPCP 1		/* idx into state table */
X
Xstruct sipcp {
X	u_long	opts;		/* IPCP options to send (bitfield) */
X	u_int	flags;
X#define IPCP_HISADDR_SEEN 1	/* have seen his address already */
X#define IPCP_MYADDR_DYN   2	/* my address is dynamically assigned */
X#define IPCP_MYADDR_SEEN  4	/* have seen his address already */
X};
X
X#define AUTHNAMELEN	32
X#define AUTHKEYLEN	16
X
Xstruct sauth {
X	u_short	proto;			/* authentication protocol to use */
X	u_short	flags;
X#define AUTHFLAG_NOCALLOUT	1	/* do not require authentication on */
X					/* callouts */
X#define AUTHFLAG_NORECHALLENGE	2	/* do not re-challenge CHAP */
X	u_char	name[AUTHNAMELEN];	/* system identification name */
X	u_char	secret[AUTHKEYLEN];	/* secret password */
X	u_char	challenge[AUTHKEYLEN];	/* random challenge */
X};
X
X#define IDX_PAP		2
X#define IDX_CHAP	3
X
X#define IDX_COUNT (IDX_CHAP + 1) /* bump this when adding cp's! */
X
X/*
X * Don't change the order of this.  Ordering the phases this way allows
X * for a comparision of ``pp_phase >= PHASE_AUTHENTICATE'' in order to
X * know whether LCP is up.
X */
Xenum ppp_phase {
X	PHASE_DEAD, PHASE_ESTABLISH, PHASE_TERMINATE,
X	PHASE_AUTHENTICATE, PHASE_NETWORK
X};
X
Xstruct sppp {
X	/* NB: pp_if _must_ be first */
X	struct  ifnet pp_if;    /* network interface data */
X	struct  ifqueue pp_fastq; /* fast output queue */
X	struct	ifqueue pp_cpq;	/* PPP control protocol queue */
X	struct  sppp *pp_next;  /* next interface in keepalive list */
X	u_int   pp_flags;       /* use Cisco protocol instead of PPP */
X	u_short pp_alivecnt;    /* keepalive packets counter */
X	u_short pp_loopcnt;     /* loopback detection counter */
X	u_long  pp_seq;         /* local sequence number */
X	u_long  pp_rseq;        /* remote sequence number */
X	time_t  pp_last_sent;
X	time_t  pp_last_recv;
X	enum ppp_phase pp_phase;	/* phase we're currently in */
X	int	state[IDX_COUNT];	/* state machine */
X	u_char  confid[IDX_COUNT];	/* id of last configuration request */
X	int	rst_counter[IDX_COUNT];	/* restart counter */
X	int	fail_counter[IDX_COUNT]; /* negotiation failure counter */
X	struct callout_handle ch[IDX_COUNT]; /* per-proto and if callouts */
X	struct callout_handle pap_my_to_ch; /* PAP needs one more... */
X	struct slcp lcp;		/* LCP params */
X	struct sipcp ipcp;		/* IPCP params */
X	struct sauth myauth;		/* auth params, i'm peer */
X	struct sauth hisauth;		/* auth params, i'm authenticator */
X	/*
X	 * These functions are filled in by sppp_attach(), and are
X	 * expected to be used by the lower layer (hardware) drivers
X	 * in order to communicate the (un)availability of the
X	 * communication link.  Lower layer drivers that are always
X	 * ready to communicate (like hardware HDLC) can shortcut
X	 * pp_up from pp_tls, and pp_down from pp_tlf.
X	 */
X	void	(*pp_up)(struct sppp *sp);
X	void	(*pp_down)(struct sppp *sp);
X	/*
X	 * These functions need to be filled in by the lower layer
X	 * (hardware) drivers if they request notification from the
X	 * PPP layer whether the link is actually required.  They
X	 * correspond to the tls and tlf actions.
X	 */
X	void	(*pp_tls)(struct sppp *sp);
X	void	(*pp_tlf)(struct sppp *sp);
X	/*
X	 * These (optional) functions may be filled by the hardware
X	 * driver if any notification of established connections
X	 * (currently: IPCP up) is desired (pp_con) or any internal
X	 * state change of the interface state machine should be
X	 * signaled for monitoring purposes (pp_chg).
X	 */
X	void	(*pp_con)(struct sppp *sp);
X	void	(*pp_chg)(struct sppp *sp, int new_state);
X	/* These two fields are for use by the lower layer */
X	void    *pp_lowerp;
X	int     pp_loweri;
X};
X
X#define PP_KEEPALIVE    0x01    /* use keepalive protocol */
X#define PP_CISCO        0x02    /* use Cisco protocol instead of PPP */
X				/* 0x04 was PP_TIMO */
X#define PP_CALLIN	0x08	/* we are being called */
X#define PP_NEEDAUTH	0x10	/* remote requested authentication */
X
X
X#define PP_MTU          1500    /* default/minimal MRU */
X#define PP_MAX_MRU	2048	/* maximal MRU we want to negotiate */
X
X/*
X * Definitions to pass struct sppp data down into the kernel using the
X * SIOC[SG]IFGENERIC ioctl interface.
X *
X * In order to use this, create a struct spppreq, fill in the cmd
X * field with SPPPIOGDEFS, and put the address of this structure into
X * the ifr_data portion of a struct ifreq.  Pass this struct to a
X * SIOCGIFGENERIC ioctl.  Then replace the cmd field by SPPPIOCDEFS,
X * modify the defs field as desired, and pass the struct ifreq now
X * to a SIOCSIFGENERIC ioctl.
X */
X
X#define SPPPIOGDEFS  ((caddr_t)(('S' << 24) + (1 << 16) + sizeof(struct sppp)))
X#define SPPPIOSDEFS  ((caddr_t)(('S' << 24) + (2 << 16) + sizeof(struct sppp)))
X
Xstruct spppreq {
X	int	cmd;
X	struct sppp defs;
X};
X
X#ifdef KERNEL
Xvoid sppp_attach (struct ifnet *ifp);
Xvoid sppp_detach (struct ifnet *ifp);
Xvoid sppp_input (struct ifnet *ifp, struct mbuf *m);
Xint sppp_ioctl (struct ifnet *ifp, u_long cmd, void *data);
Xstruct mbuf *sppp_dequeue (struct ifnet *ifp);
Xstruct mbuf *sppp_pick(struct ifnet *ifp);
Xint sppp_isempty (struct ifnet *ifp);
Xvoid sppp_flush (struct ifnet *ifp);
X#endif
X
X#endif /* _NET_IF_SPPP_H_ */
END-of-./usr/src/sys/net/if_sppp.h
echo x - ./usr/src/sys/net/if_sppp.h.orig
sed 's/^X//' >./usr/src/sys/net/if_sppp.h.orig << 'END-of-./usr/src/sys/net/if_sppp.h.orig'
X/*
X * Defines for synchronous PPP/Cisco link level subroutines.
X *
X * Copyright (C) 1994 Cronyx Ltd.
X * Author: Serge Vakulenko, <vak@cronyx.ru>
X *
X * Heavily revamped to conform to RFC 1661.
X * Copyright (C) 1997, Joerg Wunsch.
X *
X * This software is distributed with NO WARRANTIES, not even the implied
X * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
X *
X * Authors grant any other persons or organizations permission to use
X * or modify this software as long as this message is kept with the software,
X * all derivative works or modified versions.
X *
X * From: Version 2.0, Fri Oct  6 20:39:21 MSK 1995
X *
X * $Id: if_sppp.h,v 1.13 1998/12/27 21:30:44 phk Exp $
X */
X
X#ifndef _NET_IF_SPPP_H_
X#define _NET_IF_SPPP_H_ 1
X
X#define IDX_LCP 0		/* idx into state table */
X
Xstruct slcp {
X	u_long	opts;		/* LCP options to send (bitfield) */
X	u_long  magic;          /* local magic number */
X	u_long	mru;		/* our max receive unit */
X	u_long	their_mru;	/* their max receive unit */
X	u_long	protos;		/* bitmask of protos that are started */
X	u_char  echoid;         /* id of last keepalive echo request */
X	/* restart max values, see RFC 1661 */
X	int	timeout;
X	int	max_terminate;
X	int	max_configure;
X	int	max_failure;
X};
X
X#define IDX_IPCP 1		/* idx into state table */
X
Xstruct sipcp {
X	u_long	opts;		/* IPCP options to send (bitfield) */
X	u_int	flags;
X#define IPCP_HISADDR_SEEN 1	/* have seen his address already */
X#define IPCP_MYADDR_DYN   2	/* my address is dynamically assigned */
X#define IPCP_MYADDR_SEEN  4	/* have seen his address already */
X};
X
X#define AUTHNAMELEN	32
X#define AUTHKEYLEN	16
X
Xstruct sauth {
X	u_short	proto;			/* authentication protocol to use */
X	u_short	flags;
X#define AUTHFLAG_NOCALLOUT	1	/* do not require authentication on */
X					/* callouts */
X#define AUTHFLAG_NORECHALLENGE	2	/* do not re-challenge CHAP */
X	u_char	name[AUTHNAMELEN];	/* system identification name */
X	u_char	secret[AUTHKEYLEN];	/* secret password */
X	u_char	challenge[AUTHKEYLEN];	/* random challenge */
X};
X
X#define IDX_PAP		2
X#define IDX_CHAP	3
X
X#define IDX_COUNT (IDX_CHAP + 1) /* bump this when adding cp's! */
X
X/*
X * Don't change the order of this.  Ordering the phases this way allows
X * for a comparision of ``pp_phase >= PHASE_AUTHENTICATE'' in order to
X * know whether LCP is up.
X */
Xenum ppp_phase {
X	PHASE_DEAD, PHASE_ESTABLISH, PHASE_TERMINATE,
X	PHASE_AUTHENTICATE, PHASE_NETWORK
X};
X
Xstruct sppp {
X	/* NB: pp_if _must_ be first */
X	struct  ifnet pp_if;    /* network interface data */
X	struct  ifqueue pp_fastq; /* fast output queue */
X	struct	ifqueue pp_cpq;	/* PPP control protocol queue */
X	struct  sppp *pp_next;  /* next interface in keepalive list */
X	u_int   pp_flags;       /* use Cisco protocol instead of PPP */
X	u_short pp_alivecnt;    /* keepalive packets counter */
X	u_short pp_loopcnt;     /* loopback detection counter */
X	u_long  pp_seq;         /* local sequence number */
X	u_long  pp_rseq;        /* remote sequence number */
X	enum ppp_phase pp_phase;	/* phase we're currently in */
X	int	state[IDX_COUNT];	/* state machine */
X	u_char  confid[IDX_COUNT];	/* id of last configuration request */
X	int	rst_counter[IDX_COUNT];	/* restart counter */
X	int	fail_counter[IDX_COUNT]; /* negotiation failure counter */
X	struct callout_handle ch[IDX_COUNT]; /* per-proto and if callouts */
X	struct callout_handle pap_my_to_ch; /* PAP needs one more... */
X	struct slcp lcp;		/* LCP params */
X	struct sipcp ipcp;		/* IPCP params */
X	struct sauth myauth;		/* auth params, i'm peer */
X	struct sauth hisauth;		/* auth params, i'm authenticator */
X	/*
X	 * These functions are filled in by sppp_attach(), and are
X	 * expected to be used by the lower layer (hardware) drivers
X	 * in order to communicate the (un)availability of the
X	 * communication link.  Lower layer drivers that are always
X	 * ready to communicate (like hardware HDLC) can shortcut
X	 * pp_up from pp_tls, and pp_down from pp_tlf.
X	 */
X	void	(*pp_up)(struct sppp *sp);
X	void	(*pp_down)(struct sppp *sp);
X	/*
X	 * These functions need to be filled in by the lower layer
X	 * (hardware) drivers if they request notification from the
X	 * PPP layer whether the link is actually required.  They
X	 * correspond to the tls and tlf actions.
X	 */
X	void	(*pp_tls)(struct sppp *sp);
X	void	(*pp_tlf)(struct sppp *sp);
X	/*
X	 * These (optional) functions may be filled by the hardware
X	 * driver if any notification of established connections
X	 * (currently: IPCP up) is desired (pp_con) or any internal
X	 * state change of the interface state machine should be
X	 * signaled for monitoring purposes (pp_chg).
X	 */
X	void	(*pp_con)(struct sppp *sp);
X	void	(*pp_chg)(struct sppp *sp, int new_state);
X	/* These two fields are for use by the lower layer */
X	void    *pp_lowerp;
X	int     pp_loweri;
X};
X
X#define PP_KEEPALIVE    0x01    /* use keepalive protocol */
X#define PP_CISCO        0x02    /* use Cisco protocol instead of PPP */
X				/* 0x04 was PP_TIMO */
X#define PP_CALLIN	0x08	/* we are being called */
X#define PP_NEEDAUTH	0x10	/* remote requested authentication */
X
X
X#define PP_MTU          1500    /* default/minimal MRU */
X#define PP_MAX_MRU	2048	/* maximal MRU we want to negotiate */
X
X/*
X * Definitions to pass struct sppp data down into the kernel using the
X * SIOC[SG]IFGENERIC ioctl interface.
X *
X * In order to use this, create a struct spppreq, fill in the cmd
X * field with SPPPIOGDEFS, and put the address of this structure into
X * the ifr_data portion of a struct ifreq.  Pass this struct to a
X * SIOCGIFGENERIC ioctl.  Then replace the cmd field by SPPPIOCDEFS,
X * modify the defs field as desired, and pass the struct ifreq now
X * to a SIOCSIFGENERIC ioctl.
X */
X
X#define SPPPIOGDEFS  ((caddr_t)(('S' << 24) + (1 << 16) + sizeof(struct sppp)))
X#define SPPPIOSDEFS  ((caddr_t)(('S' << 24) + (2 << 16) + sizeof(struct sppp)))
X
Xstruct spppreq {
X	int	cmd;
X	struct sppp defs;
X};
X
X#ifdef KERNEL
Xvoid sppp_attach (struct ifnet *ifp);
Xvoid sppp_detach (struct ifnet *ifp);
Xvoid sppp_input (struct ifnet *ifp, struct mbuf *m);
Xint sppp_ioctl (struct ifnet *ifp, u_long cmd, void *data);
Xstruct mbuf *sppp_dequeue (struct ifnet *ifp);
Xstruct mbuf *sppp_pick(struct ifnet *ifp);
Xint sppp_isempty (struct ifnet *ifp);
Xvoid sppp_flush (struct ifnet *ifp);
X#endif
X
X#endif /* _NET_IF_SPPP_H_ */
END-of-./usr/src/sys/net/if_sppp.h.orig
echo x - ./usr/src/sys/net/if_sppp.h.patch
sed 's/^X//' >./usr/src/sys/net/if_sppp.h.patch << 'END-of-./usr/src/sys/net/if_sppp.h.patch'
X*** if_sppp.h		Tue Dec  1 21:20:19 1998
X--- if_sppp.h.new	Tue Mar 16 15:54:06 1999
X***************
X*** 90,95 ****
X--- 90,97 ----
X  	u_short pp_loopcnt;     /* loopback detection counter */
X  	u_long  pp_seq;         /* local sequence number */
X  	u_long  pp_rseq;        /* remote sequence number */
X+ 	time_t  pp_last_sent;
X+ 	time_t  pp_last_recv;
X  	enum ppp_phase pp_phase;	/* phase we're currently in */
X  	int	state[IDX_COUNT];	/* state machine */
X  	u_char  confid[IDX_COUNT];	/* id of last configuration request */
END-of-./usr/src/sys/net/if_sppp.h.patch
echo x - ./usr/src/sys/net/if_spppsubr.c
sed 's/^X//' >./usr/src/sys/net/if_spppsubr.c << 'END-of-./usr/src/sys/net/if_spppsubr.c'
X/*
X * Synchronous PPP/Cisco link level subroutines.
X * Keepalive protocol implemented in both Cisco and PPP modes.
X *
X * Copyright (C) 1994-1996 Cronyx Engineering Ltd.
X * Author: Serge Vakulenko, <vak@cronyx.ru>
X *
X * Heavily revamped to conform to RFC 1661.
X * Copyright (C) 1997, Joerg Wunsch.
X *
X * This software is distributed with NO WARRANTIES, not even the implied
X * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
X *
X * Authors grant any other persons or organisations permission to use
X * or modify this software as long as this message is kept with the software,
X * all derivative works or modified versions.
X *
X * From: Version 2.4, Thu Apr 30 17:17:21 MSD 1997
X *
X * $Id: if_spppsubr.c,v 1.52 1998/12/27 21:30:44 phk Exp $
X */
X
X#include <sys/param.h>
X
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X#include "opt_inet.h"
X#include "opt_ipx.h"
X#endif
X
X#ifdef NetBSD1_3
X#  if NetBSD1_3 > 6
X#      include "opt_inet.h"
X#      include "opt_iso.h"
X#  endif
X#endif
X
X#include <sys/systm.h>
X#include <sys/kernel.h>
X#include <sys/sockio.h>
X#include <sys/socket.h>
X#include <sys/syslog.h>
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X#include <machine/random.h>
X#endif
X#include <sys/malloc.h>
X#include <sys/mbuf.h>
X
X#if defined (__OpenBSD__)
X#include <sys/md5k.h>
X#else
X#include <sys/md5.h>
X#endif
X
X#include <net/if.h>
X#include <net/netisr.h>
X#include <net/if_types.h>
X#include <net/route.h>
X
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X#include <machine/random.h>
X#endif
X#if defined (__NetBSD__) || defined (__OpenBSD__)
X#include <machine/cpu.h> /* XXX for softnet */
X#endif
X
X#include <machine/stdarg.h>
X
X#ifdef INET
X#include <netinet/in.h>
X#include <netinet/in_systm.h>
X#include <netinet/in_var.h>
X#include <netinet/ip.h>
X#include <netinet/tcp.h>
X# if defined (__FreeBSD__) || defined (__OpenBSD__)
X#  include <netinet/if_ether.h>
X# else
X#  include <net/ethertypes.h>
X# endif
X#else
X# error Huh? sppp without INET?
X#endif
X
X#ifdef IPX
X#include <netipx/ipx.h>
X#include <netipx/ipx_if.h>
X#endif
X
X#ifdef NS
X#include <netns/ns.h>
X#include <netns/ns_if.h>
X#endif
X
X#ifdef ISO
X#include <netiso/argo_debug.h>
X#include <netiso/iso.h>
X#include <netiso/iso_var.h>
X#include <netiso/iso_snpac.h>
X#endif
X
X#include <net/if_sppp.h>
X
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X# define UNTIMEOUT(fun, arg, handle) untimeout(fun, arg, handle)
X# define TIMEOUT(fun, arg1, arg2, handle) handle = timeout(fun, arg1, arg2)
X# define IOCTL_CMD_T	u_long
X#else
X# define UNTIMEOUT(fun, arg, handle) untimeout(fun, arg)
X# define TIMEOUT(fun, arg1, arg2, handle) timeout(fun, arg1, arg2)
X# define IOCTL_CMD_T	int
X#endif
X
X#define MAXALIVECNT     3               /* max. alive packets */
X
X/*
X * Interface flags that can be set in an ifconfig command.
X *
X * Setting link0 will make the link passive, i.e. it will be marked
X * as being administrative openable, but won't be opened to begin
X * with.  Incoming calls will be answered, or subsequent calls with
X * -link1 will cause the administrative open of the LCP layer.
X *
X * Setting link1 will cause the link to auto-dial only as packets
X * arrive to be sent.
X *
X * Setting IFF_DEBUG will syslog the option negotiation and state
X * transitions at level kern.debug.  Note: all logs consistently look
X * like
X *
X *   <if-name><unit>: <proto-name> <additional info...>
X *
X * with <if-name><unit> being something like "bppp0", and <proto-name>
X * being one of "lcp", "ipcp", "cisco", "chap", "pap", etc.
X */
X
X#define IFF_PASSIVE	IFF_LINK0	/* wait passively for connection */
X#define IFF_AUTO	IFF_LINK1	/* auto-dial on output */
X
X#define PPP_ALLSTATIONS 0xff		/* All-Stations broadcast address */
X#define PPP_UI		0x03		/* Unnumbered Information */
X#define PPP_IP		0x0021		/* Internet Protocol */
X#define PPP_ISO		0x0023		/* ISO OSI Protocol */
X#define PPP_XNS		0x0025		/* Xerox NS Protocol */
X#define PPP_IPX		0x002b		/* Novell IPX Protocol */
X#define PPP_LCP		0xc021		/* Link Control Protocol */
X#define PPP_PAP		0xc023		/* Password Authentication Protocol */
X#define PPP_CHAP	0xc223		/* Challenge-Handshake Auth Protocol */
X#define PPP_IPCP	0x8021		/* Internet Protocol Control Protocol */
X
X#define CONF_REQ	1		/* PPP configure request */
X#define CONF_ACK	2		/* PPP configure acknowledge */
X#define CONF_NAK	3		/* PPP configure negative ack */
X#define CONF_REJ	4		/* PPP configure reject */
X#define TERM_REQ	5		/* PPP terminate request */
X#define TERM_ACK	6		/* PPP terminate acknowledge */
X#define CODE_REJ	7		/* PPP code reject */
X#define PROTO_REJ	8		/* PPP protocol reject */
X#define ECHO_REQ	9		/* PPP echo request */
X#define ECHO_REPLY	10		/* PPP echo reply */
X#define DISC_REQ	11		/* PPP discard request */
X
X#define LCP_OPT_MRU		1	/* maximum receive unit */
X#define LCP_OPT_ASYNC_MAP	2	/* async control character map */
X#define LCP_OPT_AUTH_PROTO	3	/* authentication protocol */
X#define LCP_OPT_QUAL_PROTO	4	/* quality protocol */
X#define LCP_OPT_MAGIC		5	/* magic number */
X#define LCP_OPT_RESERVED	6	/* reserved */
X#define LCP_OPT_PROTO_COMP	7	/* protocol field compression */
X#define LCP_OPT_ADDR_COMP	8	/* address/control field compression */
X
X#define IPCP_OPT_ADDRESSES	1	/* both IP addresses; deprecated */
X#define IPCP_OPT_COMPRESSION	2	/* IP compression protocol (VJ) */
X#define IPCP_OPT_ADDRESS	3	/* local IP address */
X
X#define PAP_REQ			1	/* PAP name/password request */
X#define PAP_ACK			2	/* PAP acknowledge */
X#define PAP_NAK			3	/* PAP fail */
X
X#define CHAP_CHALLENGE		1	/* CHAP challenge request */
X#define CHAP_RESPONSE		2	/* CHAP challenge response */
X#define CHAP_SUCCESS		3	/* CHAP response ok */
X#define CHAP_FAILURE		4	/* CHAP response failed */
X
X#define CHAP_MD5		5	/* hash algorithm - MD5 */
X
X#define CISCO_MULTICAST		0x8f	/* Cisco multicast address */
X#define CISCO_UNICAST		0x0f	/* Cisco unicast address */
X#define CISCO_KEEPALIVE		0x8035	/* Cisco keepalive protocol */
X#define CISCO_ADDR_REQ		0	/* Cisco address request */
X#define CISCO_ADDR_REPLY	1	/* Cisco address reply */
X#define CISCO_KEEPALIVE_REQ	2	/* Cisco keepalive request */
X
X/* states are named and numbered according to RFC 1661 */
X#define STATE_INITIAL	0
X#define STATE_STARTING	1
X#define STATE_CLOSED	2
X#define STATE_STOPPED	3
X#define STATE_CLOSING	4
X#define STATE_STOPPING	5
X#define STATE_REQ_SENT	6
X#define STATE_ACK_RCVD	7
X#define STATE_ACK_SENT	8
X#define STATE_OPENED	9
X
Xstruct ppp_header {
X	u_char address;
X	u_char control;
X	u_short protocol;
X};
X#define PPP_HEADER_LEN          sizeof (struct ppp_header)
X
Xstruct lcp_header {
X	u_char type;
X	u_char ident;
X	u_short len;
X};
X#define LCP_HEADER_LEN          sizeof (struct lcp_header)
X
Xstruct cisco_packet {
X	u_long type;
X	u_long par1;
X	u_long par2;
X	u_short rel;
X	u_short time0;
X	u_short time1;
X};
X#define CISCO_PACKET_LEN 18
X
X/*
X * We follow the spelling and capitalization of RFC 1661 here, to make
X * it easier comparing with the standard.  Please refer to this RFC in
X * case you can't make sense out of these abbreviation; it will also
X * explain the semantics related to the various events and actions.
X */
Xstruct cp {
X	u_short	proto;		/* PPP control protocol number */
X	u_char protoidx;	/* index into state table in struct sppp */
X	u_char flags;
X#define CP_LCP		0x01	/* this is the LCP */
X#define CP_AUTH		0x02	/* this is an authentication protocol */
X#define CP_NCP		0x04	/* this is a NCP */
X#define CP_QUAL		0x08	/* this is a quality reporting protocol */
X	const char *name;	/* name of this control protocol */
X	/* event handlers */
X	void	(*Up)(struct sppp *sp);
X	void	(*Down)(struct sppp *sp);
X	void	(*Open)(struct sppp *sp);
X	void	(*Close)(struct sppp *sp);
X	void	(*TO)(void *sp);
X	int	(*RCR)(struct sppp *sp, struct lcp_header *h, int len);
X	void	(*RCN_rej)(struct sppp *sp, struct lcp_header *h, int len);
X	void	(*RCN_nak)(struct sppp *sp, struct lcp_header *h, int len);
X	/* actions */
X	void	(*tlu)(struct sppp *sp);
X	void	(*tld)(struct sppp *sp);
X	void	(*tls)(struct sppp *sp);
X	void	(*tlf)(struct sppp *sp);
X	void	(*scr)(struct sppp *sp);
X};
X
Xstatic struct sppp *spppq;
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
Xstatic struct callout_handle keepalive_ch;
X#endif
X
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X#define	SPP_FMT		"%s%d: "
X#define	SPP_ARGS(ifp)	(ifp)->if_name, (ifp)->if_unit
X#else
X#define	SPP_FMT		"%s: "
X#define	SPP_ARGS(ifp)	(ifp)->if_xname
X#endif
X
X/*
X * The following disgusting hack gets around the problem that IP TOS
X * can't be set yet.  We want to put "interactive" traffic on a high
X * priority queue.  To decide if traffic is interactive, we check that
X * a) it is TCP and b) one of its ports is telnet, rlogin or ftp control.
X *
X * XXX is this really still necessary?  - joerg -
X */
Xstatic u_short interactive_ports[8] = {
X	0,	513,	0,	0,
X	0,	21,	0,	23,
X};
X#define INTERACTIVE(p) (interactive_ports[(p) & 7] == (p))
X
X/* almost every function needs these */
X#define STDDCL							\
X	struct ifnet *ifp = &sp->pp_if;				\
X	int debug = ifp->if_flags & IFF_DEBUG
X
Xstatic int sppp_output(struct ifnet *ifp, struct mbuf *m,
X		       struct sockaddr *dst, struct rtentry *rt);
X
Xstatic void sppp_cisco_send(struct sppp *sp, int type, long par1, long par2);
Xstatic void sppp_cisco_input(struct sppp *sp, struct mbuf *m);
X
Xstatic void sppp_cp_input(const struct cp *cp, struct sppp *sp,
X			  struct mbuf *m);
Xstatic void sppp_cp_send(struct sppp *sp, u_short proto, u_char type,
X			 u_char ident, u_short len, void *data);
X/* static void sppp_cp_timeout(void *arg); */
Xstatic void sppp_cp_change_state(const struct cp *cp, struct sppp *sp,
X				 int newstate);
Xstatic void sppp_auth_send(const struct cp *cp,
X			   struct sppp *sp, unsigned int type, unsigned int id,
X			   ...);
X
Xstatic void sppp_up_event(const struct cp *cp, struct sppp *sp);
Xstatic void sppp_down_event(const struct cp *cp, struct sppp *sp);
Xstatic void sppp_open_event(const struct cp *cp, struct sppp *sp);
Xstatic void sppp_close_event(const struct cp *cp, struct sppp *sp);
Xstatic void sppp_to_event(const struct cp *cp, struct sppp *sp);
X
Xstatic void sppp_null(struct sppp *sp);
X
Xstatic void sppp_lcp_init(struct sppp *sp);
Xstatic void sppp_lcp_up(struct sppp *sp);
Xstatic void sppp_lcp_down(struct sppp *sp);
Xstatic void sppp_lcp_open(struct sppp *sp);
Xstatic void sppp_lcp_close(struct sppp *sp);
Xstatic void sppp_lcp_TO(void *sp);
Xstatic int sppp_lcp_RCR(struct sppp *sp, struct lcp_header *h, int len);
Xstatic void sppp_lcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len);
Xstatic void sppp_lcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len);
Xstatic void sppp_lcp_tlu(struct sppp *sp);
Xstatic void sppp_lcp_tld(struct sppp *sp);
Xstatic void sppp_lcp_tls(struct sppp *sp);
Xstatic void sppp_lcp_tlf(struct sppp *sp);
Xstatic void sppp_lcp_scr(struct sppp *sp);
Xstatic void sppp_lcp_check_and_close(struct sppp *sp);
Xstatic int sppp_ncp_check(struct sppp *sp);
X
Xstatic void sppp_ipcp_init(struct sppp *sp);
Xstatic void sppp_ipcp_up(struct sppp *sp);
Xstatic void sppp_ipcp_down(struct sppp *sp);
Xstatic void sppp_ipcp_open(struct sppp *sp);
Xstatic void sppp_ipcp_close(struct sppp *sp);
Xstatic void sppp_ipcp_TO(void *sp);
Xstatic int sppp_ipcp_RCR(struct sppp *sp, struct lcp_header *h, int len);
Xstatic void sppp_ipcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len);
Xstatic void sppp_ipcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len);
Xstatic void sppp_ipcp_tlu(struct sppp *sp);
Xstatic void sppp_ipcp_tld(struct sppp *sp);
Xstatic void sppp_ipcp_tls(struct sppp *sp);
Xstatic void sppp_ipcp_tlf(struct sppp *sp);
Xstatic void sppp_ipcp_scr(struct sppp *sp);
X
Xstatic void sppp_pap_input(struct sppp *sp, struct mbuf *m);
Xstatic void sppp_pap_init(struct sppp *sp);
Xstatic void sppp_pap_open(struct sppp *sp);
Xstatic void sppp_pap_close(struct sppp *sp);
Xstatic void sppp_pap_TO(void *sp);
Xstatic void sppp_pap_my_TO(void *sp);
Xstatic void sppp_pap_tlu(struct sppp *sp);
Xstatic void sppp_pap_tld(struct sppp *sp);
Xstatic void sppp_pap_scr(struct sppp *sp);
X
Xstatic void sppp_chap_input(struct sppp *sp, struct mbuf *m);
Xstatic void sppp_chap_init(struct sppp *sp);
Xstatic void sppp_chap_open(struct sppp *sp);
Xstatic void sppp_chap_close(struct sppp *sp);
Xstatic void sppp_chap_TO(void *sp);
Xstatic void sppp_chap_tlu(struct sppp *sp);
Xstatic void sppp_chap_tld(struct sppp *sp);
Xstatic void sppp_chap_scr(struct sppp *sp);
X
Xstatic const char *sppp_auth_type_name(u_short proto, u_char type);
Xstatic const char *sppp_cp_type_name(u_char type);
Xstatic const char *sppp_dotted_quad(u_long addr);
Xstatic const char *sppp_ipcp_opt_name(u_char opt);
Xstatic const char *sppp_lcp_opt_name(u_char opt);
Xstatic const char *sppp_phase_name(enum ppp_phase phase);
Xstatic const char *sppp_proto_name(u_short proto);
Xstatic const char *sppp_state_name(int state);
Xstatic int sppp_params(struct sppp *sp, u_long cmd, void *data);
Xstatic int sppp_strnlen(u_char *p, int max);
Xstatic void sppp_get_ip_addrs(struct sppp *sp, u_long *src, u_long *dst,
X			      u_long *srcmask);
Xstatic void sppp_keepalive(void *dummy);
Xstatic void sppp_phase_network(struct sppp *sp);
Xstatic void sppp_print_bytes(const u_char *p, u_short len);
Xstatic void sppp_print_string(const char *p, u_short len);
Xstatic void sppp_qflush(struct ifqueue *ifq);
Xstatic void sppp_set_ip_addr(struct sppp *sp, u_long src);
X
X/* our control protocol descriptors */
Xstatic const struct cp lcp = {
X	PPP_LCP, IDX_LCP, CP_LCP, "lcp",
X	sppp_lcp_up, sppp_lcp_down, sppp_lcp_open, sppp_lcp_close,
X	sppp_lcp_TO, sppp_lcp_RCR, sppp_lcp_RCN_rej, sppp_lcp_RCN_nak,
X	sppp_lcp_tlu, sppp_lcp_tld, sppp_lcp_tls, sppp_lcp_tlf,
X	sppp_lcp_scr
X};
X
Xstatic const struct cp ipcp = {
X	PPP_IPCP, IDX_IPCP, CP_NCP, "ipcp",
X	sppp_ipcp_up, sppp_ipcp_down, sppp_ipcp_open, sppp_ipcp_close,
X	sppp_ipcp_TO, sppp_ipcp_RCR, sppp_ipcp_RCN_rej, sppp_ipcp_RCN_nak,
X	sppp_ipcp_tlu, sppp_ipcp_tld, sppp_ipcp_tls, sppp_ipcp_tlf,
X	sppp_ipcp_scr
X};
X
Xstatic const struct cp pap = {
X	PPP_PAP, IDX_PAP, CP_AUTH, "pap",
X	sppp_null, sppp_null, sppp_pap_open, sppp_pap_close,
X	sppp_pap_TO, 0, 0, 0,
X	sppp_pap_tlu, sppp_pap_tld, sppp_null, sppp_null,
X	sppp_pap_scr
X};
X
Xstatic const struct cp chap = {
X	PPP_CHAP, IDX_CHAP, CP_AUTH, "chap",
X	sppp_null, sppp_null, sppp_chap_open, sppp_chap_close,
X	sppp_chap_TO, 0, 0, 0,
X	sppp_chap_tlu, sppp_chap_tld, sppp_null, sppp_null,
X	sppp_chap_scr
X};
X
Xstatic const struct cp *cps[IDX_COUNT] = {
X	&lcp,			/* IDX_LCP */
X	&ipcp,			/* IDX_IPCP */
X	&pap,			/* IDX_PAP */
X	&chap,			/* IDX_CHAP */
X};
X
X
X/*
X * Exported functions, comprising our interface to the lower layer.
X */
X
X/*
X * Process the received packet.
X */
Xvoid
Xsppp_input(struct ifnet *ifp, struct mbuf *m)
X{
X	struct ppp_header *h;
X	struct ifqueue *inq = 0;
X	int s;
X	struct sppp *sp = (struct sppp *)ifp;
X	int debug = ifp->if_flags & IFF_DEBUG;
X
X	if (ifp->if_flags & IFF_UP)
X		/* Count received bytes, add FCS and one flag */
X		ifp->if_ibytes += m->m_pkthdr.len + 3;
X
X	if (m->m_pkthdr.len <= PPP_HEADER_LEN) {
X		/* Too small packet, drop it. */
X		if (debug)
X			log(LOG_DEBUG,
X			    SPP_FMT "input packet is too small, %d bytes\n",
X			    SPP_ARGS(ifp), m->m_pkthdr.len);
X	  drop:
X		++ifp->if_ierrors;
X		++ifp->if_iqdrops;
X		m_freem (m);
X		return;
X	}
X
X	/* Get PPP header. */
X	h = mtod (m, struct ppp_header*);
X	m_adj (m, PPP_HEADER_LEN);
X
X	switch (h->address) {
X	case PPP_ALLSTATIONS:
X		if (h->control != PPP_UI)
X			goto invalid;
X		if (sp->pp_flags & PP_CISCO) {
X			if (debug)
X				log(LOG_DEBUG,
X				    SPP_FMT "PPP packet in Cisco mode "
X				    "<addr=0x%x ctrl=0x%x proto=0x%x>\n",
X				    SPP_ARGS(ifp),
X				    h->address, h->control, ntohs(h->protocol));
X			goto drop;
X		}
X		switch (ntohs (h->protocol)) {
X		default:
X			if (sp->state[IDX_LCP] == STATE_OPENED)
X				sppp_cp_send (sp, PPP_LCP, PROTO_REJ,
X					++sp->pp_seq, m->m_pkthdr.len + 2,
X					&h->protocol);
X			if (debug)
X				log(LOG_DEBUG,
X				    SPP_FMT "invalid input protocol "
X				    "<addr=0x%x ctrl=0x%x proto=0x%x>\n",
X				    SPP_ARGS(ifp),
X				    h->address, h->control, ntohs(h->protocol));
X			++ifp->if_noproto;
X			goto drop;
X		case PPP_LCP:
X			sppp_cp_input(&lcp, sp, m);
X			m_freem (m);
X			return;
X		case PPP_PAP:
X			if (sp->pp_phase >= PHASE_AUTHENTICATE)
X				sppp_pap_input(sp, m);
X			m_freem (m);
X			return;
X		case PPP_CHAP:
X			if (sp->pp_phase >= PHASE_AUTHENTICATE)
X				sppp_chap_input(sp, m);
X			m_freem (m);
X			return;
X#ifdef INET
X		case PPP_IPCP:
X			if (sp->pp_phase == PHASE_NETWORK)
X				sppp_cp_input(&ipcp, sp, m);
X			m_freem (m);
X			return;
X		case PPP_IP:
X			if (sp->state[IDX_IPCP] == STATE_OPENED) {
X				schednetisr (NETISR_IP);
X				inq = &ipintrq;
X			}
X			break;
X#endif
X#ifdef IPX
X		case PPP_IPX:
X			/* IPX IPXCP not implemented yet */
X			if (sp->pp_phase == PHASE_NETWORK) {
X				schednetisr (NETISR_IPX);
X				inq = &ipxintrq;
X			}
X			break;
X#endif
X#ifdef NS
X		case PPP_XNS:
X			/* XNS IDPCP not implemented yet */
X			if (sp->pp_phase == PHASE_NETWORK) {
X				schednetisr (NETISR_NS);
X				inq = &nsintrq;
X			}
X			break;
X#endif
X#ifdef ISO
X		case PPP_ISO:
X			/* OSI NLCP not implemented yet */
X			if (sp->pp_phase == PHASE_NETWORK) {
X				schednetisr (NETISR_ISO);
X				inq = &clnlintrq;
X			}
X			break;
X#endif
X		}
X		break;
X	case CISCO_MULTICAST:
X	case CISCO_UNICAST:
X		/* Don't check the control field here (RFC 1547). */
X		if (! (sp->pp_flags & PP_CISCO)) {
X			if (debug)
X				log(LOG_DEBUG,
X				    SPP_FMT "Cisco packet in PPP mode "
X				    "<addr=0x%x ctrl=0x%x proto=0x%x>\n",
X				    SPP_ARGS(ifp),
X				    h->address, h->control, ntohs(h->protocol));
X			goto drop;
X		}
X		switch (ntohs (h->protocol)) {
X		default:
X			++ifp->if_noproto;
X			goto invalid;
X		case CISCO_KEEPALIVE:
X			sppp_cisco_input ((struct sppp*) ifp, m);
X			m_freem (m);
X			return;
X#ifdef INET
X		case ETHERTYPE_IP:
X			schednetisr (NETISR_IP);
X			inq = &ipintrq;
X			break;
X#endif
X#ifdef IPX
X		case ETHERTYPE_IPX:
X			schednetisr (NETISR_IPX);
X			inq = &ipxintrq;
X			break;
X#endif
X#ifdef NS
X		case ETHERTYPE_NS:
X			schednetisr (NETISR_NS);
X			inq = &nsintrq;
X			break;
X#endif
X		}
X		break;
X	default:        /* Invalid PPP packet. */
X	  invalid:
X		if (debug)
X			log(LOG_DEBUG,
X			    SPP_FMT "invalid input packet "
X			    "<addr=0x%x ctrl=0x%x proto=0x%x>\n",
X			    SPP_ARGS(ifp),
X			    h->address, h->control, ntohs(h->protocol));
X		goto drop;
X	}
X
X	if (! (ifp->if_flags & IFF_UP) || ! inq)
X		goto drop;
X
X	/* Check queue. */
X	s = splimp();
X	if (IF_QFULL (inq)) {
X		/* Queue overflow. */
X		IF_DROP(inq);
X		splx(s);
X		if (debug)
X			log(LOG_DEBUG, SPP_FMT "protocol queue overflow\n",
X				SPP_ARGS(ifp));
X		goto drop;
X	}
X	IF_ENQUEUE(inq, m);
X	sp->pp_last_recv = time_second;
X	splx(s);
X}
X
X/*
X * Enqueue transmit packet.
X */
Xstatic int
Xsppp_output(struct ifnet *ifp, struct mbuf *m,
X	    struct sockaddr *dst, struct rtentry *rt)
X{
X	struct sppp *sp = (struct sppp*) ifp;
X	struct ppp_header *h;
X	struct ifqueue *ifq;
X	int s, rv = 0;
X	int debug = ifp->if_flags & IFF_DEBUG;
X
X	s = splimp();
X
X	if ((ifp->if_flags & IFF_UP) == 0 ||
X	    (ifp->if_flags & (IFF_RUNNING | IFF_AUTO)) == 0) {
X		m_freem (m);
X		splx (s);
X		return (ENETDOWN);
X	}
X
X	if ((ifp->if_flags & (IFF_RUNNING | IFF_AUTO)) == IFF_AUTO) {
X		/*
X		 * Interface is not yet running, but auto-dial.  Need
X		 * to start LCP for it.
X		 */
X		ifp->if_flags |= IFF_RUNNING;
X		splx(s);
X		lcp.Open(sp);
X		s = splimp();
X	}
X
X	ifq = &ifp->if_snd;
X#ifdef INET
X	if (dst->sa_family == AF_INET) {
X		/* XXX Check mbuf length here? */
X		struct ip *ip = mtod (m, struct ip*);
X		struct tcphdr *tcp = (struct tcphdr*) ((long*)ip + ip->ip_hl);
X
X		/*
X		 * When using dynamic local IP address assignment by using
X		 * 0.0.0.0 as a local address, the first TCP session will
X		 * not connect because the local TCP checksum is computed
X		 * using 0.0.0.0 which will later become our real IP address
X		 * so the TCP checksum computed at the remote end will
X		 * become invalid. So we
X		 * - don't let packets with src ip addr 0 thru
X		 * - we flag TCP packets with src ip 0 as an error
X		 */	
X
X		if(ip->ip_src.s_addr == INADDR_ANY)	/* -hm */
X		{
X			m_freem(m);
X			splx(s);
X			if(ip->ip_p == IPPROTO_TCP)
X				return(EADDRNOTAVAIL);
X			else
X				return(0);
X		}
X		
X		/*
X		 * Put low delay, telnet, rlogin and ftp control packets
X		 * in front of the queue.
X		 */
X		if (IF_QFULL (&sp->pp_fastq))
X			;
X		else if (ip->ip_tos & IPTOS_LOWDELAY)
X			ifq = &sp->pp_fastq;
X		else if (m->m_len < sizeof *ip + sizeof *tcp)
X			;
X		else if (ip->ip_p != IPPROTO_TCP)
X			;
X		else if (INTERACTIVE (ntohs (tcp->th_sport)))
X			ifq = &sp->pp_fastq;
X		else if (INTERACTIVE (ntohs (tcp->th_dport)))
X			ifq = &sp->pp_fastq;
X	}
X#endif
X
X	/*
X	 * Prepend general data packet PPP header. For now, IP only.
X	 */
X	M_PREPEND (m, PPP_HEADER_LEN, M_DONTWAIT);
X	if (! m) {
X		if (debug)
X			log(LOG_DEBUG, SPP_FMT "no memory for transmit header\n",
X				SPP_ARGS(ifp));
X		++ifp->if_oerrors;
X		splx (s);
X		return (ENOBUFS);
X	}
X	/*
X	 * May want to check size of packet
X	 * (albeit due to the implementation it's always enough)
X	 */
X	h = mtod (m, struct ppp_header*);
X	if (sp->pp_flags & PP_CISCO) {
X		h->address = CISCO_UNICAST;        /* unicast address */
X		h->control = 0;
X	} else {
X		h->address = PPP_ALLSTATIONS;        /* broadcast address */
X		h->control = PPP_UI;                 /* Unnumbered Info */
X	}
X
X	switch (dst->sa_family) {
X#ifdef INET
X	case AF_INET:   /* Internet Protocol */
X		if (sp->pp_flags & PP_CISCO)
X			h->protocol = htons (ETHERTYPE_IP);
X		else {
X			/*
X			 * Don't choke with an ENETDOWN early.  It's
X			 * possible that we just started dialing out,
X			 * so don't drop the packet immediately.  If
X			 * we notice that we run out of buffer space
X			 * below, we will however remember that we are
X			 * not ready to carry IP packets, and return
X			 * ENETDOWN, as opposed to ENOBUFS.
X			 */
X			h->protocol = htons(PPP_IP);
X			if (sp->state[IDX_IPCP] != STATE_OPENED)
X				rv = ENETDOWN;
X		}
X		break;
X#endif
X#ifdef NS
X	case AF_NS:     /* Xerox NS Protocol */
X		h->protocol = htons ((sp->pp_flags & PP_CISCO) ?
X			ETHERTYPE_NS : PPP_XNS);
X		break;
X#endif
X#ifdef IPX
X	case AF_IPX:     /* Novell IPX Protocol */
X		h->protocol = htons ((sp->pp_flags & PP_CISCO) ?
X			ETHERTYPE_IPX : PPP_IPX);
X		break;
X#endif
X#ifdef ISO
X	case AF_ISO:    /* ISO OSI Protocol */
X		if (sp->pp_flags & PP_CISCO)
X			goto nosupport;
X		h->protocol = htons (PPP_ISO);
X		break;
Xnosupport:
X#endif
X	default:
X		m_freem (m);
X		++ifp->if_oerrors;
X		splx (s);
X		return (EAFNOSUPPORT);
X	}
X
X	/*
X	 * Queue message on interface, and start output if interface
X	 * not yet active.
X	 */
X	if (IF_QFULL (ifq)) {
X		IF_DROP (&ifp->if_snd);
X		m_freem (m);
X		++ifp->if_oerrors;
X		splx (s);
X		return (rv? rv: ENOBUFS);
X	}
X	IF_ENQUEUE (ifq, m);
X	if (! (ifp->if_flags & IFF_OACTIVE))
X		(*ifp->if_start) (ifp);
X
X	/*
X	 * Count output packets and bytes.
X	 * The packet length includes header, FCS and 1 flag,
X	 * according to RFC 1333.
X	 */
X	ifp->if_obytes += m->m_pkthdr.len + 3;
X	sp->pp_last_sent = time_second;
X	splx (s);
X	return (0);
X}
X
Xvoid
Xsppp_attach(struct ifnet *ifp)
X{
X	struct sppp *sp = (struct sppp*) ifp;
X
X	/* Initialize keepalive handler. */
X	if (! spppq)
X		TIMEOUT(sppp_keepalive, 0, hz * 10, keepalive_ch);
X
X	/* Insert new entry into the keepalive list. */
X	sp->pp_next = spppq;
X	spppq = sp;
X
X	sp->pp_if.if_mtu = PP_MTU;
X	sp->pp_if.if_flags = IFF_POINTOPOINT | IFF_MULTICAST;
X	sp->pp_if.if_type = IFT_PPP;
X	sp->pp_if.if_output = sppp_output;
X#if 0
X	sp->pp_flags = PP_KEEPALIVE;
X#endif
X	sp->pp_fastq.ifq_maxlen = 32;
X	sp->pp_cpq.ifq_maxlen = 20;
X	sp->pp_loopcnt = 0;
X	sp->pp_alivecnt = 0;
X	sp->pp_seq = 0;
X	sp->pp_rseq = 0;
X	sp->pp_phase = PHASE_DEAD;
X	sp->pp_up = lcp.Up;
X	sp->pp_down = lcp.Down;
X
X	sp->pp_last_recv = sp->pp_last_sent = time_second;
X	sppp_lcp_init(sp);
X	sppp_ipcp_init(sp);
X	sppp_pap_init(sp);
X	sppp_chap_init(sp);
X}
X
Xvoid
Xsppp_detach(struct ifnet *ifp)
X{
X	struct sppp **q, *p, *sp = (struct sppp*) ifp;
X	int i;
X
X	/* Remove the entry from the keepalive list. */
X	for (q = &spppq; (p = *q); q = &p->pp_next)
X		if (p == sp) {
X			*q = p->pp_next;
X			break;
X		}
X
X	/* Stop keepalive handler. */
X	if (! spppq)
X		UNTIMEOUT(sppp_keepalive, 0, keepalive_ch);
X
X	for (i = 0; i < IDX_COUNT; i++)
X		UNTIMEOUT((cps[i])->TO, (void *)sp, sp->ch[i]);
X	UNTIMEOUT(sppp_pap_my_TO, (void *)sp, sp->pap_my_to_ch);
X}
X
X/*
X * Flush the interface output queue.
X */
Xvoid
Xsppp_flush(struct ifnet *ifp)
X{
X	struct sppp *sp = (struct sppp*) ifp;
X
X	sppp_qflush (&sp->pp_if.if_snd);
X	sppp_qflush (&sp->pp_fastq);
X	sppp_qflush (&sp->pp_cpq);
X}
X
X/*
X * Check if the output queue is empty.
X */
Xint
Xsppp_isempty(struct ifnet *ifp)
X{
X	struct sppp *sp = (struct sppp*) ifp;
X	int empty, s;
X
X	s = splimp();
X	empty = !sp->pp_fastq.ifq_head && !sp->pp_cpq.ifq_head &&
X		!sp->pp_if.if_snd.ifq_head;
X	splx(s);
X	return (empty);
X}
X
X/*
X * Get next packet to send.
X */
Xstruct mbuf *
Xsppp_dequeue(struct ifnet *ifp)
X{
X	struct sppp *sp = (struct sppp*) ifp;
X	struct mbuf *m;
X	int s;
X
X	s = splimp();
X	/*
X	 * Process only the control protocol queue until we have at
X	 * least one NCP open.
X	 *
X	 * Do always serve all three queues in Cisco mode.
X	 */
X	IF_DEQUEUE(&sp->pp_cpq, m);
X	if (m == NULL &&
X	    (sppp_ncp_check(sp) || (sp->pp_flags & PP_CISCO) != 0)) {
X		IF_DEQUEUE(&sp->pp_fastq, m);
X		if (m == NULL)
X			IF_DEQUEUE (&sp->pp_if.if_snd, m);
X	}
X	splx(s);
X	return m;
X}
X
X/*
X * Pick the next packet, do not remove it from the queue.
X */
Xstruct mbuf *
Xsppp_pick(struct ifnet *ifp)
X{
X	struct sppp *sp = (struct sppp*)ifp;
X	struct mbuf *m;
X	int s;
X
X	s= splimp ();
X
X	m = sp->pp_cpq.ifq_head;
X	if (m == NULL &&
X	    (sp->pp_phase == PHASE_NETWORK ||
X	     (sp->pp_flags & PP_CISCO) != 0))
X		if ((m = sp->pp_fastq.ifq_head) == NULL)
X			m = sp->pp_if.if_snd.ifq_head;
X	splx (s);
X	return (m);
X}
X
X/*
X * Process an ioctl request.  Called on low priority level.
X */
Xint
Xsppp_ioctl(struct ifnet *ifp, IOCTL_CMD_T cmd, void *data)
X{
X	struct ifreq *ifr = (struct ifreq*) data;
X	struct sppp *sp = (struct sppp*) ifp;
X	int s, rv, going_up, going_down, newmode;
X
X	s = splimp();
X	rv = 0;
X	switch (cmd) {
X	case SIOCAIFADDR:
X	case SIOCSIFDSTADDR:
X		break;
X
X	case SIOCSIFADDR:
X		if_up(ifp);
X		/* fall through... */
X
X	case SIOCSIFFLAGS:
X		going_up = ifp->if_flags & IFF_UP &&
X			(ifp->if_flags & IFF_RUNNING) == 0;
X		going_down = (ifp->if_flags & IFF_UP) == 0 &&
X			ifp->if_flags & IFF_RUNNING;
X		newmode = ifp->if_flags & (IFF_AUTO | IFF_PASSIVE);
X		if (newmode == (IFF_AUTO | IFF_PASSIVE)) {
X			/* sanity */
X			newmode = IFF_PASSIVE;
X			ifp->if_flags &= ~IFF_AUTO;
X		}
X
X		if (going_up || going_down)
X			lcp.Close(sp);
X		if (going_up && newmode == 0) {
X			/* neither auto-dial nor passive */
X			ifp->if_flags |= IFF_RUNNING;
X			if (!(sp->pp_flags & PP_CISCO))
X				lcp.Open(sp);
X		} else if (going_down) {
X			sppp_flush(ifp);
X			ifp->if_flags &= ~IFF_RUNNING;
X		}
X
X		break;
X
X#ifdef SIOCSIFMTU
X#ifndef ifr_mtu
X#define ifr_mtu ifr_metric
X#endif
X	case SIOCSIFMTU:
X		if (ifr->ifr_mtu < 128 || ifr->ifr_mtu > sp->lcp.their_mru)
X			return (EINVAL);
X		ifp->if_mtu = ifr->ifr_mtu;
X		break;
X#endif
X#ifdef SLIOCSETMTU
X	case SLIOCSETMTU:
X		if (*(short*)data < 128 || *(short*)data > sp->lcp.their_mru)
X			return (EINVAL);
X		ifp->if_mtu = *(short*)data;
X		break;
X#endif
X#ifdef SIOCGIFMTU
X	case SIOCGIFMTU:
X		ifr->ifr_mtu = ifp->if_mtu;
X		break;
X#endif
X#ifdef SLIOCGETMTU
X	case SLIOCGETMTU:
X		*(short*)data = ifp->if_mtu;
X		break;
X#endif
X	case SIOCADDMULTI:
X	case SIOCDELMULTI:
X		break;
X
X	case SIOCGIFGENERIC:
X	case SIOCSIFGENERIC:
X		rv = sppp_params(sp, cmd, data);
X		break;
X
X	default:
X		rv = ENOTTY;
X	}
X	splx(s);
X	return rv;
X}
X
X
X/*
X * Cisco framing implementation.
X */
X
X/*
X * Handle incoming Cisco keepalive protocol packets.
X */
Xstatic void
Xsppp_cisco_input(struct sppp *sp, struct mbuf *m)
X{
X	STDDCL;
X	struct cisco_packet *h;
X	u_long me, mymask;
X
X	if (m->m_pkthdr.len < CISCO_PACKET_LEN) {
X		if (debug)
X			log(LOG_DEBUG,
X			    SPP_FMT "cisco invalid packet length: %d bytes\n",
X			    SPP_ARGS(ifp), m->m_pkthdr.len);
X		return;
X	}
X	h = mtod (m, struct cisco_packet*);
X	if (debug)
X		log(LOG_DEBUG,
X		    SPP_FMT "cisco input: %d bytes "
X		    "<0x%lx 0x%lx 0x%lx 0x%x 0x%x-0x%x>\n",
X		    SPP_ARGS(ifp), m->m_pkthdr.len,
X		    (u_long)ntohl (h->type), (u_long)h->par1, (u_long)h->par2, (u_int)h->rel,
X		    (u_int)h->time0, (u_int)h->time1);
X	switch (ntohl (h->type)) {
X	default:
X		if (debug)
X			addlog(SPP_FMT "cisco unknown packet type: 0x%lx\n",
X			       SPP_ARGS(ifp), (u_long)ntohl (h->type));
X		break;
X	case CISCO_ADDR_REPLY:
X		/* Reply on address request, ignore */
X		break;
X	case CISCO_KEEPALIVE_REQ:
X		sp->pp_alivecnt = 0;
X		sp->pp_rseq = ntohl (h->par1);
X		if (sp->pp_seq == sp->pp_rseq) {
X			/* Local and remote sequence numbers are equal.
X			 * Probably, the line is in loopback mode. */
X			if (sp->pp_loopcnt >= MAXALIVECNT) {
X				printf (SPP_FMT "loopback\n",
X					SPP_ARGS(ifp));
X				sp->pp_loopcnt = 0;
X				if (ifp->if_flags & IFF_UP) {
X					if_down (ifp);
X					sppp_qflush (&sp->pp_cpq);
X				}
X			}
X			++sp->pp_loopcnt;
X
X			/* Generate new local sequence number */
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X			sp->pp_seq = random();
X#else
X			sp->pp_seq ^= time.tv_sec ^ time.tv_usec;
X#endif
X			break;
X		}
X		sp->pp_loopcnt = 0;
X		if (! (ifp->if_flags & IFF_UP) &&
X		    (ifp->if_flags & IFF_RUNNING)) {
X			if_up(ifp);
X			printf (SPP_FMT "up\n", SPP_ARGS(ifp));
X		}
X		break;
X	case CISCO_ADDR_REQ:
X		sppp_get_ip_addrs(sp, &me, 0, &mymask);
X		if (me != 0L)
X			sppp_cisco_send(sp, CISCO_ADDR_REPLY, me, mymask);
X		break;
X	}
X}
X
X/*
X * Send Cisco keepalive packet.
X */
Xstatic void
Xsppp_cisco_send(struct sppp *sp, int type, long par1, long par2)
X{
X	STDDCL;
X	struct ppp_header *h;
X	struct cisco_packet *ch;
X	struct mbuf *m;
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X	struct timeval tv;
X#else
X	u_long t = (time.tv_sec - boottime.tv_sec) * 1000;
X#endif
X
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X	getmicrouptime(&tv);
X#endif
X	
X	MGETHDR (m, M_DONTWAIT, MT_DATA);
X	if (! m)
X		return;
X	m->m_pkthdr.len = m->m_len = PPP_HEADER_LEN + CISCO_PACKET_LEN;
X	m->m_pkthdr.rcvif = 0;
X
X	h = mtod (m, struct ppp_header*);
X	h->address = CISCO_MULTICAST;
X	h->control = 0;
X	h->protocol = htons (CISCO_KEEPALIVE);
X
X	ch = (struct cisco_packet*) (h + 1);
X	ch->type = htonl (type);
X	ch->par1 = htonl (par1);
X	ch->par2 = htonl (par2);
X	ch->rel = -1;
X
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X	ch->time0 = htons ((u_short) (tv.tv_sec >> 16));
X	ch->time1 = htons ((u_short) tv.tv_sec);
X#else
X	ch->time0 = htons ((u_short) (t >> 16));
X	ch->time1 = htons ((u_short) t);
X#endif
X
X	if (debug)
X		log(LOG_DEBUG,
X		    SPP_FMT "cisco output: <0x%lx 0x%lx 0x%lx 0x%x 0x%x-0x%x>\n",
X			SPP_ARGS(ifp), (u_long)ntohl (ch->type), (u_long)ch->par1,
X			(u_long)ch->par2, (u_int)ch->rel, (u_int)ch->time0, (u_int)ch->time1);
X
X	if (IF_QFULL (&sp->pp_cpq)) {
X		IF_DROP (&sp->pp_fastq);
X		IF_DROP (&ifp->if_snd);
X		m_freem (m);
X	} else
X		IF_ENQUEUE (&sp->pp_cpq, m);
X	if (! (ifp->if_flags & IFF_OACTIVE))
X		(*ifp->if_start) (ifp);
X	ifp->if_obytes += m->m_pkthdr.len + 3;
X}
X
X/*
X * PPP protocol implementation.
X */
X
X/*
X * Send PPP control protocol packet.
X */
Xstatic void
Xsppp_cp_send(struct sppp *sp, u_short proto, u_char type,
X	     u_char ident, u_short len, void *data)
X{
X	STDDCL;
X	struct ppp_header *h;
X	struct lcp_header *lh;
X	struct mbuf *m;
X
X	if (len > MHLEN - PPP_HEADER_LEN - LCP_HEADER_LEN)
X		len = MHLEN - PPP_HEADER_LEN - LCP_HEADER_LEN;
X	MGETHDR (m, M_DONTWAIT, MT_DATA);
X	if (! m)
X		return;
X	m->m_pkthdr.len = m->m_len = PPP_HEADER_LEN + LCP_HEADER_LEN + len;
X	m->m_pkthdr.rcvif = 0;
X
X	h = mtod (m, struct ppp_header*);
X	h->address = PPP_ALLSTATIONS;        /* broadcast address */
X	h->control = PPP_UI;                 /* Unnumbered Info */
X	h->protocol = htons (proto);         /* Link Control Protocol */
X
X	lh = (struct lcp_header*) (h + 1);
X	lh->type = type;
X	lh->ident = ident;
X	lh->len = htons (LCP_HEADER_LEN + len);
X	if (len)
X		bcopy (data, lh+1, len);
X
X	if (debug) {
X		log(LOG_DEBUG, SPP_FMT "%s output <%s id=0x%x len=%d",
X		    SPP_ARGS(ifp),
X		    sppp_proto_name(proto),
X		    sppp_cp_type_name (lh->type), lh->ident,
X		    ntohs (lh->len));
X		if (len)
X			sppp_print_bytes ((u_char*) (lh+1), len);
X		addlog(">\n");
X	}
X	if (IF_QFULL (&sp->pp_cpq)) {
X		IF_DROP (&sp->pp_fastq);
X		IF_DROP (&ifp->if_snd);
X		m_freem (m);
X		++ifp->if_oerrors;
X	} else
X		IF_ENQUEUE (&sp->pp_cpq, m);
X	if (! (ifp->if_flags & IFF_OACTIVE))
X		(*ifp->if_start) (ifp);
X	ifp->if_obytes += m->m_pkthdr.len + 3;
X}
X
X/*
X * Handle incoming PPP control protocol packets.
X */
Xstatic void
Xsppp_cp_input(const struct cp *cp, struct sppp *sp, struct mbuf *m)
X{
X	STDDCL;
X	struct lcp_header *h;
X	int len = m->m_pkthdr.len;
X	int rv;
X	u_char *p;
X
X	if (len < 4) {
X		if (debug)
X			log(LOG_DEBUG,
X			    SPP_FMT "%s invalid packet length: %d bytes\n",
X			    SPP_ARGS(ifp), cp->name, len);
X		return;
X	}
X	h = mtod (m, struct lcp_header*);
X	if (debug) {
X		log(LOG_DEBUG,
X		    SPP_FMT "%s input(%s): <%s id=0x%x len=%d",
X		    SPP_ARGS(ifp), cp->name,
X		    sppp_state_name(sp->state[cp->protoidx]),
X		    sppp_cp_type_name (h->type), h->ident, ntohs (h->len));
X		if (len > 4)
X			sppp_print_bytes ((u_char*) (h+1), len-4);
X		addlog(">\n");
X	}
X	if (len > ntohs (h->len))
X		len = ntohs (h->len);
X	p = (u_char *)(h + 1);
X	switch (h->type) {
X	case CONF_REQ:
X		if (len < 4) {
X			if (debug)
X				addlog(SPP_FMT "%s invalid conf-req length %d\n",
X				       SPP_ARGS(ifp), cp->name,
X				       len);
X			++ifp->if_ierrors;
X			break;
X		}
X		/* handle states where RCR doesn't get a SCA/SCN */
X		switch (sp->state[cp->protoidx]) {
X		case STATE_CLOSING:
X		case STATE_STOPPING:
X			return;
X		case STATE_CLOSED:
X			sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident,
X				     0, 0);
X			return;
X		}
X		rv = (cp->RCR)(sp, h, len);
X		switch (sp->state[cp->protoidx]) {
X		case STATE_OPENED:
X			(cp->tld)(sp);
X			(cp->scr)(sp);
X			/* fall through... */
X		case STATE_ACK_SENT:
X		case STATE_REQ_SENT:
X			sppp_cp_change_state(cp, sp, rv?
X					     STATE_ACK_SENT: STATE_REQ_SENT);
X			break;
X		case STATE_STOPPED:
X			sp->rst_counter[cp->protoidx] = sp->lcp.max_configure;
X			(cp->scr)(sp);
X			sppp_cp_change_state(cp, sp, rv?
X					     STATE_ACK_SENT: STATE_REQ_SENT);
X			break;
X		case STATE_ACK_RCVD:
X			if (rv) {
X				sppp_cp_change_state(cp, sp, STATE_OPENED);
X				if (debug)
X					log(LOG_DEBUG, SPP_FMT "%s tlu\n",
X					    SPP_ARGS(ifp),
X					    cp->name);
X				(cp->tlu)(sp);
X			} else
X				sppp_cp_change_state(cp, sp, STATE_ACK_RCVD);
X			break;
X		default:
X			printf(SPP_FMT "%s illegal %s in state %s\n",
X			       SPP_ARGS(ifp), cp->name,
X			       sppp_cp_type_name(h->type),
X			       sppp_state_name(sp->state[cp->protoidx]));
X			++ifp->if_ierrors;
X		}
X		break;
X	case CONF_ACK:
X		if (h->ident != sp->confid[cp->protoidx]) {
X			if (debug)
X				addlog(SPP_FMT "%s id mismatch 0x%x != 0x%x\n",
X				       SPP_ARGS(ifp), cp->name,
X				       h->ident, sp->confid[cp->protoidx]);
X			++ifp->if_ierrors;
X			break;
X		}
X		switch (sp->state[cp->protoidx]) {
X		case STATE_CLOSED:
X		case STATE_STOPPED:
X			sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident, 0, 0);
X			break;
X		case STATE_CLOSING:
X		case STATE_STOPPING:
X			break;
X		case STATE_REQ_SENT:
X			sp->rst_counter[cp->protoidx] = sp->lcp.max_configure;
X			sppp_cp_change_state(cp, sp, STATE_ACK_RCVD);
X			break;
X		case STATE_OPENED:
X			(cp->tld)(sp);
X			/* fall through */
X		case STATE_ACK_RCVD:
X			(cp->scr)(sp);
X			sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
X			break;
X		case STATE_ACK_SENT:
X			sp->rst_counter[cp->protoidx] = sp->lcp.max_configure;
X			sppp_cp_change_state(cp, sp, STATE_OPENED);
X			if (debug)
X				log(LOG_DEBUG, SPP_FMT "%s tlu\n",
X				       SPP_ARGS(ifp), cp->name);
X			(cp->tlu)(sp);
X			break;
X		default:
X			printf(SPP_FMT "%s illegal %s in state %s\n",
X			       SPP_ARGS(ifp), cp->name,
X			       sppp_cp_type_name(h->type),
X			       sppp_state_name(sp->state[cp->protoidx]));
X			++ifp->if_ierrors;
X		}
X		break;
X	case CONF_NAK:
X	case CONF_REJ:
X		if (h->ident != sp->confid[cp->protoidx]) {
X			if (debug)
X				addlog(SPP_FMT "%s id mismatch 0x%x != 0x%x\n",
X				       SPP_ARGS(ifp), cp->name,
X				       h->ident, sp->confid[cp->protoidx]);
X			++ifp->if_ierrors;
X			break;
X		}
X		if (h->type == CONF_NAK)
X			(cp->RCN_nak)(sp, h, len);
X		else /* CONF_REJ */
X			(cp->RCN_rej)(sp, h, len);
X
X		switch (sp->state[cp->protoidx]) {
X		case STATE_CLOSED:
X		case STATE_STOPPED:
X			sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident, 0, 0);
X			break;
X		case STATE_REQ_SENT:
X		case STATE_ACK_SENT:
X			sp->rst_counter[cp->protoidx] = sp->lcp.max_configure;
X			(cp->scr)(sp);
X			break;
X		case STATE_OPENED:
X			(cp->tld)(sp);
X			/* fall through */
X		case STATE_ACK_RCVD:
X			sppp_cp_change_state(cp, sp, STATE_ACK_SENT);
X			(cp->scr)(sp);
X			break;
X		case STATE_CLOSING:
X		case STATE_STOPPING:
X			break;
X		default:
X			printf(SPP_FMT "%s illegal %s in state %s\n",
X			       SPP_ARGS(ifp), cp->name,
X			       sppp_cp_type_name(h->type),
X			       sppp_state_name(sp->state[cp->protoidx]));
X			++ifp->if_ierrors;
X		}
X		break;
X
X	case TERM_REQ:
X		switch (sp->state[cp->protoidx]) {
X		case STATE_ACK_RCVD:
X		case STATE_ACK_SENT:
X			sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
X			/* fall through */
X		case STATE_CLOSED:
X		case STATE_STOPPED:
X		case STATE_CLOSING:
X		case STATE_STOPPING:
X		case STATE_REQ_SENT:
X		  sta:
X			/* Send Terminate-Ack packet. */
X			if (debug)
X				log(LOG_DEBUG, SPP_FMT "%s send terminate-ack\n",
X				    SPP_ARGS(ifp), cp->name);
X			sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident, 0, 0);
X			break;
X		case STATE_OPENED:
X			(cp->tld)(sp);
X			sp->rst_counter[cp->protoidx] = 0;
X			sppp_cp_change_state(cp, sp, STATE_STOPPING);
X			goto sta;
X			break;
X		default:
X			printf(SPP_FMT "%s illegal %s in state %s\n",
X			       SPP_ARGS(ifp), cp->name,
X			       sppp_cp_type_name(h->type),
X			       sppp_state_name(sp->state[cp->protoidx]));
X			++ifp->if_ierrors;
X		}
X		break;
X	case TERM_ACK:
X		switch (sp->state[cp->protoidx]) {
X		case STATE_CLOSED:
X		case STATE_STOPPED:
X		case STATE_REQ_SENT:
X		case STATE_ACK_SENT:
X			break;
X		case STATE_CLOSING:
X			sppp_cp_change_state(cp, sp, STATE_CLOSED);
X			(cp->tlf)(sp);
X			break;
X		case STATE_STOPPING:
X			sppp_cp_change_state(cp, sp, STATE_STOPPED);
X			(cp->tlf)(sp);
X			break;
X		case STATE_ACK_RCVD:
X			sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
X			break;
X		case STATE_OPENED:
X			(cp->tld)(sp);
X			(cp->scr)(sp);
X			sppp_cp_change_state(cp, sp, STATE_ACK_RCVD);
X			break;
X		default:
X			printf(SPP_FMT "%s illegal %s in state %s\n",
X			       SPP_ARGS(ifp), cp->name,
X			       sppp_cp_type_name(h->type),
X			       sppp_state_name(sp->state[cp->protoidx]));
X			++ifp->if_ierrors;
X		}
X		break;
X	case CODE_REJ:
X	case PROTO_REJ:
X		/* XXX catastrophic rejects (RXJ-) aren't handled yet. */
X		log(LOG_INFO,
X		    SPP_FMT "%s: ignoring RXJ (%s) for proto 0x%x, "
X		    "danger will robinson\n",
X		    SPP_ARGS(ifp), cp->name,
X		    sppp_cp_type_name(h->type), ntohs(*((u_short *)p)));
X		switch (sp->state[cp->protoidx]) {
X		case STATE_CLOSED:
X		case STATE_STOPPED:
X		case STATE_REQ_SENT:
X		case STATE_ACK_SENT:
X		case STATE_CLOSING:
X		case STATE_STOPPING:
X		case STATE_OPENED:
X			break;
X		case STATE_ACK_RCVD:
X			sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
X			break;
X		default:
X			printf(SPP_FMT "%s illegal %s in state %s\n",
X			       SPP_ARGS(ifp), cp->name,
X			       sppp_cp_type_name(h->type),
X			       sppp_state_name(sp->state[cp->protoidx]));
X			++ifp->if_ierrors;
X		}
X		break;
X	case DISC_REQ:
X		if (cp->proto != PPP_LCP)
X			goto illegal;
X		/* Discard the packet. */
X		break;
X	case ECHO_REQ:
X		if (cp->proto != PPP_LCP)
X			goto illegal;
X		if (sp->state[cp->protoidx] != STATE_OPENED) {
X			if (debug)
X				addlog(SPP_FMT "lcp echo req but lcp closed\n",
X				       SPP_ARGS(ifp));
X			++ifp->if_ierrors;
X			break;
X		}
X		if (len < 8) {
X			if (debug)
X				addlog(SPP_FMT "invalid lcp echo request "
X				       "packet length: %d bytes\n",
X				       SPP_ARGS(ifp), len);
X			break;
X		}
X		if (ntohl (*(long*)(h+1)) == sp->lcp.magic) {
X			/* Line loopback mode detected. */
X			printf(SPP_FMT "loopback\n", SPP_ARGS(ifp));
X			if_down (ifp);
X			sppp_qflush (&sp->pp_cpq);
X
X			/* Shut down the PPP link. */
X			/* XXX */
X			lcp.Down(sp);
X			lcp.Up(sp);
X			break;
X		}
X		*(long*)(h+1) = htonl (sp->lcp.magic);
X		if (debug)
X			addlog(SPP_FMT "got lcp echo req, sending echo rep\n",
X			       SPP_ARGS(ifp));
X		sppp_cp_send (sp, PPP_LCP, ECHO_REPLY, h->ident, len-4, h+1);
X		break;
X	case ECHO_REPLY:
X		if (cp->proto != PPP_LCP)
X			goto illegal;
X		if (h->ident != sp->lcp.echoid) {
X			++ifp->if_ierrors;
X			break;
X		}
X		if (len < 8) {
X			if (debug)
X				addlog(SPP_FMT "lcp invalid echo reply "
X				       "packet length: %d bytes\n",
X				       SPP_ARGS(ifp), len);
X			break;
X		}
X		if (debug)
X			addlog(SPP_FMT "lcp got echo rep\n",
X			       SPP_ARGS(ifp));
X		if (ntohl (*(long*)(h+1)) != sp->lcp.magic)
X			sp->pp_alivecnt = 0;
X		break;
X	default:
X		/* Unknown packet type -- send Code-Reject packet. */
X	  illegal:
X		if (debug)
X			addlog(SPP_FMT "%s send code-rej for 0x%x\n",
X			       SPP_ARGS(ifp), cp->name, h->type);
X		sppp_cp_send(sp, cp->proto, CODE_REJ, ++sp->pp_seq,
X			     m->m_pkthdr.len, h);
X		++ifp->if_ierrors;
X	}
X}
X
X
X/*
X * The generic part of all Up/Down/Open/Close/TO event handlers.
X * Basically, the state transition handling in the automaton.
X */
Xstatic void
Xsppp_up_event(const struct cp *cp, struct sppp *sp)
X{
X	STDDCL;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "%s up(%s)\n",
X		    SPP_ARGS(ifp), cp->name,
X		    sppp_state_name(sp->state[cp->protoidx]));
X
X	switch (sp->state[cp->protoidx]) {
X	case STATE_INITIAL:
X		sppp_cp_change_state(cp, sp, STATE_CLOSED);
X		break;
X	case STATE_STARTING:
X		sp->rst_counter[cp->protoidx] = sp->lcp.max_configure;
X		(cp->scr)(sp);
X		sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
X		break;
X	default:
X		printf(SPP_FMT "%s illegal up in state %s\n",
X		       SPP_ARGS(ifp), cp->name,
X		       sppp_state_name(sp->state[cp->protoidx]));
X	}
X}
X
Xstatic void
Xsppp_down_event(const struct cp *cp, struct sppp *sp)
X{
X	STDDCL;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "%s down(%s)\n",
X		    SPP_ARGS(ifp), cp->name,
X		    sppp_state_name(sp->state[cp->protoidx]));
X
X	switch (sp->state[cp->protoidx]) {
X	case STATE_CLOSED:
X	case STATE_CLOSING:
X		sppp_cp_change_state(cp, sp, STATE_INITIAL);
X		break;
X	case STATE_STOPPED:
X		sppp_cp_change_state(cp, sp, STATE_STARTING);
X		(cp->tls)(sp);
X		break;
X	case STATE_STOPPING:
X	case STATE_REQ_SENT:
X	case STATE_ACK_RCVD:
X	case STATE_ACK_SENT:
X		sppp_cp_change_state(cp, sp, STATE_STARTING);
X		break;
X	case STATE_OPENED:
X		(cp->tld)(sp);
X		sppp_cp_change_state(cp, sp, STATE_STARTING);
X		break;
X	default:
X		printf(SPP_FMT "%s illegal down in state %s\n",
X		       SPP_ARGS(ifp), cp->name,
X		       sppp_state_name(sp->state[cp->protoidx]));
X	}
X}
X
X
Xstatic void
Xsppp_open_event(const struct cp *cp, struct sppp *sp)
X{
X	STDDCL;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "%s open(%s)\n",
X		    SPP_ARGS(ifp), cp->name,
X		    sppp_state_name(sp->state[cp->protoidx]));
X
X	switch (sp->state[cp->protoidx]) {
X	case STATE_INITIAL:
X		sppp_cp_change_state(cp, sp, STATE_STARTING);
X		(cp->tls)(sp);
X		break;
X	case STATE_STARTING:
X		break;
X	case STATE_CLOSED:
X		sp->rst_counter[cp->protoidx] = sp->lcp.max_configure;
X		(cp->scr)(sp);
X		sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
X		break;
X	case STATE_STOPPED:
X	case STATE_STOPPING:
X	case STATE_REQ_SENT:
X	case STATE_ACK_RCVD:
X	case STATE_ACK_SENT:
X	case STATE_OPENED:
X		break;
X	case STATE_CLOSING:
X		sppp_cp_change_state(cp, sp, STATE_STOPPING);
X		break;
X	}
X}
X
X
Xstatic void
Xsppp_close_event(const struct cp *cp, struct sppp *sp)
X{
X	STDDCL;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "%s close(%s)\n",
X		    SPP_ARGS(ifp), cp->name,
X		    sppp_state_name(sp->state[cp->protoidx]));
X
X	switch (sp->state[cp->protoidx]) {
X	case STATE_INITIAL:
X	case STATE_CLOSED:
X	case STATE_CLOSING:
X		break;
X	case STATE_STARTING:
X		sppp_cp_change_state(cp, sp, STATE_INITIAL);
X		(cp->tlf)(sp);
X		break;
X	case STATE_STOPPED:
X		sppp_cp_change_state(cp, sp, STATE_CLOSED);
X		break;
X	case STATE_STOPPING:
X		sppp_cp_change_state(cp, sp, STATE_CLOSING);
X		break;
X	case STATE_OPENED:
X		(cp->tld)(sp);
X		/* fall through */
X	case STATE_REQ_SENT:
X	case STATE_ACK_RCVD:
X	case STATE_ACK_SENT:
X		sp->rst_counter[cp->protoidx] = sp->lcp.max_terminate;
X		sppp_cp_send(sp, cp->proto, TERM_REQ, ++sp->pp_seq, 0, 0);
X		sppp_cp_change_state(cp, sp, STATE_CLOSING);
X		break;
X	}
X}
X
Xstatic void
Xsppp_to_event(const struct cp *cp, struct sppp *sp)
X{
X	STDDCL;
X	int s;
X
X	s = splimp();
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "%s TO(%s) rst_counter = %d\n",
X		    SPP_ARGS(ifp), cp->name,
X		    sppp_state_name(sp->state[cp->protoidx]),
X		    sp->rst_counter[cp->protoidx]);
X
X	if (--sp->rst_counter[cp->protoidx] < 0)
X		/* TO- event */
X		switch (sp->state[cp->protoidx]) {
X		case STATE_CLOSING:
X			sppp_cp_change_state(cp, sp, STATE_CLOSED);
X			(cp->tlf)(sp);
X			break;
X		case STATE_STOPPING:
X			sppp_cp_change_state(cp, sp, STATE_STOPPED);
X			(cp->tlf)(sp);
X			break;
X		case STATE_REQ_SENT:
X		case STATE_ACK_RCVD:
X		case STATE_ACK_SENT:
X			sppp_cp_change_state(cp, sp, STATE_STOPPED);
X			(cp->tlf)(sp);
X			break;
X		}
X	else
X		/* TO+ event */
X		switch (sp->state[cp->protoidx]) {
X		case STATE_CLOSING:
X		case STATE_STOPPING:
X			sppp_cp_send(sp, cp->proto, TERM_REQ, ++sp->pp_seq,
X				     0, 0);
X			TIMEOUT(cp->TO, (void *)sp, sp->lcp.timeout, 
X			    sp->ch[cp->protoidx]);
X			break;
X		case STATE_REQ_SENT:
X		case STATE_ACK_RCVD:
X			(cp->scr)(sp);
X			/* sppp_cp_change_state() will restart the timer */
X			sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
X			break;
X		case STATE_ACK_SENT:
X			(cp->scr)(sp);
X			TIMEOUT(cp->TO, (void *)sp, sp->lcp.timeout,
X			    sp->ch[cp->protoidx]);
X			break;
X		}
X
X	splx(s);
X}
X
X/*
X * Change the state of a control protocol in the state automaton.
X * Takes care of starting/stopping the restart timer.
X */
Xvoid
Xsppp_cp_change_state(const struct cp *cp, struct sppp *sp, int newstate)
X{
X	sp->state[cp->protoidx] = newstate;
X
X	UNTIMEOUT(cp->TO, (void *)sp, sp->ch[cp->protoidx]);
X	switch (newstate) {
X	case STATE_INITIAL:
X	case STATE_STARTING:
X	case STATE_CLOSED:
X	case STATE_STOPPED:
X	case STATE_OPENED:
X		break;
X	case STATE_CLOSING:
X	case STATE_STOPPING:
X	case STATE_REQ_SENT:
X	case STATE_ACK_RCVD:
X	case STATE_ACK_SENT:
X		TIMEOUT(cp->TO, (void *)sp, sp->lcp.timeout, 
X		    sp->ch[cp->protoidx]);
X		break;
X	}
X}
X/*
X *--------------------------------------------------------------------------*
X *                                                                          *
X *                         The LCP implementation.                          *
X *                                                                          *
X *--------------------------------------------------------------------------*
X */
Xstatic void
Xsppp_lcp_init(struct sppp *sp)
X{
X	sp->lcp.opts = (1 << LCP_OPT_MAGIC);
X	sp->lcp.magic = 0;
X	sp->state[IDX_LCP] = STATE_INITIAL;
X	sp->fail_counter[IDX_LCP] = 0;
X	sp->lcp.protos = 0;
X	sp->lcp.mru = sp->lcp.their_mru = PP_MTU;
X
X	/*
X	 * Initialize counters and timeout values.  Note that we don't
X	 * use the 3 seconds suggested in RFC 1661 since we are likely
X	 * running on a fast link.  XXX We should probably implement
X	 * the exponential backoff option.  Note that these values are
X	 * relevant for all control protocols, not just LCP only.
X	 */
X	sp->lcp.timeout = 1 * hz;
X	sp->lcp.max_terminate = 2;
X	sp->lcp.max_configure = 10;
X	sp->lcp.max_failure = 10;
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X	callout_handle_init(&sp->ch[IDX_LCP]);
X#endif
X}
X
Xstatic void
Xsppp_lcp_up(struct sppp *sp)
X{
X	STDDCL;
X
X	/*
X	 * If this interface is passive or dial-on-demand, and we are
X	 * still in Initial state, it means we've got an incoming
X	 * call.  Activate the interface.
X	 */
X	if ((ifp->if_flags & (IFF_AUTO | IFF_PASSIVE)) != 0) {
X		if (debug)
X			log(LOG_DEBUG,
X			    SPP_FMT "Up event", SPP_ARGS(ifp));
X		ifp->if_flags |= IFF_RUNNING;
X		if (sp->state[IDX_LCP] == STATE_INITIAL) {
X			if (debug)
X				addlog("(incoming call)\n");
X			sp->pp_flags |= PP_CALLIN;
X			lcp.Open(sp);
X		} else if (debug)
X			addlog("\n");
X	}
X
X	sppp_up_event(&lcp, sp);
X}
X
Xstatic void
Xsppp_lcp_down(struct sppp *sp)
X{
X	STDDCL;
X
X	sppp_down_event(&lcp, sp);
X
X	/*
X	 * If this is neither a dial-on-demand nor a passive
X	 * interface, simulate an ``ifconfig down'' action, so the
X	 * administrator can force a redial by another ``ifconfig
X	 * up''.  XXX For leased line operation, should we immediately
X	 * try to reopen the connection here?
X	 */
X	if ((ifp->if_flags & (IFF_AUTO | IFF_PASSIVE)) == 0) {
X		log(LOG_INFO,
X		    SPP_FMT "Down event, taking interface down.\n",
X		    SPP_ARGS(ifp));
X		if_down(ifp);
X	} else {
X		if (debug)
X			log(LOG_DEBUG,
X			    SPP_FMT "Down event (carrier loss)\n",
X			    SPP_ARGS(ifp));
X	}
X	sp->pp_flags &= ~PP_CALLIN;
X	if (sp->state[IDX_LCP] != STATE_INITIAL)
X		lcp.Close(sp);
X	ifp->if_flags &= ~IFF_RUNNING;
X}
X
Xstatic void
Xsppp_lcp_open(struct sppp *sp)
X{
X	/*
X	 * If we are authenticator, negotiate LCP_AUTH
X	 */
X	if (sp->hisauth.proto != 0)
X		sp->lcp.opts |= (1 << LCP_OPT_AUTH_PROTO);
X	else
X		sp->lcp.opts &= ~(1 << LCP_OPT_AUTH_PROTO);
X	sp->pp_flags &= ~PP_NEEDAUTH;
X	sppp_open_event(&lcp, sp);
X}
X
Xstatic void
Xsppp_lcp_close(struct sppp *sp)
X{
X	sppp_close_event(&lcp, sp);
X}
X
Xstatic void
Xsppp_lcp_TO(void *cookie)
X{
X	sppp_to_event(&lcp, (struct sppp *)cookie);
X}
X
X/*
X * Analyze a configure request.  Return true if it was agreeable, and
X * caused action sca, false if it has been rejected or nak'ed, and
X * caused action scn.  (The return value is used to make the state
X * transition decision in the state automaton.)
X */
Xstatic int
Xsppp_lcp_RCR(struct sppp *sp, struct lcp_header *h, int len)
X{
X	STDDCL;
X	u_char *buf, *r, *p;
X	int origlen, rlen;
X	u_long nmagic;
X	u_short authproto;
X
X	len -= 4;
X	origlen = len;
X	buf = r = malloc (len, M_TEMP, M_NOWAIT);
X	if (! buf)
X		return (0);
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "lcp parse opts: ",
X		    SPP_ARGS(ifp));
X
X	/* pass 1: check for things that need to be rejected */
X	p = (void*) (h+1);
X	for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) {
X		if (debug)
X			addlog(" %s ", sppp_lcp_opt_name(*p));
X		switch (*p) {
X		case LCP_OPT_MAGIC:
X			/* Magic number. */
X			/* fall through, both are same length */
X		case LCP_OPT_ASYNC_MAP:
X			/* Async control character map. */
X			if (len >= 6 || p[1] == 6)
X				continue;
X			if (debug)
X				addlog("[invalid] ");
X			break;
X		case LCP_OPT_MRU:
X			/* Maximum receive unit. */
X			if (len >= 4 && p[1] == 4)
X				continue;
X			if (debug)
X				addlog("[invalid] ");
X			break;
X		case LCP_OPT_AUTH_PROTO:
X			if (len < 4) {
X				if (debug)
X					addlog("[invalid] ");
X				break;
X			}
X			authproto = (p[2] << 8) + p[3];
X			if (authproto == PPP_CHAP && p[1] != 5) {
X				if (debug)
X					addlog("[invalid chap len] ");
X				break;
X			}
X			if (sp->myauth.proto == 0) {
X				/* we are not configured to do auth */
X				if (debug)
X					addlog("[not configured] ");
X				break;
X			}
X			/*
X			 * Remote want us to authenticate, remember this,
X			 * so we stay in PHASE_AUTHENTICATE after LCP got
X			 * up.
X			 */
X			sp->pp_flags |= PP_NEEDAUTH;
X			continue;
X		default:
X			/* Others not supported. */
X			if (debug)
X				addlog("[rej] ");
X			break;
X		}
X		/* Add the option to rejected list. */
X		bcopy (p, r, p[1]);
X		r += p[1];
X		rlen += p[1];
X	}
X	if (rlen) {
X		if (debug)
X			addlog(" send conf-rej\n");
X		sppp_cp_send (sp, PPP_LCP, CONF_REJ, h->ident, rlen, buf);
X		return 0;
X	} else if (debug)
X		addlog("\n");
X
X	/*
X	 * pass 2: check for option values that are unacceptable and
X	 * thus require to be nak'ed.
X	 */
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "lcp parse opt values: ",
X		    SPP_ARGS(ifp));
X
X	p = (void*) (h+1);
X	len = origlen;
X	for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) {
X		if (debug)
X			addlog(" %s ", sppp_lcp_opt_name(*p));
X		switch (*p) {
X		case LCP_OPT_MAGIC:
X			/* Magic number -- extract. */
X			nmagic = (u_long)p[2] << 24 |
X				(u_long)p[3] << 16 | p[4] << 8 | p[5];
X			if (nmagic != sp->lcp.magic) {
X				if (debug)
X					addlog("0x%lx ", nmagic);
X				continue;
X			}
X			/*
X			 * Local and remote magics equal -- loopback?
X			 */
X			if (sp->pp_loopcnt >= MAXALIVECNT*5) {
X				printf (SPP_FMT "loopback\n",
X					SPP_ARGS(ifp));
X				sp->pp_loopcnt = 0;
X				if (ifp->if_flags & IFF_UP) {
X					if_down(ifp);
X					sppp_qflush(&sp->pp_cpq);
X					/* XXX ? */
X					lcp.Down(sp);
X					lcp.Up(sp);
X				}
X			} else if (debug)
X				addlog("[glitch] ");
X			++sp->pp_loopcnt;
X			/*
X			 * We negate our magic here, and NAK it.  If
X			 * we see it later in an NAK packet, we
X			 * suggest a new one.
X			 */
X			nmagic = ~sp->lcp.magic;
X			/* Gonna NAK it. */
X			p[2] = nmagic >> 24;
X			p[3] = nmagic >> 16;
X			p[4] = nmagic >> 8;
X			p[5] = nmagic;
X			break;
X
X		case LCP_OPT_ASYNC_MAP:
X			/* Async control character map -- check to be zero. */
X			if (! p[2] && ! p[3] && ! p[4] && ! p[5]) {
X				if (debug)
X					addlog("[empty] ");
X				continue;
X			}
X			if (debug)
X				addlog("[non-empty] ");
X			/* suggest a zero one */
X			p[2] = p[3] = p[4] = p[5] = 0;
X			break;
X
X		case LCP_OPT_MRU:
X			/*
X			 * Maximum receive unit.  Always agreeable,
X			 * but ignored by now.
X			 */
X			sp->lcp.their_mru = p[2] * 256 + p[3];
X			if (debug)
X				addlog("%lu ", sp->lcp.their_mru);
X			continue;
X
X		case LCP_OPT_AUTH_PROTO:
X			authproto = (p[2] << 8) + p[3];
X			if (sp->myauth.proto != authproto) {
X				/* not agreed, nak */
X				if (debug)
X					addlog("[mine %s != his %s] ",
X					       sppp_proto_name(sp->hisauth.proto),
X					       sppp_proto_name(authproto));
X				p[2] = sp->myauth.proto >> 8;
X				p[3] = sp->myauth.proto;
X				break;
X			}
X			if (authproto == PPP_CHAP && p[4] != CHAP_MD5) {
X				if (debug)
X					addlog("[chap not MD5] ");
X				p[4] = CHAP_MD5;
X				break;
X			}
X			continue;
X		}
X		/* Add the option to nak'ed list. */
X		bcopy (p, r, p[1]);
X		r += p[1];
X		rlen += p[1];
X	}
X	if (rlen) {
X		if (++sp->fail_counter[IDX_LCP] >= sp->lcp.max_failure) {
X			if (debug)
X				addlog(" max_failure (%d) exceeded, "
X				       "send conf-rej\n",
X				       sp->lcp.max_failure);
X			sppp_cp_send(sp, PPP_LCP, CONF_REJ, h->ident, rlen, buf);
X		} else {
X			if (debug)
X				addlog(" send conf-nak\n");
X			sppp_cp_send (sp, PPP_LCP, CONF_NAK, h->ident, rlen, buf);
X		}
X		return 0;
X	} else {
X		if (debug)
X			addlog(" send conf-ack\n");
X		sp->fail_counter[IDX_LCP] = 0;
X		sp->pp_loopcnt = 0;
X		sppp_cp_send (sp, PPP_LCP, CONF_ACK,
X			      h->ident, origlen, h+1);
X	}
X
X	free (buf, M_TEMP);
X	return (rlen == 0);
X}
X
X/*
X * Analyze the LCP Configure-Reject option list, and adjust our
X * negotiation.
X */
Xstatic void
Xsppp_lcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len)
X{
X	STDDCL;
X	u_char *buf, *p;
X
X	len -= 4;
X	buf = malloc (len, M_TEMP, M_NOWAIT);
X	if (!buf)
X		return;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "lcp rej opts: ",
X		    SPP_ARGS(ifp));
X
X	p = (void*) (h+1);
X	for (; len > 1 && p[1]; len -= p[1], p += p[1]) {
X		if (debug)
X			addlog(" %s ", sppp_lcp_opt_name(*p));
X		switch (*p) {
X		case LCP_OPT_MAGIC:
X			/* Magic number -- can't use it, use 0 */
X			sp->lcp.opts &= ~(1 << LCP_OPT_MAGIC);
X			sp->lcp.magic = 0;
X			break;
X		case LCP_OPT_MRU:
X			/*
X			 * Should not be rejected anyway, since we only
X			 * negotiate a MRU if explicitly requested by
X			 * peer.
X			 */
X			sp->lcp.opts &= ~(1 << LCP_OPT_MRU);
X			break;
X		case LCP_OPT_AUTH_PROTO:
X			/*
X			 * Peer doesn't want to authenticate himself,
X			 * deny unless this is a dialout call, and
X			 * AUTHFLAG_NOCALLOUT is set.
X			 */
X			if ((sp->pp_flags & PP_CALLIN) == 0 &&
X			    (sp->hisauth.flags & AUTHFLAG_NOCALLOUT) != 0) {
X				if (debug)
X					addlog("[don't insist on auth "
X					       "for callout]");
X				sp->lcp.opts &= ~(1 << LCP_OPT_AUTH_PROTO);
X				break;
X			}
X			if (debug)
X				addlog("[access denied]\n");
X			lcp.Close(sp);
X			break;
X		}
X	}
X	if (debug)
X		addlog("\n");
X	free (buf, M_TEMP);
X	return;
X}
X
X/*
X * Analyze the LCP Configure-NAK option list, and adjust our
X * negotiation.
X */
Xstatic void
Xsppp_lcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len)
X{
X	STDDCL;
X	u_char *buf, *p;
X	u_long magic;
X
X	len -= 4;
X	buf = malloc (len, M_TEMP, M_NOWAIT);
X	if (!buf)
X		return;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "lcp nak opts: ",
X		    SPP_ARGS(ifp));
X
X	p = (void*) (h+1);
X	for (; len > 1 && p[1]; len -= p[1], p += p[1]) {
X		if (debug)
X			addlog(" %s ", sppp_lcp_opt_name(*p));
X		switch (*p) {
X		case LCP_OPT_MAGIC:
X			/* Magic number -- renegotiate */
X			if ((sp->lcp.opts & (1 << LCP_OPT_MAGIC)) &&
X			    len >= 6 && p[1] == 6) {
X				magic = (u_long)p[2] << 24 |
X					(u_long)p[3] << 16 | p[4] << 8 | p[5];
X				/*
X				 * If the remote magic is our negated one,
X				 * this looks like a loopback problem.
X				 * Suggest a new magic to make sure.
X				 */
X				if (magic == ~sp->lcp.magic) {
X					if (debug)
X						addlog("magic glitch ");
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X					sp->lcp.magic = random();
X#else
X					sp->lcp.magic = time.tv_sec + time.tv_usec;
X#endif
X				} else {
X					sp->lcp.magic = magic;
X					if (debug)
X						addlog("%lu ", magic);
X				}
X			}
X			break;
X		case LCP_OPT_MRU:
X			/*
X			 * Peer wants to advise us to negotiate an MRU.
X			 * Agree on it if it's reasonable, or use
X			 * default otherwise.
X			 */
X			if (len >= 4 && p[1] == 4) {
X				u_int mru = p[2] * 256 + p[3];
X				if (debug)
X					addlog("%d ", mru);
X				if (mru < PP_MTU || mru > PP_MAX_MRU)
X					mru = PP_MTU;
X				sp->lcp.mru = mru;
X				sp->lcp.opts |= (1 << LCP_OPT_MRU);
X			}
X			break;
X		case LCP_OPT_AUTH_PROTO:
X			/*
X			 * Peer doesn't like our authentication method,
X			 * deny.
X			 */
X			if (debug)
X				addlog("[access denied]\n");
X			lcp.Close(sp);
X			break;
X		}
X	}
X	if (debug)
X		addlog("\n");
X	free (buf, M_TEMP);
X	return;
X}
X
Xstatic void
Xsppp_lcp_tlu(struct sppp *sp)
X{
X	STDDCL;
X	int i;
X	u_long mask;
X
X	/* XXX ? */
X	if (! (ifp->if_flags & IFF_UP) &&
X	    (ifp->if_flags & IFF_RUNNING)) {
X		/* Coming out of loopback mode. */
X		if_up(ifp);
X		printf (SPP_FMT "up\n", SPP_ARGS(ifp));
X	}
X
X	for (i = 0; i < IDX_COUNT; i++)
X		if ((cps[i])->flags & CP_QUAL)
X			(cps[i])->Open(sp);
X
X	if ((sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) != 0 ||
X	    (sp->pp_flags & PP_NEEDAUTH) != 0)
X		sp->pp_phase = PHASE_AUTHENTICATE;
X	else
X		sp->pp_phase = PHASE_NETWORK;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "phase %s\n", SPP_ARGS(ifp),
X		    sppp_phase_name(sp->pp_phase));
X
X	/*
X	 * Open all authentication protocols.  This is even required
X	 * if we already proceeded to network phase, since it might be
X	 * that remote wants us to authenticate, so we might have to
X	 * send a PAP request.  Undesired authentication protocols
X	 * don't do anything when they get an Open event.
X	 */
X	for (i = 0; i < IDX_COUNT; i++)
X		if ((cps[i])->flags & CP_AUTH)
X			(cps[i])->Open(sp);
X
X	if (sp->pp_phase == PHASE_NETWORK) {
X		/* Notify all NCPs. */
X		for (i = 0; i < IDX_COUNT; i++)
X			if ((cps[i])->flags & CP_NCP)
X				(cps[i])->Open(sp);
X	}
X
X	/* Send Up events to all started protos. */
X	for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1)
X		if (sp->lcp.protos & mask && ((cps[i])->flags & CP_LCP) == 0)
X			(cps[i])->Up(sp);
X
X	/* notify low-level driver of state change */
X	if (sp->pp_chg)
X		sp->pp_chg(sp, (int)sp->pp_phase);
X	
X	if (sp->pp_phase == PHASE_NETWORK)
X		/* if no NCP is starting, close down */
X		sppp_lcp_check_and_close(sp);
X}
X
Xstatic void
Xsppp_lcp_tld(struct sppp *sp)
X{
X	STDDCL;
X	int i;
X	u_long mask;
X
X	sp->pp_phase = PHASE_TERMINATE;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "phase %s\n", SPP_ARGS(ifp),
X		    sppp_phase_name(sp->pp_phase));
X
X	/*
X	 * Take upper layers down.  We send the Down event first and
X	 * the Close second to prevent the upper layers from sending
X	 * ``a flurry of terminate-request packets'', as the RFC
X	 * describes it.
X	 */
X	for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1)
X		if (sp->lcp.protos & mask && ((cps[i])->flags & CP_LCP) == 0) {
X			(cps[i])->Down(sp);
X			(cps[i])->Close(sp);
X		}
X}
X
Xstatic void
Xsppp_lcp_tls(struct sppp *sp)
X{
X	STDDCL;
X
X	sp->pp_phase = PHASE_ESTABLISH;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "phase %s\n", SPP_ARGS(ifp),
X		    sppp_phase_name(sp->pp_phase));
X
X	/* Notify lower layer if desired. */
X	if (sp->pp_tls)
X		(sp->pp_tls)(sp);
X	else
X		(sp->pp_up)(sp);
X}
X
Xstatic void
Xsppp_lcp_tlf(struct sppp *sp)
X{
X	STDDCL;
X
X	sp->pp_phase = PHASE_DEAD;
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "phase %s\n", SPP_ARGS(ifp),
X		    sppp_phase_name(sp->pp_phase));
X
X	/* Notify lower layer if desired. */
X	if (sp->pp_tlf)
X		(sp->pp_tlf)(sp);
X	else
X		(sp->pp_down)(sp);
X}
X
Xstatic void
Xsppp_lcp_scr(struct sppp *sp)
X{
X	char opt[6 /* magicnum */ + 4 /* mru */ + 5 /* chap */];
X	int i = 0;
X	u_short authproto;
X
X	if (sp->lcp.opts & (1 << LCP_OPT_MAGIC)) {
X		if (! sp->lcp.magic)
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X			sp->lcp.magic = random();
X#else
X			sp->lcp.magic = time.tv_sec + time.tv_usec;
X#endif
X		opt[i++] = LCP_OPT_MAGIC;
X		opt[i++] = 6;
X		opt[i++] = sp->lcp.magic >> 24;
X		opt[i++] = sp->lcp.magic >> 16;
X		opt[i++] = sp->lcp.magic >> 8;
X		opt[i++] = sp->lcp.magic;
X	}
X
X	if (sp->lcp.opts & (1 << LCP_OPT_MRU)) {
X		opt[i++] = LCP_OPT_MRU;
X		opt[i++] = 4;
X		opt[i++] = sp->lcp.mru >> 8;
X		opt[i++] = sp->lcp.mru;
X	}
X
X	if (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) {
X		authproto = sp->hisauth.proto;
X		opt[i++] = LCP_OPT_AUTH_PROTO;
X		opt[i++] = authproto == PPP_CHAP? 5: 4;
X		opt[i++] = authproto >> 8;
X		opt[i++] = authproto;
X		if (authproto == PPP_CHAP)
X			opt[i++] = CHAP_MD5;
X	}
X
X	sp->confid[IDX_LCP] = ++sp->pp_seq;
X	sppp_cp_send (sp, PPP_LCP, CONF_REQ, sp->confid[IDX_LCP], i, &opt);
X}
X
X/*
X * Check the open NCPs, return true if at least one NCP is open.
X */
Xstatic int
Xsppp_ncp_check(struct sppp *sp)
X{
X	int i, mask;
X
X	for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1)
X		if (sp->lcp.protos & mask && (cps[i])->flags & CP_NCP)
X			return 1;
X	return 0;
X}
X
X/*
X * Re-check the open NCPs and see if we should terminate the link.
X * Called by the NCPs during their tlf action handling.
X */
Xstatic void
Xsppp_lcp_check_and_close(struct sppp *sp)
X{
X
X	if (sp->pp_phase < PHASE_NETWORK)
X		/* don't bother, we are already going down */
X		return;
X
X	if (sppp_ncp_check(sp))
X		return;
X
X	lcp.Close(sp);
X}
X/*
X *--------------------------------------------------------------------------*
X *                                                                          *
X *                        The IPCP implementation.                          *
X *                                                                          *
X *--------------------------------------------------------------------------*
X */
X
Xstatic void
Xsppp_ipcp_init(struct sppp *sp)
X{
X	sp->ipcp.opts = 0;
X	sp->ipcp.flags = 0;
X	sp->state[IDX_IPCP] = STATE_INITIAL;
X	sp->fail_counter[IDX_IPCP] = 0;
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X	callout_handle_init(&sp->ch[IDX_IPCP]);
X#endif
X}
X
Xstatic void
Xsppp_ipcp_up(struct sppp *sp)
X{
X	sppp_up_event(&ipcp, sp);
X}
X
Xstatic void
Xsppp_ipcp_down(struct sppp *sp)
X{
X	sppp_down_event(&ipcp, sp);
X}
X
Xstatic void
Xsppp_ipcp_open(struct sppp *sp)
X{
X	STDDCL;
X	u_long myaddr, hisaddr;
X
X	sp->ipcp.flags &= ~(IPCP_HISADDR_SEEN|IPCP_MYADDR_SEEN|IPCP_MYADDR_DYN);
X
X	sppp_get_ip_addrs(sp, &myaddr, &hisaddr, 0);
X	/*
X	 * If we don't have his address, this probably means our
X	 * interface doesn't want to talk IP at all.  (This could
X	 * be the case if somebody wants to speak only IPX, for
X	 * example.)  Don't open IPCP in this case.
X	 */
X	if (hisaddr == 0L) {
X		/* XXX this message should go away */
X		if (debug)
X			log(LOG_DEBUG, SPP_FMT "ipcp_open(): no IP interface\n",
X			    SPP_ARGS(ifp));
X		return;
X	}
X
X	if (myaddr == 0L) {
X		/*
X		 * I don't have an assigned address, so i need to
X		 * negotiate my address.
X		 */
X		sp->ipcp.flags |= IPCP_MYADDR_DYN;
X		sp->ipcp.opts |= (1 << IPCP_OPT_ADDRESS);
X	} else
X		sp->ipcp.flags |= IPCP_MYADDR_SEEN;
X	sppp_open_event(&ipcp, sp);
X}
X
Xstatic void
Xsppp_ipcp_close(struct sppp *sp)
X{
X	sppp_close_event(&ipcp, sp);
X	if (sp->ipcp.flags & IPCP_MYADDR_DYN)
X		/*
X		 * My address was dynamic, clear it again.
X		 */
X		sppp_set_ip_addr(sp, 0L);
X}
X
Xstatic void
Xsppp_ipcp_TO(void *cookie)
X{
X	sppp_to_event(&ipcp, (struct sppp *)cookie);
X}
X
X/*
X * Analyze a configure request.  Return true if it was agreeable, and
X * caused action sca, false if it has been rejected or nak'ed, and
X * caused action scn.  (The return value is used to make the state
X * transition decision in the state automaton.)
X */
Xstatic int
Xsppp_ipcp_RCR(struct sppp *sp, struct lcp_header *h, int len)
X{
X	u_char *buf, *r, *p;
X	struct ifnet *ifp = &sp->pp_if;
X	int rlen, origlen, debug = ifp->if_flags & IFF_DEBUG;
X	u_long hisaddr, desiredaddr;
X	int gotmyaddr = 0;
X
X	len -= 4;
X	origlen = len;
X	/*
X	 * Make sure to allocate a buf that can at least hold a
X	 * conf-nak with an `address' option.  We might need it below.
X	 */
X	buf = r = malloc ((len < 6? 6: len), M_TEMP, M_NOWAIT);
X	if (! buf)
X		return (0);
X
X	/* pass 1: see if we can recognize them */
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "ipcp parse opts: ",
X		    SPP_ARGS(ifp));
X	p = (void*) (h+1);
X	for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) {
X		if (debug)
X			addlog(" %s ", sppp_ipcp_opt_name(*p));
X		switch (*p) {
X#ifdef notyet
X		case IPCP_OPT_COMPRESSION:
X			if (len >= 6 && p[1] >= 6) {
X				/* correctly formed compress option */
X				continue;
X			}
X			if (debug)
X				addlog("[invalid] ");
X			break;
X#endif
X		case IPCP_OPT_ADDRESS:
X			if (len >= 6 && p[1] == 6) {
X				/* correctly formed address option */
X				continue;
X			}
X			if (debug)
X				addlog("[invalid] ");
X			break;
X		default:
X			/* Others not supported. */
X			if (debug)
X				addlog("[rej] ");
X			break;
X		}
X		/* Add the option to rejected list. */
X		bcopy (p, r, p[1]);
X		r += p[1];
X		rlen += p[1];
X	}
X	if (rlen) {
X		if (debug)
X			addlog(" send conf-rej\n");
X		sppp_cp_send (sp, PPP_IPCP, CONF_REJ, h->ident, rlen, buf);
X		return 0;
X	} else if (debug)
X		addlog("\n");
X
X	/* pass 2: parse option values */
X	sppp_get_ip_addrs(sp, 0, &hisaddr, 0);
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "ipcp parse opt values: ",
X		       SPP_ARGS(ifp));
X	p = (void*) (h+1);
X	len = origlen;
X	for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) {
X		if (debug)
X			addlog(" %s ", sppp_ipcp_opt_name(*p));
X		switch (*p) {
X#ifdef notyet
X		case IPCP_OPT_COMPRESSION:
X			continue;
X#endif
X		case IPCP_OPT_ADDRESS:
X			/* This is the address he wants in his end */
X			desiredaddr = p[2] << 24 | p[3] << 16 |
X				p[4] << 8 | p[5];
X			if (desiredaddr == hisaddr ||
X			    (hisaddr == 1 && desiredaddr != 0)) {
X				/*
X				 * Peer's address is same as our value,
X				 * or we have set it to 0.0.0.1 to 
X				 * indicate that we do not really care,
X				 * this is agreeable.  Gonna conf-ack
X				 * it.
X				 */
X				if (debug)
X					addlog("%s [ack] ",
X						sppp_dotted_quad(hisaddr));
X				/* record that we've seen it already */
X				sp->ipcp.flags |= IPCP_HISADDR_SEEN;
X				continue;
X			}
X			/*
X			 * The address wasn't agreeable.  This is either
X			 * he sent us 0.0.0.0, asking to assign him an
X			 * address, or he send us another address not
X			 * matching our value.  Either case, we gonna
X			 * conf-nak it with our value.
X			 * XXX: we should "rej" if hisaddr == 0
X			 */
X			if (debug) {
X				if (desiredaddr == 0)
X					addlog("[addr requested] ");
X				else
X					addlog("%s [not agreed] ",
X						sppp_dotted_quad(desiredaddr));
X
X				p[2] = hisaddr >> 24;
X				p[3] = hisaddr >> 16;
X				p[4] = hisaddr >> 8;
X				p[5] = hisaddr;
X			}
X			break;
X		}
X		/* Add the option to nak'ed list. */
X		bcopy (p, r, p[1]);
X		r += p[1];
X		rlen += p[1];
X	}
X
X	/*
X	 * If we are about to conf-ack the request, but haven't seen
X	 * his address so far, gonna conf-nak it instead, with the
X	 * `address' option present and our idea of his address being
X	 * filled in there, to request negotiation of both addresses.
X	 *
X	 * XXX This can result in an endless req - nak loop if peer
X	 * doesn't want to send us his address.  Q: What should we do
X	 * about it?  XXX  A: implement the max-failure counter.
X	 */
X	if (rlen == 0 && !(sp->ipcp.flags & IPCP_HISADDR_SEEN) && !gotmyaddr) {
X		buf[0] = IPCP_OPT_ADDRESS;
X		buf[1] = 6;
X		buf[2] = hisaddr >> 24;
X		buf[3] = hisaddr >> 16;
X		buf[4] = hisaddr >> 8;
X		buf[5] = hisaddr;
X		rlen = 6;
X		if (debug)
X			addlog("still need hisaddr ");
X	}
X
X	if (rlen) {
X		if (debug)
X			addlog(" send conf-nak\n");
X		sppp_cp_send (sp, PPP_IPCP, CONF_NAK, h->ident, rlen, buf);
X	} else {
X		if (debug)
X			addlog(" send conf-ack\n");
X		sppp_cp_send (sp, PPP_IPCP, CONF_ACK,
X			      h->ident, origlen, h+1);
X	}
X
X	free (buf, M_TEMP);
X	return (rlen == 0);
X}
X
X/*
X * Analyze the IPCP Configure-Reject option list, and adjust our
X * negotiation.
X */
Xstatic void
Xsppp_ipcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len)
X{
X	u_char *buf, *p;
X	struct ifnet *ifp = &sp->pp_if;
X	int debug = ifp->if_flags & IFF_DEBUG;
X
X	len -= 4;
X	buf = malloc (len, M_TEMP, M_NOWAIT);
X	if (!buf)
X		return;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "ipcp rej opts: ",
X		    SPP_ARGS(ifp));
X
X	p = (void*) (h+1);
X	for (; len > 1 && p[1]; len -= p[1], p += p[1]) {
X		if (debug)
X			addlog(" %s ", sppp_ipcp_opt_name(*p));
X		switch (*p) {
X		case IPCP_OPT_ADDRESS:
X			/*
X			 * Peer doesn't grok address option.  This is
X			 * bad.  XXX  Should we better give up here?
X			 * XXX We could try old "addresses" option...
X			 */
X			sp->ipcp.opts &= ~(1 << IPCP_OPT_ADDRESS);
X			break;
X#ifdef notyet
X		case IPCP_OPT_COMPRESS:
X			sp->ipcp.opts &= ~(1 << IPCP_OPT_COMPRESS);
X			break;
X#endif
X		}
X	}
X	if (debug)
X		addlog("\n");
X	free (buf, M_TEMP);
X	return;
X}
X
X/*
X * Analyze the IPCP Configure-NAK option list, and adjust our
X * negotiation.
X */
Xstatic void
Xsppp_ipcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len)
X{
X	u_char *buf, *p;
X	struct ifnet *ifp = &sp->pp_if;
X	int debug = ifp->if_flags & IFF_DEBUG;
X	u_long wantaddr;
X
X	len -= 4;
X	buf = malloc (len, M_TEMP, M_NOWAIT);
X	if (!buf)
X		return;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "ipcp nak opts: ",
X		    SPP_ARGS(ifp));
X
X	p = (void*) (h+1);
X	for (; len > 1 && p[1]; len -= p[1], p += p[1]) {
X		if (debug)
X			addlog(" %s ", sppp_ipcp_opt_name(*p));
X		switch (*p) {
X		case IPCP_OPT_ADDRESS:
X			/*
X			 * Peer doesn't like our local IP address.  See
X			 * if we can do something for him.  We'll drop
X			 * him our address then.
X			 */
X			if (len >= 6 && p[1] == 6) {
X				wantaddr = p[2] << 24 | p[3] << 16 |
X					p[4] << 8 | p[5];
X				sp->ipcp.opts |= (1 << IPCP_OPT_ADDRESS);
X				if (debug)
X					addlog("[wantaddr %s] ",
X					       sppp_dotted_quad(wantaddr));
X				/*
X				 * When doing dynamic address assignment,
X				 * we accept his offer.  Otherwise, we
X				 * ignore it and thus continue to negotiate
X				 * our already existing value.
X			 	 * XXX: Bogus, if he said no once, he'll
X				 * just say no again, might as well die.
X				 */
X				if (sp->ipcp.flags & IPCP_MYADDR_DYN) {
X					sppp_set_ip_addr(sp, wantaddr);
X					if (debug)
X						addlog("[agree] ");
X					sp->ipcp.flags |= IPCP_MYADDR_SEEN;
X				}
X			}
X			break;
X#ifdef notyet
X		case IPCP_OPT_COMPRESS:
X			/*
X			 * Peer wants different compression parameters.
X			 */
X			break;
X#endif
X		}
X	}
X	if (debug)
X		addlog("\n");
X	free (buf, M_TEMP);
X	return;
X}
X
Xstatic void
Xsppp_ipcp_tlu(struct sppp *sp)
X{
X	/* we are up - notify isdn daemon */
X	if (sp->pp_con)
X		sp->pp_con(sp);
X}
X
Xstatic void
Xsppp_ipcp_tld(struct sppp *sp)
X{
X}
X
Xstatic void
Xsppp_ipcp_tls(struct sppp *sp)
X{
X	/* indicate to LCP that it must stay alive */
X	sp->lcp.protos |= (1 << IDX_IPCP);
X}
X
Xstatic void
Xsppp_ipcp_tlf(struct sppp *sp)
X{
X	/* we no longer need LCP */
X	sp->lcp.protos &= ~(1 << IDX_IPCP);
X	sppp_lcp_check_and_close(sp);
X}
X
Xstatic void
Xsppp_ipcp_scr(struct sppp *sp)
X{
X	char opt[6 /* compression */ + 6 /* address */];
X	u_long ouraddr;
X	int i = 0;
X
X#ifdef notyet
X	if (sp->ipcp.opts & (1 << IPCP_OPT_COMPRESSION)) {
X		opt[i++] = IPCP_OPT_COMPRESSION;
X		opt[i++] = 6;
X		opt[i++] = 0;	/* VJ header compression */
X		opt[i++] = 0x2d; /* VJ header compression */
X		opt[i++] = max_slot_id;
X		opt[i++] = comp_slot_id;
X	}
X#endif
X
X	if (sp->ipcp.opts & (1 << IPCP_OPT_ADDRESS)) {
X		sppp_get_ip_addrs(sp, &ouraddr, 0, 0);
X		opt[i++] = IPCP_OPT_ADDRESS;
X		opt[i++] = 6;
X		opt[i++] = ouraddr >> 24;
X		opt[i++] = ouraddr >> 16;
X		opt[i++] = ouraddr >> 8;
X		opt[i++] = ouraddr;
X	}
X
X	sp->confid[IDX_IPCP] = ++sp->pp_seq;
X	sppp_cp_send(sp, PPP_IPCP, CONF_REQ, sp->confid[IDX_IPCP], i, &opt);
X}
X
X
X/*
X *--------------------------------------------------------------------------*
X *                                                                          *
X *                        The CHAP implementation.                          *
X *                                                                          *
X *--------------------------------------------------------------------------*
X */
X
X/*
X * The authentication protocols don't employ a full-fledged state machine as
X * the control protocols do, since they do have Open and Close events, but
X * not Up and Down, nor are they explicitly terminated.  Also, use of the
X * authentication protocols may be different in both directions (this makes
X * sense, think of a machine that never accepts incoming calls but only
X * calls out, it doesn't require the called party to authenticate itself).
X *
X * Our state machine for the local authentication protocol (we are requesting
X * the peer to authenticate) looks like:
X *
X *						    RCA-
X *	      +--------------------------------------------+
X *	      V					    scn,tld|
X *	  +--------+			       Close   +---------+ RCA+
X *	  |	   |<----------------------------------|	 |------+
X *   +--->| Closed |				TO*    | Opened	 | sca	|
X *   |	  |	   |-----+		       +-------|	 |<-----+
X *   |	  +--------+ irc |		       |       +---------+
X *   |	    ^		 |		       |	   ^
X *   |	    |		 |		       |	   |
X *   |	    |		 |		       |	   |
X *   |	 TO-|		 |		       |	   |
X *   |	    |tld  TO+	 V		       |	   |
X *   |	    |	+------->+		       |	   |
X *   |	    |	|	 |		       |	   |
X *   |	  +--------+	 V		       |	   |
X *   |	  |	   |<----+<--------------------+	   |
X *   |	  | Req-   | scr				   |
X *   |	  | Sent   |					   |
X *   |	  |	   |					   |
X *   |	  +--------+					   |
X *   | RCA- |	| RCA+					   |
X *   +------+	+------------------------------------------+
X *   scn,tld	  sca,irc,ict,tlu
X *
X *
X *   with:
X *
X *	Open:	LCP reached authentication phase
X *	Close:	LCP reached terminate phase
X *
X *	RCA+:	received reply (pap-req, chap-response), acceptable
X *	RCN:	received reply (pap-req, chap-response), not acceptable
X *	TO+:	timeout with restart counter >= 0
X *	TO-:	timeout with restart counter < 0
X *	TO*:	reschedule timeout for CHAP
X *
X *	scr:	send request packet (none for PAP, chap-challenge)
X *	sca:	send ack packet (pap-ack, chap-success)
X *	scn:	send nak packet (pap-nak, chap-failure)
X *	ict:	initialize re-challenge timer (CHAP only)
X *
X *	tlu:	this-layer-up, LCP reaches network phase
X *	tld:	this-layer-down, LCP enters terminate phase
X *
X * Note that in CHAP mode, after sending a new challenge, while the state
X * automaton falls back into Req-Sent state, it doesn't signal a tld
X * event to LCP, so LCP remains in network phase.  Only after not getting
X * any response (or after getting an unacceptable response), CHAP closes,
X * causing LCP to enter terminate phase.
X *
X * With PAP, there is no initial request that can be sent.  The peer is
X * expected to send one based on the successful negotiation of PAP as
X * the authentication protocol during the LCP option negotiation.
X *
X * Incoming authentication protocol requests (remote requests
X * authentication, we are peer) don't employ a state machine at all,
X * they are simply answered.  Some peers [Ascend P50 firmware rev
X * 4.50] react allergically when sending IPCP requests while they are
X * still in authentication phase (thereby violating the standard that
X * demands that these NCP packets are to be discarded), so we keep
X * track of the peer demanding us to authenticate, and only proceed to
X * phase network once we've seen a positive acknowledge for the
X * authentication.
X */
X
X/*
X * Handle incoming CHAP packets.
X */
Xvoid
Xsppp_chap_input(struct sppp *sp, struct mbuf *m)
X{
X	STDDCL;
X	struct lcp_header *h;
X	int len, x;
X	u_char *value, *name, digest[AUTHKEYLEN], dsize;
X	int value_len, name_len;
X	MD5_CTX ctx;
X
X	len = m->m_pkthdr.len;
X	if (len < 4) {
X		if (debug)
X			log(LOG_DEBUG,
X			    SPP_FMT "chap invalid packet length: %d bytes\n",
X			    SPP_ARGS(ifp), len);
X		return;
X	}
X	h = mtod (m, struct lcp_header*);
X	if (len > ntohs (h->len))
X		len = ntohs (h->len);
X
X	switch (h->type) {
X	/* challenge, failure and success are his authproto */
X	case CHAP_CHALLENGE:
X		value = 1 + (u_char*)(h+1);
X		value_len = value[-1];
X		name = value + value_len;
X		name_len = len - value_len - 5;
X		if (name_len < 0) {
X			if (debug) {
X				log(LOG_DEBUG,
X				    SPP_FMT "chap corrupted challenge "
X				    "<%s id=0x%x len=%d",
X				    SPP_ARGS(ifp),
X				    sppp_auth_type_name(PPP_CHAP, h->type),
X				    h->ident, ntohs(h->len));
X				if (len > 4)
X					sppp_print_bytes((u_char*) (h+1), len-4);
X				addlog(">\n");
X			}
X			break;
X		}
X		
X		if (debug) {
X			log(LOG_DEBUG,
X			    SPP_FMT "chap input <%s id=0x%x len=%d name=",
X			    SPP_ARGS(ifp),
X			    sppp_auth_type_name(PPP_CHAP, h->type), h->ident,
X			    ntohs(h->len));
X			sppp_print_string((char*) name, name_len);
X			addlog(" value-size=%d value=", value_len);
X			sppp_print_bytes(value, value_len);
X			addlog(">\n");
X		}
X
X		/* Compute reply value. */
X		MD5Init(&ctx);
X		MD5Update(&ctx, &h->ident, 1);
X		MD5Update(&ctx, sp->myauth.secret,
X			  sppp_strnlen(sp->myauth.secret, AUTHKEYLEN));
X		MD5Update(&ctx, value, value_len);
X		MD5Final(digest, &ctx);
X		dsize = sizeof digest;
X
X		sppp_auth_send(&chap, sp, CHAP_RESPONSE, h->ident,
X			       sizeof dsize, (const char *)&dsize,
X			       sizeof digest, digest,
X			       (size_t)sppp_strnlen(sp->myauth.name, AUTHNAMELEN),
X			       sp->myauth.name,
X			       0);
X		break;
X
X	case CHAP_SUCCESS:
X		if (debug) {
X			log(LOG_DEBUG, SPP_FMT "chap success",
X			    SPP_ARGS(ifp));
X			if (len > 4) {
X				addlog(": ");
X				sppp_print_string((char*)(h + 1), len - 4);
X			}
X			addlog("\n");
X		}
X		x = splimp();
X		sp->pp_flags &= ~PP_NEEDAUTH;
X		if (sp->myauth.proto == PPP_CHAP &&
X		    (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) &&
X		    (sp->lcp.protos & (1 << IDX_CHAP)) == 0) {
X			/*
X			 * We are authenticator for CHAP but didn't
X			 * complete yet.  Leave it to tlu to proceed
X			 * to network phase.
X			 */
X			splx(x);
X			break;
X		}
X		splx(x);
X		sppp_phase_network(sp);
X		break;
X
X	case CHAP_FAILURE:
X		if (debug) {
X			log(LOG_INFO, SPP_FMT "chap failure",
X			    SPP_ARGS(ifp));
X			if (len > 4) {
X				addlog(": ");
X				sppp_print_string((char*)(h + 1), len - 4);
X			}
X			addlog("\n");
X		} else
X			log(LOG_INFO, SPP_FMT "chap failure\n",
X			    SPP_ARGS(ifp));
X		/* await LCP shutdown by authenticator */
X		break;
X
X	/* response is my authproto */
X	case CHAP_RESPONSE:
X		value = 1 + (u_char*)(h+1);
X		value_len = value[-1];
X		name = value + value_len;
X		name_len = len - value_len - 5;
X		if (name_len < 0) {
X			if (debug) {
X				log(LOG_DEBUG,
X				    SPP_FMT "chap corrupted response "
X				    "<%s id=0x%x len=%d",
X				    SPP_ARGS(ifp),
X				    sppp_auth_type_name(PPP_CHAP, h->type),
X				    h->ident, ntohs(h->len));
X				if (len > 4)
X					sppp_print_bytes((u_char*)(h+1), len-4);
X				addlog(">\n");
X			}
X			break;
X		}
X		if (h->ident != sp->confid[IDX_CHAP]) {
X			if (debug)
X				log(LOG_DEBUG,
X				    SPP_FMT "chap dropping response for old ID "
X				    "(got %d, expected %d)\n",
X				    SPP_ARGS(ifp),
X				    h->ident, sp->confid[IDX_CHAP]);
X			break;
X		}
X		if (name_len != sppp_strnlen(sp->hisauth.name, AUTHNAMELEN)
X		    || bcmp(name, sp->hisauth.name, name_len) != 0) {
X			log(LOG_INFO, SPP_FMT "chap response, his name ",
X			    SPP_ARGS(ifp));
X			sppp_print_string(name, name_len);
X			addlog(" != expected ");
X			sppp_print_string(sp->hisauth.name,
X					  sppp_strnlen(sp->hisauth.name, AUTHNAMELEN));
X			addlog("\n");
X		}    
X		if (debug) {
X			log(LOG_DEBUG, SPP_FMT "chap input(%s) "
X			    "<%s id=0x%x len=%d name=",
X			    SPP_ARGS(ifp),
X			    sppp_state_name(sp->state[IDX_CHAP]),
X			    sppp_auth_type_name(PPP_CHAP, h->type),
X			    h->ident, ntohs (h->len));
X			sppp_print_string((char*)name, name_len);
X			addlog(" value-size=%d value=", value_len);
X			sppp_print_bytes(value, value_len);
X			addlog(">\n");
X		}
X		if (value_len != AUTHKEYLEN) {
X			if (debug)
X				log(LOG_DEBUG,
X				    SPP_FMT "chap bad hash value length: "
X				    "%d bytes, should be %d\n",
X				    SPP_ARGS(ifp), value_len,
X				    AUTHKEYLEN);
X			break;
X		}
X
X		MD5Init(&ctx);
X		MD5Update(&ctx, &h->ident, 1);
X		MD5Update(&ctx, sp->hisauth.secret,
X			  sppp_strnlen(sp->hisauth.secret, AUTHKEYLEN));
X		MD5Update(&ctx, sp->myauth.challenge, AUTHKEYLEN);
X		MD5Final(digest, &ctx);
X
X#define FAILMSG "Failed..."
X#define SUCCMSG "Welcome!"
X
X		if (value_len != sizeof digest ||
X		    bcmp(digest, value, value_len) != 0) {
X			/* action scn, tld */
X			sppp_auth_send(&chap, sp, CHAP_FAILURE, h->ident,
X				       sizeof(FAILMSG) - 1, (u_char *)FAILMSG,
X				       0);
X			chap.tld(sp);
X			break;
X		}
X		/* action sca, perhaps tlu */
X		if (sp->state[IDX_CHAP] == STATE_REQ_SENT ||
X		    sp->state[IDX_CHAP] == STATE_OPENED)
X			sppp_auth_send(&chap, sp, CHAP_SUCCESS, h->ident,
X				       sizeof(SUCCMSG) - 1, (u_char *)SUCCMSG,
X				       0);
X		if (sp->state[IDX_CHAP] == STATE_REQ_SENT) {
X			sppp_cp_change_state(&chap, sp, STATE_OPENED);
X			chap.tlu(sp);
X		}
X		break;
X
X	default:
X		/* Unknown CHAP packet type -- ignore. */
X		if (debug) {
X			log(LOG_DEBUG, SPP_FMT "chap unknown input(%s) "
X			    "<0x%x id=0x%xh len=%d",
X			    SPP_ARGS(ifp),
X			    sppp_state_name(sp->state[IDX_CHAP]),
X			    h->type, h->ident, ntohs(h->len));
X			if (len > 4)
X				sppp_print_bytes((u_char*)(h+1), len-4);
X			addlog(">\n");
X		}
X		break;
X
X	}
X}
X
Xstatic void
Xsppp_chap_init(struct sppp *sp)
X{
X	/* Chap doesn't have STATE_INITIAL at all. */
X	sp->state[IDX_CHAP] = STATE_CLOSED;
X	sp->fail_counter[IDX_CHAP] = 0;
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X	callout_handle_init(&sp->ch[IDX_CHAP]);
X#endif
X}
X
Xstatic void
Xsppp_chap_open(struct sppp *sp)
X{
X	if (sp->myauth.proto == PPP_CHAP &&
X	    (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) != 0) {
X		/* we are authenticator for CHAP, start it */
X		chap.scr(sp);
X		sp->rst_counter[IDX_CHAP] = sp->lcp.max_configure;
X		sppp_cp_change_state(&chap, sp, STATE_REQ_SENT);
X	}
X	/* nothing to be done if we are peer, await a challenge */
X}
X
Xstatic void
Xsppp_chap_close(struct sppp *sp)
X{
X	if (sp->state[IDX_CHAP] != STATE_CLOSED)
X		sppp_cp_change_state(&chap, sp, STATE_CLOSED);
X}
X
Xstatic void
Xsppp_chap_TO(void *cookie)
X{
X	struct sppp *sp = (struct sppp *)cookie;
X	STDDCL;
X	int s;
X
X	s = splimp();
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "chap TO(%s) rst_counter = %d\n",
X		    SPP_ARGS(ifp),
X		    sppp_state_name(sp->state[IDX_CHAP]),
X		    sp->rst_counter[IDX_CHAP]);
X
X	if (--sp->rst_counter[IDX_CHAP] < 0)
X		/* TO- event */
X		switch (sp->state[IDX_CHAP]) {
X		case STATE_REQ_SENT:
X			chap.tld(sp);
X			sppp_cp_change_state(&chap, sp, STATE_CLOSED);
X			break;
X		}
X	else
X		/* TO+ (or TO*) event */
X		switch (sp->state[IDX_CHAP]) {
X		case STATE_OPENED:
X			/* TO* event */
X			sp->rst_counter[IDX_CHAP] = sp->lcp.max_configure;
X			/* fall through */
X		case STATE_REQ_SENT:
X			chap.scr(sp);
X			/* sppp_cp_change_state() will restart the timer */
X			sppp_cp_change_state(&chap, sp, STATE_REQ_SENT);
X			break;
X		}
X
X	splx(s);
X}
X
Xstatic void
Xsppp_chap_tlu(struct sppp *sp)
X{
X	STDDCL;
X	int i, x;
X
X	i = 0;
X	sp->rst_counter[IDX_CHAP] = sp->lcp.max_configure;
X
X	/*
X	 * Some broken CHAP implementations (Conware CoNet, firmware
X	 * 4.0.?) don't want to re-authenticate their CHAP once the
X	 * initial challenge-response exchange has taken place.
X	 * Provide for an option to avoid rechallenges.
X	 */
X	if ((sp->hisauth.flags & AUTHFLAG_NORECHALLENGE) == 0) {
X		/*
X		 * Compute the re-challenge timeout.  This will yield
X		 * a number between 300 and 810 seconds.
X		 */
X		i = 300 + ((unsigned)(random() & 0xff00) >> 7);
X		TIMEOUT(chap.TO, (void *)sp, i * hz, sp->ch[IDX_CHAP]);
X	}
X
X	if (debug) {
X		log(LOG_DEBUG,
X		    SPP_FMT "chap %s, ",
X		    SPP_ARGS(ifp),
X		    sp->pp_phase == PHASE_NETWORK? "reconfirmed": "tlu");
X		if ((sp->hisauth.flags & AUTHFLAG_NORECHALLENGE) == 0)
X			addlog("next re-challenge in %d seconds\n", i);
X		else
X			addlog("re-challenging supressed\n");
X	}
X
X	x = splimp();
X	/* indicate to LCP that we need to be closed down */
X	sp->lcp.protos |= (1 << IDX_CHAP);
X
X	if (sp->pp_flags & PP_NEEDAUTH) {
X		/*
X		 * Remote is authenticator, but his auth proto didn't
X		 * complete yet.  Defer the transition to network
X		 * phase.
X		 */
X		splx(x);
X		return;
X	}
X	splx(x);
X
X	/*
X	 * If we are already in phase network, we are done here.  This
X	 * is the case if this is a dummy tlu event after a re-challenge.
X	 */
X	if (sp->pp_phase != PHASE_NETWORK)
X		sppp_phase_network(sp);
X}
X
Xstatic void
Xsppp_chap_tld(struct sppp *sp)
X{
X	STDDCL;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "chap tld\n", SPP_ARGS(ifp));
X	UNTIMEOUT(chap.TO, (void *)sp, sp->ch[IDX_CHAP]);
X	sp->lcp.protos &= ~(1 << IDX_CHAP);
X
X	lcp.Close(sp);
X}
X
Xstatic void
Xsppp_chap_scr(struct sppp *sp)
X{
X	u_long *ch, seed;
X	u_char clen;
X
X	/* Compute random challenge. */
X	ch = (u_long *)sp->myauth.challenge;
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X	read_random(&seed, sizeof seed);
X#else
X	{
X	struct timeval tv;
X	microtime(&tv);
X	seed = tv.tv_sec ^ tv.tv_usec;
X	}
X#endif
X	ch[0] = seed ^ random();
X	ch[1] = seed ^ random();
X	ch[2] = seed ^ random();
X	ch[3] = seed ^ random();
X	clen = AUTHKEYLEN;
X
X	sp->confid[IDX_CHAP] = ++sp->pp_seq;
X
X	sppp_auth_send(&chap, sp, CHAP_CHALLENGE, sp->confid[IDX_CHAP],
X		       sizeof clen, (const char *)&clen,
X		       (size_t)AUTHKEYLEN, sp->myauth.challenge,
X		       (size_t)sppp_strnlen(sp->myauth.name, AUTHNAMELEN),
X		       sp->myauth.name,
X		       0);
X}
X/*
X *--------------------------------------------------------------------------*
X *                                                                          *
X *                        The PAP implementation.                           *
X *                                                                          *
X *--------------------------------------------------------------------------*
X */
X/*
X * For PAP, we need to keep a little state also if we are the peer, not the
X * authenticator.  This is since we don't get a request to authenticate, but
X * have to repeatedly authenticate ourself until we got a response (or the
X * retry counter is expired).
X */
X
X/*
X * Handle incoming PAP packets.  */
Xstatic void
Xsppp_pap_input(struct sppp *sp, struct mbuf *m)
X{
X	STDDCL;
X	struct lcp_header *h;
X	int len, x;
X	u_char *name, *passwd, mlen;
X	int name_len, passwd_len;
X
X	len = m->m_pkthdr.len;
X	if (len < 5) {
X		if (debug)
X			log(LOG_DEBUG,
X			    SPP_FMT "pap invalid packet length: %d bytes\n",
X			    SPP_ARGS(ifp), len);
X		return;
X	}
X	h = mtod (m, struct lcp_header*);
X	if (len > ntohs (h->len))
X		len = ntohs (h->len);
X	switch (h->type) {
X	/* PAP request is my authproto */
X	case PAP_REQ:
X		name = 1 + (u_char*)(h+1);
X		name_len = name[-1];
X		passwd = name + name_len + 1;
X		if (name_len > len - 6 ||
X		    (passwd_len = passwd[-1]) > len - 6 - name_len) {
X			if (debug) {
X				log(LOG_DEBUG, SPP_FMT "pap corrupted input "
X				    "<%s id=0x%x len=%d",
X				    SPP_ARGS(ifp),
X				    sppp_auth_type_name(PPP_PAP, h->type),
X				    h->ident, ntohs(h->len));
X				if (len > 4)
X					sppp_print_bytes((u_char*)(h+1), len-4);
X				addlog(">\n");
X			}
X			break;
X		}
X		if (debug) {
X			log(LOG_DEBUG, SPP_FMT "pap input(%s) "
X			    "<%s id=0x%x len=%d name=",
X			    SPP_ARGS(ifp),
X			    sppp_state_name(sp->state[IDX_PAP]),
X			    sppp_auth_type_name(PPP_PAP, h->type),
X			    h->ident, ntohs(h->len));
X			sppp_print_string((char*)name, name_len);
X			addlog(" passwd=");
X			sppp_print_string((char*)passwd, passwd_len);
X			addlog(">\n");
X		}
X		if (name_len > AUTHNAMELEN ||
X		    passwd_len > AUTHKEYLEN ||
X		    bcmp(name, sp->hisauth.name, name_len) != 0 ||
X		    bcmp(passwd, sp->hisauth.secret, passwd_len) != 0) {
X			/* action scn, tld */
X			mlen = sizeof(FAILMSG) - 1;
X			sppp_auth_send(&pap, sp, PAP_NAK, h->ident,
X				       sizeof mlen, (const char *)&mlen,
X				       sizeof(FAILMSG) - 1, (u_char *)FAILMSG,
X				       0);
X			pap.tld(sp);
X			break;
X		}
X		/* action sca, perhaps tlu */
X		if (sp->state[IDX_PAP] == STATE_REQ_SENT ||
X		    sp->state[IDX_PAP] == STATE_OPENED) {
X			mlen = sizeof(SUCCMSG) - 1;
X			sppp_auth_send(&pap, sp, PAP_ACK, h->ident,
X				       sizeof mlen, (const char *)&mlen,
X				       sizeof(SUCCMSG) - 1, (u_char *)SUCCMSG,
X				       0);
X		}
X		if (sp->state[IDX_PAP] == STATE_REQ_SENT) {
X			sppp_cp_change_state(&pap, sp, STATE_OPENED);
X			pap.tlu(sp);
X		}
X		break;
X
X	/* ack and nak are his authproto */
X	case PAP_ACK:
X		UNTIMEOUT(sppp_pap_my_TO, (void *)sp, sp->pap_my_to_ch);
X		if (debug) {
X			log(LOG_DEBUG, SPP_FMT "pap success",
X			    SPP_ARGS(ifp));
X			name_len = *((char *)h);
X			if (len > 5 && name_len) {
X				addlog(": ");
X				sppp_print_string((char*)(h+1), name_len);
X			}
X			addlog("\n");
X		}
X		x = splimp();
X		sp->pp_flags &= ~PP_NEEDAUTH;
X		if (sp->myauth.proto == PPP_PAP &&
X		    (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) &&
X		    (sp->lcp.protos & (1 << IDX_PAP)) == 0) {
X			/*
X			 * We are authenticator for PAP but didn't
X			 * complete yet.  Leave it to tlu to proceed
X			 * to network phase.
X			 */
X			splx(x);
X			break;
X		}
X		splx(x);
X		sppp_phase_network(sp);
X		break;
X
X	case PAP_NAK:
X		UNTIMEOUT(sppp_pap_my_TO, (void *)sp, sp->pap_my_to_ch);
X		if (debug) {
X			log(LOG_INFO, SPP_FMT "pap failure",
X			    SPP_ARGS(ifp));
X			name_len = *((char *)h);
X			if (len > 5 && name_len) {
X				addlog(": ");
X				sppp_print_string((char*)(h+1), name_len);
X			}
X			addlog("\n");
X		} else
X			log(LOG_INFO, SPP_FMT "pap failure\n",
X			    SPP_ARGS(ifp));
X		/* await LCP shutdown by authenticator */
X		break;
X
X	default:
X		/* Unknown PAP packet type -- ignore. */
X		if (debug) {
X			log(LOG_DEBUG, SPP_FMT "pap corrupted input "
X			    "<0x%x id=0x%x len=%d",
X			    SPP_ARGS(ifp),
X			    h->type, h->ident, ntohs(h->len));
X			if (len > 4)
X				sppp_print_bytes((u_char*)(h+1), len-4);
X			addlog(">\n");
X		}
X		break;
X
X	}
X}
X
Xstatic void
Xsppp_pap_init(struct sppp *sp)
X{
X	/* PAP doesn't have STATE_INITIAL at all. */
X	sp->state[IDX_PAP] = STATE_CLOSED;
X	sp->fail_counter[IDX_PAP] = 0;
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X	callout_handle_init(&sp->ch[IDX_PAP]);
X	callout_handle_init(&sp->pap_my_to_ch);
X#endif
X}
X
Xstatic void
Xsppp_pap_open(struct sppp *sp)
X{
X	if (sp->hisauth.proto == PPP_PAP &&
X	    (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) != 0) {
X		/* we are authenticator for PAP, start our timer */
X		sp->rst_counter[IDX_PAP] = sp->lcp.max_configure;
X		sppp_cp_change_state(&pap, sp, STATE_REQ_SENT);
X	}
X	if (sp->myauth.proto == PPP_PAP) {
X		/* we are peer, send a request, and start a timer */
X		pap.scr(sp);
X		TIMEOUT(sppp_pap_my_TO, (void *)sp, sp->lcp.timeout,
X		    sp->pap_my_to_ch);
X	}
X}
X
Xstatic void
Xsppp_pap_close(struct sppp *sp)
X{
X	if (sp->state[IDX_PAP] != STATE_CLOSED)
X		sppp_cp_change_state(&pap, sp, STATE_CLOSED);
X}
X
X/*
X * That's the timeout routine if we are authenticator.  Since the
X * authenticator is basically passive in PAP, we can't do much here.
X */
Xstatic void
Xsppp_pap_TO(void *cookie)
X{
X	struct sppp *sp = (struct sppp *)cookie;
X	STDDCL;
X	int s;
X
X	s = splimp();
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "pap TO(%s) rst_counter = %d\n",
X		    SPP_ARGS(ifp),
X		    sppp_state_name(sp->state[IDX_PAP]),
X		    sp->rst_counter[IDX_PAP]);
X
X	if (--sp->rst_counter[IDX_PAP] < 0)
X		/* TO- event */
X		switch (sp->state[IDX_PAP]) {
X		case STATE_REQ_SENT:
X			pap.tld(sp);
X			sppp_cp_change_state(&pap, sp, STATE_CLOSED);
X			break;
X		}
X	else
X		/* TO+ event, not very much we could do */
X		switch (sp->state[IDX_PAP]) {
X		case STATE_REQ_SENT:
X			/* sppp_cp_change_state() will restart the timer */
X			sppp_cp_change_state(&pap, sp, STATE_REQ_SENT);
X			break;
X		}
X
X	splx(s);
X}
X
X/*
X * That's the timeout handler if we are peer.  Since the peer is active,
X * we need to retransmit our PAP request since it is apparently lost.
X * XXX We should impose a max counter.
X */
Xstatic void
Xsppp_pap_my_TO(void *cookie)
X{
X	struct sppp *sp = (struct sppp *)cookie;
X	STDDCL;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "pap peer TO\n",
X		    SPP_ARGS(ifp));
X
X	pap.scr(sp);
X}
X
Xstatic void
Xsppp_pap_tlu(struct sppp *sp)
X{
X	STDDCL;
X	int x;
X
X	sp->rst_counter[IDX_PAP] = sp->lcp.max_configure;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "%s tlu\n",
X		    SPP_ARGS(ifp), pap.name);
X
X	x = splimp();
X	/* indicate to LCP that we need to be closed down */
X	sp->lcp.protos |= (1 << IDX_PAP);
X
X	if (sp->pp_flags & PP_NEEDAUTH) {
X		/*
X		 * Remote is authenticator, but his auth proto didn't
X		 * complete yet.  Defer the transition to network
X		 * phase.
X		 */
X		splx(x);
X		return;
X	}
X	splx(x);
X	sppp_phase_network(sp);
X}
X
Xstatic void
Xsppp_pap_tld(struct sppp *sp)
X{
X	STDDCL;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "pap tld\n", SPP_ARGS(ifp));
X	UNTIMEOUT(pap.TO, (void *)sp, sp->ch[IDX_PAP]);
X	UNTIMEOUT(sppp_pap_my_TO, (void *)sp, sp->pap_my_to_ch);
X	sp->lcp.protos &= ~(1 << IDX_PAP);
X
X	lcp.Close(sp);
X}
X
Xstatic void
Xsppp_pap_scr(struct sppp *sp)
X{
X	u_char idlen, pwdlen;
X
X	sp->confid[IDX_PAP] = ++sp->pp_seq;
X	pwdlen = sppp_strnlen(sp->myauth.secret, AUTHKEYLEN);
X	idlen = sppp_strnlen(sp->myauth.name, AUTHNAMELEN);
X
X	sppp_auth_send(&pap, sp, PAP_REQ, sp->confid[IDX_PAP],
X		       sizeof idlen, (const char *)&idlen,
X		       (size_t)idlen, sp->myauth.name,
X		       sizeof pwdlen, (const char *)&pwdlen,
X		       (size_t)pwdlen, sp->myauth.secret,
X		       0);
X}
X/*
X * Random miscellaneous functions.
X */
X
X/*
X * Send a PAP or CHAP proto packet.
X *
X * Varadic function, each of the elements for the ellipsis is of type
X * ``size_t mlen, const u_char *msg''.  Processing will stop iff
X * mlen == 0.
X * NOTE: never declare variadic functions with types subject to type
X * promotion (i.e. u_char). This is asking for big trouble depending
X * on the architecture you are on...
X */
X
Xstatic void
Xsppp_auth_send(const struct cp *cp, struct sppp *sp,
X               unsigned int type, unsigned int id,
X	       ...)
X{
X	STDDCL;
X	struct ppp_header *h;
X	struct lcp_header *lh;
X	struct mbuf *m;
X	u_char *p;
X	int len;
X	unsigned int mlen;
X	const char *msg;
X	va_list ap;
X
X	MGETHDR (m, M_DONTWAIT, MT_DATA);
X	if (! m)
X		return;
X	m->m_pkthdr.rcvif = 0;
X
X	h = mtod (m, struct ppp_header*);
X	h->address = PPP_ALLSTATIONS;		/* broadcast address */
X	h->control = PPP_UI;			/* Unnumbered Info */
X	h->protocol = htons(cp->proto);
X
X	lh = (struct lcp_header*)(h + 1);
X	lh->type = type;
X	lh->ident = id;
X	p = (u_char*) (lh+1);
X
X	va_start(ap, id);
X	len = 0;
X
X	while ((mlen = (unsigned int)va_arg(ap, size_t)) != 0) {
X		msg = va_arg(ap, const char *);
X		len += mlen;
X		if (len > MHLEN - PPP_HEADER_LEN - LCP_HEADER_LEN) {
X			va_end(ap);
X			m_freem(m);
X			return;
X		}
X
X		bcopy(msg, p, mlen);
X		p += mlen;
X	}
X	va_end(ap);
X
X	m->m_pkthdr.len = m->m_len = PPP_HEADER_LEN + LCP_HEADER_LEN + len;
X	lh->len = htons (LCP_HEADER_LEN + len);
X
X	if (debug) {
X		log(LOG_DEBUG, SPP_FMT "%s output <%s id=0x%x len=%d",
X		    SPP_ARGS(ifp), cp->name,
X		    sppp_auth_type_name(cp->proto, lh->type),
X		    lh->ident, ntohs(lh->len));
X		if (len)
X			sppp_print_bytes((u_char*) (lh+1), len);
X		addlog(">\n");
X	}
X	if (IF_QFULL (&sp->pp_cpq)) {
X		IF_DROP (&sp->pp_fastq);
X		IF_DROP (&ifp->if_snd);
X		m_freem (m);
X		++ifp->if_oerrors;
X	} else
X		IF_ENQUEUE (&sp->pp_cpq, m);
X	if (! (ifp->if_flags & IFF_OACTIVE))
X		(*ifp->if_start) (ifp);
X	ifp->if_obytes += m->m_pkthdr.len + 3;
X}
X
X/*
X * Flush interface queue.
X */
Xstatic void
Xsppp_qflush(struct ifqueue *ifq)
X{
X	struct mbuf *m, *n;
X
X	n = ifq->ifq_head;
X	while ((m = n)) {
X		n = m->m_act;
X		m_freem (m);
X	}
X	ifq->ifq_head = 0;
X	ifq->ifq_tail = 0;
X	ifq->ifq_len = 0;
X}
X
X/*
X * Send keepalive packets, every 10 seconds.
X */
Xstatic void
Xsppp_keepalive(void *dummy)
X{
X	struct sppp *sp;
X	int s;
X
X	s = splimp();
X	for (sp=spppq; sp; sp=sp->pp_next) {
X		struct ifnet *ifp = &sp->pp_if;
X
X		/* Keepalive mode disabled or channel down? */
X		if (! (sp->pp_flags & PP_KEEPALIVE) ||
X		    ! (ifp->if_flags & IFF_RUNNING))
X			continue;
X
X		/* No keepalive in PPP mode if LCP not opened yet. */
X		if (! (sp->pp_flags & PP_CISCO) &&
X		    sp->pp_phase < PHASE_AUTHENTICATE)
X			continue;
X
X		if (sp->pp_alivecnt == MAXALIVECNT) {
X			/* No keepalive packets got.  Stop the interface. */
X			printf (SPP_FMT "down\n", SPP_ARGS(ifp));
X			if_down (ifp);
X			sppp_qflush (&sp->pp_cpq);
X			if (! (sp->pp_flags & PP_CISCO)) {
X				/* XXX */
X				/* Shut down the PPP link. */
X				lcp.Down(sp);
X				/* Initiate negotiation. XXX */
X				lcp.Up(sp);
X			}
X		}
X		if (sp->pp_alivecnt <= MAXALIVECNT)
X			++sp->pp_alivecnt;
X		if (sp->pp_flags & PP_CISCO)
X			sppp_cisco_send (sp, CISCO_KEEPALIVE_REQ, ++sp->pp_seq,
X				sp->pp_rseq);
X		else if (sp->pp_phase >= PHASE_AUTHENTICATE) {
X			long nmagic = htonl (sp->lcp.magic);
X			sp->lcp.echoid = ++sp->pp_seq;
X			sppp_cp_send (sp, PPP_LCP, ECHO_REQ,
X				sp->lcp.echoid, 4, &nmagic);
X		}
X	}
X	splx(s);
X	TIMEOUT(sppp_keepalive, 0, hz * 10, keepalive_ch);
X}
X
X/*
X * Get both IP addresses.
X */
Xstatic void
Xsppp_get_ip_addrs(struct sppp *sp, u_long *src, u_long *dst, u_long *srcmask)
X{
X	struct ifnet *ifp = &sp->pp_if;
X	struct ifaddr *ifa;
X	struct sockaddr_in *si, *sm;
X	u_long ssrc, ddst;
X
X	sm = NULL;
X	ssrc = ddst = 0L;
X	/*
X	 * Pick the first AF_INET address from the list,
X	 * aliases don't make any sense on a p2p link anyway.
X	 */
X	si = 0;
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X	TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link)
X#elif defined(__NetBSD__) || defined (__OpenBSD__)
X	for (ifa = ifp->if_addrlist.tqh_first;
X	     ifa;
X	     ifa = ifa->ifa_list.tqe_next)
X#else
X	for (ifa = ifp->if_addrlist;
X	     ifa;
X	     ifa = ifa->ifa_next)
X#endif
X		if (ifa->ifa_addr->sa_family == AF_INET) {
X			si = (struct sockaddr_in *)ifa->ifa_addr;
X			sm = (struct sockaddr_in *)ifa->ifa_netmask;
X			if (si)
X				break;
X		}
X	if (ifa) {
X		if (si && si->sin_addr.s_addr) {
X			ssrc = si->sin_addr.s_addr;
X			if (srcmask)
X				*srcmask = ntohl(sm->sin_addr.s_addr);
X		}
X
X		si = (struct sockaddr_in *)ifa->ifa_dstaddr;
X		if (si && si->sin_addr.s_addr)
X			ddst = si->sin_addr.s_addr;
X	}
X
X	if (dst) *dst = ntohl(ddst);
X	if (src) *src = ntohl(ssrc);
X}
X
X/*
X * Set my IP address.  Must be called at splimp.
X */
Xstatic void
Xsppp_set_ip_addr(struct sppp *sp, u_long src)
X{
X	STDDCL;
X	struct ifaddr *ifa;
X	struct sockaddr_in *si;
X
X	/*
X	 * Pick the first AF_INET address from the list,
X	 * aliases don't make any sense on a p2p link anyway.
X	 */
X	si = 0;
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X	TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link)
X#elif defined(__NetBSD__) || defined (__OpenBSD__)
X	for (ifa = ifp->if_addrlist.tqh_first;
X	     ifa;
X	     ifa = ifa->ifa_list.tqe_next)
X#else
X	for (ifa = ifp->if_addrlist;
X	     ifa;
X	     ifa = ifa->ifa_next)
X#endif
X	{
X		if (ifa->ifa_addr->sa_family == AF_INET)
X		{
X			si = (struct sockaddr_in *)ifa->ifa_addr;
X			if (si)
X				break;
X		}
X	}
X
X	if (ifa && si)
X	{
X		int error;
X#if __NetBSD_Version__ >= 103080000
X		struct sockaddr_in new_sin = *si;
X
X		new_sin.sin_addr.s_addr = htonl(src);
X		error = in_ifinit(ifp, ifatoia(ifa), &new_sin, 1);
X		if(debug && error)
X		{
X			log(LOG_DEBUG, SPP_FMT "sppp_set_ip_addr: in_ifinit "
X			" failed, error=%d\n", SPP_ARGS(ifp), error);
X		}
X#else
X		/* delete old route */
X		error = rtinit(ifa, (int)RTM_DELETE, RTF_HOST);
X		if(debug && error)
X		{
X			log(LOG_DEBUG, SPP_FMT "sppp_set_ip_addr: rtinit DEL failed, error=%d\n",
X		    		SPP_ARGS(ifp), error);
X		}
X
X		/* set new address */
X		si->sin_addr.s_addr = htonl(src);
X
X		/* add new route */
X		error = rtinit(ifa, (int)RTM_ADD, RTF_HOST);		
X		if (debug && error)
X		{
X			log(LOG_DEBUG, SPP_FMT "sppp_set_ip_addr: rtinit ADD failed, error=%d",
X		    		SPP_ARGS(ifp), error);
X		}
X#endif
X	}
X}			
X
Xstatic int
Xsppp_params(struct sppp *sp, u_long cmd, void *data)
X{
X	u_long subcmd;
X	struct ifreq *ifr = (struct ifreq *)data;
X	struct spppreq spr;
X
X	/*
X	 * ifr->ifr_data is supposed to point to a struct spppreq.
X	 * Check the cmd word first before attempting to fetch all the
X	 * data.
X	 */
X	if ((subcmd = fuword(ifr->ifr_data)) == -1)
X		return EFAULT;
X
X	if (copyin((caddr_t)ifr->ifr_data, &spr, sizeof spr) != 0)
X		return EFAULT;
X
X	switch (subcmd) {
X	case SPPPIOGDEFS:
X		if (cmd != SIOCGIFGENERIC)
X			return EINVAL;
X		/*
X		 * We copy over the entire current state, but clean
X		 * out some of the stuff we don't wanna pass up.
X		 * Remember, SIOCGIFGENERIC is unprotected, and can be
X		 * called by any user.  No need to ever get PAP or
X		 * CHAP secrets back to userland anyway.
X		 */
X		bcopy(sp, &spr.defs, sizeof(struct sppp));
X		bzero(spr.defs.myauth.secret, AUTHKEYLEN);
X		bzero(spr.defs.myauth.challenge, AUTHKEYLEN);
X		bzero(spr.defs.hisauth.secret, AUTHKEYLEN);
X		bzero(spr.defs.hisauth.challenge, AUTHKEYLEN);
X		return copyout(&spr, (caddr_t)ifr->ifr_data, sizeof spr);
X
X	case SPPPIOSDEFS:
X		if (cmd != SIOCSIFGENERIC)
X			return EINVAL;
X		/*
X		 * We have a very specific idea of which fields we allow
X		 * being passed back from userland, so to not clobber our
X		 * current state.  For one, we only allow setting
X		 * anything if LCP is in dead phase.  Once the LCP
X		 * negotiations started, the authentication settings must
X		 * not be changed again.  (The administrator can force an
X		 * ifconfig down in order to get LCP back into dead
X		 * phase.)
X		 *
X		 * Also, we only allow for authentication parameters to be
X		 * specified.
X		 *
X		 * XXX Should allow to set or clear pp_flags.
X		 *
X		 * Finally, if the respective authentication protocol to
X		 * be used is set differently than 0, but the secret is
X		 * passed as all zeros, we don't trash the existing secret.
X		 * This allows an administrator to change the system name
X		 * only without clobbering the secret (which he didn't get
X		 * back in a previous SPPPIOGDEFS call).  However, the
X		 * secrets are cleared if the authentication protocol is
X		 * reset to 0.
X		 */
X		if (sp->pp_phase != PHASE_DEAD)
X			return EBUSY;
X
X		if ((spr.defs.myauth.proto != 0 && spr.defs.myauth.proto != PPP_PAP &&
X		     spr.defs.myauth.proto != PPP_CHAP) ||
X		    (spr.defs.hisauth.proto != 0 && spr.defs.hisauth.proto != PPP_PAP &&
X		     spr.defs.hisauth.proto != PPP_CHAP))
X			return EINVAL;
X
X		if (spr.defs.myauth.proto == 0)
X			/* resetting myauth */
X			bzero(&sp->myauth, sizeof sp->myauth);
X		else {
X			/* setting/changing myauth */
X			sp->myauth.proto = spr.defs.myauth.proto;
X			bcopy(spr.defs.myauth.name, sp->myauth.name, AUTHNAMELEN);
X			if (spr.defs.myauth.secret[0] != '\0')
X				bcopy(spr.defs.myauth.secret, sp->myauth.secret,
X				      AUTHKEYLEN);
X		}
X		if (spr.defs.hisauth.proto == 0)
X			/* resetting hisauth */
X			bzero(&sp->hisauth, sizeof sp->hisauth);
X		else {
X			/* setting/changing hisauth */
X			sp->hisauth.proto = spr.defs.hisauth.proto;
X			sp->hisauth.flags = spr.defs.hisauth.flags;
X			bcopy(spr.defs.hisauth.name, sp->hisauth.name, AUTHNAMELEN);
X			if (spr.defs.hisauth.secret[0] != '\0')
X				bcopy(spr.defs.hisauth.secret, sp->hisauth.secret,
X				      AUTHKEYLEN);
X		}
X		break;
X
X	default:
X		return EINVAL;
X	}
X
X	return 0;
X}
X
Xstatic void
Xsppp_phase_network(struct sppp *sp)
X{
X	STDDCL;
X	int i;
X	u_long mask;
X
X	sp->pp_phase = PHASE_NETWORK;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "phase %s\n", SPP_ARGS(ifp),
X		    sppp_phase_name(sp->pp_phase));
X
X	/* Notify NCPs now. */
X	for (i = 0; i < IDX_COUNT; i++)
X		if ((cps[i])->flags & CP_NCP)
X			(cps[i])->Open(sp);
X
X	/* Send Up events to all NCPs. */
X	for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1)
X		if (sp->lcp.protos & mask && ((cps[i])->flags & CP_NCP))
X			(cps[i])->Up(sp);
X
X	/* if no NCP is starting, all this was in vain, close down */
X	sppp_lcp_check_and_close(sp);
X}
X	
X
Xstatic const char *
Xsppp_cp_type_name(u_char type)
X{
X	static char buf[12];
X	switch (type) {
X	case CONF_REQ:   return "conf-req";
X	case CONF_ACK:   return "conf-ack";
X	case CONF_NAK:   return "conf-nak";
X	case CONF_REJ:   return "conf-rej";
X	case TERM_REQ:   return "term-req";
X	case TERM_ACK:   return "term-ack";
X	case CODE_REJ:   return "code-rej";
X	case PROTO_REJ:  return "proto-rej";
X	case ECHO_REQ:   return "echo-req";
X	case ECHO_REPLY: return "echo-reply";
X	case DISC_REQ:   return "discard-req";
X	}
X	snprintf (buf, sizeof(buf), "0x%x", type);
X	return buf;
X}
X
Xstatic const char *
Xsppp_auth_type_name(u_short proto, u_char type)
X{
X	static char buf[12];
X	switch (proto) {
X	case PPP_CHAP:
X		switch (type) {
X		case CHAP_CHALLENGE:	return "challenge";
X		case CHAP_RESPONSE:	return "response";
X		case CHAP_SUCCESS:	return "success";
X		case CHAP_FAILURE:	return "failure";
X		}
X	case PPP_PAP:
X		switch (type) {
X		case PAP_REQ:		return "req";
X		case PAP_ACK:		return "ack";
X		case PAP_NAK:		return "nak";
X		}
X	}
X	snprintf (buf, sizeof(buf), "0x%x", type);
X	return buf;
X}
X
Xstatic const char *
Xsppp_lcp_opt_name(u_char opt)
X{
X	static char buf[12];
X	switch (opt) {
X	case LCP_OPT_MRU:		return "mru";
X	case LCP_OPT_ASYNC_MAP:		return "async-map";
X	case LCP_OPT_AUTH_PROTO:	return "auth-proto";
X	case LCP_OPT_QUAL_PROTO:	return "qual-proto";
X	case LCP_OPT_MAGIC:		return "magic";
X	case LCP_OPT_PROTO_COMP:	return "proto-comp";
X	case LCP_OPT_ADDR_COMP:		return "addr-comp";
X	}
X	snprintf (buf, sizeof(buf), "0x%x", opt);
X	return buf;
X}
X
Xstatic const char *
Xsppp_ipcp_opt_name(u_char opt)
X{
X	static char buf[12];
X	switch (opt) {
X	case IPCP_OPT_ADDRESSES:	return "addresses";
X	case IPCP_OPT_COMPRESSION:	return "compression";
X	case IPCP_OPT_ADDRESS:		return "address";
X	}
X	snprintf (buf, sizeof(buf), "0x%x", opt);
X	return buf;
X}
X
Xstatic const char *
Xsppp_state_name(int state)
X{
X	switch (state) {
X	case STATE_INITIAL:	return "initial";
X	case STATE_STARTING:	return "starting";
X	case STATE_CLOSED:	return "closed";
X	case STATE_STOPPED:	return "stopped";
X	case STATE_CLOSING:	return "closing";
X	case STATE_STOPPING:	return "stopping";
X	case STATE_REQ_SENT:	return "req-sent";
X	case STATE_ACK_RCVD:	return "ack-rcvd";
X	case STATE_ACK_SENT:	return "ack-sent";
X	case STATE_OPENED:	return "opened";
X	}
X	return "illegal";
X}
X
Xstatic const char *
Xsppp_phase_name(enum ppp_phase phase)
X{
X	switch (phase) {
X	case PHASE_DEAD:	return "dead";
X	case PHASE_ESTABLISH:	return "establish";
X	case PHASE_TERMINATE:	return "terminate";
X	case PHASE_AUTHENTICATE: return "authenticate";
X	case PHASE_NETWORK:	return "network";
X	}
X	return "illegal";
X}
X
Xstatic const char *
Xsppp_proto_name(u_short proto)
X{
X	static char buf[12];
X	switch (proto) {
X	case PPP_LCP:	return "lcp";
X	case PPP_IPCP:	return "ipcp";
X	case PPP_PAP:	return "pap";
X	case PPP_CHAP:	return "chap";
X	}
X	snprintf(buf, sizeof(buf), "0x%x", (unsigned)proto);
X	return buf;
X}
X
Xstatic void
Xsppp_print_bytes(const u_char *p, u_short len)
X{
X	addlog(" %02x", *p++);
X	while (--len > 0)
X		addlog("-%02x", *p++);
X}
X
Xstatic void
Xsppp_print_string(const char *p, u_short len)
X{
X	u_char c;
X
X	while (len-- > 0) {
X		c = *p++;
X		/*
X		 * Print only ASCII chars directly.  RFC 1994 recommends
X		 * using only them, but we don't rely on it.  */
X		if (c < ' ' || c > '~')
X			addlog("\\x%x", c);
X		else
X			addlog("%c", c);
X	}
X}
X
Xstatic const char *
Xsppp_dotted_quad(u_long addr)
X{
X	static char s[16];
X	sprintf(s, "%d.%d.%d.%d",
X		(int)((addr >> 24) & 0xff),
X		(int)((addr >> 16) & 0xff),
X		(int)((addr >> 8) & 0xff),
X		(int)(addr & 0xff));
X	return s;
X}
X
Xstatic int
Xsppp_strnlen(u_char *p, int max)
X{
X	int len;
X
X	for (len = 0; len < max && *p; ++p)
X		++len;
X	return len;
X}
X
X/* a dummy, used to drop uninteresting events */
Xstatic void
Xsppp_null(struct sppp *unused)
X{
X	/* do just nothing */
X}
END-of-./usr/src/sys/net/if_spppsubr.c
echo x - ./usr/src/sys/net/if_spppsubr.c.patch
sed 's/^X//' >./usr/src/sys/net/if_spppsubr.c.patch << 'END-of-./usr/src/sys/net/if_spppsubr.c.patch'
X*** if_spppsubr.c	Tue Dec  1 21:20:21 1998
X--- if_spppsubr.c.new	Tue Mar 16 15:54:10 1999
X***************
X*** 603,608 ****
X--- 603,609 ----
X  		goto drop;
X  	}
X  	IF_ENQUEUE(inq, m);
X+ 	sp->pp_last_recv = time_second;
X  	splx(s);
X  }
X  
X***************
X*** 777,782 ****
X--- 778,784 ----
X  	 * according to RFC 1333.
X  	 */
X  	ifp->if_obytes += m->m_pkthdr.len + 3;
X+ 	sp->pp_last_sent = time_second;
X  	splx (s);
X  	return (0);
X  }
X***************
X*** 809,814 ****
X--- 811,817 ----
X  	sp->pp_up = lcp.Up;
X  	sp->pp_down = lcp.Down;
X  
X+ 	sp->pp_last_recv = sp->pp_last_sent = time_second;
X  	sppp_lcp_init(sp);
X  	sppp_ipcp_init(sp);
X  	sppp_pap_init(sp);
END-of-./usr/src/sys/net/if_spppsubr.c.patch
echo x - ./usr/src/sys/net/if_spppsubr.c.orig
sed 's/^X//' >./usr/src/sys/net/if_spppsubr.c.orig << 'END-of-./usr/src/sys/net/if_spppsubr.c.orig'
X/*
X * Synchronous PPP/Cisco link level subroutines.
X * Keepalive protocol implemented in both Cisco and PPP modes.
X *
X * Copyright (C) 1994-1996 Cronyx Engineering Ltd.
X * Author: Serge Vakulenko, <vak@cronyx.ru>
X *
X * Heavily revamped to conform to RFC 1661.
X * Copyright (C) 1997, Joerg Wunsch.
X *
X * This software is distributed with NO WARRANTIES, not even the implied
X * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
X *
X * Authors grant any other persons or organisations permission to use
X * or modify this software as long as this message is kept with the software,
X * all derivative works or modified versions.
X *
X * From: Version 2.4, Thu Apr 30 17:17:21 MSD 1997
X *
X * $Id: if_spppsubr.c,v 1.52 1998/12/27 21:30:44 phk Exp $
X */
X
X#include <sys/param.h>
X
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X#include "opt_inet.h"
X#include "opt_ipx.h"
X#endif
X
X#ifdef NetBSD1_3
X#  if NetBSD1_3 > 6
X#      include "opt_inet.h"
X#      include "opt_iso.h"
X#  endif
X#endif
X
X#include <sys/systm.h>
X#include <sys/kernel.h>
X#include <sys/sockio.h>
X#include <sys/socket.h>
X#include <sys/syslog.h>
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X#include <machine/random.h>
X#endif
X#include <sys/malloc.h>
X#include <sys/mbuf.h>
X
X#if defined (__OpenBSD__)
X#include <sys/md5k.h>
X#else
X#include <sys/md5.h>
X#endif
X
X#include <net/if.h>
X#include <net/netisr.h>
X#include <net/if_types.h>
X#include <net/route.h>
X
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X#include <machine/random.h>
X#endif
X#if defined (__NetBSD__) || defined (__OpenBSD__)
X#include <machine/cpu.h> /* XXX for softnet */
X#endif
X
X#include <machine/stdarg.h>
X
X#ifdef INET
X#include <netinet/in.h>
X#include <netinet/in_systm.h>
X#include <netinet/in_var.h>
X#include <netinet/ip.h>
X#include <netinet/tcp.h>
X# if defined (__FreeBSD__) || defined (__OpenBSD__)
X#  include <netinet/if_ether.h>
X# else
X#  include <net/ethertypes.h>
X# endif
X#else
X# error Huh? sppp without INET?
X#endif
X
X#ifdef IPX
X#include <netipx/ipx.h>
X#include <netipx/ipx_if.h>
X#endif
X
X#ifdef NS
X#include <netns/ns.h>
X#include <netns/ns_if.h>
X#endif
X
X#ifdef ISO
X#include <netiso/argo_debug.h>
X#include <netiso/iso.h>
X#include <netiso/iso_var.h>
X#include <netiso/iso_snpac.h>
X#endif
X
X#include <net/if_sppp.h>
X
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X# define UNTIMEOUT(fun, arg, handle) untimeout(fun, arg, handle)
X# define TIMEOUT(fun, arg1, arg2, handle) handle = timeout(fun, arg1, arg2)
X# define IOCTL_CMD_T	u_long
X#else
X# define UNTIMEOUT(fun, arg, handle) untimeout(fun, arg)
X# define TIMEOUT(fun, arg1, arg2, handle) timeout(fun, arg1, arg2)
X# define IOCTL_CMD_T	int
X#endif
X
X#define MAXALIVECNT     3               /* max. alive packets */
X
X/*
X * Interface flags that can be set in an ifconfig command.
X *
X * Setting link0 will make the link passive, i.e. it will be marked
X * as being administrative openable, but won't be opened to begin
X * with.  Incoming calls will be answered, or subsequent calls with
X * -link1 will cause the administrative open of the LCP layer.
X *
X * Setting link1 will cause the link to auto-dial only as packets
X * arrive to be sent.
X *
X * Setting IFF_DEBUG will syslog the option negotiation and state
X * transitions at level kern.debug.  Note: all logs consistently look
X * like
X *
X *   <if-name><unit>: <proto-name> <additional info...>
X *
X * with <if-name><unit> being something like "bppp0", and <proto-name>
X * being one of "lcp", "ipcp", "cisco", "chap", "pap", etc.
X */
X
X#define IFF_PASSIVE	IFF_LINK0	/* wait passively for connection */
X#define IFF_AUTO	IFF_LINK1	/* auto-dial on output */
X
X#define PPP_ALLSTATIONS 0xff		/* All-Stations broadcast address */
X#define PPP_UI		0x03		/* Unnumbered Information */
X#define PPP_IP		0x0021		/* Internet Protocol */
X#define PPP_ISO		0x0023		/* ISO OSI Protocol */
X#define PPP_XNS		0x0025		/* Xerox NS Protocol */
X#define PPP_IPX		0x002b		/* Novell IPX Protocol */
X#define PPP_LCP		0xc021		/* Link Control Protocol */
X#define PPP_PAP		0xc023		/* Password Authentication Protocol */
X#define PPP_CHAP	0xc223		/* Challenge-Handshake Auth Protocol */
X#define PPP_IPCP	0x8021		/* Internet Protocol Control Protocol */
X
X#define CONF_REQ	1		/* PPP configure request */
X#define CONF_ACK	2		/* PPP configure acknowledge */
X#define CONF_NAK	3		/* PPP configure negative ack */
X#define CONF_REJ	4		/* PPP configure reject */
X#define TERM_REQ	5		/* PPP terminate request */
X#define TERM_ACK	6		/* PPP terminate acknowledge */
X#define CODE_REJ	7		/* PPP code reject */
X#define PROTO_REJ	8		/* PPP protocol reject */
X#define ECHO_REQ	9		/* PPP echo request */
X#define ECHO_REPLY	10		/* PPP echo reply */
X#define DISC_REQ	11		/* PPP discard request */
X
X#define LCP_OPT_MRU		1	/* maximum receive unit */
X#define LCP_OPT_ASYNC_MAP	2	/* async control character map */
X#define LCP_OPT_AUTH_PROTO	3	/* authentication protocol */
X#define LCP_OPT_QUAL_PROTO	4	/* quality protocol */
X#define LCP_OPT_MAGIC		5	/* magic number */
X#define LCP_OPT_RESERVED	6	/* reserved */
X#define LCP_OPT_PROTO_COMP	7	/* protocol field compression */
X#define LCP_OPT_ADDR_COMP	8	/* address/control field compression */
X
X#define IPCP_OPT_ADDRESSES	1	/* both IP addresses; deprecated */
X#define IPCP_OPT_COMPRESSION	2	/* IP compression protocol (VJ) */
X#define IPCP_OPT_ADDRESS	3	/* local IP address */
X
X#define PAP_REQ			1	/* PAP name/password request */
X#define PAP_ACK			2	/* PAP acknowledge */
X#define PAP_NAK			3	/* PAP fail */
X
X#define CHAP_CHALLENGE		1	/* CHAP challenge request */
X#define CHAP_RESPONSE		2	/* CHAP challenge response */
X#define CHAP_SUCCESS		3	/* CHAP response ok */
X#define CHAP_FAILURE		4	/* CHAP response failed */
X
X#define CHAP_MD5		5	/* hash algorithm - MD5 */
X
X#define CISCO_MULTICAST		0x8f	/* Cisco multicast address */
X#define CISCO_UNICAST		0x0f	/* Cisco unicast address */
X#define CISCO_KEEPALIVE		0x8035	/* Cisco keepalive protocol */
X#define CISCO_ADDR_REQ		0	/* Cisco address request */
X#define CISCO_ADDR_REPLY	1	/* Cisco address reply */
X#define CISCO_KEEPALIVE_REQ	2	/* Cisco keepalive request */
X
X/* states are named and numbered according to RFC 1661 */
X#define STATE_INITIAL	0
X#define STATE_STARTING	1
X#define STATE_CLOSED	2
X#define STATE_STOPPED	3
X#define STATE_CLOSING	4
X#define STATE_STOPPING	5
X#define STATE_REQ_SENT	6
X#define STATE_ACK_RCVD	7
X#define STATE_ACK_SENT	8
X#define STATE_OPENED	9
X
Xstruct ppp_header {
X	u_char address;
X	u_char control;
X	u_short protocol;
X};
X#define PPP_HEADER_LEN          sizeof (struct ppp_header)
X
Xstruct lcp_header {
X	u_char type;
X	u_char ident;
X	u_short len;
X};
X#define LCP_HEADER_LEN          sizeof (struct lcp_header)
X
Xstruct cisco_packet {
X	u_long type;
X	u_long par1;
X	u_long par2;
X	u_short rel;
X	u_short time0;
X	u_short time1;
X};
X#define CISCO_PACKET_LEN 18
X
X/*
X * We follow the spelling and capitalization of RFC 1661 here, to make
X * it easier comparing with the standard.  Please refer to this RFC in
X * case you can't make sense out of these abbreviation; it will also
X * explain the semantics related to the various events and actions.
X */
Xstruct cp {
X	u_short	proto;		/* PPP control protocol number */
X	u_char protoidx;	/* index into state table in struct sppp */
X	u_char flags;
X#define CP_LCP		0x01	/* this is the LCP */
X#define CP_AUTH		0x02	/* this is an authentication protocol */
X#define CP_NCP		0x04	/* this is a NCP */
X#define CP_QUAL		0x08	/* this is a quality reporting protocol */
X	const char *name;	/* name of this control protocol */
X	/* event handlers */
X	void	(*Up)(struct sppp *sp);
X	void	(*Down)(struct sppp *sp);
X	void	(*Open)(struct sppp *sp);
X	void	(*Close)(struct sppp *sp);
X	void	(*TO)(void *sp);
X	int	(*RCR)(struct sppp *sp, struct lcp_header *h, int len);
X	void	(*RCN_rej)(struct sppp *sp, struct lcp_header *h, int len);
X	void	(*RCN_nak)(struct sppp *sp, struct lcp_header *h, int len);
X	/* actions */
X	void	(*tlu)(struct sppp *sp);
X	void	(*tld)(struct sppp *sp);
X	void	(*tls)(struct sppp *sp);
X	void	(*tlf)(struct sppp *sp);
X	void	(*scr)(struct sppp *sp);
X};
X
Xstatic struct sppp *spppq;
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
Xstatic struct callout_handle keepalive_ch;
X#endif
X
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X#define	SPP_FMT		"%s%d: "
X#define	SPP_ARGS(ifp)	(ifp)->if_name, (ifp)->if_unit
X#else
X#define	SPP_FMT		"%s: "
X#define	SPP_ARGS(ifp)	(ifp)->if_xname
X#endif
X
X/*
X * The following disgusting hack gets around the problem that IP TOS
X * can't be set yet.  We want to put "interactive" traffic on a high
X * priority queue.  To decide if traffic is interactive, we check that
X * a) it is TCP and b) one of its ports is telnet, rlogin or ftp control.
X *
X * XXX is this really still necessary?  - joerg -
X */
Xstatic u_short interactive_ports[8] = {
X	0,	513,	0,	0,
X	0,	21,	0,	23,
X};
X#define INTERACTIVE(p) (interactive_ports[(p) & 7] == (p))
X
X/* almost every function needs these */
X#define STDDCL							\
X	struct ifnet *ifp = &sp->pp_if;				\
X	int debug = ifp->if_flags & IFF_DEBUG
X
Xstatic int sppp_output(struct ifnet *ifp, struct mbuf *m,
X		       struct sockaddr *dst, struct rtentry *rt);
X
Xstatic void sppp_cisco_send(struct sppp *sp, int type, long par1, long par2);
Xstatic void sppp_cisco_input(struct sppp *sp, struct mbuf *m);
X
Xstatic void sppp_cp_input(const struct cp *cp, struct sppp *sp,
X			  struct mbuf *m);
Xstatic void sppp_cp_send(struct sppp *sp, u_short proto, u_char type,
X			 u_char ident, u_short len, void *data);
X/* static void sppp_cp_timeout(void *arg); */
Xstatic void sppp_cp_change_state(const struct cp *cp, struct sppp *sp,
X				 int newstate);
Xstatic void sppp_auth_send(const struct cp *cp,
X			   struct sppp *sp, unsigned int type, unsigned int id,
X			   ...);
X
Xstatic void sppp_up_event(const struct cp *cp, struct sppp *sp);
Xstatic void sppp_down_event(const struct cp *cp, struct sppp *sp);
Xstatic void sppp_open_event(const struct cp *cp, struct sppp *sp);
Xstatic void sppp_close_event(const struct cp *cp, struct sppp *sp);
Xstatic void sppp_to_event(const struct cp *cp, struct sppp *sp);
X
Xstatic void sppp_null(struct sppp *sp);
X
Xstatic void sppp_lcp_init(struct sppp *sp);
Xstatic void sppp_lcp_up(struct sppp *sp);
Xstatic void sppp_lcp_down(struct sppp *sp);
Xstatic void sppp_lcp_open(struct sppp *sp);
Xstatic void sppp_lcp_close(struct sppp *sp);
Xstatic void sppp_lcp_TO(void *sp);
Xstatic int sppp_lcp_RCR(struct sppp *sp, struct lcp_header *h, int len);
Xstatic void sppp_lcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len);
Xstatic void sppp_lcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len);
Xstatic void sppp_lcp_tlu(struct sppp *sp);
Xstatic void sppp_lcp_tld(struct sppp *sp);
Xstatic void sppp_lcp_tls(struct sppp *sp);
Xstatic void sppp_lcp_tlf(struct sppp *sp);
Xstatic void sppp_lcp_scr(struct sppp *sp);
Xstatic void sppp_lcp_check_and_close(struct sppp *sp);
Xstatic int sppp_ncp_check(struct sppp *sp);
X
Xstatic void sppp_ipcp_init(struct sppp *sp);
Xstatic void sppp_ipcp_up(struct sppp *sp);
Xstatic void sppp_ipcp_down(struct sppp *sp);
Xstatic void sppp_ipcp_open(struct sppp *sp);
Xstatic void sppp_ipcp_close(struct sppp *sp);
Xstatic void sppp_ipcp_TO(void *sp);
Xstatic int sppp_ipcp_RCR(struct sppp *sp, struct lcp_header *h, int len);
Xstatic void sppp_ipcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len);
Xstatic void sppp_ipcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len);
Xstatic void sppp_ipcp_tlu(struct sppp *sp);
Xstatic void sppp_ipcp_tld(struct sppp *sp);
Xstatic void sppp_ipcp_tls(struct sppp *sp);
Xstatic void sppp_ipcp_tlf(struct sppp *sp);
Xstatic void sppp_ipcp_scr(struct sppp *sp);
X
Xstatic void sppp_pap_input(struct sppp *sp, struct mbuf *m);
Xstatic void sppp_pap_init(struct sppp *sp);
Xstatic void sppp_pap_open(struct sppp *sp);
Xstatic void sppp_pap_close(struct sppp *sp);
Xstatic void sppp_pap_TO(void *sp);
Xstatic void sppp_pap_my_TO(void *sp);
Xstatic void sppp_pap_tlu(struct sppp *sp);
Xstatic void sppp_pap_tld(struct sppp *sp);
Xstatic void sppp_pap_scr(struct sppp *sp);
X
Xstatic void sppp_chap_input(struct sppp *sp, struct mbuf *m);
Xstatic void sppp_chap_init(struct sppp *sp);
Xstatic void sppp_chap_open(struct sppp *sp);
Xstatic void sppp_chap_close(struct sppp *sp);
Xstatic void sppp_chap_TO(void *sp);
Xstatic void sppp_chap_tlu(struct sppp *sp);
Xstatic void sppp_chap_tld(struct sppp *sp);
Xstatic void sppp_chap_scr(struct sppp *sp);
X
Xstatic const char *sppp_auth_type_name(u_short proto, u_char type);
Xstatic const char *sppp_cp_type_name(u_char type);
Xstatic const char *sppp_dotted_quad(u_long addr);
Xstatic const char *sppp_ipcp_opt_name(u_char opt);
Xstatic const char *sppp_lcp_opt_name(u_char opt);
Xstatic const char *sppp_phase_name(enum ppp_phase phase);
Xstatic const char *sppp_proto_name(u_short proto);
Xstatic const char *sppp_state_name(int state);
Xstatic int sppp_params(struct sppp *sp, u_long cmd, void *data);
Xstatic int sppp_strnlen(u_char *p, int max);
Xstatic void sppp_get_ip_addrs(struct sppp *sp, u_long *src, u_long *dst,
X			      u_long *srcmask);
Xstatic void sppp_keepalive(void *dummy);
Xstatic void sppp_phase_network(struct sppp *sp);
Xstatic void sppp_print_bytes(const u_char *p, u_short len);
Xstatic void sppp_print_string(const char *p, u_short len);
Xstatic void sppp_qflush(struct ifqueue *ifq);
Xstatic void sppp_set_ip_addr(struct sppp *sp, u_long src);
X
X/* our control protocol descriptors */
Xstatic const struct cp lcp = {
X	PPP_LCP, IDX_LCP, CP_LCP, "lcp",
X	sppp_lcp_up, sppp_lcp_down, sppp_lcp_open, sppp_lcp_close,
X	sppp_lcp_TO, sppp_lcp_RCR, sppp_lcp_RCN_rej, sppp_lcp_RCN_nak,
X	sppp_lcp_tlu, sppp_lcp_tld, sppp_lcp_tls, sppp_lcp_tlf,
X	sppp_lcp_scr
X};
X
Xstatic const struct cp ipcp = {
X	PPP_IPCP, IDX_IPCP, CP_NCP, "ipcp",
X	sppp_ipcp_up, sppp_ipcp_down, sppp_ipcp_open, sppp_ipcp_close,
X	sppp_ipcp_TO, sppp_ipcp_RCR, sppp_ipcp_RCN_rej, sppp_ipcp_RCN_nak,
X	sppp_ipcp_tlu, sppp_ipcp_tld, sppp_ipcp_tls, sppp_ipcp_tlf,
X	sppp_ipcp_scr
X};
X
Xstatic const struct cp pap = {
X	PPP_PAP, IDX_PAP, CP_AUTH, "pap",
X	sppp_null, sppp_null, sppp_pap_open, sppp_pap_close,
X	sppp_pap_TO, 0, 0, 0,
X	sppp_pap_tlu, sppp_pap_tld, sppp_null, sppp_null,
X	sppp_pap_scr
X};
X
Xstatic const struct cp chap = {
X	PPP_CHAP, IDX_CHAP, CP_AUTH, "chap",
X	sppp_null, sppp_null, sppp_chap_open, sppp_chap_close,
X	sppp_chap_TO, 0, 0, 0,
X	sppp_chap_tlu, sppp_chap_tld, sppp_null, sppp_null,
X	sppp_chap_scr
X};
X
Xstatic const struct cp *cps[IDX_COUNT] = {
X	&lcp,			/* IDX_LCP */
X	&ipcp,			/* IDX_IPCP */
X	&pap,			/* IDX_PAP */
X	&chap,			/* IDX_CHAP */
X};
X
X
X/*
X * Exported functions, comprising our interface to the lower layer.
X */
X
X/*
X * Process the received packet.
X */
Xvoid
Xsppp_input(struct ifnet *ifp, struct mbuf *m)
X{
X	struct ppp_header *h;
X	struct ifqueue *inq = 0;
X	int s;
X	struct sppp *sp = (struct sppp *)ifp;
X	int debug = ifp->if_flags & IFF_DEBUG;
X
X	if (ifp->if_flags & IFF_UP)
X		/* Count received bytes, add FCS and one flag */
X		ifp->if_ibytes += m->m_pkthdr.len + 3;
X
X	if (m->m_pkthdr.len <= PPP_HEADER_LEN) {
X		/* Too small packet, drop it. */
X		if (debug)
X			log(LOG_DEBUG,
X			    SPP_FMT "input packet is too small, %d bytes\n",
X			    SPP_ARGS(ifp), m->m_pkthdr.len);
X	  drop:
X		++ifp->if_ierrors;
X		++ifp->if_iqdrops;
X		m_freem (m);
X		return;
X	}
X
X	/* Get PPP header. */
X	h = mtod (m, struct ppp_header*);
X	m_adj (m, PPP_HEADER_LEN);
X
X	switch (h->address) {
X	case PPP_ALLSTATIONS:
X		if (h->control != PPP_UI)
X			goto invalid;
X		if (sp->pp_flags & PP_CISCO) {
X			if (debug)
X				log(LOG_DEBUG,
X				    SPP_FMT "PPP packet in Cisco mode "
X				    "<addr=0x%x ctrl=0x%x proto=0x%x>\n",
X				    SPP_ARGS(ifp),
X				    h->address, h->control, ntohs(h->protocol));
X			goto drop;
X		}
X		switch (ntohs (h->protocol)) {
X		default:
X			if (sp->state[IDX_LCP] == STATE_OPENED)
X				sppp_cp_send (sp, PPP_LCP, PROTO_REJ,
X					++sp->pp_seq, m->m_pkthdr.len + 2,
X					&h->protocol);
X			if (debug)
X				log(LOG_DEBUG,
X				    SPP_FMT "invalid input protocol "
X				    "<addr=0x%x ctrl=0x%x proto=0x%x>\n",
X				    SPP_ARGS(ifp),
X				    h->address, h->control, ntohs(h->protocol));
X			++ifp->if_noproto;
X			goto drop;
X		case PPP_LCP:
X			sppp_cp_input(&lcp, sp, m);
X			m_freem (m);
X			return;
X		case PPP_PAP:
X			if (sp->pp_phase >= PHASE_AUTHENTICATE)
X				sppp_pap_input(sp, m);
X			m_freem (m);
X			return;
X		case PPP_CHAP:
X			if (sp->pp_phase >= PHASE_AUTHENTICATE)
X				sppp_chap_input(sp, m);
X			m_freem (m);
X			return;
X#ifdef INET
X		case PPP_IPCP:
X			if (sp->pp_phase == PHASE_NETWORK)
X				sppp_cp_input(&ipcp, sp, m);
X			m_freem (m);
X			return;
X		case PPP_IP:
X			if (sp->state[IDX_IPCP] == STATE_OPENED) {
X				schednetisr (NETISR_IP);
X				inq = &ipintrq;
X			}
X			break;
X#endif
X#ifdef IPX
X		case PPP_IPX:
X			/* IPX IPXCP not implemented yet */
X			if (sp->pp_phase == PHASE_NETWORK) {
X				schednetisr (NETISR_IPX);
X				inq = &ipxintrq;
X			}
X			break;
X#endif
X#ifdef NS
X		case PPP_XNS:
X			/* XNS IDPCP not implemented yet */
X			if (sp->pp_phase == PHASE_NETWORK) {
X				schednetisr (NETISR_NS);
X				inq = &nsintrq;
X			}
X			break;
X#endif
X#ifdef ISO
X		case PPP_ISO:
X			/* OSI NLCP not implemented yet */
X			if (sp->pp_phase == PHASE_NETWORK) {
X				schednetisr (NETISR_ISO);
X				inq = &clnlintrq;
X			}
X			break;
X#endif
X		}
X		break;
X	case CISCO_MULTICAST:
X	case CISCO_UNICAST:
X		/* Don't check the control field here (RFC 1547). */
X		if (! (sp->pp_flags & PP_CISCO)) {
X			if (debug)
X				log(LOG_DEBUG,
X				    SPP_FMT "Cisco packet in PPP mode "
X				    "<addr=0x%x ctrl=0x%x proto=0x%x>\n",
X				    SPP_ARGS(ifp),
X				    h->address, h->control, ntohs(h->protocol));
X			goto drop;
X		}
X		switch (ntohs (h->protocol)) {
X		default:
X			++ifp->if_noproto;
X			goto invalid;
X		case CISCO_KEEPALIVE:
X			sppp_cisco_input ((struct sppp*) ifp, m);
X			m_freem (m);
X			return;
X#ifdef INET
X		case ETHERTYPE_IP:
X			schednetisr (NETISR_IP);
X			inq = &ipintrq;
X			break;
X#endif
X#ifdef IPX
X		case ETHERTYPE_IPX:
X			schednetisr (NETISR_IPX);
X			inq = &ipxintrq;
X			break;
X#endif
X#ifdef NS
X		case ETHERTYPE_NS:
X			schednetisr (NETISR_NS);
X			inq = &nsintrq;
X			break;
X#endif
X		}
X		break;
X	default:        /* Invalid PPP packet. */
X	  invalid:
X		if (debug)
X			log(LOG_DEBUG,
X			    SPP_FMT "invalid input packet "
X			    "<addr=0x%x ctrl=0x%x proto=0x%x>\n",
X			    SPP_ARGS(ifp),
X			    h->address, h->control, ntohs(h->protocol));
X		goto drop;
X	}
X
X	if (! (ifp->if_flags & IFF_UP) || ! inq)
X		goto drop;
X
X	/* Check queue. */
X	s = splimp();
X	if (IF_QFULL (inq)) {
X		/* Queue overflow. */
X		IF_DROP(inq);
X		splx(s);
X		if (debug)
X			log(LOG_DEBUG, SPP_FMT "protocol queue overflow\n",
X				SPP_ARGS(ifp));
X		goto drop;
X	}
X	IF_ENQUEUE(inq, m);
X	splx(s);
X}
X
X/*
X * Enqueue transmit packet.
X */
Xstatic int
Xsppp_output(struct ifnet *ifp, struct mbuf *m,
X	    struct sockaddr *dst, struct rtentry *rt)
X{
X	struct sppp *sp = (struct sppp*) ifp;
X	struct ppp_header *h;
X	struct ifqueue *ifq;
X	int s, rv = 0;
X	int debug = ifp->if_flags & IFF_DEBUG;
X
X	s = splimp();
X
X	if ((ifp->if_flags & IFF_UP) == 0 ||
X	    (ifp->if_flags & (IFF_RUNNING | IFF_AUTO)) == 0) {
X		m_freem (m);
X		splx (s);
X		return (ENETDOWN);
X	}
X
X	if ((ifp->if_flags & (IFF_RUNNING | IFF_AUTO)) == IFF_AUTO) {
X		/*
X		 * Interface is not yet running, but auto-dial.  Need
X		 * to start LCP for it.
X		 */
X		ifp->if_flags |= IFF_RUNNING;
X		splx(s);
X		lcp.Open(sp);
X		s = splimp();
X	}
X
X	ifq = &ifp->if_snd;
X#ifdef INET
X	if (dst->sa_family == AF_INET) {
X		/* XXX Check mbuf length here? */
X		struct ip *ip = mtod (m, struct ip*);
X		struct tcphdr *tcp = (struct tcphdr*) ((long*)ip + ip->ip_hl);
X
X		/*
X		 * When using dynamic local IP address assignment by using
X		 * 0.0.0.0 as a local address, the first TCP session will
X		 * not connect because the local TCP checksum is computed
X		 * using 0.0.0.0 which will later become our real IP address
X		 * so the TCP checksum computed at the remote end will
X		 * become invalid. So we
X		 * - don't let packets with src ip addr 0 thru
X		 * - we flag TCP packets with src ip 0 as an error
X		 */	
X
X		if(ip->ip_src.s_addr == INADDR_ANY)	/* -hm */
X		{
X			m_freem(m);
X			splx(s);
X			if(ip->ip_p == IPPROTO_TCP)
X				return(EADDRNOTAVAIL);
X			else
X				return(0);
X		}
X		
X		/*
X		 * Put low delay, telnet, rlogin and ftp control packets
X		 * in front of the queue.
X		 */
X		if (IF_QFULL (&sp->pp_fastq))
X			;
X		else if (ip->ip_tos & IPTOS_LOWDELAY)
X			ifq = &sp->pp_fastq;
X		else if (m->m_len < sizeof *ip + sizeof *tcp)
X			;
X		else if (ip->ip_p != IPPROTO_TCP)
X			;
X		else if (INTERACTIVE (ntohs (tcp->th_sport)))
X			ifq = &sp->pp_fastq;
X		else if (INTERACTIVE (ntohs (tcp->th_dport)))
X			ifq = &sp->pp_fastq;
X	}
X#endif
X
X	/*
X	 * Prepend general data packet PPP header. For now, IP only.
X	 */
X	M_PREPEND (m, PPP_HEADER_LEN, M_DONTWAIT);
X	if (! m) {
X		if (debug)
X			log(LOG_DEBUG, SPP_FMT "no memory for transmit header\n",
X				SPP_ARGS(ifp));
X		++ifp->if_oerrors;
X		splx (s);
X		return (ENOBUFS);
X	}
X	/*
X	 * May want to check size of packet
X	 * (albeit due to the implementation it's always enough)
X	 */
X	h = mtod (m, struct ppp_header*);
X	if (sp->pp_flags & PP_CISCO) {
X		h->address = CISCO_UNICAST;        /* unicast address */
X		h->control = 0;
X	} else {
X		h->address = PPP_ALLSTATIONS;        /* broadcast address */
X		h->control = PPP_UI;                 /* Unnumbered Info */
X	}
X
X	switch (dst->sa_family) {
X#ifdef INET
X	case AF_INET:   /* Internet Protocol */
X		if (sp->pp_flags & PP_CISCO)
X			h->protocol = htons (ETHERTYPE_IP);
X		else {
X			/*
X			 * Don't choke with an ENETDOWN early.  It's
X			 * possible that we just started dialing out,
X			 * so don't drop the packet immediately.  If
X			 * we notice that we run out of buffer space
X			 * below, we will however remember that we are
X			 * not ready to carry IP packets, and return
X			 * ENETDOWN, as opposed to ENOBUFS.
X			 */
X			h->protocol = htons(PPP_IP);
X			if (sp->state[IDX_IPCP] != STATE_OPENED)
X				rv = ENETDOWN;
X		}
X		break;
X#endif
X#ifdef NS
X	case AF_NS:     /* Xerox NS Protocol */
X		h->protocol = htons ((sp->pp_flags & PP_CISCO) ?
X			ETHERTYPE_NS : PPP_XNS);
X		break;
X#endif
X#ifdef IPX
X	case AF_IPX:     /* Novell IPX Protocol */
X		h->protocol = htons ((sp->pp_flags & PP_CISCO) ?
X			ETHERTYPE_IPX : PPP_IPX);
X		break;
X#endif
X#ifdef ISO
X	case AF_ISO:    /* ISO OSI Protocol */
X		if (sp->pp_flags & PP_CISCO)
X			goto nosupport;
X		h->protocol = htons (PPP_ISO);
X		break;
Xnosupport:
X#endif
X	default:
X		m_freem (m);
X		++ifp->if_oerrors;
X		splx (s);
X		return (EAFNOSUPPORT);
X	}
X
X	/*
X	 * Queue message on interface, and start output if interface
X	 * not yet active.
X	 */
X	if (IF_QFULL (ifq)) {
X		IF_DROP (&ifp->if_snd);
X		m_freem (m);
X		++ifp->if_oerrors;
X		splx (s);
X		return (rv? rv: ENOBUFS);
X	}
X	IF_ENQUEUE (ifq, m);
X	if (! (ifp->if_flags & IFF_OACTIVE))
X		(*ifp->if_start) (ifp);
X
X	/*
X	 * Count output packets and bytes.
X	 * The packet length includes header, FCS and 1 flag,
X	 * according to RFC 1333.
X	 */
X	ifp->if_obytes += m->m_pkthdr.len + 3;
X	splx (s);
X	return (0);
X}
X
Xvoid
Xsppp_attach(struct ifnet *ifp)
X{
X	struct sppp *sp = (struct sppp*) ifp;
X
X	/* Initialize keepalive handler. */
X	if (! spppq)
X		TIMEOUT(sppp_keepalive, 0, hz * 10, keepalive_ch);
X
X	/* Insert new entry into the keepalive list. */
X	sp->pp_next = spppq;
X	spppq = sp;
X
X	sp->pp_if.if_mtu = PP_MTU;
X	sp->pp_if.if_flags = IFF_POINTOPOINT | IFF_MULTICAST;
X	sp->pp_if.if_type = IFT_PPP;
X	sp->pp_if.if_output = sppp_output;
X#if 0
X	sp->pp_flags = PP_KEEPALIVE;
X#endif
X	sp->pp_fastq.ifq_maxlen = 32;
X	sp->pp_cpq.ifq_maxlen = 20;
X	sp->pp_loopcnt = 0;
X	sp->pp_alivecnt = 0;
X	sp->pp_seq = 0;
X	sp->pp_rseq = 0;
X	sp->pp_phase = PHASE_DEAD;
X	sp->pp_up = lcp.Up;
X	sp->pp_down = lcp.Down;
X
X	sppp_lcp_init(sp);
X	sppp_ipcp_init(sp);
X	sppp_pap_init(sp);
X	sppp_chap_init(sp);
X}
X
Xvoid
Xsppp_detach(struct ifnet *ifp)
X{
X	struct sppp **q, *p, *sp = (struct sppp*) ifp;
X	int i;
X
X	/* Remove the entry from the keepalive list. */
X	for (q = &spppq; (p = *q); q = &p->pp_next)
X		if (p == sp) {
X			*q = p->pp_next;
X			break;
X		}
X
X	/* Stop keepalive handler. */
X	if (! spppq)
X		UNTIMEOUT(sppp_keepalive, 0, keepalive_ch);
X
X	for (i = 0; i < IDX_COUNT; i++)
X		UNTIMEOUT((cps[i])->TO, (void *)sp, sp->ch[i]);
X	UNTIMEOUT(sppp_pap_my_TO, (void *)sp, sp->pap_my_to_ch);
X}
X
X/*
X * Flush the interface output queue.
X */
Xvoid
Xsppp_flush(struct ifnet *ifp)
X{
X	struct sppp *sp = (struct sppp*) ifp;
X
X	sppp_qflush (&sp->pp_if.if_snd);
X	sppp_qflush (&sp->pp_fastq);
X	sppp_qflush (&sp->pp_cpq);
X}
X
X/*
X * Check if the output queue is empty.
X */
Xint
Xsppp_isempty(struct ifnet *ifp)
X{
X	struct sppp *sp = (struct sppp*) ifp;
X	int empty, s;
X
X	s = splimp();
X	empty = !sp->pp_fastq.ifq_head && !sp->pp_cpq.ifq_head &&
X		!sp->pp_if.if_snd.ifq_head;
X	splx(s);
X	return (empty);
X}
X
X/*
X * Get next packet to send.
X */
Xstruct mbuf *
Xsppp_dequeue(struct ifnet *ifp)
X{
X	struct sppp *sp = (struct sppp*) ifp;
X	struct mbuf *m;
X	int s;
X
X	s = splimp();
X	/*
X	 * Process only the control protocol queue until we have at
X	 * least one NCP open.
X	 *
X	 * Do always serve all three queues in Cisco mode.
X	 */
X	IF_DEQUEUE(&sp->pp_cpq, m);
X	if (m == NULL &&
X	    (sppp_ncp_check(sp) || (sp->pp_flags & PP_CISCO) != 0)) {
X		IF_DEQUEUE(&sp->pp_fastq, m);
X		if (m == NULL)
X			IF_DEQUEUE (&sp->pp_if.if_snd, m);
X	}
X	splx(s);
X	return m;
X}
X
X/*
X * Pick the next packet, do not remove it from the queue.
X */
Xstruct mbuf *
Xsppp_pick(struct ifnet *ifp)
X{
X	struct sppp *sp = (struct sppp*)ifp;
X	struct mbuf *m;
X	int s;
X
X	s= splimp ();
X
X	m = sp->pp_cpq.ifq_head;
X	if (m == NULL &&
X	    (sp->pp_phase == PHASE_NETWORK ||
X	     (sp->pp_flags & PP_CISCO) != 0))
X		if ((m = sp->pp_fastq.ifq_head) == NULL)
X			m = sp->pp_if.if_snd.ifq_head;
X	splx (s);
X	return (m);
X}
X
X/*
X * Process an ioctl request.  Called on low priority level.
X */
Xint
Xsppp_ioctl(struct ifnet *ifp, IOCTL_CMD_T cmd, void *data)
X{
X	struct ifreq *ifr = (struct ifreq*) data;
X	struct sppp *sp = (struct sppp*) ifp;
X	int s, rv, going_up, going_down, newmode;
X
X	s = splimp();
X	rv = 0;
X	switch (cmd) {
X	case SIOCAIFADDR:
X	case SIOCSIFDSTADDR:
X		break;
X
X	case SIOCSIFADDR:
X		if_up(ifp);
X		/* fall through... */
X
X	case SIOCSIFFLAGS:
X		going_up = ifp->if_flags & IFF_UP &&
X			(ifp->if_flags & IFF_RUNNING) == 0;
X		going_down = (ifp->if_flags & IFF_UP) == 0 &&
X			ifp->if_flags & IFF_RUNNING;
X		newmode = ifp->if_flags & (IFF_AUTO | IFF_PASSIVE);
X		if (newmode == (IFF_AUTO | IFF_PASSIVE)) {
X			/* sanity */
X			newmode = IFF_PASSIVE;
X			ifp->if_flags &= ~IFF_AUTO;
X		}
X
X		if (going_up || going_down)
X			lcp.Close(sp);
X		if (going_up && newmode == 0) {
X			/* neither auto-dial nor passive */
X			ifp->if_flags |= IFF_RUNNING;
X			if (!(sp->pp_flags & PP_CISCO))
X				lcp.Open(sp);
X		} else if (going_down) {
X			sppp_flush(ifp);
X			ifp->if_flags &= ~IFF_RUNNING;
X		}
X
X		break;
X
X#ifdef SIOCSIFMTU
X#ifndef ifr_mtu
X#define ifr_mtu ifr_metric
X#endif
X	case SIOCSIFMTU:
X		if (ifr->ifr_mtu < 128 || ifr->ifr_mtu > sp->lcp.their_mru)
X			return (EINVAL);
X		ifp->if_mtu = ifr->ifr_mtu;
X		break;
X#endif
X#ifdef SLIOCSETMTU
X	case SLIOCSETMTU:
X		if (*(short*)data < 128 || *(short*)data > sp->lcp.their_mru)
X			return (EINVAL);
X		ifp->if_mtu = *(short*)data;
X		break;
X#endif
X#ifdef SIOCGIFMTU
X	case SIOCGIFMTU:
X		ifr->ifr_mtu = ifp->if_mtu;
X		break;
X#endif
X#ifdef SLIOCGETMTU
X	case SLIOCGETMTU:
X		*(short*)data = ifp->if_mtu;
X		break;
X#endif
X	case SIOCADDMULTI:
X	case SIOCDELMULTI:
X		break;
X
X	case SIOCGIFGENERIC:
X	case SIOCSIFGENERIC:
X		rv = sppp_params(sp, cmd, data);
X		break;
X
X	default:
X		rv = ENOTTY;
X	}
X	splx(s);
X	return rv;
X}
X
X
X/*
X * Cisco framing implementation.
X */
X
X/*
X * Handle incoming Cisco keepalive protocol packets.
X */
Xstatic void
Xsppp_cisco_input(struct sppp *sp, struct mbuf *m)
X{
X	STDDCL;
X	struct cisco_packet *h;
X	u_long me, mymask;
X
X	if (m->m_pkthdr.len < CISCO_PACKET_LEN) {
X		if (debug)
X			log(LOG_DEBUG,
X			    SPP_FMT "cisco invalid packet length: %d bytes\n",
X			    SPP_ARGS(ifp), m->m_pkthdr.len);
X		return;
X	}
X	h = mtod (m, struct cisco_packet*);
X	if (debug)
X		log(LOG_DEBUG,
X		    SPP_FMT "cisco input: %d bytes "
X		    "<0x%lx 0x%lx 0x%lx 0x%x 0x%x-0x%x>\n",
X		    SPP_ARGS(ifp), m->m_pkthdr.len,
X		    (u_long)ntohl (h->type), (u_long)h->par1, (u_long)h->par2, (u_int)h->rel,
X		    (u_int)h->time0, (u_int)h->time1);
X	switch (ntohl (h->type)) {
X	default:
X		if (debug)
X			addlog(SPP_FMT "cisco unknown packet type: 0x%lx\n",
X			       SPP_ARGS(ifp), (u_long)ntohl (h->type));
X		break;
X	case CISCO_ADDR_REPLY:
X		/* Reply on address request, ignore */
X		break;
X	case CISCO_KEEPALIVE_REQ:
X		sp->pp_alivecnt = 0;
X		sp->pp_rseq = ntohl (h->par1);
X		if (sp->pp_seq == sp->pp_rseq) {
X			/* Local and remote sequence numbers are equal.
X			 * Probably, the line is in loopback mode. */
X			if (sp->pp_loopcnt >= MAXALIVECNT) {
X				printf (SPP_FMT "loopback\n",
X					SPP_ARGS(ifp));
X				sp->pp_loopcnt = 0;
X				if (ifp->if_flags & IFF_UP) {
X					if_down (ifp);
X					sppp_qflush (&sp->pp_cpq);
X				}
X			}
X			++sp->pp_loopcnt;
X
X			/* Generate new local sequence number */
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X			sp->pp_seq = random();
X#else
X			sp->pp_seq ^= time.tv_sec ^ time.tv_usec;
X#endif
X			break;
X		}
X		sp->pp_loopcnt = 0;
X		if (! (ifp->if_flags & IFF_UP) &&
X		    (ifp->if_flags & IFF_RUNNING)) {
X			if_up(ifp);
X			printf (SPP_FMT "up\n", SPP_ARGS(ifp));
X		}
X		break;
X	case CISCO_ADDR_REQ:
X		sppp_get_ip_addrs(sp, &me, 0, &mymask);
X		if (me != 0L)
X			sppp_cisco_send(sp, CISCO_ADDR_REPLY, me, mymask);
X		break;
X	}
X}
X
X/*
X * Send Cisco keepalive packet.
X */
Xstatic void
Xsppp_cisco_send(struct sppp *sp, int type, long par1, long par2)
X{
X	STDDCL;
X	struct ppp_header *h;
X	struct cisco_packet *ch;
X	struct mbuf *m;
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X	struct timeval tv;
X#else
X	u_long t = (time.tv_sec - boottime.tv_sec) * 1000;
X#endif
X
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X	getmicrouptime(&tv);
X#endif
X	
X	MGETHDR (m, M_DONTWAIT, MT_DATA);
X	if (! m)
X		return;
X	m->m_pkthdr.len = m->m_len = PPP_HEADER_LEN + CISCO_PACKET_LEN;
X	m->m_pkthdr.rcvif = 0;
X
X	h = mtod (m, struct ppp_header*);
X	h->address = CISCO_MULTICAST;
X	h->control = 0;
X	h->protocol = htons (CISCO_KEEPALIVE);
X
X	ch = (struct cisco_packet*) (h + 1);
X	ch->type = htonl (type);
X	ch->par1 = htonl (par1);
X	ch->par2 = htonl (par2);
X	ch->rel = -1;
X
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X	ch->time0 = htons ((u_short) (tv.tv_sec >> 16));
X	ch->time1 = htons ((u_short) tv.tv_sec);
X#else
X	ch->time0 = htons ((u_short) (t >> 16));
X	ch->time1 = htons ((u_short) t);
X#endif
X
X	if (debug)
X		log(LOG_DEBUG,
X		    SPP_FMT "cisco output: <0x%lx 0x%lx 0x%lx 0x%x 0x%x-0x%x>\n",
X			SPP_ARGS(ifp), (u_long)ntohl (ch->type), (u_long)ch->par1,
X			(u_long)ch->par2, (u_int)ch->rel, (u_int)ch->time0, (u_int)ch->time1);
X
X	if (IF_QFULL (&sp->pp_cpq)) {
X		IF_DROP (&sp->pp_fastq);
X		IF_DROP (&ifp->if_snd);
X		m_freem (m);
X	} else
X		IF_ENQUEUE (&sp->pp_cpq, m);
X	if (! (ifp->if_flags & IFF_OACTIVE))
X		(*ifp->if_start) (ifp);
X	ifp->if_obytes += m->m_pkthdr.len + 3;
X}
X
X/*
X * PPP protocol implementation.
X */
X
X/*
X * Send PPP control protocol packet.
X */
Xstatic void
Xsppp_cp_send(struct sppp *sp, u_short proto, u_char type,
X	     u_char ident, u_short len, void *data)
X{
X	STDDCL;
X	struct ppp_header *h;
X	struct lcp_header *lh;
X	struct mbuf *m;
X
X	if (len > MHLEN - PPP_HEADER_LEN - LCP_HEADER_LEN)
X		len = MHLEN - PPP_HEADER_LEN - LCP_HEADER_LEN;
X	MGETHDR (m, M_DONTWAIT, MT_DATA);
X	if (! m)
X		return;
X	m->m_pkthdr.len = m->m_len = PPP_HEADER_LEN + LCP_HEADER_LEN + len;
X	m->m_pkthdr.rcvif = 0;
X
X	h = mtod (m, struct ppp_header*);
X	h->address = PPP_ALLSTATIONS;        /* broadcast address */
X	h->control = PPP_UI;                 /* Unnumbered Info */
X	h->protocol = htons (proto);         /* Link Control Protocol */
X
X	lh = (struct lcp_header*) (h + 1);
X	lh->type = type;
X	lh->ident = ident;
X	lh->len = htons (LCP_HEADER_LEN + len);
X	if (len)
X		bcopy (data, lh+1, len);
X
X	if (debug) {
X		log(LOG_DEBUG, SPP_FMT "%s output <%s id=0x%x len=%d",
X		    SPP_ARGS(ifp),
X		    sppp_proto_name(proto),
X		    sppp_cp_type_name (lh->type), lh->ident,
X		    ntohs (lh->len));
X		if (len)
X			sppp_print_bytes ((u_char*) (lh+1), len);
X		addlog(">\n");
X	}
X	if (IF_QFULL (&sp->pp_cpq)) {
X		IF_DROP (&sp->pp_fastq);
X		IF_DROP (&ifp->if_snd);
X		m_freem (m);
X		++ifp->if_oerrors;
X	} else
X		IF_ENQUEUE (&sp->pp_cpq, m);
X	if (! (ifp->if_flags & IFF_OACTIVE))
X		(*ifp->if_start) (ifp);
X	ifp->if_obytes += m->m_pkthdr.len + 3;
X}
X
X/*
X * Handle incoming PPP control protocol packets.
X */
Xstatic void
Xsppp_cp_input(const struct cp *cp, struct sppp *sp, struct mbuf *m)
X{
X	STDDCL;
X	struct lcp_header *h;
X	int len = m->m_pkthdr.len;
X	int rv;
X	u_char *p;
X
X	if (len < 4) {
X		if (debug)
X			log(LOG_DEBUG,
X			    SPP_FMT "%s invalid packet length: %d bytes\n",
X			    SPP_ARGS(ifp), cp->name, len);
X		return;
X	}
X	h = mtod (m, struct lcp_header*);
X	if (debug) {
X		log(LOG_DEBUG,
X		    SPP_FMT "%s input(%s): <%s id=0x%x len=%d",
X		    SPP_ARGS(ifp), cp->name,
X		    sppp_state_name(sp->state[cp->protoidx]),
X		    sppp_cp_type_name (h->type), h->ident, ntohs (h->len));
X		if (len > 4)
X			sppp_print_bytes ((u_char*) (h+1), len-4);
X		addlog(">\n");
X	}
X	if (len > ntohs (h->len))
X		len = ntohs (h->len);
X	p = (u_char *)(h + 1);
X	switch (h->type) {
X	case CONF_REQ:
X		if (len < 4) {
X			if (debug)
X				addlog(SPP_FMT "%s invalid conf-req length %d\n",
X				       SPP_ARGS(ifp), cp->name,
X				       len);
X			++ifp->if_ierrors;
X			break;
X		}
X		/* handle states where RCR doesn't get a SCA/SCN */
X		switch (sp->state[cp->protoidx]) {
X		case STATE_CLOSING:
X		case STATE_STOPPING:
X			return;
X		case STATE_CLOSED:
X			sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident,
X				     0, 0);
X			return;
X		}
X		rv = (cp->RCR)(sp, h, len);
X		switch (sp->state[cp->protoidx]) {
X		case STATE_OPENED:
X			(cp->tld)(sp);
X			(cp->scr)(sp);
X			/* fall through... */
X		case STATE_ACK_SENT:
X		case STATE_REQ_SENT:
X			sppp_cp_change_state(cp, sp, rv?
X					     STATE_ACK_SENT: STATE_REQ_SENT);
X			break;
X		case STATE_STOPPED:
X			sp->rst_counter[cp->protoidx] = sp->lcp.max_configure;
X			(cp->scr)(sp);
X			sppp_cp_change_state(cp, sp, rv?
X					     STATE_ACK_SENT: STATE_REQ_SENT);
X			break;
X		case STATE_ACK_RCVD:
X			if (rv) {
X				sppp_cp_change_state(cp, sp, STATE_OPENED);
X				if (debug)
X					log(LOG_DEBUG, SPP_FMT "%s tlu\n",
X					    SPP_ARGS(ifp),
X					    cp->name);
X				(cp->tlu)(sp);
X			} else
X				sppp_cp_change_state(cp, sp, STATE_ACK_RCVD);
X			break;
X		default:
X			printf(SPP_FMT "%s illegal %s in state %s\n",
X			       SPP_ARGS(ifp), cp->name,
X			       sppp_cp_type_name(h->type),
X			       sppp_state_name(sp->state[cp->protoidx]));
X			++ifp->if_ierrors;
X		}
X		break;
X	case CONF_ACK:
X		if (h->ident != sp->confid[cp->protoidx]) {
X			if (debug)
X				addlog(SPP_FMT "%s id mismatch 0x%x != 0x%x\n",
X				       SPP_ARGS(ifp), cp->name,
X				       h->ident, sp->confid[cp->protoidx]);
X			++ifp->if_ierrors;
X			break;
X		}
X		switch (sp->state[cp->protoidx]) {
X		case STATE_CLOSED:
X		case STATE_STOPPED:
X			sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident, 0, 0);
X			break;
X		case STATE_CLOSING:
X		case STATE_STOPPING:
X			break;
X		case STATE_REQ_SENT:
X			sp->rst_counter[cp->protoidx] = sp->lcp.max_configure;
X			sppp_cp_change_state(cp, sp, STATE_ACK_RCVD);
X			break;
X		case STATE_OPENED:
X			(cp->tld)(sp);
X			/* fall through */
X		case STATE_ACK_RCVD:
X			(cp->scr)(sp);
X			sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
X			break;
X		case STATE_ACK_SENT:
X			sp->rst_counter[cp->protoidx] = sp->lcp.max_configure;
X			sppp_cp_change_state(cp, sp, STATE_OPENED);
X			if (debug)
X				log(LOG_DEBUG, SPP_FMT "%s tlu\n",
X				       SPP_ARGS(ifp), cp->name);
X			(cp->tlu)(sp);
X			break;
X		default:
X			printf(SPP_FMT "%s illegal %s in state %s\n",
X			       SPP_ARGS(ifp), cp->name,
X			       sppp_cp_type_name(h->type),
X			       sppp_state_name(sp->state[cp->protoidx]));
X			++ifp->if_ierrors;
X		}
X		break;
X	case CONF_NAK:
X	case CONF_REJ:
X		if (h->ident != sp->confid[cp->protoidx]) {
X			if (debug)
X				addlog(SPP_FMT "%s id mismatch 0x%x != 0x%x\n",
X				       SPP_ARGS(ifp), cp->name,
X				       h->ident, sp->confid[cp->protoidx]);
X			++ifp->if_ierrors;
X			break;
X		}
X		if (h->type == CONF_NAK)
X			(cp->RCN_nak)(sp, h, len);
X		else /* CONF_REJ */
X			(cp->RCN_rej)(sp, h, len);
X
X		switch (sp->state[cp->protoidx]) {
X		case STATE_CLOSED:
X		case STATE_STOPPED:
X			sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident, 0, 0);
X			break;
X		case STATE_REQ_SENT:
X		case STATE_ACK_SENT:
X			sp->rst_counter[cp->protoidx] = sp->lcp.max_configure;
X			(cp->scr)(sp);
X			break;
X		case STATE_OPENED:
X			(cp->tld)(sp);
X			/* fall through */
X		case STATE_ACK_RCVD:
X			sppp_cp_change_state(cp, sp, STATE_ACK_SENT);
X			(cp->scr)(sp);
X			break;
X		case STATE_CLOSING:
X		case STATE_STOPPING:
X			break;
X		default:
X			printf(SPP_FMT "%s illegal %s in state %s\n",
X			       SPP_ARGS(ifp), cp->name,
X			       sppp_cp_type_name(h->type),
X			       sppp_state_name(sp->state[cp->protoidx]));
X			++ifp->if_ierrors;
X		}
X		break;
X
X	case TERM_REQ:
X		switch (sp->state[cp->protoidx]) {
X		case STATE_ACK_RCVD:
X		case STATE_ACK_SENT:
X			sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
X			/* fall through */
X		case STATE_CLOSED:
X		case STATE_STOPPED:
X		case STATE_CLOSING:
X		case STATE_STOPPING:
X		case STATE_REQ_SENT:
X		  sta:
X			/* Send Terminate-Ack packet. */
X			if (debug)
X				log(LOG_DEBUG, SPP_FMT "%s send terminate-ack\n",
X				    SPP_ARGS(ifp), cp->name);
X			sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident, 0, 0);
X			break;
X		case STATE_OPENED:
X			(cp->tld)(sp);
X			sp->rst_counter[cp->protoidx] = 0;
X			sppp_cp_change_state(cp, sp, STATE_STOPPING);
X			goto sta;
X			break;
X		default:
X			printf(SPP_FMT "%s illegal %s in state %s\n",
X			       SPP_ARGS(ifp), cp->name,
X			       sppp_cp_type_name(h->type),
X			       sppp_state_name(sp->state[cp->protoidx]));
X			++ifp->if_ierrors;
X		}
X		break;
X	case TERM_ACK:
X		switch (sp->state[cp->protoidx]) {
X		case STATE_CLOSED:
X		case STATE_STOPPED:
X		case STATE_REQ_SENT:
X		case STATE_ACK_SENT:
X			break;
X		case STATE_CLOSING:
X			sppp_cp_change_state(cp, sp, STATE_CLOSED);
X			(cp->tlf)(sp);
X			break;
X		case STATE_STOPPING:
X			sppp_cp_change_state(cp, sp, STATE_STOPPED);
X			(cp->tlf)(sp);
X			break;
X		case STATE_ACK_RCVD:
X			sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
X			break;
X		case STATE_OPENED:
X			(cp->tld)(sp);
X			(cp->scr)(sp);
X			sppp_cp_change_state(cp, sp, STATE_ACK_RCVD);
X			break;
X		default:
X			printf(SPP_FMT "%s illegal %s in state %s\n",
X			       SPP_ARGS(ifp), cp->name,
X			       sppp_cp_type_name(h->type),
X			       sppp_state_name(sp->state[cp->protoidx]));
X			++ifp->if_ierrors;
X		}
X		break;
X	case CODE_REJ:
X	case PROTO_REJ:
X		/* XXX catastrophic rejects (RXJ-) aren't handled yet. */
X		log(LOG_INFO,
X		    SPP_FMT "%s: ignoring RXJ (%s) for proto 0x%x, "
X		    "danger will robinson\n",
X		    SPP_ARGS(ifp), cp->name,
X		    sppp_cp_type_name(h->type), ntohs(*((u_short *)p)));
X		switch (sp->state[cp->protoidx]) {
X		case STATE_CLOSED:
X		case STATE_STOPPED:
X		case STATE_REQ_SENT:
X		case STATE_ACK_SENT:
X		case STATE_CLOSING:
X		case STATE_STOPPING:
X		case STATE_OPENED:
X			break;
X		case STATE_ACK_RCVD:
X			sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
X			break;
X		default:
X			printf(SPP_FMT "%s illegal %s in state %s\n",
X			       SPP_ARGS(ifp), cp->name,
X			       sppp_cp_type_name(h->type),
X			       sppp_state_name(sp->state[cp->protoidx]));
X			++ifp->if_ierrors;
X		}
X		break;
X	case DISC_REQ:
X		if (cp->proto != PPP_LCP)
X			goto illegal;
X		/* Discard the packet. */
X		break;
X	case ECHO_REQ:
X		if (cp->proto != PPP_LCP)
X			goto illegal;
X		if (sp->state[cp->protoidx] != STATE_OPENED) {
X			if (debug)
X				addlog(SPP_FMT "lcp echo req but lcp closed\n",
X				       SPP_ARGS(ifp));
X			++ifp->if_ierrors;
X			break;
X		}
X		if (len < 8) {
X			if (debug)
X				addlog(SPP_FMT "invalid lcp echo request "
X				       "packet length: %d bytes\n",
X				       SPP_ARGS(ifp), len);
X			break;
X		}
X		if (ntohl (*(long*)(h+1)) == sp->lcp.magic) {
X			/* Line loopback mode detected. */
X			printf(SPP_FMT "loopback\n", SPP_ARGS(ifp));
X			if_down (ifp);
X			sppp_qflush (&sp->pp_cpq);
X
X			/* Shut down the PPP link. */
X			/* XXX */
X			lcp.Down(sp);
X			lcp.Up(sp);
X			break;
X		}
X		*(long*)(h+1) = htonl (sp->lcp.magic);
X		if (debug)
X			addlog(SPP_FMT "got lcp echo req, sending echo rep\n",
X			       SPP_ARGS(ifp));
X		sppp_cp_send (sp, PPP_LCP, ECHO_REPLY, h->ident, len-4, h+1);
X		break;
X	case ECHO_REPLY:
X		if (cp->proto != PPP_LCP)
X			goto illegal;
X		if (h->ident != sp->lcp.echoid) {
X			++ifp->if_ierrors;
X			break;
X		}
X		if (len < 8) {
X			if (debug)
X				addlog(SPP_FMT "lcp invalid echo reply "
X				       "packet length: %d bytes\n",
X				       SPP_ARGS(ifp), len);
X			break;
X		}
X		if (debug)
X			addlog(SPP_FMT "lcp got echo rep\n",
X			       SPP_ARGS(ifp));
X		if (ntohl (*(long*)(h+1)) != sp->lcp.magic)
X			sp->pp_alivecnt = 0;
X		break;
X	default:
X		/* Unknown packet type -- send Code-Reject packet. */
X	  illegal:
X		if (debug)
X			addlog(SPP_FMT "%s send code-rej for 0x%x\n",
X			       SPP_ARGS(ifp), cp->name, h->type);
X		sppp_cp_send(sp, cp->proto, CODE_REJ, ++sp->pp_seq,
X			     m->m_pkthdr.len, h);
X		++ifp->if_ierrors;
X	}
X}
X
X
X/*
X * The generic part of all Up/Down/Open/Close/TO event handlers.
X * Basically, the state transition handling in the automaton.
X */
Xstatic void
Xsppp_up_event(const struct cp *cp, struct sppp *sp)
X{
X	STDDCL;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "%s up(%s)\n",
X		    SPP_ARGS(ifp), cp->name,
X		    sppp_state_name(sp->state[cp->protoidx]));
X
X	switch (sp->state[cp->protoidx]) {
X	case STATE_INITIAL:
X		sppp_cp_change_state(cp, sp, STATE_CLOSED);
X		break;
X	case STATE_STARTING:
X		sp->rst_counter[cp->protoidx] = sp->lcp.max_configure;
X		(cp->scr)(sp);
X		sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
X		break;
X	default:
X		printf(SPP_FMT "%s illegal up in state %s\n",
X		       SPP_ARGS(ifp), cp->name,
X		       sppp_state_name(sp->state[cp->protoidx]));
X	}
X}
X
Xstatic void
Xsppp_down_event(const struct cp *cp, struct sppp *sp)
X{
X	STDDCL;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "%s down(%s)\n",
X		    SPP_ARGS(ifp), cp->name,
X		    sppp_state_name(sp->state[cp->protoidx]));
X
X	switch (sp->state[cp->protoidx]) {
X	case STATE_CLOSED:
X	case STATE_CLOSING:
X		sppp_cp_change_state(cp, sp, STATE_INITIAL);
X		break;
X	case STATE_STOPPED:
X		sppp_cp_change_state(cp, sp, STATE_STARTING);
X		(cp->tls)(sp);
X		break;
X	case STATE_STOPPING:
X	case STATE_REQ_SENT:
X	case STATE_ACK_RCVD:
X	case STATE_ACK_SENT:
X		sppp_cp_change_state(cp, sp, STATE_STARTING);
X		break;
X	case STATE_OPENED:
X		(cp->tld)(sp);
X		sppp_cp_change_state(cp, sp, STATE_STARTING);
X		break;
X	default:
X		printf(SPP_FMT "%s illegal down in state %s\n",
X		       SPP_ARGS(ifp), cp->name,
X		       sppp_state_name(sp->state[cp->protoidx]));
X	}
X}
X
X
Xstatic void
Xsppp_open_event(const struct cp *cp, struct sppp *sp)
X{
X	STDDCL;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "%s open(%s)\n",
X		    SPP_ARGS(ifp), cp->name,
X		    sppp_state_name(sp->state[cp->protoidx]));
X
X	switch (sp->state[cp->protoidx]) {
X	case STATE_INITIAL:
X		sppp_cp_change_state(cp, sp, STATE_STARTING);
X		(cp->tls)(sp);
X		break;
X	case STATE_STARTING:
X		break;
X	case STATE_CLOSED:
X		sp->rst_counter[cp->protoidx] = sp->lcp.max_configure;
X		(cp->scr)(sp);
X		sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
X		break;
X	case STATE_STOPPED:
X	case STATE_STOPPING:
X	case STATE_REQ_SENT:
X	case STATE_ACK_RCVD:
X	case STATE_ACK_SENT:
X	case STATE_OPENED:
X		break;
X	case STATE_CLOSING:
X		sppp_cp_change_state(cp, sp, STATE_STOPPING);
X		break;
X	}
X}
X
X
Xstatic void
Xsppp_close_event(const struct cp *cp, struct sppp *sp)
X{
X	STDDCL;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "%s close(%s)\n",
X		    SPP_ARGS(ifp), cp->name,
X		    sppp_state_name(sp->state[cp->protoidx]));
X
X	switch (sp->state[cp->protoidx]) {
X	case STATE_INITIAL:
X	case STATE_CLOSED:
X	case STATE_CLOSING:
X		break;
X	case STATE_STARTING:
X		sppp_cp_change_state(cp, sp, STATE_INITIAL);
X		(cp->tlf)(sp);
X		break;
X	case STATE_STOPPED:
X		sppp_cp_change_state(cp, sp, STATE_CLOSED);
X		break;
X	case STATE_STOPPING:
X		sppp_cp_change_state(cp, sp, STATE_CLOSING);
X		break;
X	case STATE_OPENED:
X		(cp->tld)(sp);
X		/* fall through */
X	case STATE_REQ_SENT:
X	case STATE_ACK_RCVD:
X	case STATE_ACK_SENT:
X		sp->rst_counter[cp->protoidx] = sp->lcp.max_terminate;
X		sppp_cp_send(sp, cp->proto, TERM_REQ, ++sp->pp_seq, 0, 0);
X		sppp_cp_change_state(cp, sp, STATE_CLOSING);
X		break;
X	}
X}
X
Xstatic void
Xsppp_to_event(const struct cp *cp, struct sppp *sp)
X{
X	STDDCL;
X	int s;
X
X	s = splimp();
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "%s TO(%s) rst_counter = %d\n",
X		    SPP_ARGS(ifp), cp->name,
X		    sppp_state_name(sp->state[cp->protoidx]),
X		    sp->rst_counter[cp->protoidx]);
X
X	if (--sp->rst_counter[cp->protoidx] < 0)
X		/* TO- event */
X		switch (sp->state[cp->protoidx]) {
X		case STATE_CLOSING:
X			sppp_cp_change_state(cp, sp, STATE_CLOSED);
X			(cp->tlf)(sp);
X			break;
X		case STATE_STOPPING:
X			sppp_cp_change_state(cp, sp, STATE_STOPPED);
X			(cp->tlf)(sp);
X			break;
X		case STATE_REQ_SENT:
X		case STATE_ACK_RCVD:
X		case STATE_ACK_SENT:
X			sppp_cp_change_state(cp, sp, STATE_STOPPED);
X			(cp->tlf)(sp);
X			break;
X		}
X	else
X		/* TO+ event */
X		switch (sp->state[cp->protoidx]) {
X		case STATE_CLOSING:
X		case STATE_STOPPING:
X			sppp_cp_send(sp, cp->proto, TERM_REQ, ++sp->pp_seq,
X				     0, 0);
X			TIMEOUT(cp->TO, (void *)sp, sp->lcp.timeout, 
X			    sp->ch[cp->protoidx]);
X			break;
X		case STATE_REQ_SENT:
X		case STATE_ACK_RCVD:
X			(cp->scr)(sp);
X			/* sppp_cp_change_state() will restart the timer */
X			sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
X			break;
X		case STATE_ACK_SENT:
X			(cp->scr)(sp);
X			TIMEOUT(cp->TO, (void *)sp, sp->lcp.timeout,
X			    sp->ch[cp->protoidx]);
X			break;
X		}
X
X	splx(s);
X}
X
X/*
X * Change the state of a control protocol in the state automaton.
X * Takes care of starting/stopping the restart timer.
X */
Xvoid
Xsppp_cp_change_state(const struct cp *cp, struct sppp *sp, int newstate)
X{
X	sp->state[cp->protoidx] = newstate;
X
X	UNTIMEOUT(cp->TO, (void *)sp, sp->ch[cp->protoidx]);
X	switch (newstate) {
X	case STATE_INITIAL:
X	case STATE_STARTING:
X	case STATE_CLOSED:
X	case STATE_STOPPED:
X	case STATE_OPENED:
X		break;
X	case STATE_CLOSING:
X	case STATE_STOPPING:
X	case STATE_REQ_SENT:
X	case STATE_ACK_RCVD:
X	case STATE_ACK_SENT:
X		TIMEOUT(cp->TO, (void *)sp, sp->lcp.timeout, 
X		    sp->ch[cp->protoidx]);
X		break;
X	}
X}
X/*
X *--------------------------------------------------------------------------*
X *                                                                          *
X *                         The LCP implementation.                          *
X *                                                                          *
X *--------------------------------------------------------------------------*
X */
Xstatic void
Xsppp_lcp_init(struct sppp *sp)
X{
X	sp->lcp.opts = (1 << LCP_OPT_MAGIC);
X	sp->lcp.magic = 0;
X	sp->state[IDX_LCP] = STATE_INITIAL;
X	sp->fail_counter[IDX_LCP] = 0;
X	sp->lcp.protos = 0;
X	sp->lcp.mru = sp->lcp.their_mru = PP_MTU;
X
X	/*
X	 * Initialize counters and timeout values.  Note that we don't
X	 * use the 3 seconds suggested in RFC 1661 since we are likely
X	 * running on a fast link.  XXX We should probably implement
X	 * the exponential backoff option.  Note that these values are
X	 * relevant for all control protocols, not just LCP only.
X	 */
X	sp->lcp.timeout = 1 * hz;
X	sp->lcp.max_terminate = 2;
X	sp->lcp.max_configure = 10;
X	sp->lcp.max_failure = 10;
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X	callout_handle_init(&sp->ch[IDX_LCP]);
X#endif
X}
X
Xstatic void
Xsppp_lcp_up(struct sppp *sp)
X{
X	STDDCL;
X
X	/*
X	 * If this interface is passive or dial-on-demand, and we are
X	 * still in Initial state, it means we've got an incoming
X	 * call.  Activate the interface.
X	 */
X	if ((ifp->if_flags & (IFF_AUTO | IFF_PASSIVE)) != 0) {
X		if (debug)
X			log(LOG_DEBUG,
X			    SPP_FMT "Up event", SPP_ARGS(ifp));
X		ifp->if_flags |= IFF_RUNNING;
X		if (sp->state[IDX_LCP] == STATE_INITIAL) {
X			if (debug)
X				addlog("(incoming call)\n");
X			sp->pp_flags |= PP_CALLIN;
X			lcp.Open(sp);
X		} else if (debug)
X			addlog("\n");
X	}
X
X	sppp_up_event(&lcp, sp);
X}
X
Xstatic void
Xsppp_lcp_down(struct sppp *sp)
X{
X	STDDCL;
X
X	sppp_down_event(&lcp, sp);
X
X	/*
X	 * If this is neither a dial-on-demand nor a passive
X	 * interface, simulate an ``ifconfig down'' action, so the
X	 * administrator can force a redial by another ``ifconfig
X	 * up''.  XXX For leased line operation, should we immediately
X	 * try to reopen the connection here?
X	 */
X	if ((ifp->if_flags & (IFF_AUTO | IFF_PASSIVE)) == 0) {
X		log(LOG_INFO,
X		    SPP_FMT "Down event, taking interface down.\n",
X		    SPP_ARGS(ifp));
X		if_down(ifp);
X	} else {
X		if (debug)
X			log(LOG_DEBUG,
X			    SPP_FMT "Down event (carrier loss)\n",
X			    SPP_ARGS(ifp));
X	}
X	sp->pp_flags &= ~PP_CALLIN;
X	if (sp->state[IDX_LCP] != STATE_INITIAL)
X		lcp.Close(sp);
X	ifp->if_flags &= ~IFF_RUNNING;
X}
X
Xstatic void
Xsppp_lcp_open(struct sppp *sp)
X{
X	/*
X	 * If we are authenticator, negotiate LCP_AUTH
X	 */
X	if (sp->hisauth.proto != 0)
X		sp->lcp.opts |= (1 << LCP_OPT_AUTH_PROTO);
X	else
X		sp->lcp.opts &= ~(1 << LCP_OPT_AUTH_PROTO);
X	sp->pp_flags &= ~PP_NEEDAUTH;
X	sppp_open_event(&lcp, sp);
X}
X
Xstatic void
Xsppp_lcp_close(struct sppp *sp)
X{
X	sppp_close_event(&lcp, sp);
X}
X
Xstatic void
Xsppp_lcp_TO(void *cookie)
X{
X	sppp_to_event(&lcp, (struct sppp *)cookie);
X}
X
X/*
X * Analyze a configure request.  Return true if it was agreeable, and
X * caused action sca, false if it has been rejected or nak'ed, and
X * caused action scn.  (The return value is used to make the state
X * transition decision in the state automaton.)
X */
Xstatic int
Xsppp_lcp_RCR(struct sppp *sp, struct lcp_header *h, int len)
X{
X	STDDCL;
X	u_char *buf, *r, *p;
X	int origlen, rlen;
X	u_long nmagic;
X	u_short authproto;
X
X	len -= 4;
X	origlen = len;
X	buf = r = malloc (len, M_TEMP, M_NOWAIT);
X	if (! buf)
X		return (0);
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "lcp parse opts: ",
X		    SPP_ARGS(ifp));
X
X	/* pass 1: check for things that need to be rejected */
X	p = (void*) (h+1);
X	for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) {
X		if (debug)
X			addlog(" %s ", sppp_lcp_opt_name(*p));
X		switch (*p) {
X		case LCP_OPT_MAGIC:
X			/* Magic number. */
X			/* fall through, both are same length */
X		case LCP_OPT_ASYNC_MAP:
X			/* Async control character map. */
X			if (len >= 6 || p[1] == 6)
X				continue;
X			if (debug)
X				addlog("[invalid] ");
X			break;
X		case LCP_OPT_MRU:
X			/* Maximum receive unit. */
X			if (len >= 4 && p[1] == 4)
X				continue;
X			if (debug)
X				addlog("[invalid] ");
X			break;
X		case LCP_OPT_AUTH_PROTO:
X			if (len < 4) {
X				if (debug)
X					addlog("[invalid] ");
X				break;
X			}
X			authproto = (p[2] << 8) + p[3];
X			if (authproto == PPP_CHAP && p[1] != 5) {
X				if (debug)
X					addlog("[invalid chap len] ");
X				break;
X			}
X			if (sp->myauth.proto == 0) {
X				/* we are not configured to do auth */
X				if (debug)
X					addlog("[not configured] ");
X				break;
X			}
X			/*
X			 * Remote want us to authenticate, remember this,
X			 * so we stay in PHASE_AUTHENTICATE after LCP got
X			 * up.
X			 */
X			sp->pp_flags |= PP_NEEDAUTH;
X			continue;
X		default:
X			/* Others not supported. */
X			if (debug)
X				addlog("[rej] ");
X			break;
X		}
X		/* Add the option to rejected list. */
X		bcopy (p, r, p[1]);
X		r += p[1];
X		rlen += p[1];
X	}
X	if (rlen) {
X		if (debug)
X			addlog(" send conf-rej\n");
X		sppp_cp_send (sp, PPP_LCP, CONF_REJ, h->ident, rlen, buf);
X		return 0;
X	} else if (debug)
X		addlog("\n");
X
X	/*
X	 * pass 2: check for option values that are unacceptable and
X	 * thus require to be nak'ed.
X	 */
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "lcp parse opt values: ",
X		    SPP_ARGS(ifp));
X
X	p = (void*) (h+1);
X	len = origlen;
X	for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) {
X		if (debug)
X			addlog(" %s ", sppp_lcp_opt_name(*p));
X		switch (*p) {
X		case LCP_OPT_MAGIC:
X			/* Magic number -- extract. */
X			nmagic = (u_long)p[2] << 24 |
X				(u_long)p[3] << 16 | p[4] << 8 | p[5];
X			if (nmagic != sp->lcp.magic) {
X				if (debug)
X					addlog("0x%lx ", nmagic);
X				continue;
X			}
X			/*
X			 * Local and remote magics equal -- loopback?
X			 */
X			if (sp->pp_loopcnt >= MAXALIVECNT*5) {
X				printf (SPP_FMT "loopback\n",
X					SPP_ARGS(ifp));
X				sp->pp_loopcnt = 0;
X				if (ifp->if_flags & IFF_UP) {
X					if_down(ifp);
X					sppp_qflush(&sp->pp_cpq);
X					/* XXX ? */
X					lcp.Down(sp);
X					lcp.Up(sp);
X				}
X			} else if (debug)
X				addlog("[glitch] ");
X			++sp->pp_loopcnt;
X			/*
X			 * We negate our magic here, and NAK it.  If
X			 * we see it later in an NAK packet, we
X			 * suggest a new one.
X			 */
X			nmagic = ~sp->lcp.magic;
X			/* Gonna NAK it. */
X			p[2] = nmagic >> 24;
X			p[3] = nmagic >> 16;
X			p[4] = nmagic >> 8;
X			p[5] = nmagic;
X			break;
X
X		case LCP_OPT_ASYNC_MAP:
X			/* Async control character map -- check to be zero. */
X			if (! p[2] && ! p[3] && ! p[4] && ! p[5]) {
X				if (debug)
X					addlog("[empty] ");
X				continue;
X			}
X			if (debug)
X				addlog("[non-empty] ");
X			/* suggest a zero one */
X			p[2] = p[3] = p[4] = p[5] = 0;
X			break;
X
X		case LCP_OPT_MRU:
X			/*
X			 * Maximum receive unit.  Always agreeable,
X			 * but ignored by now.
X			 */
X			sp->lcp.their_mru = p[2] * 256 + p[3];
X			if (debug)
X				addlog("%lu ", sp->lcp.their_mru);
X			continue;
X
X		case LCP_OPT_AUTH_PROTO:
X			authproto = (p[2] << 8) + p[3];
X			if (sp->myauth.proto != authproto) {
X				/* not agreed, nak */
X				if (debug)
X					addlog("[mine %s != his %s] ",
X					       sppp_proto_name(sp->hisauth.proto),
X					       sppp_proto_name(authproto));
X				p[2] = sp->myauth.proto >> 8;
X				p[3] = sp->myauth.proto;
X				break;
X			}
X			if (authproto == PPP_CHAP && p[4] != CHAP_MD5) {
X				if (debug)
X					addlog("[chap not MD5] ");
X				p[4] = CHAP_MD5;
X				break;
X			}
X			continue;
X		}
X		/* Add the option to nak'ed list. */
X		bcopy (p, r, p[1]);
X		r += p[1];
X		rlen += p[1];
X	}
X	if (rlen) {
X		if (++sp->fail_counter[IDX_LCP] >= sp->lcp.max_failure) {
X			if (debug)
X				addlog(" max_failure (%d) exceeded, "
X				       "send conf-rej\n",
X				       sp->lcp.max_failure);
X			sppp_cp_send(sp, PPP_LCP, CONF_REJ, h->ident, rlen, buf);
X		} else {
X			if (debug)
X				addlog(" send conf-nak\n");
X			sppp_cp_send (sp, PPP_LCP, CONF_NAK, h->ident, rlen, buf);
X		}
X		return 0;
X	} else {
X		if (debug)
X			addlog(" send conf-ack\n");
X		sp->fail_counter[IDX_LCP] = 0;
X		sp->pp_loopcnt = 0;
X		sppp_cp_send (sp, PPP_LCP, CONF_ACK,
X			      h->ident, origlen, h+1);
X	}
X
X	free (buf, M_TEMP);
X	return (rlen == 0);
X}
X
X/*
X * Analyze the LCP Configure-Reject option list, and adjust our
X * negotiation.
X */
Xstatic void
Xsppp_lcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len)
X{
X	STDDCL;
X	u_char *buf, *p;
X
X	len -= 4;
X	buf = malloc (len, M_TEMP, M_NOWAIT);
X	if (!buf)
X		return;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "lcp rej opts: ",
X		    SPP_ARGS(ifp));
X
X	p = (void*) (h+1);
X	for (; len > 1 && p[1]; len -= p[1], p += p[1]) {
X		if (debug)
X			addlog(" %s ", sppp_lcp_opt_name(*p));
X		switch (*p) {
X		case LCP_OPT_MAGIC:
X			/* Magic number -- can't use it, use 0 */
X			sp->lcp.opts &= ~(1 << LCP_OPT_MAGIC);
X			sp->lcp.magic = 0;
X			break;
X		case LCP_OPT_MRU:
X			/*
X			 * Should not be rejected anyway, since we only
X			 * negotiate a MRU if explicitly requested by
X			 * peer.
X			 */
X			sp->lcp.opts &= ~(1 << LCP_OPT_MRU);
X			break;
X		case LCP_OPT_AUTH_PROTO:
X			/*
X			 * Peer doesn't want to authenticate himself,
X			 * deny unless this is a dialout call, and
X			 * AUTHFLAG_NOCALLOUT is set.
X			 */
X			if ((sp->pp_flags & PP_CALLIN) == 0 &&
X			    (sp->hisauth.flags & AUTHFLAG_NOCALLOUT) != 0) {
X				if (debug)
X					addlog("[don't insist on auth "
X					       "for callout]");
X				sp->lcp.opts &= ~(1 << LCP_OPT_AUTH_PROTO);
X				break;
X			}
X			if (debug)
X				addlog("[access denied]\n");
X			lcp.Close(sp);
X			break;
X		}
X	}
X	if (debug)
X		addlog("\n");
X	free (buf, M_TEMP);
X	return;
X}
X
X/*
X * Analyze the LCP Configure-NAK option list, and adjust our
X * negotiation.
X */
Xstatic void
Xsppp_lcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len)
X{
X	STDDCL;
X	u_char *buf, *p;
X	u_long magic;
X
X	len -= 4;
X	buf = malloc (len, M_TEMP, M_NOWAIT);
X	if (!buf)
X		return;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "lcp nak opts: ",
X		    SPP_ARGS(ifp));
X
X	p = (void*) (h+1);
X	for (; len > 1 && p[1]; len -= p[1], p += p[1]) {
X		if (debug)
X			addlog(" %s ", sppp_lcp_opt_name(*p));
X		switch (*p) {
X		case LCP_OPT_MAGIC:
X			/* Magic number -- renegotiate */
X			if ((sp->lcp.opts & (1 << LCP_OPT_MAGIC)) &&
X			    len >= 6 && p[1] == 6) {
X				magic = (u_long)p[2] << 24 |
X					(u_long)p[3] << 16 | p[4] << 8 | p[5];
X				/*
X				 * If the remote magic is our negated one,
X				 * this looks like a loopback problem.
X				 * Suggest a new magic to make sure.
X				 */
X				if (magic == ~sp->lcp.magic) {
X					if (debug)
X						addlog("magic glitch ");
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X					sp->lcp.magic = random();
X#else
X					sp->lcp.magic = time.tv_sec + time.tv_usec;
X#endif
X				} else {
X					sp->lcp.magic = magic;
X					if (debug)
X						addlog("%lu ", magic);
X				}
X			}
X			break;
X		case LCP_OPT_MRU:
X			/*
X			 * Peer wants to advise us to negotiate an MRU.
X			 * Agree on it if it's reasonable, or use
X			 * default otherwise.
X			 */
X			if (len >= 4 && p[1] == 4) {
X				u_int mru = p[2] * 256 + p[3];
X				if (debug)
X					addlog("%d ", mru);
X				if (mru < PP_MTU || mru > PP_MAX_MRU)
X					mru = PP_MTU;
X				sp->lcp.mru = mru;
X				sp->lcp.opts |= (1 << LCP_OPT_MRU);
X			}
X			break;
X		case LCP_OPT_AUTH_PROTO:
X			/*
X			 * Peer doesn't like our authentication method,
X			 * deny.
X			 */
X			if (debug)
X				addlog("[access denied]\n");
X			lcp.Close(sp);
X			break;
X		}
X	}
X	if (debug)
X		addlog("\n");
X	free (buf, M_TEMP);
X	return;
X}
X
Xstatic void
Xsppp_lcp_tlu(struct sppp *sp)
X{
X	STDDCL;
X	int i;
X	u_long mask;
X
X	/* XXX ? */
X	if (! (ifp->if_flags & IFF_UP) &&
X	    (ifp->if_flags & IFF_RUNNING)) {
X		/* Coming out of loopback mode. */
X		if_up(ifp);
X		printf (SPP_FMT "up\n", SPP_ARGS(ifp));
X	}
X
X	for (i = 0; i < IDX_COUNT; i++)
X		if ((cps[i])->flags & CP_QUAL)
X			(cps[i])->Open(sp);
X
X	if ((sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) != 0 ||
X	    (sp->pp_flags & PP_NEEDAUTH) != 0)
X		sp->pp_phase = PHASE_AUTHENTICATE;
X	else
X		sp->pp_phase = PHASE_NETWORK;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "phase %s\n", SPP_ARGS(ifp),
X		    sppp_phase_name(sp->pp_phase));
X
X	/*
X	 * Open all authentication protocols.  This is even required
X	 * if we already proceeded to network phase, since it might be
X	 * that remote wants us to authenticate, so we might have to
X	 * send a PAP request.  Undesired authentication protocols
X	 * don't do anything when they get an Open event.
X	 */
X	for (i = 0; i < IDX_COUNT; i++)
X		if ((cps[i])->flags & CP_AUTH)
X			(cps[i])->Open(sp);
X
X	if (sp->pp_phase == PHASE_NETWORK) {
X		/* Notify all NCPs. */
X		for (i = 0; i < IDX_COUNT; i++)
X			if ((cps[i])->flags & CP_NCP)
X				(cps[i])->Open(sp);
X	}
X
X	/* Send Up events to all started protos. */
X	for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1)
X		if (sp->lcp.protos & mask && ((cps[i])->flags & CP_LCP) == 0)
X			(cps[i])->Up(sp);
X
X	/* notify low-level driver of state change */
X	if (sp->pp_chg)
X		sp->pp_chg(sp, (int)sp->pp_phase);
X	
X	if (sp->pp_phase == PHASE_NETWORK)
X		/* if no NCP is starting, close down */
X		sppp_lcp_check_and_close(sp);
X}
X
Xstatic void
Xsppp_lcp_tld(struct sppp *sp)
X{
X	STDDCL;
X	int i;
X	u_long mask;
X
X	sp->pp_phase = PHASE_TERMINATE;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "phase %s\n", SPP_ARGS(ifp),
X		    sppp_phase_name(sp->pp_phase));
X
X	/*
X	 * Take upper layers down.  We send the Down event first and
X	 * the Close second to prevent the upper layers from sending
X	 * ``a flurry of terminate-request packets'', as the RFC
X	 * describes it.
X	 */
X	for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1)
X		if (sp->lcp.protos & mask && ((cps[i])->flags & CP_LCP) == 0) {
X			(cps[i])->Down(sp);
X			(cps[i])->Close(sp);
X		}
X}
X
Xstatic void
Xsppp_lcp_tls(struct sppp *sp)
X{
X	STDDCL;
X
X	sp->pp_phase = PHASE_ESTABLISH;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "phase %s\n", SPP_ARGS(ifp),
X		    sppp_phase_name(sp->pp_phase));
X
X	/* Notify lower layer if desired. */
X	if (sp->pp_tls)
X		(sp->pp_tls)(sp);
X	else
X		(sp->pp_up)(sp);
X}
X
Xstatic void
Xsppp_lcp_tlf(struct sppp *sp)
X{
X	STDDCL;
X
X	sp->pp_phase = PHASE_DEAD;
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "phase %s\n", SPP_ARGS(ifp),
X		    sppp_phase_name(sp->pp_phase));
X
X	/* Notify lower layer if desired. */
X	if (sp->pp_tlf)
X		(sp->pp_tlf)(sp);
X	else
X		(sp->pp_down)(sp);
X}
X
Xstatic void
Xsppp_lcp_scr(struct sppp *sp)
X{
X	char opt[6 /* magicnum */ + 4 /* mru */ + 5 /* chap */];
X	int i = 0;
X	u_short authproto;
X
X	if (sp->lcp.opts & (1 << LCP_OPT_MAGIC)) {
X		if (! sp->lcp.magic)
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X			sp->lcp.magic = random();
X#else
X			sp->lcp.magic = time.tv_sec + time.tv_usec;
X#endif
X		opt[i++] = LCP_OPT_MAGIC;
X		opt[i++] = 6;
X		opt[i++] = sp->lcp.magic >> 24;
X		opt[i++] = sp->lcp.magic >> 16;
X		opt[i++] = sp->lcp.magic >> 8;
X		opt[i++] = sp->lcp.magic;
X	}
X
X	if (sp->lcp.opts & (1 << LCP_OPT_MRU)) {
X		opt[i++] = LCP_OPT_MRU;
X		opt[i++] = 4;
X		opt[i++] = sp->lcp.mru >> 8;
X		opt[i++] = sp->lcp.mru;
X	}
X
X	if (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) {
X		authproto = sp->hisauth.proto;
X		opt[i++] = LCP_OPT_AUTH_PROTO;
X		opt[i++] = authproto == PPP_CHAP? 5: 4;
X		opt[i++] = authproto >> 8;
X		opt[i++] = authproto;
X		if (authproto == PPP_CHAP)
X			opt[i++] = CHAP_MD5;
X	}
X
X	sp->confid[IDX_LCP] = ++sp->pp_seq;
X	sppp_cp_send (sp, PPP_LCP, CONF_REQ, sp->confid[IDX_LCP], i, &opt);
X}
X
X/*
X * Check the open NCPs, return true if at least one NCP is open.
X */
Xstatic int
Xsppp_ncp_check(struct sppp *sp)
X{
X	int i, mask;
X
X	for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1)
X		if (sp->lcp.protos & mask && (cps[i])->flags & CP_NCP)
X			return 1;
X	return 0;
X}
X
X/*
X * Re-check the open NCPs and see if we should terminate the link.
X * Called by the NCPs during their tlf action handling.
X */
Xstatic void
Xsppp_lcp_check_and_close(struct sppp *sp)
X{
X
X	if (sp->pp_phase < PHASE_NETWORK)
X		/* don't bother, we are already going down */
X		return;
X
X	if (sppp_ncp_check(sp))
X		return;
X
X	lcp.Close(sp);
X}
X/*
X *--------------------------------------------------------------------------*
X *                                                                          *
X *                        The IPCP implementation.                          *
X *                                                                          *
X *--------------------------------------------------------------------------*
X */
X
Xstatic void
Xsppp_ipcp_init(struct sppp *sp)
X{
X	sp->ipcp.opts = 0;
X	sp->ipcp.flags = 0;
X	sp->state[IDX_IPCP] = STATE_INITIAL;
X	sp->fail_counter[IDX_IPCP] = 0;
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X	callout_handle_init(&sp->ch[IDX_IPCP]);
X#endif
X}
X
Xstatic void
Xsppp_ipcp_up(struct sppp *sp)
X{
X	sppp_up_event(&ipcp, sp);
X}
X
Xstatic void
Xsppp_ipcp_down(struct sppp *sp)
X{
X	sppp_down_event(&ipcp, sp);
X}
X
Xstatic void
Xsppp_ipcp_open(struct sppp *sp)
X{
X	STDDCL;
X	u_long myaddr, hisaddr;
X
X	sp->ipcp.flags &= ~(IPCP_HISADDR_SEEN|IPCP_MYADDR_SEEN|IPCP_MYADDR_DYN);
X
X	sppp_get_ip_addrs(sp, &myaddr, &hisaddr, 0);
X	/*
X	 * If we don't have his address, this probably means our
X	 * interface doesn't want to talk IP at all.  (This could
X	 * be the case if somebody wants to speak only IPX, for
X	 * example.)  Don't open IPCP in this case.
X	 */
X	if (hisaddr == 0L) {
X		/* XXX this message should go away */
X		if (debug)
X			log(LOG_DEBUG, SPP_FMT "ipcp_open(): no IP interface\n",
X			    SPP_ARGS(ifp));
X		return;
X	}
X
X	if (myaddr == 0L) {
X		/*
X		 * I don't have an assigned address, so i need to
X		 * negotiate my address.
X		 */
X		sp->ipcp.flags |= IPCP_MYADDR_DYN;
X		sp->ipcp.opts |= (1 << IPCP_OPT_ADDRESS);
X	} else
X		sp->ipcp.flags |= IPCP_MYADDR_SEEN;
X	sppp_open_event(&ipcp, sp);
X}
X
Xstatic void
Xsppp_ipcp_close(struct sppp *sp)
X{
X	sppp_close_event(&ipcp, sp);
X	if (sp->ipcp.flags & IPCP_MYADDR_DYN)
X		/*
X		 * My address was dynamic, clear it again.
X		 */
X		sppp_set_ip_addr(sp, 0L);
X}
X
Xstatic void
Xsppp_ipcp_TO(void *cookie)
X{
X	sppp_to_event(&ipcp, (struct sppp *)cookie);
X}
X
X/*
X * Analyze a configure request.  Return true if it was agreeable, and
X * caused action sca, false if it has been rejected or nak'ed, and
X * caused action scn.  (The return value is used to make the state
X * transition decision in the state automaton.)
X */
Xstatic int
Xsppp_ipcp_RCR(struct sppp *sp, struct lcp_header *h, int len)
X{
X	u_char *buf, *r, *p;
X	struct ifnet *ifp = &sp->pp_if;
X	int rlen, origlen, debug = ifp->if_flags & IFF_DEBUG;
X	u_long hisaddr, desiredaddr;
X	int gotmyaddr = 0;
X
X	len -= 4;
X	origlen = len;
X	/*
X	 * Make sure to allocate a buf that can at least hold a
X	 * conf-nak with an `address' option.  We might need it below.
X	 */
X	buf = r = malloc ((len < 6? 6: len), M_TEMP, M_NOWAIT);
X	if (! buf)
X		return (0);
X
X	/* pass 1: see if we can recognize them */
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "ipcp parse opts: ",
X		    SPP_ARGS(ifp));
X	p = (void*) (h+1);
X	for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) {
X		if (debug)
X			addlog(" %s ", sppp_ipcp_opt_name(*p));
X		switch (*p) {
X#ifdef notyet
X		case IPCP_OPT_COMPRESSION:
X			if (len >= 6 && p[1] >= 6) {
X				/* correctly formed compress option */
X				continue;
X			}
X			if (debug)
X				addlog("[invalid] ");
X			break;
X#endif
X		case IPCP_OPT_ADDRESS:
X			if (len >= 6 && p[1] == 6) {
X				/* correctly formed address option */
X				continue;
X			}
X			if (debug)
X				addlog("[invalid] ");
X			break;
X		default:
X			/* Others not supported. */
X			if (debug)
X				addlog("[rej] ");
X			break;
X		}
X		/* Add the option to rejected list. */
X		bcopy (p, r, p[1]);
X		r += p[1];
X		rlen += p[1];
X	}
X	if (rlen) {
X		if (debug)
X			addlog(" send conf-rej\n");
X		sppp_cp_send (sp, PPP_IPCP, CONF_REJ, h->ident, rlen, buf);
X		return 0;
X	} else if (debug)
X		addlog("\n");
X
X	/* pass 2: parse option values */
X	sppp_get_ip_addrs(sp, 0, &hisaddr, 0);
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "ipcp parse opt values: ",
X		       SPP_ARGS(ifp));
X	p = (void*) (h+1);
X	len = origlen;
X	for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) {
X		if (debug)
X			addlog(" %s ", sppp_ipcp_opt_name(*p));
X		switch (*p) {
X#ifdef notyet
X		case IPCP_OPT_COMPRESSION:
X			continue;
X#endif
X		case IPCP_OPT_ADDRESS:
X			/* This is the address he wants in his end */
X			desiredaddr = p[2] << 24 | p[3] << 16 |
X				p[4] << 8 | p[5];
X			if (desiredaddr == hisaddr ||
X			    (hisaddr == 1 && desiredaddr != 0)) {
X				/*
X				 * Peer's address is same as our value,
X				 * or we have set it to 0.0.0.1 to 
X				 * indicate that we do not really care,
X				 * this is agreeable.  Gonna conf-ack
X				 * it.
X				 */
X				if (debug)
X					addlog("%s [ack] ",
X						sppp_dotted_quad(hisaddr));
X				/* record that we've seen it already */
X				sp->ipcp.flags |= IPCP_HISADDR_SEEN;
X				continue;
X			}
X			/*
X			 * The address wasn't agreeable.  This is either
X			 * he sent us 0.0.0.0, asking to assign him an
X			 * address, or he send us another address not
X			 * matching our value.  Either case, we gonna
X			 * conf-nak it with our value.
X			 * XXX: we should "rej" if hisaddr == 0
X			 */
X			if (debug) {
X				if (desiredaddr == 0)
X					addlog("[addr requested] ");
X				else
X					addlog("%s [not agreed] ",
X						sppp_dotted_quad(desiredaddr));
X
X				p[2] = hisaddr >> 24;
X				p[3] = hisaddr >> 16;
X				p[4] = hisaddr >> 8;
X				p[5] = hisaddr;
X			}
X			break;
X		}
X		/* Add the option to nak'ed list. */
X		bcopy (p, r, p[1]);
X		r += p[1];
X		rlen += p[1];
X	}
X
X	/*
X	 * If we are about to conf-ack the request, but haven't seen
X	 * his address so far, gonna conf-nak it instead, with the
X	 * `address' option present and our idea of his address being
X	 * filled in there, to request negotiation of both addresses.
X	 *
X	 * XXX This can result in an endless req - nak loop if peer
X	 * doesn't want to send us his address.  Q: What should we do
X	 * about it?  XXX  A: implement the max-failure counter.
X	 */
X	if (rlen == 0 && !(sp->ipcp.flags & IPCP_HISADDR_SEEN) && !gotmyaddr) {
X		buf[0] = IPCP_OPT_ADDRESS;
X		buf[1] = 6;
X		buf[2] = hisaddr >> 24;
X		buf[3] = hisaddr >> 16;
X		buf[4] = hisaddr >> 8;
X		buf[5] = hisaddr;
X		rlen = 6;
X		if (debug)
X			addlog("still need hisaddr ");
X	}
X
X	if (rlen) {
X		if (debug)
X			addlog(" send conf-nak\n");
X		sppp_cp_send (sp, PPP_IPCP, CONF_NAK, h->ident, rlen, buf);
X	} else {
X		if (debug)
X			addlog(" send conf-ack\n");
X		sppp_cp_send (sp, PPP_IPCP, CONF_ACK,
X			      h->ident, origlen, h+1);
X	}
X
X	free (buf, M_TEMP);
X	return (rlen == 0);
X}
X
X/*
X * Analyze the IPCP Configure-Reject option list, and adjust our
X * negotiation.
X */
Xstatic void
Xsppp_ipcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len)
X{
X	u_char *buf, *p;
X	struct ifnet *ifp = &sp->pp_if;
X	int debug = ifp->if_flags & IFF_DEBUG;
X
X	len -= 4;
X	buf = malloc (len, M_TEMP, M_NOWAIT);
X	if (!buf)
X		return;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "ipcp rej opts: ",
X		    SPP_ARGS(ifp));
X
X	p = (void*) (h+1);
X	for (; len > 1 && p[1]; len -= p[1], p += p[1]) {
X		if (debug)
X			addlog(" %s ", sppp_ipcp_opt_name(*p));
X		switch (*p) {
X		case IPCP_OPT_ADDRESS:
X			/*
X			 * Peer doesn't grok address option.  This is
X			 * bad.  XXX  Should we better give up here?
X			 * XXX We could try old "addresses" option...
X			 */
X			sp->ipcp.opts &= ~(1 << IPCP_OPT_ADDRESS);
X			break;
X#ifdef notyet
X		case IPCP_OPT_COMPRESS:
X			sp->ipcp.opts &= ~(1 << IPCP_OPT_COMPRESS);
X			break;
X#endif
X		}
X	}
X	if (debug)
X		addlog("\n");
X	free (buf, M_TEMP);
X	return;
X}
X
X/*
X * Analyze the IPCP Configure-NAK option list, and adjust our
X * negotiation.
X */
Xstatic void
Xsppp_ipcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len)
X{
X	u_char *buf, *p;
X	struct ifnet *ifp = &sp->pp_if;
X	int debug = ifp->if_flags & IFF_DEBUG;
X	u_long wantaddr;
X
X	len -= 4;
X	buf = malloc (len, M_TEMP, M_NOWAIT);
X	if (!buf)
X		return;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "ipcp nak opts: ",
X		    SPP_ARGS(ifp));
X
X	p = (void*) (h+1);
X	for (; len > 1 && p[1]; len -= p[1], p += p[1]) {
X		if (debug)
X			addlog(" %s ", sppp_ipcp_opt_name(*p));
X		switch (*p) {
X		case IPCP_OPT_ADDRESS:
X			/*
X			 * Peer doesn't like our local IP address.  See
X			 * if we can do something for him.  We'll drop
X			 * him our address then.
X			 */
X			if (len >= 6 && p[1] == 6) {
X				wantaddr = p[2] << 24 | p[3] << 16 |
X					p[4] << 8 | p[5];
X				sp->ipcp.opts |= (1 << IPCP_OPT_ADDRESS);
X				if (debug)
X					addlog("[wantaddr %s] ",
X					       sppp_dotted_quad(wantaddr));
X				/*
X				 * When doing dynamic address assignment,
X				 * we accept his offer.  Otherwise, we
X				 * ignore it and thus continue to negotiate
X				 * our already existing value.
X			 	 * XXX: Bogus, if he said no once, he'll
X				 * just say no again, might as well die.
X				 */
X				if (sp->ipcp.flags & IPCP_MYADDR_DYN) {
X					sppp_set_ip_addr(sp, wantaddr);
X					if (debug)
X						addlog("[agree] ");
X					sp->ipcp.flags |= IPCP_MYADDR_SEEN;
X				}
X			}
X			break;
X#ifdef notyet
X		case IPCP_OPT_COMPRESS:
X			/*
X			 * Peer wants different compression parameters.
X			 */
X			break;
X#endif
X		}
X	}
X	if (debug)
X		addlog("\n");
X	free (buf, M_TEMP);
X	return;
X}
X
Xstatic void
Xsppp_ipcp_tlu(struct sppp *sp)
X{
X	/* we are up - notify isdn daemon */
X	if (sp->pp_con)
X		sp->pp_con(sp);
X}
X
Xstatic void
Xsppp_ipcp_tld(struct sppp *sp)
X{
X}
X
Xstatic void
Xsppp_ipcp_tls(struct sppp *sp)
X{
X	/* indicate to LCP that it must stay alive */
X	sp->lcp.protos |= (1 << IDX_IPCP);
X}
X
Xstatic void
Xsppp_ipcp_tlf(struct sppp *sp)
X{
X	/* we no longer need LCP */
X	sp->lcp.protos &= ~(1 << IDX_IPCP);
X	sppp_lcp_check_and_close(sp);
X}
X
Xstatic void
Xsppp_ipcp_scr(struct sppp *sp)
X{
X	char opt[6 /* compression */ + 6 /* address */];
X	u_long ouraddr;
X	int i = 0;
X
X#ifdef notyet
X	if (sp->ipcp.opts & (1 << IPCP_OPT_COMPRESSION)) {
X		opt[i++] = IPCP_OPT_COMPRESSION;
X		opt[i++] = 6;
X		opt[i++] = 0;	/* VJ header compression */
X		opt[i++] = 0x2d; /* VJ header compression */
X		opt[i++] = max_slot_id;
X		opt[i++] = comp_slot_id;
X	}
X#endif
X
X	if (sp->ipcp.opts & (1 << IPCP_OPT_ADDRESS)) {
X		sppp_get_ip_addrs(sp, &ouraddr, 0, 0);
X		opt[i++] = IPCP_OPT_ADDRESS;
X		opt[i++] = 6;
X		opt[i++] = ouraddr >> 24;
X		opt[i++] = ouraddr >> 16;
X		opt[i++] = ouraddr >> 8;
X		opt[i++] = ouraddr;
X	}
X
X	sp->confid[IDX_IPCP] = ++sp->pp_seq;
X	sppp_cp_send(sp, PPP_IPCP, CONF_REQ, sp->confid[IDX_IPCP], i, &opt);
X}
X
X
X/*
X *--------------------------------------------------------------------------*
X *                                                                          *
X *                        The CHAP implementation.                          *
X *                                                                          *
X *--------------------------------------------------------------------------*
X */
X
X/*
X * The authentication protocols don't employ a full-fledged state machine as
X * the control protocols do, since they do have Open and Close events, but
X * not Up and Down, nor are they explicitly terminated.  Also, use of the
X * authentication protocols may be different in both directions (this makes
X * sense, think of a machine that never accepts incoming calls but only
X * calls out, it doesn't require the called party to authenticate itself).
X *
X * Our state machine for the local authentication protocol (we are requesting
X * the peer to authenticate) looks like:
X *
X *						    RCA-
X *	      +--------------------------------------------+
X *	      V					    scn,tld|
X *	  +--------+			       Close   +---------+ RCA+
X *	  |	   |<----------------------------------|	 |------+
X *   +--->| Closed |				TO*    | Opened	 | sca	|
X *   |	  |	   |-----+		       +-------|	 |<-----+
X *   |	  +--------+ irc |		       |       +---------+
X *   |	    ^		 |		       |	   ^
X *   |	    |		 |		       |	   |
X *   |	    |		 |		       |	   |
X *   |	 TO-|		 |		       |	   |
X *   |	    |tld  TO+	 V		       |	   |
X *   |	    |	+------->+		       |	   |
X *   |	    |	|	 |		       |	   |
X *   |	  +--------+	 V		       |	   |
X *   |	  |	   |<----+<--------------------+	   |
X *   |	  | Req-   | scr				   |
X *   |	  | Sent   |					   |
X *   |	  |	   |					   |
X *   |	  +--------+					   |
X *   | RCA- |	| RCA+					   |
X *   +------+	+------------------------------------------+
X *   scn,tld	  sca,irc,ict,tlu
X *
X *
X *   with:
X *
X *	Open:	LCP reached authentication phase
X *	Close:	LCP reached terminate phase
X *
X *	RCA+:	received reply (pap-req, chap-response), acceptable
X *	RCN:	received reply (pap-req, chap-response), not acceptable
X *	TO+:	timeout with restart counter >= 0
X *	TO-:	timeout with restart counter < 0
X *	TO*:	reschedule timeout for CHAP
X *
X *	scr:	send request packet (none for PAP, chap-challenge)
X *	sca:	send ack packet (pap-ack, chap-success)
X *	scn:	send nak packet (pap-nak, chap-failure)
X *	ict:	initialize re-challenge timer (CHAP only)
X *
X *	tlu:	this-layer-up, LCP reaches network phase
X *	tld:	this-layer-down, LCP enters terminate phase
X *
X * Note that in CHAP mode, after sending a new challenge, while the state
X * automaton falls back into Req-Sent state, it doesn't signal a tld
X * event to LCP, so LCP remains in network phase.  Only after not getting
X * any response (or after getting an unacceptable response), CHAP closes,
X * causing LCP to enter terminate phase.
X *
X * With PAP, there is no initial request that can be sent.  The peer is
X * expected to send one based on the successful negotiation of PAP as
X * the authentication protocol during the LCP option negotiation.
X *
X * Incoming authentication protocol requests (remote requests
X * authentication, we are peer) don't employ a state machine at all,
X * they are simply answered.  Some peers [Ascend P50 firmware rev
X * 4.50] react allergically when sending IPCP requests while they are
X * still in authentication phase (thereby violating the standard that
X * demands that these NCP packets are to be discarded), so we keep
X * track of the peer demanding us to authenticate, and only proceed to
X * phase network once we've seen a positive acknowledge for the
X * authentication.
X */
X
X/*
X * Handle incoming CHAP packets.
X */
Xvoid
Xsppp_chap_input(struct sppp *sp, struct mbuf *m)
X{
X	STDDCL;
X	struct lcp_header *h;
X	int len, x;
X	u_char *value, *name, digest[AUTHKEYLEN], dsize;
X	int value_len, name_len;
X	MD5_CTX ctx;
X
X	len = m->m_pkthdr.len;
X	if (len < 4) {
X		if (debug)
X			log(LOG_DEBUG,
X			    SPP_FMT "chap invalid packet length: %d bytes\n",
X			    SPP_ARGS(ifp), len);
X		return;
X	}
X	h = mtod (m, struct lcp_header*);
X	if (len > ntohs (h->len))
X		len = ntohs (h->len);
X
X	switch (h->type) {
X	/* challenge, failure and success are his authproto */
X	case CHAP_CHALLENGE:
X		value = 1 + (u_char*)(h+1);
X		value_len = value[-1];
X		name = value + value_len;
X		name_len = len - value_len - 5;
X		if (name_len < 0) {
X			if (debug) {
X				log(LOG_DEBUG,
X				    SPP_FMT "chap corrupted challenge "
X				    "<%s id=0x%x len=%d",
X				    SPP_ARGS(ifp),
X				    sppp_auth_type_name(PPP_CHAP, h->type),
X				    h->ident, ntohs(h->len));
X				if (len > 4)
X					sppp_print_bytes((u_char*) (h+1), len-4);
X				addlog(">\n");
X			}
X			break;
X		}
X		
X		if (debug) {
X			log(LOG_DEBUG,
X			    SPP_FMT "chap input <%s id=0x%x len=%d name=",
X			    SPP_ARGS(ifp),
X			    sppp_auth_type_name(PPP_CHAP, h->type), h->ident,
X			    ntohs(h->len));
X			sppp_print_string((char*) name, name_len);
X			addlog(" value-size=%d value=", value_len);
X			sppp_print_bytes(value, value_len);
X			addlog(">\n");
X		}
X
X		/* Compute reply value. */
X		MD5Init(&ctx);
X		MD5Update(&ctx, &h->ident, 1);
X		MD5Update(&ctx, sp->myauth.secret,
X			  sppp_strnlen(sp->myauth.secret, AUTHKEYLEN));
X		MD5Update(&ctx, value, value_len);
X		MD5Final(digest, &ctx);
X		dsize = sizeof digest;
X
X		sppp_auth_send(&chap, sp, CHAP_RESPONSE, h->ident,
X			       sizeof dsize, (const char *)&dsize,
X			       sizeof digest, digest,
X			       (size_t)sppp_strnlen(sp->myauth.name, AUTHNAMELEN),
X			       sp->myauth.name,
X			       0);
X		break;
X
X	case CHAP_SUCCESS:
X		if (debug) {
X			log(LOG_DEBUG, SPP_FMT "chap success",
X			    SPP_ARGS(ifp));
X			if (len > 4) {
X				addlog(": ");
X				sppp_print_string((char*)(h + 1), len - 4);
X			}
X			addlog("\n");
X		}
X		x = splimp();
X		sp->pp_flags &= ~PP_NEEDAUTH;
X		if (sp->myauth.proto == PPP_CHAP &&
X		    (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) &&
X		    (sp->lcp.protos & (1 << IDX_CHAP)) == 0) {
X			/*
X			 * We are authenticator for CHAP but didn't
X			 * complete yet.  Leave it to tlu to proceed
X			 * to network phase.
X			 */
X			splx(x);
X			break;
X		}
X		splx(x);
X		sppp_phase_network(sp);
X		break;
X
X	case CHAP_FAILURE:
X		if (debug) {
X			log(LOG_INFO, SPP_FMT "chap failure",
X			    SPP_ARGS(ifp));
X			if (len > 4) {
X				addlog(": ");
X				sppp_print_string((char*)(h + 1), len - 4);
X			}
X			addlog("\n");
X		} else
X			log(LOG_INFO, SPP_FMT "chap failure\n",
X			    SPP_ARGS(ifp));
X		/* await LCP shutdown by authenticator */
X		break;
X
X	/* response is my authproto */
X	case CHAP_RESPONSE:
X		value = 1 + (u_char*)(h+1);
X		value_len = value[-1];
X		name = value + value_len;
X		name_len = len - value_len - 5;
X		if (name_len < 0) {
X			if (debug) {
X				log(LOG_DEBUG,
X				    SPP_FMT "chap corrupted response "
X				    "<%s id=0x%x len=%d",
X				    SPP_ARGS(ifp),
X				    sppp_auth_type_name(PPP_CHAP, h->type),
X				    h->ident, ntohs(h->len));
X				if (len > 4)
X					sppp_print_bytes((u_char*)(h+1), len-4);
X				addlog(">\n");
X			}
X			break;
X		}
X		if (h->ident != sp->confid[IDX_CHAP]) {
X			if (debug)
X				log(LOG_DEBUG,
X				    SPP_FMT "chap dropping response for old ID "
X				    "(got %d, expected %d)\n",
X				    SPP_ARGS(ifp),
X				    h->ident, sp->confid[IDX_CHAP]);
X			break;
X		}
X		if (name_len != sppp_strnlen(sp->hisauth.name, AUTHNAMELEN)
X		    || bcmp(name, sp->hisauth.name, name_len) != 0) {
X			log(LOG_INFO, SPP_FMT "chap response, his name ",
X			    SPP_ARGS(ifp));
X			sppp_print_string(name, name_len);
X			addlog(" != expected ");
X			sppp_print_string(sp->hisauth.name,
X					  sppp_strnlen(sp->hisauth.name, AUTHNAMELEN));
X			addlog("\n");
X		}    
X		if (debug) {
X			log(LOG_DEBUG, SPP_FMT "chap input(%s) "
X			    "<%s id=0x%x len=%d name=",
X			    SPP_ARGS(ifp),
X			    sppp_state_name(sp->state[IDX_CHAP]),
X			    sppp_auth_type_name(PPP_CHAP, h->type),
X			    h->ident, ntohs (h->len));
X			sppp_print_string((char*)name, name_len);
X			addlog(" value-size=%d value=", value_len);
X			sppp_print_bytes(value, value_len);
X			addlog(">\n");
X		}
X		if (value_len != AUTHKEYLEN) {
X			if (debug)
X				log(LOG_DEBUG,
X				    SPP_FMT "chap bad hash value length: "
X				    "%d bytes, should be %d\n",
X				    SPP_ARGS(ifp), value_len,
X				    AUTHKEYLEN);
X			break;
X		}
X
X		MD5Init(&ctx);
X		MD5Update(&ctx, &h->ident, 1);
X		MD5Update(&ctx, sp->hisauth.secret,
X			  sppp_strnlen(sp->hisauth.secret, AUTHKEYLEN));
X		MD5Update(&ctx, sp->myauth.challenge, AUTHKEYLEN);
X		MD5Final(digest, &ctx);
X
X#define FAILMSG "Failed..."
X#define SUCCMSG "Welcome!"
X
X		if (value_len != sizeof digest ||
X		    bcmp(digest, value, value_len) != 0) {
X			/* action scn, tld */
X			sppp_auth_send(&chap, sp, CHAP_FAILURE, h->ident,
X				       sizeof(FAILMSG) - 1, (u_char *)FAILMSG,
X				       0);
X			chap.tld(sp);
X			break;
X		}
X		/* action sca, perhaps tlu */
X		if (sp->state[IDX_CHAP] == STATE_REQ_SENT ||
X		    sp->state[IDX_CHAP] == STATE_OPENED)
X			sppp_auth_send(&chap, sp, CHAP_SUCCESS, h->ident,
X				       sizeof(SUCCMSG) - 1, (u_char *)SUCCMSG,
X				       0);
X		if (sp->state[IDX_CHAP] == STATE_REQ_SENT) {
X			sppp_cp_change_state(&chap, sp, STATE_OPENED);
X			chap.tlu(sp);
X		}
X		break;
X
X	default:
X		/* Unknown CHAP packet type -- ignore. */
X		if (debug) {
X			log(LOG_DEBUG, SPP_FMT "chap unknown input(%s) "
X			    "<0x%x id=0x%xh len=%d",
X			    SPP_ARGS(ifp),
X			    sppp_state_name(sp->state[IDX_CHAP]),
X			    h->type, h->ident, ntohs(h->len));
X			if (len > 4)
X				sppp_print_bytes((u_char*)(h+1), len-4);
X			addlog(">\n");
X		}
X		break;
X
X	}
X}
X
Xstatic void
Xsppp_chap_init(struct sppp *sp)
X{
X	/* Chap doesn't have STATE_INITIAL at all. */
X	sp->state[IDX_CHAP] = STATE_CLOSED;
X	sp->fail_counter[IDX_CHAP] = 0;
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X	callout_handle_init(&sp->ch[IDX_CHAP]);
X#endif
X}
X
Xstatic void
Xsppp_chap_open(struct sppp *sp)
X{
X	if (sp->myauth.proto == PPP_CHAP &&
X	    (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) != 0) {
X		/* we are authenticator for CHAP, start it */
X		chap.scr(sp);
X		sp->rst_counter[IDX_CHAP] = sp->lcp.max_configure;
X		sppp_cp_change_state(&chap, sp, STATE_REQ_SENT);
X	}
X	/* nothing to be done if we are peer, await a challenge */
X}
X
Xstatic void
Xsppp_chap_close(struct sppp *sp)
X{
X	if (sp->state[IDX_CHAP] != STATE_CLOSED)
X		sppp_cp_change_state(&chap, sp, STATE_CLOSED);
X}
X
Xstatic void
Xsppp_chap_TO(void *cookie)
X{
X	struct sppp *sp = (struct sppp *)cookie;
X	STDDCL;
X	int s;
X
X	s = splimp();
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "chap TO(%s) rst_counter = %d\n",
X		    SPP_ARGS(ifp),
X		    sppp_state_name(sp->state[IDX_CHAP]),
X		    sp->rst_counter[IDX_CHAP]);
X
X	if (--sp->rst_counter[IDX_CHAP] < 0)
X		/* TO- event */
X		switch (sp->state[IDX_CHAP]) {
X		case STATE_REQ_SENT:
X			chap.tld(sp);
X			sppp_cp_change_state(&chap, sp, STATE_CLOSED);
X			break;
X		}
X	else
X		/* TO+ (or TO*) event */
X		switch (sp->state[IDX_CHAP]) {
X		case STATE_OPENED:
X			/* TO* event */
X			sp->rst_counter[IDX_CHAP] = sp->lcp.max_configure;
X			/* fall through */
X		case STATE_REQ_SENT:
X			chap.scr(sp);
X			/* sppp_cp_change_state() will restart the timer */
X			sppp_cp_change_state(&chap, sp, STATE_REQ_SENT);
X			break;
X		}
X
X	splx(s);
X}
X
Xstatic void
Xsppp_chap_tlu(struct sppp *sp)
X{
X	STDDCL;
X	int i, x;
X
X	i = 0;
X	sp->rst_counter[IDX_CHAP] = sp->lcp.max_configure;
X
X	/*
X	 * Some broken CHAP implementations (Conware CoNet, firmware
X	 * 4.0.?) don't want to re-authenticate their CHAP once the
X	 * initial challenge-response exchange has taken place.
X	 * Provide for an option to avoid rechallenges.
X	 */
X	if ((sp->hisauth.flags & AUTHFLAG_NORECHALLENGE) == 0) {
X		/*
X		 * Compute the re-challenge timeout.  This will yield
X		 * a number between 300 and 810 seconds.
X		 */
X		i = 300 + ((unsigned)(random() & 0xff00) >> 7);
X		TIMEOUT(chap.TO, (void *)sp, i * hz, sp->ch[IDX_CHAP]);
X	}
X
X	if (debug) {
X		log(LOG_DEBUG,
X		    SPP_FMT "chap %s, ",
X		    SPP_ARGS(ifp),
X		    sp->pp_phase == PHASE_NETWORK? "reconfirmed": "tlu");
X		if ((sp->hisauth.flags & AUTHFLAG_NORECHALLENGE) == 0)
X			addlog("next re-challenge in %d seconds\n", i);
X		else
X			addlog("re-challenging supressed\n");
X	}
X
X	x = splimp();
X	/* indicate to LCP that we need to be closed down */
X	sp->lcp.protos |= (1 << IDX_CHAP);
X
X	if (sp->pp_flags & PP_NEEDAUTH) {
X		/*
X		 * Remote is authenticator, but his auth proto didn't
X		 * complete yet.  Defer the transition to network
X		 * phase.
X		 */
X		splx(x);
X		return;
X	}
X	splx(x);
X
X	/*
X	 * If we are already in phase network, we are done here.  This
X	 * is the case if this is a dummy tlu event after a re-challenge.
X	 */
X	if (sp->pp_phase != PHASE_NETWORK)
X		sppp_phase_network(sp);
X}
X
Xstatic void
Xsppp_chap_tld(struct sppp *sp)
X{
X	STDDCL;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "chap tld\n", SPP_ARGS(ifp));
X	UNTIMEOUT(chap.TO, (void *)sp, sp->ch[IDX_CHAP]);
X	sp->lcp.protos &= ~(1 << IDX_CHAP);
X
X	lcp.Close(sp);
X}
X
Xstatic void
Xsppp_chap_scr(struct sppp *sp)
X{
X	u_long *ch, seed;
X	u_char clen;
X
X	/* Compute random challenge. */
X	ch = (u_long *)sp->myauth.challenge;
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X	read_random(&seed, sizeof seed);
X#else
X	{
X	struct timeval tv;
X	microtime(&tv);
X	seed = tv.tv_sec ^ tv.tv_usec;
X	}
X#endif
X	ch[0] = seed ^ random();
X	ch[1] = seed ^ random();
X	ch[2] = seed ^ random();
X	ch[3] = seed ^ random();
X	clen = AUTHKEYLEN;
X
X	sp->confid[IDX_CHAP] = ++sp->pp_seq;
X
X	sppp_auth_send(&chap, sp, CHAP_CHALLENGE, sp->confid[IDX_CHAP],
X		       sizeof clen, (const char *)&clen,
X		       (size_t)AUTHKEYLEN, sp->myauth.challenge,
X		       (size_t)sppp_strnlen(sp->myauth.name, AUTHNAMELEN),
X		       sp->myauth.name,
X		       0);
X}
X/*
X *--------------------------------------------------------------------------*
X *                                                                          *
X *                        The PAP implementation.                           *
X *                                                                          *
X *--------------------------------------------------------------------------*
X */
X/*
X * For PAP, we need to keep a little state also if we are the peer, not the
X * authenticator.  This is since we don't get a request to authenticate, but
X * have to repeatedly authenticate ourself until we got a response (or the
X * retry counter is expired).
X */
X
X/*
X * Handle incoming PAP packets.  */
Xstatic void
Xsppp_pap_input(struct sppp *sp, struct mbuf *m)
X{
X	STDDCL;
X	struct lcp_header *h;
X	int len, x;
X	u_char *name, *passwd, mlen;
X	int name_len, passwd_len;
X
X	len = m->m_pkthdr.len;
X	if (len < 5) {
X		if (debug)
X			log(LOG_DEBUG,
X			    SPP_FMT "pap invalid packet length: %d bytes\n",
X			    SPP_ARGS(ifp), len);
X		return;
X	}
X	h = mtod (m, struct lcp_header*);
X	if (len > ntohs (h->len))
X		len = ntohs (h->len);
X	switch (h->type) {
X	/* PAP request is my authproto */
X	case PAP_REQ:
X		name = 1 + (u_char*)(h+1);
X		name_len = name[-1];
X		passwd = name + name_len + 1;
X		if (name_len > len - 6 ||
X		    (passwd_len = passwd[-1]) > len - 6 - name_len) {
X			if (debug) {
X				log(LOG_DEBUG, SPP_FMT "pap corrupted input "
X				    "<%s id=0x%x len=%d",
X				    SPP_ARGS(ifp),
X				    sppp_auth_type_name(PPP_PAP, h->type),
X				    h->ident, ntohs(h->len));
X				if (len > 4)
X					sppp_print_bytes((u_char*)(h+1), len-4);
X				addlog(">\n");
X			}
X			break;
X		}
X		if (debug) {
X			log(LOG_DEBUG, SPP_FMT "pap input(%s) "
X			    "<%s id=0x%x len=%d name=",
X			    SPP_ARGS(ifp),
X			    sppp_state_name(sp->state[IDX_PAP]),
X			    sppp_auth_type_name(PPP_PAP, h->type),
X			    h->ident, ntohs(h->len));
X			sppp_print_string((char*)name, name_len);
X			addlog(" passwd=");
X			sppp_print_string((char*)passwd, passwd_len);
X			addlog(">\n");
X		}
X		if (name_len > AUTHNAMELEN ||
X		    passwd_len > AUTHKEYLEN ||
X		    bcmp(name, sp->hisauth.name, name_len) != 0 ||
X		    bcmp(passwd, sp->hisauth.secret, passwd_len) != 0) {
X			/* action scn, tld */
X			mlen = sizeof(FAILMSG) - 1;
X			sppp_auth_send(&pap, sp, PAP_NAK, h->ident,
X				       sizeof mlen, (const char *)&mlen,
X				       sizeof(FAILMSG) - 1, (u_char *)FAILMSG,
X				       0);
X			pap.tld(sp);
X			break;
X		}
X		/* action sca, perhaps tlu */
X		if (sp->state[IDX_PAP] == STATE_REQ_SENT ||
X		    sp->state[IDX_PAP] == STATE_OPENED) {
X			mlen = sizeof(SUCCMSG) - 1;
X			sppp_auth_send(&pap, sp, PAP_ACK, h->ident,
X				       sizeof mlen, (const char *)&mlen,
X				       sizeof(SUCCMSG) - 1, (u_char *)SUCCMSG,
X				       0);
X		}
X		if (sp->state[IDX_PAP] == STATE_REQ_SENT) {
X			sppp_cp_change_state(&pap, sp, STATE_OPENED);
X			pap.tlu(sp);
X		}
X		break;
X
X	/* ack and nak are his authproto */
X	case PAP_ACK:
X		UNTIMEOUT(sppp_pap_my_TO, (void *)sp, sp->pap_my_to_ch);
X		if (debug) {
X			log(LOG_DEBUG, SPP_FMT "pap success",
X			    SPP_ARGS(ifp));
X			name_len = *((char *)h);
X			if (len > 5 && name_len) {
X				addlog(": ");
X				sppp_print_string((char*)(h+1), name_len);
X			}
X			addlog("\n");
X		}
X		x = splimp();
X		sp->pp_flags &= ~PP_NEEDAUTH;
X		if (sp->myauth.proto == PPP_PAP &&
X		    (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) &&
X		    (sp->lcp.protos & (1 << IDX_PAP)) == 0) {
X			/*
X			 * We are authenticator for PAP but didn't
X			 * complete yet.  Leave it to tlu to proceed
X			 * to network phase.
X			 */
X			splx(x);
X			break;
X		}
X		splx(x);
X		sppp_phase_network(sp);
X		break;
X
X	case PAP_NAK:
X		UNTIMEOUT(sppp_pap_my_TO, (void *)sp, sp->pap_my_to_ch);
X		if (debug) {
X			log(LOG_INFO, SPP_FMT "pap failure",
X			    SPP_ARGS(ifp));
X			name_len = *((char *)h);
X			if (len > 5 && name_len) {
X				addlog(": ");
X				sppp_print_string((char*)(h+1), name_len);
X			}
X			addlog("\n");
X		} else
X			log(LOG_INFO, SPP_FMT "pap failure\n",
X			    SPP_ARGS(ifp));
X		/* await LCP shutdown by authenticator */
X		break;
X
X	default:
X		/* Unknown PAP packet type -- ignore. */
X		if (debug) {
X			log(LOG_DEBUG, SPP_FMT "pap corrupted input "
X			    "<0x%x id=0x%x len=%d",
X			    SPP_ARGS(ifp),
X			    h->type, h->ident, ntohs(h->len));
X			if (len > 4)
X				sppp_print_bytes((u_char*)(h+1), len-4);
X			addlog(">\n");
X		}
X		break;
X
X	}
X}
X
Xstatic void
Xsppp_pap_init(struct sppp *sp)
X{
X	/* PAP doesn't have STATE_INITIAL at all. */
X	sp->state[IDX_PAP] = STATE_CLOSED;
X	sp->fail_counter[IDX_PAP] = 0;
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X	callout_handle_init(&sp->ch[IDX_PAP]);
X	callout_handle_init(&sp->pap_my_to_ch);
X#endif
X}
X
Xstatic void
Xsppp_pap_open(struct sppp *sp)
X{
X	if (sp->hisauth.proto == PPP_PAP &&
X	    (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) != 0) {
X		/* we are authenticator for PAP, start our timer */
X		sp->rst_counter[IDX_PAP] = sp->lcp.max_configure;
X		sppp_cp_change_state(&pap, sp, STATE_REQ_SENT);
X	}
X	if (sp->myauth.proto == PPP_PAP) {
X		/* we are peer, send a request, and start a timer */
X		pap.scr(sp);
X		TIMEOUT(sppp_pap_my_TO, (void *)sp, sp->lcp.timeout,
X		    sp->pap_my_to_ch);
X	}
X}
X
Xstatic void
Xsppp_pap_close(struct sppp *sp)
X{
X	if (sp->state[IDX_PAP] != STATE_CLOSED)
X		sppp_cp_change_state(&pap, sp, STATE_CLOSED);
X}
X
X/*
X * That's the timeout routine if we are authenticator.  Since the
X * authenticator is basically passive in PAP, we can't do much here.
X */
Xstatic void
Xsppp_pap_TO(void *cookie)
X{
X	struct sppp *sp = (struct sppp *)cookie;
X	STDDCL;
X	int s;
X
X	s = splimp();
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "pap TO(%s) rst_counter = %d\n",
X		    SPP_ARGS(ifp),
X		    sppp_state_name(sp->state[IDX_PAP]),
X		    sp->rst_counter[IDX_PAP]);
X
X	if (--sp->rst_counter[IDX_PAP] < 0)
X		/* TO- event */
X		switch (sp->state[IDX_PAP]) {
X		case STATE_REQ_SENT:
X			pap.tld(sp);
X			sppp_cp_change_state(&pap, sp, STATE_CLOSED);
X			break;
X		}
X	else
X		/* TO+ event, not very much we could do */
X		switch (sp->state[IDX_PAP]) {
X		case STATE_REQ_SENT:
X			/* sppp_cp_change_state() will restart the timer */
X			sppp_cp_change_state(&pap, sp, STATE_REQ_SENT);
X			break;
X		}
X
X	splx(s);
X}
X
X/*
X * That's the timeout handler if we are peer.  Since the peer is active,
X * we need to retransmit our PAP request since it is apparently lost.
X * XXX We should impose a max counter.
X */
Xstatic void
Xsppp_pap_my_TO(void *cookie)
X{
X	struct sppp *sp = (struct sppp *)cookie;
X	STDDCL;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "pap peer TO\n",
X		    SPP_ARGS(ifp));
X
X	pap.scr(sp);
X}
X
Xstatic void
Xsppp_pap_tlu(struct sppp *sp)
X{
X	STDDCL;
X	int x;
X
X	sp->rst_counter[IDX_PAP] = sp->lcp.max_configure;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "%s tlu\n",
X		    SPP_ARGS(ifp), pap.name);
X
X	x = splimp();
X	/* indicate to LCP that we need to be closed down */
X	sp->lcp.protos |= (1 << IDX_PAP);
X
X	if (sp->pp_flags & PP_NEEDAUTH) {
X		/*
X		 * Remote is authenticator, but his auth proto didn't
X		 * complete yet.  Defer the transition to network
X		 * phase.
X		 */
X		splx(x);
X		return;
X	}
X	splx(x);
X	sppp_phase_network(sp);
X}
X
Xstatic void
Xsppp_pap_tld(struct sppp *sp)
X{
X	STDDCL;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "pap tld\n", SPP_ARGS(ifp));
X	UNTIMEOUT(pap.TO, (void *)sp, sp->ch[IDX_PAP]);
X	UNTIMEOUT(sppp_pap_my_TO, (void *)sp, sp->pap_my_to_ch);
X	sp->lcp.protos &= ~(1 << IDX_PAP);
X
X	lcp.Close(sp);
X}
X
Xstatic void
Xsppp_pap_scr(struct sppp *sp)
X{
X	u_char idlen, pwdlen;
X
X	sp->confid[IDX_PAP] = ++sp->pp_seq;
X	pwdlen = sppp_strnlen(sp->myauth.secret, AUTHKEYLEN);
X	idlen = sppp_strnlen(sp->myauth.name, AUTHNAMELEN);
X
X	sppp_auth_send(&pap, sp, PAP_REQ, sp->confid[IDX_PAP],
X		       sizeof idlen, (const char *)&idlen,
X		       (size_t)idlen, sp->myauth.name,
X		       sizeof pwdlen, (const char *)&pwdlen,
X		       (size_t)pwdlen, sp->myauth.secret,
X		       0);
X}
X/*
X * Random miscellaneous functions.
X */
X
X/*
X * Send a PAP or CHAP proto packet.
X *
X * Varadic function, each of the elements for the ellipsis is of type
X * ``size_t mlen, const u_char *msg''.  Processing will stop iff
X * mlen == 0.
X * NOTE: never declare variadic functions with types subject to type
X * promotion (i.e. u_char). This is asking for big trouble depending
X * on the architecture you are on...
X */
X
Xstatic void
Xsppp_auth_send(const struct cp *cp, struct sppp *sp,
X               unsigned int type, unsigned int id,
X	       ...)
X{
X	STDDCL;
X	struct ppp_header *h;
X	struct lcp_header *lh;
X	struct mbuf *m;
X	u_char *p;
X	int len;
X	unsigned int mlen;
X	const char *msg;
X	va_list ap;
X
X	MGETHDR (m, M_DONTWAIT, MT_DATA);
X	if (! m)
X		return;
X	m->m_pkthdr.rcvif = 0;
X
X	h = mtod (m, struct ppp_header*);
X	h->address = PPP_ALLSTATIONS;		/* broadcast address */
X	h->control = PPP_UI;			/* Unnumbered Info */
X	h->protocol = htons(cp->proto);
X
X	lh = (struct lcp_header*)(h + 1);
X	lh->type = type;
X	lh->ident = id;
X	p = (u_char*) (lh+1);
X
X	va_start(ap, id);
X	len = 0;
X
X	while ((mlen = (unsigned int)va_arg(ap, size_t)) != 0) {
X		msg = va_arg(ap, const char *);
X		len += mlen;
X		if (len > MHLEN - PPP_HEADER_LEN - LCP_HEADER_LEN) {
X			va_end(ap);
X			m_freem(m);
X			return;
X		}
X
X		bcopy(msg, p, mlen);
X		p += mlen;
X	}
X	va_end(ap);
X
X	m->m_pkthdr.len = m->m_len = PPP_HEADER_LEN + LCP_HEADER_LEN + len;
X	lh->len = htons (LCP_HEADER_LEN + len);
X
X	if (debug) {
X		log(LOG_DEBUG, SPP_FMT "%s output <%s id=0x%x len=%d",
X		    SPP_ARGS(ifp), cp->name,
X		    sppp_auth_type_name(cp->proto, lh->type),
X		    lh->ident, ntohs(lh->len));
X		if (len)
X			sppp_print_bytes((u_char*) (lh+1), len);
X		addlog(">\n");
X	}
X	if (IF_QFULL (&sp->pp_cpq)) {
X		IF_DROP (&sp->pp_fastq);
X		IF_DROP (&ifp->if_snd);
X		m_freem (m);
X		++ifp->if_oerrors;
X	} else
X		IF_ENQUEUE (&sp->pp_cpq, m);
X	if (! (ifp->if_flags & IFF_OACTIVE))
X		(*ifp->if_start) (ifp);
X	ifp->if_obytes += m->m_pkthdr.len + 3;
X}
X
X/*
X * Flush interface queue.
X */
Xstatic void
Xsppp_qflush(struct ifqueue *ifq)
X{
X	struct mbuf *m, *n;
X
X	n = ifq->ifq_head;
X	while ((m = n)) {
X		n = m->m_act;
X		m_freem (m);
X	}
X	ifq->ifq_head = 0;
X	ifq->ifq_tail = 0;
X	ifq->ifq_len = 0;
X}
X
X/*
X * Send keepalive packets, every 10 seconds.
X */
Xstatic void
Xsppp_keepalive(void *dummy)
X{
X	struct sppp *sp;
X	int s;
X
X	s = splimp();
X	for (sp=spppq; sp; sp=sp->pp_next) {
X		struct ifnet *ifp = &sp->pp_if;
X
X		/* Keepalive mode disabled or channel down? */
X		if (! (sp->pp_flags & PP_KEEPALIVE) ||
X		    ! (ifp->if_flags & IFF_RUNNING))
X			continue;
X
X		/* No keepalive in PPP mode if LCP not opened yet. */
X		if (! (sp->pp_flags & PP_CISCO) &&
X		    sp->pp_phase < PHASE_AUTHENTICATE)
X			continue;
X
X		if (sp->pp_alivecnt == MAXALIVECNT) {
X			/* No keepalive packets got.  Stop the interface. */
X			printf (SPP_FMT "down\n", SPP_ARGS(ifp));
X			if_down (ifp);
X			sppp_qflush (&sp->pp_cpq);
X			if (! (sp->pp_flags & PP_CISCO)) {
X				/* XXX */
X				/* Shut down the PPP link. */
X				lcp.Down(sp);
X				/* Initiate negotiation. XXX */
X				lcp.Up(sp);
X			}
X		}
X		if (sp->pp_alivecnt <= MAXALIVECNT)
X			++sp->pp_alivecnt;
X		if (sp->pp_flags & PP_CISCO)
X			sppp_cisco_send (sp, CISCO_KEEPALIVE_REQ, ++sp->pp_seq,
X				sp->pp_rseq);
X		else if (sp->pp_phase >= PHASE_AUTHENTICATE) {
X			long nmagic = htonl (sp->lcp.magic);
X			sp->lcp.echoid = ++sp->pp_seq;
X			sppp_cp_send (sp, PPP_LCP, ECHO_REQ,
X				sp->lcp.echoid, 4, &nmagic);
X		}
X	}
X	splx(s);
X	TIMEOUT(sppp_keepalive, 0, hz * 10, keepalive_ch);
X}
X
X/*
X * Get both IP addresses.
X */
Xstatic void
Xsppp_get_ip_addrs(struct sppp *sp, u_long *src, u_long *dst, u_long *srcmask)
X{
X	struct ifnet *ifp = &sp->pp_if;
X	struct ifaddr *ifa;
X	struct sockaddr_in *si, *sm;
X	u_long ssrc, ddst;
X
X	sm = NULL;
X	ssrc = ddst = 0L;
X	/*
X	 * Pick the first AF_INET address from the list,
X	 * aliases don't make any sense on a p2p link anyway.
X	 */
X	si = 0;
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X	TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link)
X#elif defined(__NetBSD__) || defined (__OpenBSD__)
X	for (ifa = ifp->if_addrlist.tqh_first;
X	     ifa;
X	     ifa = ifa->ifa_list.tqe_next)
X#else
X	for (ifa = ifp->if_addrlist;
X	     ifa;
X	     ifa = ifa->ifa_next)
X#endif
X		if (ifa->ifa_addr->sa_family == AF_INET) {
X			si = (struct sockaddr_in *)ifa->ifa_addr;
X			sm = (struct sockaddr_in *)ifa->ifa_netmask;
X			if (si)
X				break;
X		}
X	if (ifa) {
X		if (si && si->sin_addr.s_addr) {
X			ssrc = si->sin_addr.s_addr;
X			if (srcmask)
X				*srcmask = ntohl(sm->sin_addr.s_addr);
X		}
X
X		si = (struct sockaddr_in *)ifa->ifa_dstaddr;
X		if (si && si->sin_addr.s_addr)
X			ddst = si->sin_addr.s_addr;
X	}
X
X	if (dst) *dst = ntohl(ddst);
X	if (src) *src = ntohl(ssrc);
X}
X
X/*
X * Set my IP address.  Must be called at splimp.
X */
Xstatic void
Xsppp_set_ip_addr(struct sppp *sp, u_long src)
X{
X	STDDCL;
X	struct ifaddr *ifa;
X	struct sockaddr_in *si;
X
X	/*
X	 * Pick the first AF_INET address from the list,
X	 * aliases don't make any sense on a p2p link anyway.
X	 */
X	si = 0;
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X	TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link)
X#elif defined(__NetBSD__) || defined (__OpenBSD__)
X	for (ifa = ifp->if_addrlist.tqh_first;
X	     ifa;
X	     ifa = ifa->ifa_list.tqe_next)
X#else
X	for (ifa = ifp->if_addrlist;
X	     ifa;
X	     ifa = ifa->ifa_next)
X#endif
X	{
X		if (ifa->ifa_addr->sa_family == AF_INET)
X		{
X			si = (struct sockaddr_in *)ifa->ifa_addr;
X			if (si)
X				break;
X		}
X	}
X
X	if (ifa && si)
X	{
X		int error;
X#if __NetBSD_Version__ >= 103080000
X		struct sockaddr_in new_sin = *si;
X
X		new_sin.sin_addr.s_addr = htonl(src);
X		error = in_ifinit(ifp, ifatoia(ifa), &new_sin, 1);
X		if(debug && error)
X		{
X			log(LOG_DEBUG, SPP_FMT "sppp_set_ip_addr: in_ifinit "
X			" failed, error=%d\n", SPP_ARGS(ifp), error);
X		}
X#else
X		/* delete old route */
X		error = rtinit(ifa, (int)RTM_DELETE, RTF_HOST);
X		if(debug && error)
X		{
X			log(LOG_DEBUG, SPP_FMT "sppp_set_ip_addr: rtinit DEL failed, error=%d\n",
X		    		SPP_ARGS(ifp), error);
X		}
X
X		/* set new address */
X		si->sin_addr.s_addr = htonl(src);
X
X		/* add new route */
X		error = rtinit(ifa, (int)RTM_ADD, RTF_HOST);		
X		if (debug && error)
X		{
X			log(LOG_DEBUG, SPP_FMT "sppp_set_ip_addr: rtinit ADD failed, error=%d",
X		    		SPP_ARGS(ifp), error);
X		}
X#endif
X	}
X}			
X
Xstatic int
Xsppp_params(struct sppp *sp, u_long cmd, void *data)
X{
X	u_long subcmd;
X	struct ifreq *ifr = (struct ifreq *)data;
X	struct spppreq spr;
X
X	/*
X	 * ifr->ifr_data is supposed to point to a struct spppreq.
X	 * Check the cmd word first before attempting to fetch all the
X	 * data.
X	 */
X	if ((subcmd = fuword(ifr->ifr_data)) == -1)
X		return EFAULT;
X
X	if (copyin((caddr_t)ifr->ifr_data, &spr, sizeof spr) != 0)
X		return EFAULT;
X
X	switch (subcmd) {
X	case SPPPIOGDEFS:
X		if (cmd != SIOCGIFGENERIC)
X			return EINVAL;
X		/*
X		 * We copy over the entire current state, but clean
X		 * out some of the stuff we don't wanna pass up.
X		 * Remember, SIOCGIFGENERIC is unprotected, and can be
X		 * called by any user.  No need to ever get PAP or
X		 * CHAP secrets back to userland anyway.
X		 */
X		bcopy(sp, &spr.defs, sizeof(struct sppp));
X		bzero(spr.defs.myauth.secret, AUTHKEYLEN);
X		bzero(spr.defs.myauth.challenge, AUTHKEYLEN);
X		bzero(spr.defs.hisauth.secret, AUTHKEYLEN);
X		bzero(spr.defs.hisauth.challenge, AUTHKEYLEN);
X		return copyout(&spr, (caddr_t)ifr->ifr_data, sizeof spr);
X
X	case SPPPIOSDEFS:
X		if (cmd != SIOCSIFGENERIC)
X			return EINVAL;
X		/*
X		 * We have a very specific idea of which fields we allow
X		 * being passed back from userland, so to not clobber our
X		 * current state.  For one, we only allow setting
X		 * anything if LCP is in dead phase.  Once the LCP
X		 * negotiations started, the authentication settings must
X		 * not be changed again.  (The administrator can force an
X		 * ifconfig down in order to get LCP back into dead
X		 * phase.)
X		 *
X		 * Also, we only allow for authentication parameters to be
X		 * specified.
X		 *
X		 * XXX Should allow to set or clear pp_flags.
X		 *
X		 * Finally, if the respective authentication protocol to
X		 * be used is set differently than 0, but the secret is
X		 * passed as all zeros, we don't trash the existing secret.
X		 * This allows an administrator to change the system name
X		 * only without clobbering the secret (which he didn't get
X		 * back in a previous SPPPIOGDEFS call).  However, the
X		 * secrets are cleared if the authentication protocol is
X		 * reset to 0.
X		 */
X		if (sp->pp_phase != PHASE_DEAD)
X			return EBUSY;
X
X		if ((spr.defs.myauth.proto != 0 && spr.defs.myauth.proto != PPP_PAP &&
X		     spr.defs.myauth.proto != PPP_CHAP) ||
X		    (spr.defs.hisauth.proto != 0 && spr.defs.hisauth.proto != PPP_PAP &&
X		     spr.defs.hisauth.proto != PPP_CHAP))
X			return EINVAL;
X
X		if (spr.defs.myauth.proto == 0)
X			/* resetting myauth */
X			bzero(&sp->myauth, sizeof sp->myauth);
X		else {
X			/* setting/changing myauth */
X			sp->myauth.proto = spr.defs.myauth.proto;
X			bcopy(spr.defs.myauth.name, sp->myauth.name, AUTHNAMELEN);
X			if (spr.defs.myauth.secret[0] != '\0')
X				bcopy(spr.defs.myauth.secret, sp->myauth.secret,
X				      AUTHKEYLEN);
X		}
X		if (spr.defs.hisauth.proto == 0)
X			/* resetting hisauth */
X			bzero(&sp->hisauth, sizeof sp->hisauth);
X		else {
X			/* setting/changing hisauth */
X			sp->hisauth.proto = spr.defs.hisauth.proto;
X			sp->hisauth.flags = spr.defs.hisauth.flags;
X			bcopy(spr.defs.hisauth.name, sp->hisauth.name, AUTHNAMELEN);
X			if (spr.defs.hisauth.secret[0] != '\0')
X				bcopy(spr.defs.hisauth.secret, sp->hisauth.secret,
X				      AUTHKEYLEN);
X		}
X		break;
X
X	default:
X		return EINVAL;
X	}
X
X	return 0;
X}
X
Xstatic void
Xsppp_phase_network(struct sppp *sp)
X{
X	STDDCL;
X	int i;
X	u_long mask;
X
X	sp->pp_phase = PHASE_NETWORK;
X
X	if (debug)
X		log(LOG_DEBUG, SPP_FMT "phase %s\n", SPP_ARGS(ifp),
X		    sppp_phase_name(sp->pp_phase));
X
X	/* Notify NCPs now. */
X	for (i = 0; i < IDX_COUNT; i++)
X		if ((cps[i])->flags & CP_NCP)
X			(cps[i])->Open(sp);
X
X	/* Send Up events to all NCPs. */
X	for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1)
X		if (sp->lcp.protos & mask && ((cps[i])->flags & CP_NCP))
X			(cps[i])->Up(sp);
X
X	/* if no NCP is starting, all this was in vain, close down */
X	sppp_lcp_check_and_close(sp);
X}
X	
X
Xstatic const char *
Xsppp_cp_type_name(u_char type)
X{
X	static char buf[12];
X	switch (type) {
X	case CONF_REQ:   return "conf-req";
X	case CONF_ACK:   return "conf-ack";
X	case CONF_NAK:   return "conf-nak";
X	case CONF_REJ:   return "conf-rej";
X	case TERM_REQ:   return "term-req";
X	case TERM_ACK:   return "term-ack";
X	case CODE_REJ:   return "code-rej";
X	case PROTO_REJ:  return "proto-rej";
X	case ECHO_REQ:   return "echo-req";
X	case ECHO_REPLY: return "echo-reply";
X	case DISC_REQ:   return "discard-req";
X	}
X	snprintf (buf, sizeof(buf), "0x%x", type);
X	return buf;
X}
X
Xstatic const char *
Xsppp_auth_type_name(u_short proto, u_char type)
X{
X	static char buf[12];
X	switch (proto) {
X	case PPP_CHAP:
X		switch (type) {
X		case CHAP_CHALLENGE:	return "challenge";
X		case CHAP_RESPONSE:	return "response";
X		case CHAP_SUCCESS:	return "success";
X		case CHAP_FAILURE:	return "failure";
X		}
X	case PPP_PAP:
X		switch (type) {
X		case PAP_REQ:		return "req";
X		case PAP_ACK:		return "ack";
X		case PAP_NAK:		return "nak";
X		}
X	}
X	snprintf (buf, sizeof(buf), "0x%x", type);
X	return buf;
X}
X
Xstatic const char *
Xsppp_lcp_opt_name(u_char opt)
X{
X	static char buf[12];
X	switch (opt) {
X	case LCP_OPT_MRU:		return "mru";
X	case LCP_OPT_ASYNC_MAP:		return "async-map";
X	case LCP_OPT_AUTH_PROTO:	return "auth-proto";
X	case LCP_OPT_QUAL_PROTO:	return "qual-proto";
X	case LCP_OPT_MAGIC:		return "magic";
X	case LCP_OPT_PROTO_COMP:	return "proto-comp";
X	case LCP_OPT_ADDR_COMP:		return "addr-comp";
X	}
X	snprintf (buf, sizeof(buf), "0x%x", opt);
X	return buf;
X}
X
Xstatic const char *
Xsppp_ipcp_opt_name(u_char opt)
X{
X	static char buf[12];
X	switch (opt) {
X	case IPCP_OPT_ADDRESSES:	return "addresses";
X	case IPCP_OPT_COMPRESSION:	return "compression";
X	case IPCP_OPT_ADDRESS:		return "address";
X	}
X	snprintf (buf, sizeof(buf), "0x%x", opt);
X	return buf;
X}
X
Xstatic const char *
Xsppp_state_name(int state)
X{
X	switch (state) {
X	case STATE_INITIAL:	return "initial";
X	case STATE_STARTING:	return "starting";
X	case STATE_CLOSED:	return "closed";
X	case STATE_STOPPED:	return "stopped";
X	case STATE_CLOSING:	return "closing";
X	case STATE_STOPPING:	return "stopping";
X	case STATE_REQ_SENT:	return "req-sent";
X	case STATE_ACK_RCVD:	return "ack-rcvd";
X	case STATE_ACK_SENT:	return "ack-sent";
X	case STATE_OPENED:	return "opened";
X	}
X	return "illegal";
X}
X
Xstatic const char *
Xsppp_phase_name(enum ppp_phase phase)
X{
X	switch (phase) {
X	case PHASE_DEAD:	return "dead";
X	case PHASE_ESTABLISH:	return "establish";
X	case PHASE_TERMINATE:	return "terminate";
X	case PHASE_AUTHENTICATE: return "authenticate";
X	case PHASE_NETWORK:	return "network";
X	}
X	return "illegal";
X}
X
Xstatic const char *
Xsppp_proto_name(u_short proto)
X{
X	static char buf[12];
X	switch (proto) {
X	case PPP_LCP:	return "lcp";
X	case PPP_IPCP:	return "ipcp";
X	case PPP_PAP:	return "pap";
X	case PPP_CHAP:	return "chap";
X	}
X	snprintf(buf, sizeof(buf), "0x%x", (unsigned)proto);
X	return buf;
X}
X
Xstatic void
Xsppp_print_bytes(const u_char *p, u_short len)
X{
X	addlog(" %02x", *p++);
X	while (--len > 0)
X		addlog("-%02x", *p++);
X}
X
Xstatic void
Xsppp_print_string(const char *p, u_short len)
X{
X	u_char c;
X
X	while (len-- > 0) {
X		c = *p++;
X		/*
X		 * Print only ASCII chars directly.  RFC 1994 recommends
X		 * using only them, but we don't rely on it.  */
X		if (c < ' ' || c > '~')
X			addlog("\\x%x", c);
X		else
X			addlog("%c", c);
X	}
X}
X
Xstatic const char *
Xsppp_dotted_quad(u_long addr)
X{
X	static char s[16];
X	sprintf(s, "%d.%d.%d.%d",
X		(int)((addr >> 24) & 0xff),
X		(int)((addr >> 16) & 0xff),
X		(int)((addr >> 8) & 0xff),
X		(int)(addr & 0xff));
X	return s;
X}
X
Xstatic int
Xsppp_strnlen(u_char *p, int max)
X{
X	int len;
X
X	for (len = 0; len < max && *p; ++p)
X		++len;
X	return len;
X}
X
X/* a dummy, used to drop uninteresting events */
Xstatic void
Xsppp_null(struct sppp *unused)
X{
X	/* do just nothing */
X}
END-of-./usr/src/sys/net/if_spppsubr.c.orig
echo c - ./usr/src/sys/i4b
mkdir -p ./usr/src/sys/i4b > /dev/null 2>&1
echo c - ./usr/src/sys/i4b/driver
mkdir -p ./usr/src/sys/i4b/driver > /dev/null 2>&1
echo x - ./usr/src/sys/i4b/driver/i4b_isppp.c
sed 's/^X//' >./usr/src/sys/i4b/driver/i4b_isppp.c << 'END-of-./usr/src/sys/i4b/driver/i4b_isppp.c'
X/*
X *   Copyright (c) 1997 Joerg Wunsch. All rights reserved.
X *
X *   Copyright (c) 1997, 1998 Hellmuth Michaelis. All rights reserved.
X *
X *   Redistribution and use in source and binary forms, with or without
X *   modification, are permitted provided that the following conditions
X *   are met:
X *
X *   1. Redistributions of source code must retain the above copyright
X *      notice, this list of conditions and the following disclaimer.
X *   2. Redistributions in binary form must reproduce the above copyright
X *      notice, this list of conditions and the following disclaimer in the
X *      documentation and/or other materials provided with the distribution.
X *   
X *   THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
X *   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X *   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X *   ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
X *   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X *   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X *   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X *   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X *   SUCH DAMAGE.
X *
X *---------------------------------------------------------------------------
X *
X *	i4b_isppp.c - isdn4bsd kernel SyncPPP driver
X *	--------------------------------------------
X *
X * 	Uses Serge Vakulenko's sppp backend (originally contributed with
X *	the "cx" driver for Cronyx's HDLC-in-hardware device).  This driver
X *	is only the glue between sppp and i4b.
X *
X *	$Id: i4b_isppp.c,v 1.1 1998/12/27 21:46:42 phk Exp $
X *
X *	last edit-date: [Fri Dec 18 11:47:58 1998]
X *
X *---------------------------------------------------------------------------*/
X
X#include "i4bisppp.h"
X#ifdef __FreeBSD__
X#include "sppp.h"
X#endif
X
X#if NI4BISPPP == 0
X# error "You need to define `pseudo-device sppp <N>' with options ISPPP"
X#endif
X
X#include <sys/param.h>
X#include <sys/systm.h>
X#include <sys/mbuf.h>
X#include <sys/socket.h>
X#include <sys/errno.h>
X#include <sys/ioccom.h>
X#include <sys/sockio.h>
X#include <sys/kernel.h>
X#include <sys/protosw.h>
X
X#include <net/if.h>
X#include <net/if_types.h>
X#include <net/netisr.h>
X#include <net/route.h>
X#ifdef __FreeBSD__
X#include <net/if_sppp.h>
X#else
X#include <i4b/sppp/if_sppp.h>
X#endif
X
X#include <netinet/in.h>
X#include <netinet/in_systm.h>
X#include <netinet/in_var.h>
X#include <netinet/ip.h>
X
X#include "bpfilter.h"
X#if NBPFILTER > 0
X#include <sys/time.h>
X#include <net/bpf.h>
X#endif
X
X#ifdef __FreeBSD__
X#include <machine/i4b_ioctl.h>
X#include <machine/i4b_cause.h>
X#else
X#include <i4b/i4b_ioctl.h>
X#include <i4b/i4b_cause.h>
X#endif
X
X#include <i4b/include/i4b_global.h>
X#include <i4b/include/i4b_mbuf.h>
X#include <i4b/include/i4b_l3l4.h>
X
X#include <i4b/layer4/i4b_l4.h>
X
X#ifdef __FreeBSD__
X#define ISPPP_FMT	"isp%d: "
X#define	ISPPP_ARG(sc)	((sc)->sc_if.if_unit)
X#define	PDEVSTATIC	static
X#define IFP2UNIT(ifp)	(ifp)->if_unit
X		
X# if __FreeBSD_version >= 300001
X#  define CALLOUT_INIT(chan)		callout_handle_init(chan)
X#  define TIMEOUT(fun, arg, chan, tick)	chan = timeout(fun, arg, tick)
X#  define UNTIMEOUT(fun, arg, chan)	untimeout(fun, arg, chan)
X#  define IOCTL_CMD_T u_long
X# else
X#  define CALLOUT_INIT(chan)		do {} while(0)
X#  define TIMEOUT(fun, arg, chan, tick)	timeout(fun, arg, tick)
X#  define UNTIMEOUT(fun, arg, chan)	untimeout(fun, arg)
X#  define IOCTL_CMD_T int
X# endif
X
X#elif defined __NetBSD__ || defined __OpenBSD__
X#define	ISPPP_FMT	"%s: "
X#define	ISPPP_ARG(sc)	((sc)->sc_if.if_xname)
X#define	PDEVSTATIC	/* not static */
X#define IOCTL_CMD_T	u_long
X#define IFP2UNIT(ifp)	((struct i4bisppp_softc *)ifp->if_softc)->sc_unit
X#else
X# error "What system are you using?"
X#endif
X
X#ifdef __FreeBSD__
XPDEVSTATIC void i4bispppattach(void *);
XPSEUDO_SET(i4bispppattach, i4b_isppp);
X#else
XPDEVSTATIC void i4bispppattach __P((void));
X#endif
X
X#define I4BISPPPACCT		1	/* enable accounting messages */
X#define	I4BISPPPACCTINTVL	2	/* accounting msg interval in secs */
X#define I4BISPPPDISCDEBUG	1	
X
X#define PPP_HDRLEN   		4	/* 4 octetts PPP header length	*/
X
Xstruct i4bisppp_softc {
X	/*
X	 * struct sppp starts with a struct ifnet, but we gotta allocate
X	 * more space for it.  NB: do not relocate this union, it must
X	 * be first in isppp_softc.  The tls and tlf hooks below want to
X	 * convert a ``struct sppp *'' into a ``struct isppp_softc *''.
X	 */
X	union {
X		struct ifnet scu_if;
X		struct sppp scu_sp;
X	} sc_if_un;
X#define sc_if sc_if_un.scu_if
X
X	int	sc_state;	/* state of the interface	*/
X
X#ifndef __FreeBSD__
X	int	sc_unit;	/* unit number for Net/OpenBSD	*/
X#endif
X
X	call_desc_t *sc_cdp;	/* ptr to call descriptor	*/
X
X#ifdef I4BISPPPACCT
X	int sc_iinb;		/* isdn driver # of inbytes	*/
X	int sc_ioutb;		/* isdn driver # of outbytes	*/
X	int sc_inb;		/* # of bytes rx'd		*/
X	int sc_outb;		/* # of bytes tx'd	 	*/
X	int sc_linb;		/* last # of bytes rx'd		*/
X	int sc_loutb;		/* last # of bytes tx'd 	*/
X	int sc_fn;		/* flag, first null acct	*/
X#endif
X
X#if defined(__FreeBSD_version) && __FreeBSD_version >= 300001
X	struct callout_handle sc_ch;
X#endif
X
X} i4bisppp_softc[NI4BISPPP];
X
Xstatic void	i4bisppp_init_linktab(int unit);
Xstatic int	i4bisppp_ioctl(struct ifnet *ifp, IOCTL_CMD_T cmd, caddr_t data);
X
X#if 0
Xstatic void	i4bisppp_send(struct ifnet *ifp);
X#endif
X
Xstatic void	i4bisppp_start(struct ifnet *ifp);
X
X#if 0 /* never used ??? */
Xstatic void	i4bisppp_timeout(void *cookie);
X#endif
X
Xstatic void	i4bisppp_tls(struct sppp *sp);
Xstatic void	i4bisppp_tlf(struct sppp *sp);
Xstatic void	i4bisppp_state_changed(struct sppp *sp, int new_state);
Xstatic void	i4bisppp_negotiation_complete(struct sppp *sp);
Xstatic void	i4bisppp_watchdog(struct ifnet *ifp);
Xtime_t   	i4bisppp_idletime(int unit);
X
X/* initialized by L4 */
X
Xstatic drvr_link_t i4bisppp_drvr_linktab[NI4BISPPP];
Xstatic isdn_link_t *isdn_linktab[NI4BISPPP];
X
Xenum i4bisppp_states {
X	ST_IDLE,			/* initialized, ready, idle	*/
X	ST_DIALING,			/* dialling out to remote	*/
X	ST_CONNECTED,			/* connected to remote		*/
X};
X
X/*===========================================================================*
X *			DEVICE DRIVER ROUTINES
X *===========================================================================*/
X
X/*---------------------------------------------------------------------------*
X *	interface attach routine at kernel boot time
X *---------------------------------------------------------------------------*/
XPDEVSTATIC void
X#ifdef __FreeBSD__
Xi4bispppattach(void *dummy)
X#else
Xi4bispppattach(void)
X#endif
X{
X	struct i4bisppp_softc *sc = i4bisppp_softc;
X	int i;
X
X#ifndef HACK_NO_PSEUDO_ATTACH_MSG
X	printf("i4bisppp: %d ISDN SyncPPP device(s) attached\n",
X	       NI4BISPPP);
X#endif
X
X	for(i = 0; i < NI4BISPPP; sc++, i++) {
X		i4bisppp_init_linktab(i);
X		
X		sc->sc_if.if_softc = sc;
X
X#ifdef __FreeBSD__		
X		sc->sc_if.if_name = "isp";
X#if defined(__FreeBSD_version) && __FreeBSD_version < 300001
X		sc->sc_if.if_next = NULL;
X#endif
X		sc->sc_if.if_unit = i;
X#else
X		sprintf(sc->sc_if.if_xname, "isp%d", i);
X		sc->sc_unit = i;
X#endif
X
X		sc->sc_if.if_mtu = PP_MTU;
X		sc->sc_if.if_flags = IFF_SIMPLEX | IFF_POINTOPOINT;
X		sc->sc_if.if_type = IFT_ISDNBASIC;
X		sc->sc_state = ST_IDLE;
X
X		sc->sc_if.if_ioctl = i4bisppp_ioctl;
X
X		/* actually initialized by sppp_attach() */
X		/* sc->sc_if.if_output = sppp_output; */
X
X		sc->sc_if.if_start = i4bisppp_start;
X
X		sc->sc_if.if_hdrlen = 0;
X		sc->sc_if.if_addrlen = 0;
X		sc->sc_if.if_snd.ifq_maxlen = IFQ_MAXLEN;
X
X		sc->sc_if.if_ipackets = 0;
X		sc->sc_if.if_ierrors = 0;
X		sc->sc_if.if_opackets = 0;
X		sc->sc_if.if_oerrors = 0;
X		sc->sc_if.if_collisions = 0;
X		sc->sc_if.if_ibytes = 0;
X		sc->sc_if.if_obytes = 0;
X		sc->sc_if.if_imcasts = 0;
X		sc->sc_if.if_omcasts = 0;
X		sc->sc_if.if_iqdrops = 0;
X		sc->sc_if.if_noproto = 0;
X
X#if I4BISPPPACCT
X		sc->sc_if.if_timer = 0;	
X		sc->sc_if.if_watchdog = i4bisppp_watchdog;	
X		sc->sc_iinb = 0;
X		sc->sc_ioutb = 0;
X		sc->sc_inb = 0;
X		sc->sc_outb = 0;
X		sc->sc_linb = 0;
X		sc->sc_loutb = 0;
X		sc->sc_fn = 1;
X#endif
X
X		sc->sc_if_un.scu_sp.pp_tls = i4bisppp_tls;
X		sc->sc_if_un.scu_sp.pp_tlf = i4bisppp_tlf;
X		sc->sc_if_un.scu_sp.pp_con = i4bisppp_negotiation_complete;
X		sc->sc_if_un.scu_sp.pp_chg = i4bisppp_state_changed;
X
X		sppp_attach(&sc->sc_if);
X		if_attach(&sc->sc_if);
X
X#if NBPFILTER > 0
X#ifdef __FreeBSD__
X		bpfattach(&sc->sc_if, DLT_PPP, PPP_HDRLEN);
X		CALLOUT_INIT(&sc->sc_ch);
X#endif /* __FreeBSD__ */
X#ifdef __NetBSD__
X		bpfattach(&sc->sc_if.if_bpf, &sc->sc_if, DLT_PPP, sizeof(u_int));
X#endif
X#endif		
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	process ioctl
X *---------------------------------------------------------------------------*/
Xstatic int
Xi4bisppp_ioctl(struct ifnet *ifp, IOCTL_CMD_T cmd, caddr_t data)
X{
X	struct i4bisppp_softc *sc = ifp->if_softc;
X#if 0
X	struct sppp *sp = (struct sppp *)sc;
X	struct ifaddr *ifa = (struct ifaddr *) data;
X	struct ifreq *ifr = (struct ifreq *) data;
X#endif
X
X	int error;
X
X	error = sppp_ioctl(&sc->sc_if, cmd, data);
X	if (error)
X		return error;
X
X	switch(cmd) {
X	case SIOCSIFFLAGS:
X#if 0 /* never used ??? */
X		x = splimp();
X		if ((ifp->if_flags & IFF_UP) == 0)
X			UNTIMEOUT(i4bisppp_timeout, (void *)sp, sc->sc_ch);
X		splx(x);
X#endif
X		break;
X	}
X
X	return 0;
X}
X
X/*---------------------------------------------------------------------------*
X *	start output to ISDN B-channel
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4bisppp_start(struct ifnet *ifp)
X{
X	struct i4bisppp_softc *sc = ifp->if_softc;
X	struct mbuf *m;
X	int s;
X	int unit = IFP2UNIT(ifp);
X
X	if (sppp_isempty(ifp))
X		return;
X
X	if(sc->sc_state != ST_CONNECTED)
X		return;
X
X	s = splimp();
X	/*ifp->if_flags |= IFF_OACTIVE; - need to clear this somewhere */
X	splx(s);
X
X	while ((m = sppp_dequeue(&sc->sc_if)) != NULL)
X	{
X
X#if NBPFILTER > 0
X#ifdef __FreeBSD__
X		if (ifp->if_bpf)
X			bpf_mtap(ifp, m);
X#endif /* __FreeBSD__ */
X
X#ifdef __NetBSD__
X		if (ifp->if_bpf)
X			bpf_mtap(ifp->if_bpf, m);
X#endif
X#endif /* NBPFILTER > 0 */
X
X		microtime(&ifp->if_lastchange);
X
X		IF_ENQUEUE(isdn_linktab[unit]->tx_queue, m);
X
X		sc->sc_if.if_obytes += m->m_pkthdr.len;
X		sc->sc_outb += m->m_pkthdr.len;
X		sc->sc_if.if_opackets++;
X	}
X	isdn_linktab[unit]->bch_tx_start(isdn_linktab[unit]->unit,
X					 isdn_linktab[unit]->channel);
X}
X
X#ifdef I4BISPPPACCT
X/*---------------------------------------------------------------------------*
X *	watchdog routine
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4bisppp_watchdog(struct ifnet *ifp)
X{
X	struct i4bisppp_softc *sc = ifp->if_softc;
X	int unit = IFP2UNIT(ifp);
X	bchan_statistics_t bs;
X	
X	(*isdn_linktab[unit]->bch_stat)
X		(isdn_linktab[unit]->unit, isdn_linktab[unit]->channel, &bs);
X
X	sc->sc_ioutb += bs.outbytes;
X	sc->sc_iinb += bs.inbytes;
X	
X	if((sc->sc_iinb != sc->sc_linb) || (sc->sc_ioutb != sc->sc_loutb) || sc->sc_fn)
X	{
X		int ri = (sc->sc_iinb - sc->sc_linb)/I4BISPPPACCTINTVL;
X		int ro = (sc->sc_ioutb - sc->sc_loutb)/I4BISPPPACCTINTVL;
X
X		if((sc->sc_iinb == sc->sc_linb) && (sc->sc_ioutb == sc->sc_loutb))
X			sc->sc_fn = 0;
X		else
X			sc->sc_fn = 1;
X			
X		sc->sc_linb = sc->sc_iinb;
X		sc->sc_loutb = sc->sc_ioutb;
X
X		i4b_l4_accounting(BDRV_ISPPP, unit, ACCT_DURING,
X			 sc->sc_ioutb, sc->sc_iinb, ro, ri, sc->sc_outb, sc->sc_inb);
X 	}
X	sc->sc_if.if_timer = I4BISPPPACCTINTVL; 	
X
X#if 0 /* old stuff, keep it around */
X	printf(ISPPP_FMT "transmit timeout\n", ISPPP_ARG(sc));
X	i4bisppp_start(ifp);
X#endif
X}
X#endif /* I4BISPPPACCT */
X
X/*
X *===========================================================================*
X *			SyncPPP layer interface routines
X *===========================================================================*
X */
X
X#if 0 /* never used ??? */
X/*---------------------------------------------------------------------------*
X *	just an alias for i4bisppp_tls, but of type timeout_t
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4bisppp_timeout(void *cookie)
X{
X	i4bisppp_tls((struct sppp *)cookie);
X}
X#endif
X
X/*---------------------------------------------------------------------------*
X *	PPP this-layer-started action
X *---------------------------------------------------------------------------*
X */
Xstatic void
Xi4bisppp_tls(struct sppp *sp)
X{
X	struct i4bisppp_softc *sc = (struct i4bisppp_softc *)sp;
X	struct ifnet *ifp = (struct ifnet *)sp;
X
X	if(sc->sc_state == ST_CONNECTED)
X		return;
X
X	i4b_l4_dialout(BDRV_ISPPP, IFP2UNIT(ifp));
X}
X
X/*---------------------------------------------------------------------------*
X *	PPP this-layer-finished action
X *---------------------------------------------------------------------------*
X */
Xstatic void
Xi4bisppp_tlf(struct sppp *sp)
X{
X	struct i4bisppp_softc *sc = (struct i4bisppp_softc *)sp;
X/*	call_desc_t *cd = sc->sc_cdp;	*/
X        struct ifnet *ifp = (struct ifnet *)sp;	
X	
X	if(sc->sc_state != ST_CONNECTED)
X		return;
X
X#if 0 /* never used ??? */
X	UNTIMEOUT(i4bisppp_timeout, (void *)sp, sc->sc_ch);
X#endif
X
X	i4b_l4_drvrdisc(BDRV_ISPPP, IFP2UNIT(ifp));
X}
X/*---------------------------------------------------------------------------*
X *	PPP interface phase change
X *---------------------------------------------------------------------------*
X */
Xstatic void
Xi4bisppp_state_changed(struct sppp *sp, int new_state)
X{
X	struct i4bisppp_softc *sc = (struct i4bisppp_softc *)sp;
X	
X	i4b_l4_ifstate_changed(sc->sc_cdp, new_state);
X}
X
X/*---------------------------------------------------------------------------*
X *	PPP control protocol negotiation complete (run ip-up script now)
X *---------------------------------------------------------------------------*
X */
Xstatic void
Xi4bisppp_negotiation_complete(struct sppp *sp)
X{
X	struct i4bisppp_softc *sc = (struct i4bisppp_softc *)sp;
X	
X	i4b_l4_negcomplete(sc->sc_cdp);
X}
X
X/*===========================================================================*
X *			ISDN INTERFACE ROUTINES
X *===========================================================================*/
X
X/*---------------------------------------------------------------------------*
X *	this routine is called from L4 handler at connect time
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4bisppp_connect(int unit, void *cdp)
X{
X	struct i4bisppp_softc *sc = &i4bisppp_softc[unit];
X	struct sppp *sp = &sc->sc_if_un.scu_sp;
X	int s = splimp();
X
X	sc->sc_cdp = (call_desc_t *)cdp;
X	sc->sc_state = ST_CONNECTED;
X
X#if I4BISPPPACCT
X	sc->sc_iinb = 0;
X	sc->sc_ioutb = 0;
X	sc->sc_inb = 0;
X	sc->sc_outb = 0;
X	sc->sc_linb = 0;
X	sc->sc_loutb = 0;
X	sc->sc_if.if_timer = I4BISPPPACCTINTVL;
X#endif
X	
X#if 0 /* never used ??? */
X	UNTIMEOUT(i4bisppp_timeout, (void *)sp, sc->sc_ch);
X#endif
X
X	sp->pp_up(sp);		/* tell PPP we are ready */
X	sp->pp_last_sent = sp->pp_last_recv = SECOND;
X	splx(s);
X}
X
X/*---------------------------------------------------------------------------*
X *	this routine is called from L4 handler at disconnect time
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4bisppp_disconnect(int unit, void *cdp)
X{
X	call_desc_t *cd = (call_desc_t *)cdp;
X	struct i4bisppp_softc *sc = &i4bisppp_softc[unit];
X	struct sppp *sp = &sc->sc_if_un.scu_sp;
X
X	int s = splimp();
X
X	/* new stuff to check that the active channel is being closed */
X	if (cd != sc->sc_cdp)
X	{
X#ifdef I4BISPPPDISCDEBUG		
X		printf("i4bisppp_disconnect: isppp%d channel%d not active\n",
X			cd->driver_unit, cd->channelid);
X#endif
X		splx(s);
X		return;
X	}
X
X#if I4BISPPPACCT
X	sc->sc_if.if_timer = 0;
X#endif
X
X	i4b_l4_accounting(BDRV_ISPPP, unit, ACCT_FINAL,
X		 sc->sc_ioutb, sc->sc_iinb, 0, 0, sc->sc_outb, sc->sc_inb);
X	
X	if (sc->sc_state == ST_CONNECTED)
X	{
X#if 0 /* never used ??? */
X		UNTIMEOUT(i4bisppp_timeout, (void *)sp, sc->sc_ch);
X#endif
X		sc->sc_cdp = (call_desc_t *)0;	
X		/* do thhis here because pp_down calls i4bisppp_tlf */
X		sc->sc_state = ST_IDLE;
X		sp->pp_down(sp);	/* tell PPP we have hung up */
X	}
X
X	splx(s);
X}
X
X/*---------------------------------------------------------------------------*
X *	this routine is used to give a feedback from userland demon
X *	in case of dial problems
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4bisppp_dialresponse(int unit, int status)
X{
X/*	struct i4bisppp_softc *sc = &i4bisppp_softc[unit];	*/
X}
X	
X/*---------------------------------------------------------------------------*
X *	interface up/down
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4bisppp_updown(int unit, int updown)
X{
X	/* could probably do something useful here */
X}
X	
X/*---------------------------------------------------------------------------*
X *	this routine is called from the HSCX interrupt handler
X *	when a new frame (mbuf) has been received and was put on
X *	the rx queue.
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4bisppp_rx_data_rdy(int unit)
X{
X	struct i4bisppp_softc *sc = &i4bisppp_softc[unit];
X	struct mbuf *m;
X	int s;
X	
X	if((m = *isdn_linktab[unit]->rx_mbuf) == NULL)
X		return;
X
X	m->m_pkthdr.rcvif = &sc->sc_if;
X	m->m_pkthdr.len = m->m_len;
X
X	microtime(&sc->sc_if.if_lastchange);
X
X	sc->sc_if.if_ipackets++;
X	sc->sc_if.if_ibytes += m->m_pkthdr.len;
X
X#if I4BISPPPACCT
X	sc->sc_inb += m->m_pkthdr.len;
X#endif
X	
X#ifdef I4BISPPPDEBUG
X	printf("i4bisppp_rx_data_ready: received packet!\n");
X#endif
X
X#if NBPFILTER > 0
X
X#ifdef __FreeBSD__	
X	if(sc->sc_if.if_bpf)
X		bpf_mtap(&sc->sc_if, m);
X#endif /* __FreeBSD__ */
X
X#ifdef __NetBSD__
X	if(sc->sc_if.if_bpf)
X		bpf_mtap(sc->sc_if.if_bpf, m);
X#endif
X
X#endif /* NBPFILTER > 0 */
X
X	s = splimp();
X
X	sppp_input(&sc->sc_if, m);
X
X	splx(s);
X}
X
X/*---------------------------------------------------------------------------*
X *	this routine is called from the HSCX interrupt handler
X *	when the last frame has been sent out and there is no
X *	further frame (mbuf) in the tx queue.
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4bisppp_tx_queue_empty(int unit)
X{
X	i4bisppp_start(&i4bisppp_softc[unit].sc_if);	
X}
X/*---------------------------------------------------------------------------*
X *	THIS should be used instead of last_active_time to implement
X *	an activity timeout mechanism.
X *---------------------------------------------------------------------------*/
Xtime_t
Xi4bisppp_idletime(int unit)
X{
X	struct sppp *sp;
X	time_t now_time, sent_time, recv_time;
X	sp = (struct sppp *) &i4bisppp_softc[unit];
X
X	now_time = SECOND;
X	sent_time = now_time - sp->pp_last_sent;
X	recv_time = now_time - sp->pp_last_recv;
X	return (sent_time<recv_time)? sent_time : recv_time;
X}
X/*---------------------------------------------------------------------------*
X *	this routine is called from the HSCX interrupt handler
X *	each time a packet is received or transmitted. It should
X *	be used to implement an activity timeout mechanism.
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4bisppp_activity(int unit, int rxtx)
X{
X	i4bisppp_softc[unit].sc_cdp->last_active_time = SECOND;
X}
X
X/*---------------------------------------------------------------------------*
X *	return this drivers linktab address
X *---------------------------------------------------------------------------*/
Xdrvr_link_t *
Xi4bisppp_ret_linktab(int unit)
X{
X	return(&i4bisppp_drvr_linktab[unit]);
X}
X
X/*---------------------------------------------------------------------------*
X *	setup the isdn_linktab for this driver
X *---------------------------------------------------------------------------*/
Xvoid
Xi4bisppp_set_linktab(int unit, isdn_link_t *ilt)
X{
X	isdn_linktab[unit] = ilt;
X}
X
X/*---------------------------------------------------------------------------*
X *	initialize this drivers linktab
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4bisppp_init_linktab(int unit)
X{
X	i4bisppp_drvr_linktab[unit].unit = unit;
X	i4bisppp_drvr_linktab[unit].bch_rx_data_ready = i4bisppp_rx_data_rdy;
X	i4bisppp_drvr_linktab[unit].bch_tx_queue_empty = i4bisppp_tx_queue_empty;
X	i4bisppp_drvr_linktab[unit].bch_activity = i4bisppp_activity;
X	i4bisppp_drvr_linktab[unit].line_connected = i4bisppp_connect;
X	i4bisppp_drvr_linktab[unit].line_disconnected = i4bisppp_disconnect;
X	i4bisppp_drvr_linktab[unit].dial_response = i4bisppp_dialresponse;	
X	i4bisppp_drvr_linktab[unit].updown_ind = i4bisppp_updown;	
X}
X
X/*===========================================================================*/
END-of-./usr/src/sys/i4b/driver/i4b_isppp.c
echo x - ./usr/src/sys/i4b/driver/i4b_isppp.c.patch
sed 's/^X//' >./usr/src/sys/i4b/driver/i4b_isppp.c.patch << 'END-of-./usr/src/sys/i4b/driver/i4b_isppp.c.patch'
X*** i4b_isppp.c	Fri Dec 18 15:20:44 1998
X--- i4b_isppp.c.new	Wed Mar 17 13:16:02 1999
X***************
X*** 190,195 ****
X--- 190,196 ----
X  static void	i4bisppp_state_changed(struct sppp *sp, int new_state);
X  static void	i4bisppp_negotiation_complete(struct sppp *sp);
X  static void	i4bisppp_watchdog(struct ifnet *ifp);
X+ time_t   	i4bisppp_idletime(int unit);
X  
X  /* initialized by L4 */
X  
X***************
X*** 532,538 ****
X  #endif
X  
X  	sp->pp_up(sp);		/* tell PPP we are ready */
X! 
X  	splx(s);
X  }
X  
X--- 533,539 ----
X  #endif
X  
X  	sp->pp_up(sp);		/* tell PPP we are ready */
X! 	sp->pp_last_sent = sp->pp_last_recv = SECOND;
X  	splx(s);
X  }
X  
X***************
X*** 661,667 ****
X  {
X  	i4bisppp_start(&i4bisppp_softc[unit].sc_if);	
X  }
X! 
X  /*---------------------------------------------------------------------------*
X   *	this routine is called from the HSCX interrupt handler
X   *	each time a packet is received or transmitted. It should
X--- 662,683 ----
X  {
X  	i4bisppp_start(&i4bisppp_softc[unit].sc_if);	
X  }
X! /*---------------------------------------------------------------------------*
X!  *	THIS should be used instead of last_active_time to implement
X!  *	an activity timeout mechanism.
X!  *---------------------------------------------------------------------------*/
X! time_t
X! i4bisppp_idletime(int unit)
X! {
X! 	struct sppp *sp;
X! 	time_t now_time, sent_time, recv_time;
X! 	sp = (struct sppp *) &i4bisppp_softc[unit];
X! 
X! 	now_time = SECOND;
X! 	sent_time = now_time - sp->pp_last_sent;
X! 	recv_time = now_time - sp->pp_last_recv;
X! 	return (sent_time<recv_time)? sent_time : recv_time;
X! }
X  /*---------------------------------------------------------------------------*
X   *	this routine is called from the HSCX interrupt handler
X   *	each time a packet is received or transmitted. It should
END-of-./usr/src/sys/i4b/driver/i4b_isppp.c.patch
echo x - ./usr/src/sys/i4b/driver/i4b_isppp.c.orig
sed 's/^X//' >./usr/src/sys/i4b/driver/i4b_isppp.c.orig << 'END-of-./usr/src/sys/i4b/driver/i4b_isppp.c.orig'
X/*
X *   Copyright (c) 1997 Joerg Wunsch. All rights reserved.
X *
X *   Copyright (c) 1997, 1998 Hellmuth Michaelis. All rights reserved.
X *
X *   Redistribution and use in source and binary forms, with or without
X *   modification, are permitted provided that the following conditions
X *   are met:
X *
X *   1. Redistributions of source code must retain the above copyright
X *      notice, this list of conditions and the following disclaimer.
X *   2. Redistributions in binary form must reproduce the above copyright
X *      notice, this list of conditions and the following disclaimer in the
X *      documentation and/or other materials provided with the distribution.
X *   
X *   THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
X *   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X *   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X *   ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
X *   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X *   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X *   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X *   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X *   SUCH DAMAGE.
X *
X *---------------------------------------------------------------------------
X *
X *	i4b_isppp.c - isdn4bsd kernel SyncPPP driver
X *	--------------------------------------------
X *
X * 	Uses Serge Vakulenko's sppp backend (originally contributed with
X *	the "cx" driver for Cronyx's HDLC-in-hardware device).  This driver
X *	is only the glue between sppp and i4b.
X *
X *	$Id: i4b_isppp.c,v 1.1 1998/12/27 21:46:42 phk Exp $
X *
X *	last edit-date: [Fri Dec 18 11:47:58 1998]
X *
X *---------------------------------------------------------------------------*/
X
X#include "i4bisppp.h"
X#ifdef __FreeBSD__
X#include "sppp.h"
X#endif
X
X#if NI4BISPPP == 0
X# error "You need to define `pseudo-device sppp <N>' with options ISPPP"
X#endif
X
X#include <sys/param.h>
X#include <sys/systm.h>
X#include <sys/mbuf.h>
X#include <sys/socket.h>
X#include <sys/errno.h>
X#include <sys/ioccom.h>
X#include <sys/sockio.h>
X#include <sys/kernel.h>
X#include <sys/protosw.h>
X
X#include <net/if.h>
X#include <net/if_types.h>
X#include <net/netisr.h>
X#include <net/route.h>
X#ifdef __FreeBSD__
X#include <net/if_sppp.h>
X#else
X#include <i4b/sppp/if_sppp.h>
X#endif
X
X#include <netinet/in.h>
X#include <netinet/in_systm.h>
X#include <netinet/in_var.h>
X#include <netinet/ip.h>
X
X#include "bpfilter.h"
X#if NBPFILTER > 0
X#include <sys/time.h>
X#include <net/bpf.h>
X#endif
X
X#ifdef __FreeBSD__
X#include <machine/i4b_ioctl.h>
X#include <machine/i4b_cause.h>
X#else
X#include <i4b/i4b_ioctl.h>
X#include <i4b/i4b_cause.h>
X#endif
X
X#include <i4b/include/i4b_global.h>
X#include <i4b/include/i4b_mbuf.h>
X#include <i4b/include/i4b_l3l4.h>
X
X#include <i4b/layer4/i4b_l4.h>
X
X#ifdef __FreeBSD__
X#define ISPPP_FMT	"isp%d: "
X#define	ISPPP_ARG(sc)	((sc)->sc_if.if_unit)
X#define	PDEVSTATIC	static
X#define IFP2UNIT(ifp)	(ifp)->if_unit
X		
X# if __FreeBSD_version >= 300001
X#  define CALLOUT_INIT(chan)		callout_handle_init(chan)
X#  define TIMEOUT(fun, arg, chan, tick)	chan = timeout(fun, arg, tick)
X#  define UNTIMEOUT(fun, arg, chan)	untimeout(fun, arg, chan)
X#  define IOCTL_CMD_T u_long
X# else
X#  define CALLOUT_INIT(chan)		do {} while(0)
X#  define TIMEOUT(fun, arg, chan, tick)	timeout(fun, arg, tick)
X#  define UNTIMEOUT(fun, arg, chan)	untimeout(fun, arg)
X#  define IOCTL_CMD_T int
X# endif
X
X#elif defined __NetBSD__ || defined __OpenBSD__
X#define	ISPPP_FMT	"%s: "
X#define	ISPPP_ARG(sc)	((sc)->sc_if.if_xname)
X#define	PDEVSTATIC	/* not static */
X#define IOCTL_CMD_T	u_long
X#define IFP2UNIT(ifp)	((struct i4bisppp_softc *)ifp->if_softc)->sc_unit
X#else
X# error "What system are you using?"
X#endif
X
X#ifdef __FreeBSD__
XPDEVSTATIC void i4bispppattach(void *);
XPSEUDO_SET(i4bispppattach, i4b_isppp);
X#else
XPDEVSTATIC void i4bispppattach __P((void));
X#endif
X
X#define I4BISPPPACCT		1	/* enable accounting messages */
X#define	I4BISPPPACCTINTVL	2	/* accounting msg interval in secs */
X#define I4BISPPPDISCDEBUG	1	
X
X#define PPP_HDRLEN   		4	/* 4 octetts PPP header length	*/
X
Xstruct i4bisppp_softc {
X	/*
X	 * struct sppp starts with a struct ifnet, but we gotta allocate
X	 * more space for it.  NB: do not relocate this union, it must
X	 * be first in isppp_softc.  The tls and tlf hooks below want to
X	 * convert a ``struct sppp *'' into a ``struct isppp_softc *''.
X	 */
X	union {
X		struct ifnet scu_if;
X		struct sppp scu_sp;
X	} sc_if_un;
X#define sc_if sc_if_un.scu_if
X
X	int	sc_state;	/* state of the interface	*/
X
X#ifndef __FreeBSD__
X	int	sc_unit;	/* unit number for Net/OpenBSD	*/
X#endif
X
X	call_desc_t *sc_cdp;	/* ptr to call descriptor	*/
X
X#ifdef I4BISPPPACCT
X	int sc_iinb;		/* isdn driver # of inbytes	*/
X	int sc_ioutb;		/* isdn driver # of outbytes	*/
X	int sc_inb;		/* # of bytes rx'd		*/
X	int sc_outb;		/* # of bytes tx'd	 	*/
X	int sc_linb;		/* last # of bytes rx'd		*/
X	int sc_loutb;		/* last # of bytes tx'd 	*/
X	int sc_fn;		/* flag, first null acct	*/
X#endif
X
X#if defined(__FreeBSD_version) && __FreeBSD_version >= 300001
X	struct callout_handle sc_ch;
X#endif
X
X} i4bisppp_softc[NI4BISPPP];
X
Xstatic void	i4bisppp_init_linktab(int unit);
Xstatic int	i4bisppp_ioctl(struct ifnet *ifp, IOCTL_CMD_T cmd, caddr_t data);
X
X#if 0
Xstatic void	i4bisppp_send(struct ifnet *ifp);
X#endif
X
Xstatic void	i4bisppp_start(struct ifnet *ifp);
X
X#if 0 /* never used ??? */
Xstatic void	i4bisppp_timeout(void *cookie);
X#endif
X
Xstatic void	i4bisppp_tls(struct sppp *sp);
Xstatic void	i4bisppp_tlf(struct sppp *sp);
Xstatic void	i4bisppp_state_changed(struct sppp *sp, int new_state);
Xstatic void	i4bisppp_negotiation_complete(struct sppp *sp);
Xstatic void	i4bisppp_watchdog(struct ifnet *ifp);
X
X/* initialized by L4 */
X
Xstatic drvr_link_t i4bisppp_drvr_linktab[NI4BISPPP];
Xstatic isdn_link_t *isdn_linktab[NI4BISPPP];
X
Xenum i4bisppp_states {
X	ST_IDLE,			/* initialized, ready, idle	*/
X	ST_DIALING,			/* dialling out to remote	*/
X	ST_CONNECTED,			/* connected to remote		*/
X};
X
X/*===========================================================================*
X *			DEVICE DRIVER ROUTINES
X *===========================================================================*/
X
X/*---------------------------------------------------------------------------*
X *	interface attach routine at kernel boot time
X *---------------------------------------------------------------------------*/
XPDEVSTATIC void
X#ifdef __FreeBSD__
Xi4bispppattach(void *dummy)
X#else
Xi4bispppattach(void)
X#endif
X{
X	struct i4bisppp_softc *sc = i4bisppp_softc;
X	int i;
X
X#ifndef HACK_NO_PSEUDO_ATTACH_MSG
X	printf("i4bisppp: %d ISDN SyncPPP device(s) attached\n",
X	       NI4BISPPP);
X#endif
X
X	for(i = 0; i < NI4BISPPP; sc++, i++) {
X		i4bisppp_init_linktab(i);
X		
X		sc->sc_if.if_softc = sc;
X
X#ifdef __FreeBSD__		
X		sc->sc_if.if_name = "isp";
X#if defined(__FreeBSD_version) && __FreeBSD_version < 300001
X		sc->sc_if.if_next = NULL;
X#endif
X		sc->sc_if.if_unit = i;
X#else
X		sprintf(sc->sc_if.if_xname, "isp%d", i);
X		sc->sc_unit = i;
X#endif
X
X		sc->sc_if.if_mtu = PP_MTU;
X		sc->sc_if.if_flags = IFF_SIMPLEX | IFF_POINTOPOINT;
X		sc->sc_if.if_type = IFT_ISDNBASIC;
X		sc->sc_state = ST_IDLE;
X
X		sc->sc_if.if_ioctl = i4bisppp_ioctl;
X
X		/* actually initialized by sppp_attach() */
X		/* sc->sc_if.if_output = sppp_output; */
X
X		sc->sc_if.if_start = i4bisppp_start;
X
X		sc->sc_if.if_hdrlen = 0;
X		sc->sc_if.if_addrlen = 0;
X		sc->sc_if.if_snd.ifq_maxlen = IFQ_MAXLEN;
X
X		sc->sc_if.if_ipackets = 0;
X		sc->sc_if.if_ierrors = 0;
X		sc->sc_if.if_opackets = 0;
X		sc->sc_if.if_oerrors = 0;
X		sc->sc_if.if_collisions = 0;
X		sc->sc_if.if_ibytes = 0;
X		sc->sc_if.if_obytes = 0;
X		sc->sc_if.if_imcasts = 0;
X		sc->sc_if.if_omcasts = 0;
X		sc->sc_if.if_iqdrops = 0;
X		sc->sc_if.if_noproto = 0;
X
X#if I4BISPPPACCT
X		sc->sc_if.if_timer = 0;	
X		sc->sc_if.if_watchdog = i4bisppp_watchdog;	
X		sc->sc_iinb = 0;
X		sc->sc_ioutb = 0;
X		sc->sc_inb = 0;
X		sc->sc_outb = 0;
X		sc->sc_linb = 0;
X		sc->sc_loutb = 0;
X		sc->sc_fn = 1;
X#endif
X
X		sc->sc_if_un.scu_sp.pp_tls = i4bisppp_tls;
X		sc->sc_if_un.scu_sp.pp_tlf = i4bisppp_tlf;
X		sc->sc_if_un.scu_sp.pp_con = i4bisppp_negotiation_complete;
X		sc->sc_if_un.scu_sp.pp_chg = i4bisppp_state_changed;
X
X		sppp_attach(&sc->sc_if);
X		if_attach(&sc->sc_if);
X
X#if NBPFILTER > 0
X#ifdef __FreeBSD__
X		bpfattach(&sc->sc_if, DLT_PPP, PPP_HDRLEN);
X		CALLOUT_INIT(&sc->sc_ch);
X#endif /* __FreeBSD__ */
X#ifdef __NetBSD__
X		bpfattach(&sc->sc_if.if_bpf, &sc->sc_if, DLT_PPP, sizeof(u_int));
X#endif
X#endif		
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	process ioctl
X *---------------------------------------------------------------------------*/
Xstatic int
Xi4bisppp_ioctl(struct ifnet *ifp, IOCTL_CMD_T cmd, caddr_t data)
X{
X	struct i4bisppp_softc *sc = ifp->if_softc;
X#if 0
X	struct sppp *sp = (struct sppp *)sc;
X	struct ifaddr *ifa = (struct ifaddr *) data;
X	struct ifreq *ifr = (struct ifreq *) data;
X#endif
X
X	int error;
X
X	error = sppp_ioctl(&sc->sc_if, cmd, data);
X	if (error)
X		return error;
X
X	switch(cmd) {
X	case SIOCSIFFLAGS:
X#if 0 /* never used ??? */
X		x = splimp();
X		if ((ifp->if_flags & IFF_UP) == 0)
X			UNTIMEOUT(i4bisppp_timeout, (void *)sp, sc->sc_ch);
X		splx(x);
X#endif
X		break;
X	}
X
X	return 0;
X}
X
X/*---------------------------------------------------------------------------*
X *	start output to ISDN B-channel
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4bisppp_start(struct ifnet *ifp)
X{
X	struct i4bisppp_softc *sc = ifp->if_softc;
X	struct mbuf *m;
X	int s;
X	int unit = IFP2UNIT(ifp);
X
X	if (sppp_isempty(ifp))
X		return;
X
X	if(sc->sc_state != ST_CONNECTED)
X		return;
X
X	s = splimp();
X	/*ifp->if_flags |= IFF_OACTIVE; - need to clear this somewhere */
X	splx(s);
X
X	while ((m = sppp_dequeue(&sc->sc_if)) != NULL)
X	{
X
X#if NBPFILTER > 0
X#ifdef __FreeBSD__
X		if (ifp->if_bpf)
X			bpf_mtap(ifp, m);
X#endif /* __FreeBSD__ */
X
X#ifdef __NetBSD__
X		if (ifp->if_bpf)
X			bpf_mtap(ifp->if_bpf, m);
X#endif
X#endif /* NBPFILTER > 0 */
X
X		microtime(&ifp->if_lastchange);
X
X		IF_ENQUEUE(isdn_linktab[unit]->tx_queue, m);
X
X		sc->sc_if.if_obytes += m->m_pkthdr.len;
X		sc->sc_outb += m->m_pkthdr.len;
X		sc->sc_if.if_opackets++;
X	}
X	isdn_linktab[unit]->bch_tx_start(isdn_linktab[unit]->unit,
X					 isdn_linktab[unit]->channel);
X}
X
X#ifdef I4BISPPPACCT
X/*---------------------------------------------------------------------------*
X *	watchdog routine
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4bisppp_watchdog(struct ifnet *ifp)
X{
X	struct i4bisppp_softc *sc = ifp->if_softc;
X	int unit = IFP2UNIT(ifp);
X	bchan_statistics_t bs;
X	
X	(*isdn_linktab[unit]->bch_stat)
X		(isdn_linktab[unit]->unit, isdn_linktab[unit]->channel, &bs);
X
X	sc->sc_ioutb += bs.outbytes;
X	sc->sc_iinb += bs.inbytes;
X	
X	if((sc->sc_iinb != sc->sc_linb) || (sc->sc_ioutb != sc->sc_loutb) || sc->sc_fn)
X	{
X		int ri = (sc->sc_iinb - sc->sc_linb)/I4BISPPPACCTINTVL;
X		int ro = (sc->sc_ioutb - sc->sc_loutb)/I4BISPPPACCTINTVL;
X
X		if((sc->sc_iinb == sc->sc_linb) && (sc->sc_ioutb == sc->sc_loutb))
X			sc->sc_fn = 0;
X		else
X			sc->sc_fn = 1;
X			
X		sc->sc_linb = sc->sc_iinb;
X		sc->sc_loutb = sc->sc_ioutb;
X
X		i4b_l4_accounting(BDRV_ISPPP, unit, ACCT_DURING,
X			 sc->sc_ioutb, sc->sc_iinb, ro, ri, sc->sc_outb, sc->sc_inb);
X 	}
X	sc->sc_if.if_timer = I4BISPPPACCTINTVL; 	
X
X#if 0 /* old stuff, keep it around */
X	printf(ISPPP_FMT "transmit timeout\n", ISPPP_ARG(sc));
X	i4bisppp_start(ifp);
X#endif
X}
X#endif /* I4BISPPPACCT */
X
X/*
X *===========================================================================*
X *			SyncPPP layer interface routines
X *===========================================================================*
X */
X
X#if 0 /* never used ??? */
X/*---------------------------------------------------------------------------*
X *	just an alias for i4bisppp_tls, but of type timeout_t
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4bisppp_timeout(void *cookie)
X{
X	i4bisppp_tls((struct sppp *)cookie);
X}
X#endif
X
X/*---------------------------------------------------------------------------*
X *	PPP this-layer-started action
X *---------------------------------------------------------------------------*
X */
Xstatic void
Xi4bisppp_tls(struct sppp *sp)
X{
X	struct i4bisppp_softc *sc = (struct i4bisppp_softc *)sp;
X	struct ifnet *ifp = (struct ifnet *)sp;
X
X	if(sc->sc_state == ST_CONNECTED)
X		return;
X
X	i4b_l4_dialout(BDRV_ISPPP, IFP2UNIT(ifp));
X}
X
X/*---------------------------------------------------------------------------*
X *	PPP this-layer-finished action
X *---------------------------------------------------------------------------*
X */
Xstatic void
Xi4bisppp_tlf(struct sppp *sp)
X{
X	struct i4bisppp_softc *sc = (struct i4bisppp_softc *)sp;
X/*	call_desc_t *cd = sc->sc_cdp;	*/
X        struct ifnet *ifp = (struct ifnet *)sp;	
X	
X	if(sc->sc_state != ST_CONNECTED)
X		return;
X
X#if 0 /* never used ??? */
X	UNTIMEOUT(i4bisppp_timeout, (void *)sp, sc->sc_ch);
X#endif
X
X	i4b_l4_drvrdisc(BDRV_ISPPP, IFP2UNIT(ifp));
X}
X/*---------------------------------------------------------------------------*
X *	PPP interface phase change
X *---------------------------------------------------------------------------*
X */
Xstatic void
Xi4bisppp_state_changed(struct sppp *sp, int new_state)
X{
X	struct i4bisppp_softc *sc = (struct i4bisppp_softc *)sp;
X	
X	i4b_l4_ifstate_changed(sc->sc_cdp, new_state);
X}
X
X/*---------------------------------------------------------------------------*
X *	PPP control protocol negotiation complete (run ip-up script now)
X *---------------------------------------------------------------------------*
X */
Xstatic void
Xi4bisppp_negotiation_complete(struct sppp *sp)
X{
X	struct i4bisppp_softc *sc = (struct i4bisppp_softc *)sp;
X	
X	i4b_l4_negcomplete(sc->sc_cdp);
X}
X
X/*===========================================================================*
X *			ISDN INTERFACE ROUTINES
X *===========================================================================*/
X
X/*---------------------------------------------------------------------------*
X *	this routine is called from L4 handler at connect time
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4bisppp_connect(int unit, void *cdp)
X{
X	struct i4bisppp_softc *sc = &i4bisppp_softc[unit];
X	struct sppp *sp = &sc->sc_if_un.scu_sp;
X	int s = splimp();
X
X	sc->sc_cdp = (call_desc_t *)cdp;
X	sc->sc_state = ST_CONNECTED;
X
X#if I4BISPPPACCT
X	sc->sc_iinb = 0;
X	sc->sc_ioutb = 0;
X	sc->sc_inb = 0;
X	sc->sc_outb = 0;
X	sc->sc_linb = 0;
X	sc->sc_loutb = 0;
X	sc->sc_if.if_timer = I4BISPPPACCTINTVL;
X#endif
X	
X#if 0 /* never used ??? */
X	UNTIMEOUT(i4bisppp_timeout, (void *)sp, sc->sc_ch);
X#endif
X
X	sp->pp_up(sp);		/* tell PPP we are ready */
X
X	splx(s);
X}
X
X/*---------------------------------------------------------------------------*
X *	this routine is called from L4 handler at disconnect time
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4bisppp_disconnect(int unit, void *cdp)
X{
X	call_desc_t *cd = (call_desc_t *)cdp;
X	struct i4bisppp_softc *sc = &i4bisppp_softc[unit];
X	struct sppp *sp = &sc->sc_if_un.scu_sp;
X
X	int s = splimp();
X
X	/* new stuff to check that the active channel is being closed */
X	if (cd != sc->sc_cdp)
X	{
X#ifdef I4BISPPPDISCDEBUG		
X		printf("i4bisppp_disconnect: isppp%d channel%d not active\n",
X			cd->driver_unit, cd->channelid);
X#endif
X		splx(s);
X		return;
X	}
X
X#if I4BISPPPACCT
X	sc->sc_if.if_timer = 0;
X#endif
X
X	i4b_l4_accounting(BDRV_ISPPP, unit, ACCT_FINAL,
X		 sc->sc_ioutb, sc->sc_iinb, 0, 0, sc->sc_outb, sc->sc_inb);
X	
X	if (sc->sc_state == ST_CONNECTED)
X	{
X#if 0 /* never used ??? */
X		UNTIMEOUT(i4bisppp_timeout, (void *)sp, sc->sc_ch);
X#endif
X		sc->sc_cdp = (call_desc_t *)0;	
X		/* do thhis here because pp_down calls i4bisppp_tlf */
X		sc->sc_state = ST_IDLE;
X		sp->pp_down(sp);	/* tell PPP we have hung up */
X	}
X
X	splx(s);
X}
X
X/*---------------------------------------------------------------------------*
X *	this routine is used to give a feedback from userland demon
X *	in case of dial problems
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4bisppp_dialresponse(int unit, int status)
X{
X/*	struct i4bisppp_softc *sc = &i4bisppp_softc[unit];	*/
X}
X	
X/*---------------------------------------------------------------------------*
X *	interface up/down
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4bisppp_updown(int unit, int updown)
X{
X	/* could probably do something useful here */
X}
X	
X/*---------------------------------------------------------------------------*
X *	this routine is called from the HSCX interrupt handler
X *	when a new frame (mbuf) has been received and was put on
X *	the rx queue.
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4bisppp_rx_data_rdy(int unit)
X{
X	struct i4bisppp_softc *sc = &i4bisppp_softc[unit];
X	struct mbuf *m;
X	int s;
X	
X	if((m = *isdn_linktab[unit]->rx_mbuf) == NULL)
X		return;
X
X	m->m_pkthdr.rcvif = &sc->sc_if;
X	m->m_pkthdr.len = m->m_len;
X
X	microtime(&sc->sc_if.if_lastchange);
X
X	sc->sc_if.if_ipackets++;
X	sc->sc_if.if_ibytes += m->m_pkthdr.len;
X
X#if I4BISPPPACCT
X	sc->sc_inb += m->m_pkthdr.len;
X#endif
X	
X#ifdef I4BISPPPDEBUG
X	printf("i4bisppp_rx_data_ready: received packet!\n");
X#endif
X
X#if NBPFILTER > 0
X
X#ifdef __FreeBSD__	
X	if(sc->sc_if.if_bpf)
X		bpf_mtap(&sc->sc_if, m);
X#endif /* __FreeBSD__ */
X
X#ifdef __NetBSD__
X	if(sc->sc_if.if_bpf)
X		bpf_mtap(sc->sc_if.if_bpf, m);
X#endif
X
X#endif /* NBPFILTER > 0 */
X
X	s = splimp();
X
X	sppp_input(&sc->sc_if, m);
X
X	splx(s);
X}
X
X/*---------------------------------------------------------------------------*
X *	this routine is called from the HSCX interrupt handler
X *	when the last frame has been sent out and there is no
X *	further frame (mbuf) in the tx queue.
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4bisppp_tx_queue_empty(int unit)
X{
X	i4bisppp_start(&i4bisppp_softc[unit].sc_if);	
X}
X
X/*---------------------------------------------------------------------------*
X *	this routine is called from the HSCX interrupt handler
X *	each time a packet is received or transmitted. It should
X *	be used to implement an activity timeout mechanism.
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4bisppp_activity(int unit, int rxtx)
X{
X	i4bisppp_softc[unit].sc_cdp->last_active_time = SECOND;
X}
X
X/*---------------------------------------------------------------------------*
X *	return this drivers linktab address
X *---------------------------------------------------------------------------*/
Xdrvr_link_t *
Xi4bisppp_ret_linktab(int unit)
X{
X	return(&i4bisppp_drvr_linktab[unit]);
X}
X
X/*---------------------------------------------------------------------------*
X *	setup the isdn_linktab for this driver
X *---------------------------------------------------------------------------*/
Xvoid
Xi4bisppp_set_linktab(int unit, isdn_link_t *ilt)
X{
X	isdn_linktab[unit] = ilt;
X}
X
X/*---------------------------------------------------------------------------*
X *	initialize this drivers linktab
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4bisppp_init_linktab(int unit)
X{
X	i4bisppp_drvr_linktab[unit].unit = unit;
X	i4bisppp_drvr_linktab[unit].bch_rx_data_ready = i4bisppp_rx_data_rdy;
X	i4bisppp_drvr_linktab[unit].bch_tx_queue_empty = i4bisppp_tx_queue_empty;
X	i4bisppp_drvr_linktab[unit].bch_activity = i4bisppp_activity;
X	i4bisppp_drvr_linktab[unit].line_connected = i4bisppp_connect;
X	i4bisppp_drvr_linktab[unit].line_disconnected = i4bisppp_disconnect;
X	i4bisppp_drvr_linktab[unit].dial_response = i4bisppp_dialresponse;	
X	i4bisppp_drvr_linktab[unit].updown_ind = i4bisppp_updown;	
X}
X
X/*===========================================================================*/
END-of-./usr/src/sys/i4b/driver/i4b_isppp.c.orig
echo c - ./usr/src/sys/i4b/layer4
mkdir -p ./usr/src/sys/i4b/layer4 > /dev/null 2>&1
echo x - ./usr/src/sys/i4b/layer4/i4b_l4.c
sed 's/^X//' >./usr/src/sys/i4b/layer4/i4b_l4.c << 'END-of-./usr/src/sys/i4b/layer4/i4b_l4.c'
X/*
X * Copyright (c) 1997, 1998 Hellmuth Michaelis. All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X *
X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X *
X *---------------------------------------------------------------------------
X *
X *	i4b_l4.c - kernel interface to userland
X *	-----------------------------------------
X *
X *	$Id: i4b_l4.c,v 1.1 1998/12/27 21:46:52 phk Exp $ 
X *
X *      last edit-date: [Sat Dec  5 18:35:16 1998]
X *
X *---------------------------------------------------------------------------*/
X
X#include "i4b.h"
X#include "i4bipr.h"
X#include "i4bisppp.h"
X#include "i4brbch.h"
X#include "i4btel.h"
X
X#if NI4B > 0
X
X#include <sys/param.h>
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X#include <sys/ioccom.h>
X#else
X#include <sys/ioctl.h>
X#endif
X#include <sys/kernel.h>
X#include <sys/systm.h>
X#include <sys/conf.h>
X#include <sys/mbuf.h>
X#include <sys/proc.h>
X#include <sys/fcntl.h>
X#include <sys/socket.h>
X#include <net/if.h>
X
X#ifdef __FreeBSD__
X#include <machine/i4b_debug.h>
X#include <machine/i4b_ioctl.h>
X#include <machine/i4b_cause.h>
X#else
X#include <i4b/i4b_debug.h>
X#include <i4b/i4b_ioctl.h>
X#include <i4b/i4b_cause.h>
X#endif
X
X#include <i4b/include/i4b_global.h>
X#include <i4b/include/i4b_l3l4.h>
X#include <i4b/include/i4b_mbuf.h>
X#include <i4b/layer3/i4b_l3.h>
X#include <i4b/layer4/i4b_l4.h>
X
Xunsigned int i4b_l4_debug = L4_DEBUG_DEFAULT;
X
Xstruct ctrl_type_desc ctrl_types[CTRL_NUMTYPES] = { { NULL, NULL} };
X
Xstatic int i4b_link_bchandrvr(call_desc_t *cd);
Xstatic void i4b_unlink_bchandrvr(call_desc_t *cd);
Xstatic void i4b_l4_setup_timeout(call_desc_t *cd);
Xstatic time_t i4b_get_idletime(call_desc_t *cd);
X
X/*---------------------------------------------------------------------------*
X *	send MSG_PDEACT_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_pdeact(int controller, int numactive)
X{
X	struct mbuf *m;
X	int i;
X	call_desc_t *cd;
X	
X	for(i=0; i < N_CALL_DESC; i++)
X	{
X		if((call_desc[i].cdid != CDID_UNUSED)				  &&
X		   (ctrl_desc[call_desc[i].controller].ctrl_type == CTRL_PASSIVE) &&
X		   (ctrl_desc[call_desc[i].controller].unit == controller))
X		{
X			cd = &call_desc[i];
X			
X			if(cd->timeout_active)
X			{
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X				untimeout((TIMEOUT_FUNC_T)i4b_idle_check,(void *)cd, cd->idle_timeout_handle);	
X#else
X				untimeout((TIMEOUT_FUNC_T)i4b_idle_check,(void *)cd);	
X#endif
X			}
X			
X			if(cd->dlt != NULL)
X			{
X				(*cd->dlt->line_disconnected)(cd->driver_unit, (void *)cd);
X				i4b_unlink_bchandrvr(cd);
X			}
X		
X			if((cd->channelid == CHAN_B1) || (cd->channelid == CHAN_B2))
X			{
X				ctrl_desc[cd->controller].bch_state[cd->channelid] = BCH_ST_FREE;
X			}
X
X			cd->cdid = CDID_UNUSED;
X		}
X	}
X	
X	if((m = i4b_Dgetmbuf(sizeof(msg_pdeact_ind_t))) != NULL)
X	{
X		msg_pdeact_ind_t *md = (msg_pdeact_ind_t *)m->m_data;
X
X		md->header.type = MSG_PDEACT_IND;
X		md->header.cdid = -1;
X
X		md->controller = controller;
X		md->numactive = numactive;
X
X		i4bputqueue_hipri(m);		/* URGENT !!! */
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_L12STAT_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_l12stat(int controller, int layer, int state)
X{
X	struct mbuf *m;
X
X	if((m = i4b_Dgetmbuf(sizeof(msg_l12stat_ind_t))) != NULL)
X	{
X		msg_l12stat_ind_t *md = (msg_l12stat_ind_t *)m->m_data;
X
X		md->header.type = MSG_L12STAT_IND;
X		md->header.cdid = -1;
X
X		md->controller = controller;
X		md->layer = layer;
X		md->state = state;
X
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_TEIASG_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_teiasg(int controller, int tei)
X{
X	struct mbuf *m;
X
X	if((m = i4b_Dgetmbuf(sizeof(msg_teiasg_ind_t))) != NULL)
X	{
X		msg_teiasg_ind_t *md = (msg_teiasg_ind_t *)m->m_data;
X
X		md->header.type = MSG_TEIASG_IND;
X		md->header.cdid = -1;
X
X		md->controller = controller;
X		md->tei = ctrl_desc[controller].tei;
X
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_DIALOUT_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_dialout(int driver, int driver_unit)
X{
X	struct mbuf *m;
X
X	if((m = i4b_Dgetmbuf(sizeof(msg_dialout_ind_t))) != NULL)
X	{
X		msg_dialout_ind_t *md = (msg_dialout_ind_t *)m->m_data;
X
X		md->header.type = MSG_DIALOUT_IND;
X		md->header.cdid = -1;
X
X		md->driver = driver;
X		md->driver_unit = driver_unit;	
X
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_NEGOTIATION_COMPL message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_negcomplete(call_desc_t *cd)
X{
X	struct mbuf *m;
X
X	if((m = i4b_Dgetmbuf(sizeof(msg_negcomplete_ind_t))) != NULL)
X	{
X		msg_negcomplete_ind_t *md = (msg_negcomplete_ind_t *)m->m_data;
X
X		md->header.type = MSG_NEGCOMP_IND;
X		md->header.cdid = cd->cdid;
X
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_IFSTATE_CHANGED_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_ifstate_changed(call_desc_t *cd, int new_state)
X{
X	struct mbuf *m;
X
X	if((m = i4b_Dgetmbuf(sizeof(msg_ifstatechg_ind_t))) != NULL)
X	{
X		msg_ifstatechg_ind_t *md = (msg_ifstatechg_ind_t *)m->m_data;
X
X		md->header.type = MSG_IFSTATE_CHANGED_IND;
X		md->header.cdid = cd->cdid;
X		md->state = new_state;
X
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_DRVRDISC_REQ message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_drvrdisc(int driver, int driver_unit)
X{
X	struct mbuf *m;
X
X	if((m = i4b_Dgetmbuf(sizeof(msg_drvrdisc_req_t))) != NULL)
X	{
X		msg_drvrdisc_req_t *md = (msg_drvrdisc_req_t *)m->m_data;
X
X		md->header.type = MSG_DRVRDISC_REQ;
X		md->header.cdid = -1;
X
X		md->driver = driver;
X		md->driver_unit = driver_unit;	
X
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_ACCT_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_accounting(int driver, int driver_unit, int accttype, int ioutbytes,
X		int iinbytes, int ro, int ri, int outbytes, int inbytes)
X{
X	struct mbuf *m;
X
X	if((m = i4b_Dgetmbuf(sizeof(msg_accounting_ind_t))) != NULL)
X	{
X		msg_accounting_ind_t *md = (msg_accounting_ind_t *)m->m_data;
X
X		md->header.type = MSG_ACCT_IND;
X		md->header.cdid = -1;
X
X		md->driver = driver;
X		md->driver_unit = driver_unit;	
X
X		md->accttype = accttype;
X		md->ioutbytes = ioutbytes;
X		md->iinbytes = iinbytes;
X		md->outbps = ro;
X		md->inbps = ri;
X		md->outbytes = outbytes;
X		md->inbytes = inbytes;
X		
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_CONNECT_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_connect_ind(call_desc_t *cd)
X{
X	struct mbuf *m;
X
X	if((m = i4b_Dgetmbuf(sizeof(msg_connect_ind_t))) != NULL)
X	{
X		msg_connect_ind_t *mp = (msg_connect_ind_t *)m->m_data;
X
X		mp->header.type = MSG_CONNECT_IND;
X		mp->header.cdid = cd->cdid;
X
X		mp->controller = cd->controller;
X		mp->channel = cd->channelid;
X		mp->bprot = cd->bprot;
X
X		cd->dir = DIR_INCOMING;
X
X		if(strlen(cd->dst_telno) > 0)
X			strcpy(mp->dst_telno, cd->dst_telno);
X		else
X			strcpy(mp->dst_telno, TELNO_EMPTY);
X
X		if(strlen(cd->src_telno) > 0)
X			strcpy(mp->src_telno, cd->src_telno);
X		else
X			strcpy(mp->src_telno, TELNO_EMPTY);
X			
X		strcpy(mp->display, cd->display);
X
X		mp->scr_ind = cd->scr_ind;
X		
X		T400_start(cd);
X		
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_CONNECT_ACTIVE_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_connect_active_ind(call_desc_t *cd)
X{
X	int s;
X	struct mbuf *m;
X
X	s = SPLI4B();
X
X	cd->last_active_time = cd->connect_time = SECOND;
X
X	DBGL4(L4_TIMO, "i4b_l4_connect_active_ind", ("last_active/connect_time=%ld\n", (long)cd->connect_time));
X	
X	i4b_link_bchandrvr(cd);
X
X	(*cd->dlt->line_connected)(cd->driver_unit, (void *)cd);
X
X	i4b_l4_setup_timeout(cd);
X	
X	splx(s);	
X	
X	if((m = i4b_Dgetmbuf(sizeof(msg_connect_active_ind_t))) != NULL)
X	{
X		msg_connect_active_ind_t *mp = (msg_connect_active_ind_t *)m->m_data;
X
X		mp->header.type = MSG_CONNECT_ACTIVE_IND;
X		mp->header.cdid = cd->cdid;
X		mp->controller = cd->controller;
X		mp->channel = cd->channelid;
X		if(cd->datetime[0] != '\0')
X			strcpy(mp->datetime, cd->datetime);
X		else
X			mp->datetime[0] = '\0';
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_DISCONNECT_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_disconnect_ind(call_desc_t *cd)
X{
X	struct mbuf *m;
X
X	if(cd->timeout_active)
X#if defined(__FreeBSD_version) && __FreeBSD_version >= 300001
X		untimeout((TIMEOUT_FUNC_T)i4b_idle_check,(void *)cd, cd->idle_timeout_handle);	
X#else
X		untimeout((TIMEOUT_FUNC_T)i4b_idle_check,(void *)cd);	
X#endif
X
X	if(cd->dlt != NULL)
X	{
X		(*cd->dlt->line_disconnected)(cd->driver_unit, (void *)cd);
X		i4b_unlink_bchandrvr(cd);
X	}
X
X	if((cd->channelid == CHAN_B1) || (cd->channelid == CHAN_B2))
X	{
X		ctrl_desc[cd->controller].bch_state[cd->channelid] = BCH_ST_FREE;
X	}
X	else
X	{
X		/* no error, might be hunting call for callback */
X		DBGL4(L4_MSG, "i4b_l4_disconnect_ind", ("channel free not B1/B2 but %d!\n", cd->channelid));
X	}
X	
X	if((m = i4b_Dgetmbuf(sizeof(msg_disconnect_ind_t))) != NULL)
X	{
X		msg_disconnect_ind_t *mp = (msg_disconnect_ind_t *)m->m_data;
X
X		mp->header.type = MSG_DISCONNECT_IND;
X		mp->header.cdid = cd->cdid;
X		mp->cause = cd->cause_in;
X
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_IDLE_TIMEOUT_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_idle_timeout_ind(call_desc_t *cd)
X{
X	struct mbuf *m;
X
X	if((m = i4b_Dgetmbuf(sizeof(msg_idle_timeout_ind_t))) != NULL)
X	{
X		msg_idle_timeout_ind_t *mp = (msg_idle_timeout_ind_t *)m->m_data;
X
X		mp->header.type = MSG_IDLE_TIMEOUT_IND;
X		mp->header.cdid = cd->cdid;
X
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_CHARGING_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_charging_ind(call_desc_t *cd)
X{
X	struct mbuf *m;
X
X	if((m = i4b_Dgetmbuf(sizeof(msg_charging_ind_t))) != NULL)
X	{
X		msg_charging_ind_t *mp = (msg_charging_ind_t *)m->m_data;
X
X		mp->header.type = MSG_CHARGING_IND;
X		mp->header.cdid = cd->cdid;
X		mp->units_type = cd->units_type;
X
X/*XXX*/		if(mp->units_type == CHARGE_CALC)
X			mp->units = cd->cunits;
X		else
X			mp->units = cd->units;
X
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_STATUS_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_status_ind(call_desc_t *cd)
X{
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_ALERT_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_alert_ind(call_desc_t *cd)
X{
X	struct mbuf *m;
X
X	if((m = i4b_Dgetmbuf(sizeof(msg_alert_ind_t))) != NULL)
X	{
X		msg_alert_ind_t *mp = (msg_alert_ind_t *)m->m_data;
X
X		mp->header.type = MSG_ALERT_IND;
X		mp->header.cdid = cd->cdid;
X
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_INFO_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_info_ind(call_desc_t *cd)
X{
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_INFO_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_proceeding_ind(call_desc_t *cd)
X{
X	struct mbuf *m;
X
X	if((m = i4b_Dgetmbuf(sizeof(msg_proceeding_ind_t))) != NULL)
X	{
X		msg_proceeding_ind_t *mp = (msg_proceeding_ind_t *)m->m_data;
X
X		mp->header.type = MSG_PROCEEDING_IND;
X		mp->header.cdid = cd->cdid;
X		mp->controller = cd->controller;
X		mp->channel = cd->channelid;
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	link a driver(unit) to a B-channel(controller,unit,channel)
X *---------------------------------------------------------------------------*/
Xstatic int
Xi4b_link_bchandrvr(call_desc_t *cd)
X{
X	int t = ctrl_desc[cd->controller].ctrl_type;
X	
X	if(t < 0 || t >= CTRL_NUMTYPES || ctrl_types[t].get_linktab == NULL)
X	{
X			cd->ilt = NULL;
X	}
X	else
X	{
X		cd->ilt = ctrl_types[t].get_linktab(
X			ctrl_desc[cd->controller].unit,
X			cd->channelid);
X	}
X
X	switch(cd->driver)
X	{
X#if NI4BRBCH > 0
X		case BDRV_RBCH:
X			cd->dlt = rbch_ret_linktab(cd->driver_unit);
X			break;
X#endif
X		
X#if NI4BTEL > 0
X		case BDRV_TEL:
X			cd->dlt = tel_ret_linktab(cd->driver_unit);
X			break;
X#endif
X
X#if NI4BIPR > 0
X		case BDRV_IPR:
X			cd->dlt = ipr_ret_linktab(cd->driver_unit);
X			break;
X#endif
X
X#if NI4BISPPP > 0
X		case BDRV_ISPPP:
X			cd->dlt = i4bisppp_ret_linktab(cd->driver_unit);
X			break;
X#endif
X
X		default:
X			cd->dlt = NULL;
X			break;
X	}
X
X	if(cd->dlt == NULL || cd->ilt == NULL)
X		return(-1);
X
X	if(t >= 0 && t < CTRL_NUMTYPES && ctrl_types[t].set_linktab != NULL)
X	{
X		ctrl_types[t].set_linktab(
X				ctrl_desc[cd->controller].unit,
X				cd->channelid,
X				cd->dlt);
X	}
X
X	switch(cd->driver)
X	{
X#if NI4BRBCH > 0
X		case BDRV_RBCH:
X			rbch_set_linktab(cd->driver_unit, cd->ilt);
X			break;
X#endif
X
X#if NI4BTEL > 0
X		case BDRV_TEL:
X			tel_set_linktab(cd->driver_unit, cd->ilt);
X			break;
X#endif
X
X#if NI4BIPR > 0
X		case BDRV_IPR:
X			ipr_set_linktab(cd->driver_unit, cd->ilt);
X			break;
X#endif
X
X#if NI4BISPPP > 0
X		case BDRV_ISPPP:
X			i4bisppp_set_linktab(cd->driver_unit, cd->ilt);
X			break;
X#endif
X		default:
X			return(0);
X			break;
X	}
X
X	/* activate B channel */
X		
X	(*cd->ilt->bch_config)(ctrl_desc[cd->controller].unit,
X				cd->channelid, cd->bprot, 1);
X
X	return(0);
X}
X
X/*---------------------------------------------------------------------------*
X *	unlink a driver(unit) from a B-channel(controller,unit,channel)
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4b_unlink_bchandrvr(call_desc_t *cd)
X{
X	int t = ctrl_desc[cd->controller].ctrl_type;
X
X	if(t < 0 || t >= CTRL_NUMTYPES || ctrl_types[t].get_linktab == NULL)
X	{
X		cd->ilt = NULL;
X		return;
X	}
X	else
X	{
X		cd->ilt = ctrl_types[t].get_linktab(
X				ctrl_desc[cd->controller].unit,
X				cd->channelid);
X	}
X	
X	/* deactivate B channel */
X		
X	(*cd->ilt->bch_config)(ctrl_desc[cd->controller].unit,
X				cd->channelid, cd->bprot, 0);
X} 
X
X/*---------------------------------------------------------------------------
X
X	How shorthold mode works for OUTGOING connections
X	=================================================
X
X	|<---- unchecked-window ------->|<-checkwindow->|<-safetywindow>|
X
Xidletime_state:      IST_NONCHK             IST_CHECK       IST_SAFE	
X	
X	|				|		|		|
X  time>>+-------------------------------+---------------+---------------+-...
X	|				|		|		|
X	|				|<--idle_time-->|<--earlyhup--->|
X	|<-----------------------unitlen------------------------------->|
X
X	
X	  unitlen - specifies the time a charging unit lasts
X	idle_time - specifies the thime the line must be idle at the
X		    end of the unit to be elected for hangup
X	 earlyhup - is the beginning of a timing safety zone before the
X		    next charging unit starts
X
X	The algorithm works as follows: lets assume the unitlen is 100
X	secons, idle_time is 40 seconds and earlyhup is 10 seconds.
X	The line then must be idle 50 seconds after the begin of the
X	current unit and it must then be quiet for 40 seconds. if it
X	has been quiet for this 40 seconds, the line is closed 10
X	seconds before the next charging unit starts. In case there was
X	any traffic within the idle_time, the line is not closed.
X	It does not matter whether there was any traffic between second
X	0 and second 50 or not.
X
X
X	How shorthold mode works for INCOMING connections
X	=================================================
X
X	it is just possible to specify a maximum idle time for incoming
X	connections, after this time of no activity on the line the line
X	is closed.
X	
X---------------------------------------------------------------------------*/	
X
X/*---------------------------------------------------------------------------*
X *	B channel idle check timeout setup
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4b_l4_setup_timeout(call_desc_t *cd)
X{
X	cd->timeout_active = 0;
X	cd->idletime_state = IST_IDLE;
X	
X	if((cd->dir == DIR_INCOMING) && (cd->max_idle_time > 0))
X	{
X		/* incoming call: simple max idletime check */
X	
X#if defined(__FreeBSD_version) && __FreeBSD_version >= 300001
X		cd->idle_timeout_handle =
X#endif
X		timeout((TIMEOUT_FUNC_T)i4b_idle_check,(void *)cd, hz/2);
X		cd->timeout_active = 1;
X		DBGL4(L4_TIMO, "i4b_l4_setup_timeout", ("%ld: incoming-call, setup max_idle_time to %ld\n", (long)SECOND, (long)cd->max_idle_time));
X	}
X	else if((cd->dir == DIR_OUTGOING) && (cd->idle_time > 0))
X	{
X		/* outgoing call */
X		
X		if((cd->idle_time > 0) && (cd->unitlen_time == 0))
X		{
X			/* outgoing call: simple max idletime check */
X			
X#if defined(__FreeBSD_version) && __FreeBSD_version >= 300001
X			cd->idle_timeout_handle =
X#endif
X			timeout((TIMEOUT_FUNC_T)i4b_idle_check,(void *)cd, hz/2);
X			cd->timeout_active = 1;
X			DBGL4(L4_TIMO, "i4b_l4_setup_timeout", ("%ld: outgoing-call, setup idle_time to %ld\n", (long)SECOND, (long)cd->idle_time));
X		}
X		else if((cd->unitlen_time > 0) && (cd->unitlen_time > (cd->idle_time + cd->earlyhup_time)))
X		{
X			/* outgoing call: full shorthold mode check */
X			
X#if defined(__FreeBSD_version) && __FreeBSD_version >= 300001
X			cd->idle_timeout_handle =
X#endif
X			timeout((TIMEOUT_FUNC_T)i4b_idle_check,(void *)cd, hz*(cd->unitlen_time - (cd->idle_time + cd->earlyhup_time)));
X			cd->timeout_active = 1;
X			cd->idletime_state = IST_NONCHK;
X			DBGL4(L4_TIMO, "i4b_l4_setup_timeout", ("%ld: outgoing-call, start %ld sec nocheck window\n", 
X				(long)SECOND, (long)(cd->unitlen_time - (cd->idle_time + cd->earlyhup_time))));
X
X			if(cd->aocd_flag == 0)
X			{
X				cd->units_type = CHARGE_CALC;
X				cd->cunits++;
X				i4b_l4_charging_ind(cd);
X			}
X		}
X		else
X		{
X			/* parms somehow got wrong .. */
X			
X			DBGL4(L4_ERR, "i4b_l4_setup_timeout", ("%ld: ERROR: idletime[%ld]+earlyhup[%ld] > unitlength[%ld]!\n",
X				(long)SECOND, (long)cd->idle_time, (long)cd->earlyhup_time, (long)cd->unitlen_time));
X		}
X	}
X	else
X	{
X		DBGL4(L4_TIMO, "i4b_l4_setup_timeout", ("no idle_timeout configured\n"));
X	}
X}
X
Xstatic time_t
Xi4b_get_idletime(call_desc_t *cd)
X{
X	time_t t;
X	switch (cd->driver) {
X#if NI4BISPPP > 0
X		case BDRV_ISPPP:
X			return i4bisppp_idletime(cd->driver_unit);
X		break;
X#endif
X		default:
X			return SECOND - cd->last_active_time;
X		break;
X	}
X}
X/*---------------------------------------------------------------------------*
X *	B channel idle check timeout function
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_idle_check(call_desc_t *cd)
X{
X	int s;
X
X	if(cd->cdid == CDID_UNUSED)
X		return;
X	
X	s = SPLI4B();
X
X	/* failsafe */
X
X	if(cd->timeout_active == 0)
X	{
X		DBGL4(L4_ERR, "i4b_idle_check", ("ERROR: timeout_active == 0 !!!\n"));
X	}
X	else
X	{	
X		cd->timeout_active = 0;
X	}
X	
X	/* incoming connections, simple idletime check */
X
X	if(cd->dir == DIR_INCOMING)
X	{
X		if(i4b_get_idletime(cd) >= cd->max_idle_time)
X		{
X			DBGL4(L4_TIMO, "i4b_idle_check", ("%ld: incoming-call, line idle timeout, disconnecting!\n", (long)SECOND));
X			(*ctrl_desc[cd->controller].N_DISCONNECT_REQUEST)(cd->cdid,
X					(CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
X			i4b_l4_idle_timeout_ind(cd);
X		}
X		else
X		{
X			DBGL4(L4_TIMO, "i4b_idle_check", ("%ld: incoming-call, activity, last_active=%ld, max_idle=%ld\n", (long)SECOND, (long)cd->last_active_time, (long)cd->max_idle_time));
X#if defined(__FreeBSD_version) && __FreeBSD_version >= 300001
X			cd->idle_timeout_handle =
X#endif
X			timeout((TIMEOUT_FUNC_T)i4b_idle_check,(void *)cd, hz/2);
X			cd->timeout_active = 1;
X		}
X	}
X
X	/* outgoing connections */
X
X	else if(cd->dir == DIR_OUTGOING)
X	{
X
X		/* simple idletime calculation */
X
X		if((cd->idle_time > 0) && (cd->unitlen_time == 0))
X		{
X			if(i4b_get_idletime(cd) >= cd->idle_time)
X			{
X				DBGL4(L4_TIMO, "i4b_idle_check", ("%ld: outgoing-call-st, idle timeout, disconnecting!\n", (long)SECOND));
X				(*ctrl_desc[cd->controller].N_DISCONNECT_REQUEST)(cd->cdid,
X					(CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
X				i4b_l4_idle_timeout_ind(cd);
X			}
X			else
X			{
X				DBGL4(L4_TIMO, "i4b_idle_check", ("%ld: outgoing-call-st, activity, last_active=%ld, max_idle=%ld\n",
X								(long)SECOND, (long)cd->last_active_time, (long)cd->idle_time));
X#if defined(__FreeBSD_version) && __FreeBSD_version >= 300001
X			cd->idle_timeout_handle =
X#endif
X				timeout((TIMEOUT_FUNC_T)i4b_idle_check,(void *)cd, hz/2);
X				cd->timeout_active = 1;
X			}
X		}
X
X		/* full shorthold mode calculation */
X
X		else if((cd->unitlen_time > 0) && (cd->unitlen_time > (cd->idle_time + cd->earlyhup_time)))
X		{
X			switch(cd->idletime_state)
X			{
X			case IST_NONCHK:	/* end of non-check time */
X#if defined(__FreeBSD_version) && __FreeBSD_version >= 300001
X				cd->idle_timeout_handle =
X#endif
X				timeout((TIMEOUT_FUNC_T)i4b_idle_check,(void *)cd, hz*(cd->idle_time));
X				cd->idletimechk_start = SECOND;
X				cd->idletime_state = IST_CHECK;
X				cd->timeout_active = 1;
X				DBGL4(L4_TIMO, "i4b_idle_check", ("%ld: outgoing-call, idletime check window reached!\n", (long)SECOND));
X				break;
X
X			case IST_CHECK:		/* end of idletime chk */
X				if((cd->last_active_time > cd->idletimechk_start) &&
X				   (cd->last_active_time <= SECOND))
X				{	/* activity detected */
X#if defined(__FreeBSD_version) && __FreeBSD_version >= 300001
X					cd->idle_timeout_handle =
X#endif
X					timeout((TIMEOUT_FUNC_T)i4b_idle_check,(void *)cd, hz*(cd->earlyhup_time));
X					cd->timeout_active = 1;
X					cd->idletime_state = IST_SAFE;
X					DBGL4(L4_TIMO, "i4b_idle_check", ("%ld: outgoing-call, activity at %ld, wait earlyhup-end\n", (long)SECOND, (long)cd->last_active_time));
X				}
X				else
X				{	/* no activity, hangup */
X					DBGL4(L4_TIMO, "i4b_idle_check", ("%ld: outgoing-call, idle timeout, last activity at %ld\n", (long)SECOND, (long)cd->last_active_time));
X					(*ctrl_desc[cd->controller].N_DISCONNECT_REQUEST)(cd->cdid,
X						(CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
X					i4b_l4_idle_timeout_ind(cd);
X					cd->idletime_state = IST_IDLE;
X				}
X				break;
X
X			case IST_SAFE:	/* end of earlyhup time */
X#if defined(__FreeBSD_version) && __FreeBSD_version >= 300001
X				cd->idle_timeout_handle =
X#endif
X				timeout((TIMEOUT_FUNC_T)i4b_idle_check,(void *)cd, hz*(cd->unitlen_time - (cd->idle_time+cd->earlyhup_time)));
X				cd->timeout_active = 1;
X				cd->idletime_state = IST_NONCHK;
X
X				if(cd->aocd_flag == 0)
X				{
X					cd->units_type = CHARGE_CALC;
X					cd->cunits++;
X					i4b_l4_charging_ind(cd);
X				}
X				
X				DBGL4(L4_TIMO, "i4b_idle_check", ("%ld: outgoing-call, earlyhup end, wait for idletime start\n", (long)SECOND));
X				break;
X
X			default:
X				DBGL4(L4_ERR, "i4b_idle_check", ("outgoing-call: invalid idletime_state value!\n"));
X				cd->idletime_state = IST_IDLE;
X				break;
X			}
X		}
X	}
X	splx(s);
X}
X
X#endif /* NI4B > 0 */
END-of-./usr/src/sys/i4b/layer4/i4b_l4.c
echo x - ./usr/src/sys/i4b/layer4/i4b_l4.c.patch
sed 's/^X//' >./usr/src/sys/i4b/layer4/i4b_l4.c.patch << 'END-of-./usr/src/sys/i4b/layer4/i4b_l4.c.patch'
X*** i4b_l4.c	Sat Dec  5 19:05:49 1998
X--- i4b_l4.c.new	Wed Mar 17 13:19:36 1999
X***************
X*** 79,84 ****
X--- 79,85 ----
X  static int i4b_link_bchandrvr(call_desc_t *cd);
X  static void i4b_unlink_bchandrvr(call_desc_t *cd);
X  static void i4b_l4_setup_timeout(call_desc_t *cd);
X+ static time_t i4b_get_idletime(call_desc_t *cd);
X  
X  /*---------------------------------------------------------------------------*
X   *	send MSG_PDEACT_IND message to userland
X***************
X*** 753,758 ****
X--- 754,774 ----
X  	}
X  }
X  
X+ static time_t
X+ i4b_get_idletime(call_desc_t *cd)
X+ {
X+ 	time_t t;
X+ 	switch (cd->driver) {
X+ #if NI4BISPPP > 0
X+ 		case BDRV_ISPPP:
X+ 			return i4bisppp_idletime(cd->driver_unit);
X+ 		break;
X+ #endif
X+ 		default:
X+ 			return SECOND - cd->last_active_time;
X+ 		break;
X+ 	}
X+ }
X  /*---------------------------------------------------------------------------*
X   *	B channel idle check timeout function
X   *---------------------------------------------------------------------------*/
X***************
X*** 781,787 ****
X  
X  	if(cd->dir == DIR_INCOMING)
X  	{
X! 		if((cd->last_active_time + cd->max_idle_time) <= SECOND)
X  		{
X  			DBGL4(L4_TIMO, "i4b_idle_check", ("%ld: incoming-call, line idle timeout, disconnecting!\n", (long)SECOND));
X  			(*ctrl_desc[cd->controller].N_DISCONNECT_REQUEST)(cd->cdid,
X--- 797,803 ----
X  
X  	if(cd->dir == DIR_INCOMING)
X  	{
X! 		if(i4b_get_idletime(cd) >= cd->max_idle_time)
X  		{
X  			DBGL4(L4_TIMO, "i4b_idle_check", ("%ld: incoming-call, line idle timeout, disconnecting!\n", (long)SECOND));
X  			(*ctrl_desc[cd->controller].N_DISCONNECT_REQUEST)(cd->cdid,
X***************
X*** 808,814 ****
X  
X  		if((cd->idle_time > 0) && (cd->unitlen_time == 0))
X  		{
X! 			if((cd->last_active_time + cd->idle_time) <= SECOND)
X  			{
X  				DBGL4(L4_TIMO, "i4b_idle_check", ("%ld: outgoing-call-st, idle timeout, disconnecting!\n", (long)SECOND));
X  				(*ctrl_desc[cd->controller].N_DISCONNECT_REQUEST)(cd->cdid,
X--- 824,830 ----
X  
X  		if((cd->idle_time > 0) && (cd->unitlen_time == 0))
X  		{
X! 			if(i4b_get_idletime(cd) >= cd->idle_time)
X  			{
X  				DBGL4(L4_TIMO, "i4b_idle_check", ("%ld: outgoing-call-st, idle timeout, disconnecting!\n", (long)SECOND));
X  				(*ctrl_desc[cd->controller].N_DISCONNECT_REQUEST)(cd->cdid,
END-of-./usr/src/sys/i4b/layer4/i4b_l4.c.patch
echo x - ./usr/src/sys/i4b/layer4/i4b_l4.c.orig
sed 's/^X//' >./usr/src/sys/i4b/layer4/i4b_l4.c.orig << 'END-of-./usr/src/sys/i4b/layer4/i4b_l4.c.orig'
X/*
X * Copyright (c) 1997, 1998 Hellmuth Michaelis. All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X *
X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X *
X *---------------------------------------------------------------------------
X *
X *	i4b_l4.c - kernel interface to userland
X *	-----------------------------------------
X *
X *	$Id: i4b_l4.c,v 1.1 1998/12/27 21:46:52 phk Exp $ 
X *
X *      last edit-date: [Sat Dec  5 18:35:16 1998]
X *
X *---------------------------------------------------------------------------*/
X
X#include "i4b.h"
X#include "i4bipr.h"
X#include "i4bisppp.h"
X#include "i4brbch.h"
X#include "i4btel.h"
X
X#if NI4B > 0
X
X#include <sys/param.h>
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X#include <sys/ioccom.h>
X#else
X#include <sys/ioctl.h>
X#endif
X#include <sys/kernel.h>
X#include <sys/systm.h>
X#include <sys/conf.h>
X#include <sys/mbuf.h>
X#include <sys/proc.h>
X#include <sys/fcntl.h>
X#include <sys/socket.h>
X#include <net/if.h>
X
X#ifdef __FreeBSD__
X#include <machine/i4b_debug.h>
X#include <machine/i4b_ioctl.h>
X#include <machine/i4b_cause.h>
X#else
X#include <i4b/i4b_debug.h>
X#include <i4b/i4b_ioctl.h>
X#include <i4b/i4b_cause.h>
X#endif
X
X#include <i4b/include/i4b_global.h>
X#include <i4b/include/i4b_l3l4.h>
X#include <i4b/include/i4b_mbuf.h>
X#include <i4b/layer3/i4b_l3.h>
X#include <i4b/layer4/i4b_l4.h>
X
Xunsigned int i4b_l4_debug = L4_DEBUG_DEFAULT;
X
Xstruct ctrl_type_desc ctrl_types[CTRL_NUMTYPES] = { { NULL, NULL} };
X
Xstatic int i4b_link_bchandrvr(call_desc_t *cd);
Xstatic void i4b_unlink_bchandrvr(call_desc_t *cd);
Xstatic void i4b_l4_setup_timeout(call_desc_t *cd);
X
X/*---------------------------------------------------------------------------*
X *	send MSG_PDEACT_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_pdeact(int controller, int numactive)
X{
X	struct mbuf *m;
X	int i;
X	call_desc_t *cd;
X	
X	for(i=0; i < N_CALL_DESC; i++)
X	{
X		if((call_desc[i].cdid != CDID_UNUSED)				  &&
X		   (ctrl_desc[call_desc[i].controller].ctrl_type == CTRL_PASSIVE) &&
X		   (ctrl_desc[call_desc[i].controller].unit == controller))
X		{
X			cd = &call_desc[i];
X			
X			if(cd->timeout_active)
X			{
X#if defined(__FreeBSD__) && __FreeBSD__ >= 3
X				untimeout((TIMEOUT_FUNC_T)i4b_idle_check,(void *)cd, cd->idle_timeout_handle);	
X#else
X				untimeout((TIMEOUT_FUNC_T)i4b_idle_check,(void *)cd);	
X#endif
X			}
X			
X			if(cd->dlt != NULL)
X			{
X				(*cd->dlt->line_disconnected)(cd->driver_unit, (void *)cd);
X				i4b_unlink_bchandrvr(cd);
X			}
X		
X			if((cd->channelid == CHAN_B1) || (cd->channelid == CHAN_B2))
X			{
X				ctrl_desc[cd->controller].bch_state[cd->channelid] = BCH_ST_FREE;
X			}
X
X			cd->cdid = CDID_UNUSED;
X		}
X	}
X	
X	if((m = i4b_Dgetmbuf(sizeof(msg_pdeact_ind_t))) != NULL)
X	{
X		msg_pdeact_ind_t *md = (msg_pdeact_ind_t *)m->m_data;
X
X		md->header.type = MSG_PDEACT_IND;
X		md->header.cdid = -1;
X
X		md->controller = controller;
X		md->numactive = numactive;
X
X		i4bputqueue_hipri(m);		/* URGENT !!! */
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_L12STAT_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_l12stat(int controller, int layer, int state)
X{
X	struct mbuf *m;
X
X	if((m = i4b_Dgetmbuf(sizeof(msg_l12stat_ind_t))) != NULL)
X	{
X		msg_l12stat_ind_t *md = (msg_l12stat_ind_t *)m->m_data;
X
X		md->header.type = MSG_L12STAT_IND;
X		md->header.cdid = -1;
X
X		md->controller = controller;
X		md->layer = layer;
X		md->state = state;
X
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_TEIASG_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_teiasg(int controller, int tei)
X{
X	struct mbuf *m;
X
X	if((m = i4b_Dgetmbuf(sizeof(msg_teiasg_ind_t))) != NULL)
X	{
X		msg_teiasg_ind_t *md = (msg_teiasg_ind_t *)m->m_data;
X
X		md->header.type = MSG_TEIASG_IND;
X		md->header.cdid = -1;
X
X		md->controller = controller;
X		md->tei = ctrl_desc[controller].tei;
X
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_DIALOUT_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_dialout(int driver, int driver_unit)
X{
X	struct mbuf *m;
X
X	if((m = i4b_Dgetmbuf(sizeof(msg_dialout_ind_t))) != NULL)
X	{
X		msg_dialout_ind_t *md = (msg_dialout_ind_t *)m->m_data;
X
X		md->header.type = MSG_DIALOUT_IND;
X		md->header.cdid = -1;
X
X		md->driver = driver;
X		md->driver_unit = driver_unit;	
X
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_NEGOTIATION_COMPL message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_negcomplete(call_desc_t *cd)
X{
X	struct mbuf *m;
X
X	if((m = i4b_Dgetmbuf(sizeof(msg_negcomplete_ind_t))) != NULL)
X	{
X		msg_negcomplete_ind_t *md = (msg_negcomplete_ind_t *)m->m_data;
X
X		md->header.type = MSG_NEGCOMP_IND;
X		md->header.cdid = cd->cdid;
X
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_IFSTATE_CHANGED_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_ifstate_changed(call_desc_t *cd, int new_state)
X{
X	struct mbuf *m;
X
X	if((m = i4b_Dgetmbuf(sizeof(msg_ifstatechg_ind_t))) != NULL)
X	{
X		msg_ifstatechg_ind_t *md = (msg_ifstatechg_ind_t *)m->m_data;
X
X		md->header.type = MSG_IFSTATE_CHANGED_IND;
X		md->header.cdid = cd->cdid;
X		md->state = new_state;
X
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_DRVRDISC_REQ message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_drvrdisc(int driver, int driver_unit)
X{
X	struct mbuf *m;
X
X	if((m = i4b_Dgetmbuf(sizeof(msg_drvrdisc_req_t))) != NULL)
X	{
X		msg_drvrdisc_req_t *md = (msg_drvrdisc_req_t *)m->m_data;
X
X		md->header.type = MSG_DRVRDISC_REQ;
X		md->header.cdid = -1;
X
X		md->driver = driver;
X		md->driver_unit = driver_unit;	
X
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_ACCT_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_accounting(int driver, int driver_unit, int accttype, int ioutbytes,
X		int iinbytes, int ro, int ri, int outbytes, int inbytes)
X{
X	struct mbuf *m;
X
X	if((m = i4b_Dgetmbuf(sizeof(msg_accounting_ind_t))) != NULL)
X	{
X		msg_accounting_ind_t *md = (msg_accounting_ind_t *)m->m_data;
X
X		md->header.type = MSG_ACCT_IND;
X		md->header.cdid = -1;
X
X		md->driver = driver;
X		md->driver_unit = driver_unit;	
X
X		md->accttype = accttype;
X		md->ioutbytes = ioutbytes;
X		md->iinbytes = iinbytes;
X		md->outbps = ro;
X		md->inbps = ri;
X		md->outbytes = outbytes;
X		md->inbytes = inbytes;
X		
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_CONNECT_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_connect_ind(call_desc_t *cd)
X{
X	struct mbuf *m;
X
X	if((m = i4b_Dgetmbuf(sizeof(msg_connect_ind_t))) != NULL)
X	{
X		msg_connect_ind_t *mp = (msg_connect_ind_t *)m->m_data;
X
X		mp->header.type = MSG_CONNECT_IND;
X		mp->header.cdid = cd->cdid;
X
X		mp->controller = cd->controller;
X		mp->channel = cd->channelid;
X		mp->bprot = cd->bprot;
X
X		cd->dir = DIR_INCOMING;
X
X		if(strlen(cd->dst_telno) > 0)
X			strcpy(mp->dst_telno, cd->dst_telno);
X		else
X			strcpy(mp->dst_telno, TELNO_EMPTY);
X
X		if(strlen(cd->src_telno) > 0)
X			strcpy(mp->src_telno, cd->src_telno);
X		else
X			strcpy(mp->src_telno, TELNO_EMPTY);
X			
X		strcpy(mp->display, cd->display);
X
X		mp->scr_ind = cd->scr_ind;
X		
X		T400_start(cd);
X		
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_CONNECT_ACTIVE_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_connect_active_ind(call_desc_t *cd)
X{
X	int s;
X	struct mbuf *m;
X
X	s = SPLI4B();
X
X	cd->last_active_time = cd->connect_time = SECOND;
X
X	DBGL4(L4_TIMO, "i4b_l4_connect_active_ind", ("last_active/connect_time=%ld\n", (long)cd->connect_time));
X	
X	i4b_link_bchandrvr(cd);
X
X	(*cd->dlt->line_connected)(cd->driver_unit, (void *)cd);
X
X	i4b_l4_setup_timeout(cd);
X	
X	splx(s);	
X	
X	if((m = i4b_Dgetmbuf(sizeof(msg_connect_active_ind_t))) != NULL)
X	{
X		msg_connect_active_ind_t *mp = (msg_connect_active_ind_t *)m->m_data;
X
X		mp->header.type = MSG_CONNECT_ACTIVE_IND;
X		mp->header.cdid = cd->cdid;
X		mp->controller = cd->controller;
X		mp->channel = cd->channelid;
X		if(cd->datetime[0] != '\0')
X			strcpy(mp->datetime, cd->datetime);
X		else
X			mp->datetime[0] = '\0';
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_DISCONNECT_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_disconnect_ind(call_desc_t *cd)
X{
X	struct mbuf *m;
X
X	if(cd->timeout_active)
X#if defined(__FreeBSD_version) && __FreeBSD_version >= 300001
X		untimeout((TIMEOUT_FUNC_T)i4b_idle_check,(void *)cd, cd->idle_timeout_handle);	
X#else
X		untimeout((TIMEOUT_FUNC_T)i4b_idle_check,(void *)cd);	
X#endif
X
X	if(cd->dlt != NULL)
X	{
X		(*cd->dlt->line_disconnected)(cd->driver_unit, (void *)cd);
X		i4b_unlink_bchandrvr(cd);
X	}
X
X	if((cd->channelid == CHAN_B1) || (cd->channelid == CHAN_B2))
X	{
X		ctrl_desc[cd->controller].bch_state[cd->channelid] = BCH_ST_FREE;
X	}
X	else
X	{
X		/* no error, might be hunting call for callback */
X		DBGL4(L4_MSG, "i4b_l4_disconnect_ind", ("channel free not B1/B2 but %d!\n", cd->channelid));
X	}
X	
X	if((m = i4b_Dgetmbuf(sizeof(msg_disconnect_ind_t))) != NULL)
X	{
X		msg_disconnect_ind_t *mp = (msg_disconnect_ind_t *)m->m_data;
X
X		mp->header.type = MSG_DISCONNECT_IND;
X		mp->header.cdid = cd->cdid;
X		mp->cause = cd->cause_in;
X
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_IDLE_TIMEOUT_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_idle_timeout_ind(call_desc_t *cd)
X{
X	struct mbuf *m;
X
X	if((m = i4b_Dgetmbuf(sizeof(msg_idle_timeout_ind_t))) != NULL)
X	{
X		msg_idle_timeout_ind_t *mp = (msg_idle_timeout_ind_t *)m->m_data;
X
X		mp->header.type = MSG_IDLE_TIMEOUT_IND;
X		mp->header.cdid = cd->cdid;
X
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_CHARGING_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_charging_ind(call_desc_t *cd)
X{
X	struct mbuf *m;
X
X	if((m = i4b_Dgetmbuf(sizeof(msg_charging_ind_t))) != NULL)
X	{
X		msg_charging_ind_t *mp = (msg_charging_ind_t *)m->m_data;
X
X		mp->header.type = MSG_CHARGING_IND;
X		mp->header.cdid = cd->cdid;
X		mp->units_type = cd->units_type;
X
X/*XXX*/		if(mp->units_type == CHARGE_CALC)
X			mp->units = cd->cunits;
X		else
X			mp->units = cd->units;
X
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_STATUS_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_status_ind(call_desc_t *cd)
X{
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_ALERT_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_alert_ind(call_desc_t *cd)
X{
X	struct mbuf *m;
X
X	if((m = i4b_Dgetmbuf(sizeof(msg_alert_ind_t))) != NULL)
X	{
X		msg_alert_ind_t *mp = (msg_alert_ind_t *)m->m_data;
X
X		mp->header.type = MSG_ALERT_IND;
X		mp->header.cdid = cd->cdid;
X
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_INFO_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_info_ind(call_desc_t *cd)
X{
X}
X
X/*---------------------------------------------------------------------------*
X *	send MSG_INFO_IND message to userland
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_l4_proceeding_ind(call_desc_t *cd)
X{
X	struct mbuf *m;
X
X	if((m = i4b_Dgetmbuf(sizeof(msg_proceeding_ind_t))) != NULL)
X	{
X		msg_proceeding_ind_t *mp = (msg_proceeding_ind_t *)m->m_data;
X
X		mp->header.type = MSG_PROCEEDING_IND;
X		mp->header.cdid = cd->cdid;
X		mp->controller = cd->controller;
X		mp->channel = cd->channelid;
X		i4bputqueue(m);
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	link a driver(unit) to a B-channel(controller,unit,channel)
X *---------------------------------------------------------------------------*/
Xstatic int
Xi4b_link_bchandrvr(call_desc_t *cd)
X{
X	int t = ctrl_desc[cd->controller].ctrl_type;
X	
X	if(t < 0 || t >= CTRL_NUMTYPES || ctrl_types[t].get_linktab == NULL)
X	{
X			cd->ilt = NULL;
X	}
X	else
X	{
X		cd->ilt = ctrl_types[t].get_linktab(
X			ctrl_desc[cd->controller].unit,
X			cd->channelid);
X	}
X
X	switch(cd->driver)
X	{
X#if NI4BRBCH > 0
X		case BDRV_RBCH:
X			cd->dlt = rbch_ret_linktab(cd->driver_unit);
X			break;
X#endif
X		
X#if NI4BTEL > 0
X		case BDRV_TEL:
X			cd->dlt = tel_ret_linktab(cd->driver_unit);
X			break;
X#endif
X
X#if NI4BIPR > 0
X		case BDRV_IPR:
X			cd->dlt = ipr_ret_linktab(cd->driver_unit);
X			break;
X#endif
X
X#if NI4BISPPP > 0
X		case BDRV_ISPPP:
X			cd->dlt = i4bisppp_ret_linktab(cd->driver_unit);
X			break;
X#endif
X
X		default:
X			cd->dlt = NULL;
X			break;
X	}
X
X	if(cd->dlt == NULL || cd->ilt == NULL)
X		return(-1);
X
X	if(t >= 0 && t < CTRL_NUMTYPES && ctrl_types[t].set_linktab != NULL)
X	{
X		ctrl_types[t].set_linktab(
X				ctrl_desc[cd->controller].unit,
X				cd->channelid,
X				cd->dlt);
X	}
X
X	switch(cd->driver)
X	{
X#if NI4BRBCH > 0
X		case BDRV_RBCH:
X			rbch_set_linktab(cd->driver_unit, cd->ilt);
X			break;
X#endif
X
X#if NI4BTEL > 0
X		case BDRV_TEL:
X			tel_set_linktab(cd->driver_unit, cd->ilt);
X			break;
X#endif
X
X#if NI4BIPR > 0
X		case BDRV_IPR:
X			ipr_set_linktab(cd->driver_unit, cd->ilt);
X			break;
X#endif
X
X#if NI4BISPPP > 0
X		case BDRV_ISPPP:
X			i4bisppp_set_linktab(cd->driver_unit, cd->ilt);
X			break;
X#endif
X		default:
X			return(0);
X			break;
X	}
X
X	/* activate B channel */
X		
X	(*cd->ilt->bch_config)(ctrl_desc[cd->controller].unit,
X				cd->channelid, cd->bprot, 1);
X
X	return(0);
X}
X
X/*---------------------------------------------------------------------------*
X *	unlink a driver(unit) from a B-channel(controller,unit,channel)
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4b_unlink_bchandrvr(call_desc_t *cd)
X{
X	int t = ctrl_desc[cd->controller].ctrl_type;
X
X	if(t < 0 || t >= CTRL_NUMTYPES || ctrl_types[t].get_linktab == NULL)
X	{
X		cd->ilt = NULL;
X		return;
X	}
X	else
X	{
X		cd->ilt = ctrl_types[t].get_linktab(
X				ctrl_desc[cd->controller].unit,
X				cd->channelid);
X	}
X	
X	/* deactivate B channel */
X		
X	(*cd->ilt->bch_config)(ctrl_desc[cd->controller].unit,
X				cd->channelid, cd->bprot, 0);
X} 
X
X/*---------------------------------------------------------------------------
X
X	How shorthold mode works for OUTGOING connections
X	=================================================
X
X	|<---- unchecked-window ------->|<-checkwindow->|<-safetywindow>|
X
Xidletime_state:      IST_NONCHK             IST_CHECK       IST_SAFE	
X	
X	|				|		|		|
X  time>>+-------------------------------+---------------+---------------+-...
X	|				|		|		|
X	|				|<--idle_time-->|<--earlyhup--->|
X	|<-----------------------unitlen------------------------------->|
X
X	
X	  unitlen - specifies the time a charging unit lasts
X	idle_time - specifies the thime the line must be idle at the
X		    end of the unit to be elected for hangup
X	 earlyhup - is the beginning of a timing safety zone before the
X		    next charging unit starts
X
X	The algorithm works as follows: lets assume the unitlen is 100
X	secons, idle_time is 40 seconds and earlyhup is 10 seconds.
X	The line then must be idle 50 seconds after the begin of the
X	current unit and it must then be quiet for 40 seconds. if it
X	has been quiet for this 40 seconds, the line is closed 10
X	seconds before the next charging unit starts. In case there was
X	any traffic within the idle_time, the line is not closed.
X	It does not matter whether there was any traffic between second
X	0 and second 50 or not.
X
X
X	How shorthold mode works for INCOMING connections
X	=================================================
X
X	it is just possible to specify a maximum idle time for incoming
X	connections, after this time of no activity on the line the line
X	is closed.
X	
X---------------------------------------------------------------------------*/	
X
X/*---------------------------------------------------------------------------*
X *	B channel idle check timeout setup
X *---------------------------------------------------------------------------*/
Xstatic void
Xi4b_l4_setup_timeout(call_desc_t *cd)
X{
X	cd->timeout_active = 0;
X	cd->idletime_state = IST_IDLE;
X	
X	if((cd->dir == DIR_INCOMING) && (cd->max_idle_time > 0))
X	{
X		/* incoming call: simple max idletime check */
X	
X#if defined(__FreeBSD_version) && __FreeBSD_version >= 300001
X		cd->idle_timeout_handle =
X#endif
X		timeout((TIMEOUT_FUNC_T)i4b_idle_check,(void *)cd, hz/2);
X		cd->timeout_active = 1;
X		DBGL4(L4_TIMO, "i4b_l4_setup_timeout", ("%ld: incoming-call, setup max_idle_time to %ld\n", (long)SECOND, (long)cd->max_idle_time));
X	}
X	else if((cd->dir == DIR_OUTGOING) && (cd->idle_time > 0))
X	{
X		/* outgoing call */
X		
X		if((cd->idle_time > 0) && (cd->unitlen_time == 0))
X		{
X			/* outgoing call: simple max idletime check */
X			
X#if defined(__FreeBSD_version) && __FreeBSD_version >= 300001
X			cd->idle_timeout_handle =
X#endif
X			timeout((TIMEOUT_FUNC_T)i4b_idle_check,(void *)cd, hz/2);
X			cd->timeout_active = 1;
X			DBGL4(L4_TIMO, "i4b_l4_setup_timeout", ("%ld: outgoing-call, setup idle_time to %ld\n", (long)SECOND, (long)cd->idle_time));
X		}
X		else if((cd->unitlen_time > 0) && (cd->unitlen_time > (cd->idle_time + cd->earlyhup_time)))
X		{
X			/* outgoing call: full shorthold mode check */
X			
X#if defined(__FreeBSD_version) && __FreeBSD_version >= 300001
X			cd->idle_timeout_handle =
X#endif
X			timeout((TIMEOUT_FUNC_T)i4b_idle_check,(void *)cd, hz*(cd->unitlen_time - (cd->idle_time + cd->earlyhup_time)));
X			cd->timeout_active = 1;
X			cd->idletime_state = IST_NONCHK;
X			DBGL4(L4_TIMO, "i4b_l4_setup_timeout", ("%ld: outgoing-call, start %ld sec nocheck window\n", 
X				(long)SECOND, (long)(cd->unitlen_time - (cd->idle_time + cd->earlyhup_time))));
X
X			if(cd->aocd_flag == 0)
X			{
X				cd->units_type = CHARGE_CALC;
X				cd->cunits++;
X				i4b_l4_charging_ind(cd);
X			}
X		}
X		else
X		{
X			/* parms somehow got wrong .. */
X			
X			DBGL4(L4_ERR, "i4b_l4_setup_timeout", ("%ld: ERROR: idletime[%ld]+earlyhup[%ld] > unitlength[%ld]!\n",
X				(long)SECOND, (long)cd->idle_time, (long)cd->earlyhup_time, (long)cd->unitlen_time));
X		}
X	}
X	else
X	{
X		DBGL4(L4_TIMO, "i4b_l4_setup_timeout", ("no idle_timeout configured\n"));
X	}
X}
X
X/*---------------------------------------------------------------------------*
X *	B channel idle check timeout function
X *---------------------------------------------------------------------------*/
Xvoid
Xi4b_idle_check(call_desc_t *cd)
X{
X	int s;
X
X	if(cd->cdid == CDID_UNUSED)
X		return;
X	
X	s = SPLI4B();
X
X	/* failsafe */
X
X	if(cd->timeout_active == 0)
X	{
X		DBGL4(L4_ERR, "i4b_idle_check", ("ERROR: timeout_active == 0 !!!\n"));
X	}
X	else
X	{	
X		cd->timeout_active = 0;
X	}
X	
X	/* incoming connections, simple idletime check */
X
X	if(cd->dir == DIR_INCOMING)
X	{
X		if((cd->last_active_time + cd->max_idle_time) <= SECOND)
X		{
X			DBGL4(L4_TIMO, "i4b_idle_check", ("%ld: incoming-call, line idle timeout, disconnecting!\n", (long)SECOND));
X			(*ctrl_desc[cd->controller].N_DISCONNECT_REQUEST)(cd->cdid,
X					(CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
X			i4b_l4_idle_timeout_ind(cd);
X		}
X		else
X		{
X			DBGL4(L4_TIMO, "i4b_idle_check", ("%ld: incoming-call, activity, last_active=%ld, max_idle=%ld\n", (long)SECOND, (long)cd->last_active_time, (long)cd->max_idle_time));
X#if defined(__FreeBSD_version) && __FreeBSD_version >= 300001
X			cd->idle_timeout_handle =
X#endif
X			timeout((TIMEOUT_FUNC_T)i4b_idle_check,(void *)cd, hz/2);
X			cd->timeout_active = 1;
X		}
X	}
X
X	/* outgoing connections */
X
X	else if(cd->dir == DIR_OUTGOING)
X	{
X
X		/* simple idletime calculation */
X
X		if((cd->idle_time > 0) && (cd->unitlen_time == 0))
X		{
X			if((cd->last_active_time + cd->idle_time) <= SECOND)
X			{
X				DBGL4(L4_TIMO, "i4b_idle_check", ("%ld: outgoing-call-st, idle timeout, disconnecting!\n", (long)SECOND));
X				(*ctrl_desc[cd->controller].N_DISCONNECT_REQUEST)(cd->cdid,
X					(CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
X				i4b_l4_idle_timeout_ind(cd);
X			}
X			else
X			{
X				DBGL4(L4_TIMO, "i4b_idle_check", ("%ld: outgoing-call-st, activity, last_active=%ld, max_idle=%ld\n",
X								(long)SECOND, (long)cd->last_active_time, (long)cd->idle_time));
X#if defined(__FreeBSD_version) && __FreeBSD_version >= 300001
X			cd->idle_timeout_handle =
X#endif
X				timeout((TIMEOUT_FUNC_T)i4b_idle_check,(void *)cd, hz/2);
X				cd->timeout_active = 1;
X			}
X		}
X
X		/* full shorthold mode calculation */
X
X		else if((cd->unitlen_time > 0) && (cd->unitlen_time > (cd->idle_time + cd->earlyhup_time)))
X		{
X			switch(cd->idletime_state)
X			{
X			case IST_NONCHK:	/* end of non-check time */
X#if defined(__FreeBSD_version) && __FreeBSD_version >= 300001
X				cd->idle_timeout_handle =
X#endif
X				timeout((TIMEOUT_FUNC_T)i4b_idle_check,(void *)cd, hz*(cd->idle_time));
X				cd->idletimechk_start = SECOND;
X				cd->idletime_state = IST_CHECK;
X				cd->timeout_active = 1;
X				DBGL4(L4_TIMO, "i4b_idle_check", ("%ld: outgoing-call, idletime check window reached!\n", (long)SECOND));
X				break;
X
X			case IST_CHECK:		/* end of idletime chk */
X				if((cd->last_active_time > cd->idletimechk_start) &&
X				   (cd->last_active_time <= SECOND))
X				{	/* activity detected */
X#if defined(__FreeBSD_version) && __FreeBSD_version >= 300001
X					cd->idle_timeout_handle =
X#endif
X					timeout((TIMEOUT_FUNC_T)i4b_idle_check,(void *)cd, hz*(cd->earlyhup_time));
X					cd->timeout_active = 1;
X					cd->idletime_state = IST_SAFE;
X					DBGL4(L4_TIMO, "i4b_idle_check", ("%ld: outgoing-call, activity at %ld, wait earlyhup-end\n", (long)SECOND, (long)cd->last_active_time));
X				}
X				else
X				{	/* no activity, hangup */
X					DBGL4(L4_TIMO, "i4b_idle_check", ("%ld: outgoing-call, idle timeout, last activity at %ld\n", (long)SECOND, (long)cd->last_active_time));
X					(*ctrl_desc[cd->controller].N_DISCONNECT_REQUEST)(cd->cdid,
X						(CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
X					i4b_l4_idle_timeout_ind(cd);
X					cd->idletime_state = IST_IDLE;
X				}
X				break;
X
X			case IST_SAFE:	/* end of earlyhup time */
X#if defined(__FreeBSD_version) && __FreeBSD_version >= 300001
X				cd->idle_timeout_handle =
X#endif
X				timeout((TIMEOUT_FUNC_T)i4b_idle_check,(void *)cd, hz*(cd->unitlen_time - (cd->idle_time+cd->earlyhup_time)));
X				cd->timeout_active = 1;
X				cd->idletime_state = IST_NONCHK;
X
X				if(cd->aocd_flag == 0)
X				{
X					cd->units_type = CHARGE_CALC;
X					cd->cunits++;
X					i4b_l4_charging_ind(cd);
X				}
X				
X				DBGL4(L4_TIMO, "i4b_idle_check", ("%ld: outgoing-call, earlyhup end, wait for idletime start\n", (long)SECOND));
X				break;
X
X			default:
X				DBGL4(L4_ERR, "i4b_idle_check", ("outgoing-call: invalid idletime_state value!\n"));
X				cd->idletime_state = IST_IDLE;
X				break;
X			}
X		}
X	}
X	splx(s);
X}
X
X#endif /* NI4B > 0 */
END-of-./usr/src/sys/i4b/layer4/i4b_l4.c.orig
echo c - ./usr/src/sbin
mkdir -p ./usr/src/sbin > /dev/null 2>&1
echo c - ./usr/src/sbin/spppcontrol
mkdir -p ./usr/src/sbin/spppcontrol > /dev/null 2>&1
echo x - ./usr/src/sbin/spppcontrol/Makefile
sed 's/^X//' >./usr/src/sbin/spppcontrol/Makefile << 'END-of-./usr/src/sbin/spppcontrol/Makefile'
XPROG=	spppcontrol
X
X.include <bsd.prog.mk>
END-of-./usr/src/sbin/spppcontrol/Makefile
echo x - ./usr/src/sbin/spppcontrol/spppcontrol.1
sed 's/^X//' >./usr/src/sbin/spppcontrol/spppcontrol.1 << 'END-of-./usr/src/sbin/spppcontrol/spppcontrol.1'
X.\" Copyright (C) 1997 by Joerg Wunsch, Dresden
X.\" All rights reserved.
X.\"
X.\" Redistribution and use in source and binary forms, with or without
X.\" modification, are permitted provided that the following conditions
X.\" are met:
X.\" 1. Redistributions of source code must retain the above copyright
X.\"    notice, this list of conditions and the following disclaimer.
X.\" 2. Redistributions in binary form must reproduce the above copyright
X.\"    notice, this list of conditions and the following disclaimer in the
X.\"    documentation and/or other materials provided with the distribution.
X.\"
X.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
X.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
X.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
X.\" DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
X.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
X.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
X.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
X.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
X.\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
X.\" POSSIBILITY OF SUCH DAMAGE.
X.\"
X.\" $Id: spppcontrol.1,v 1.3 1998/08/03 06:24:59 charnier Exp $
X.\"
X.Dd October 11, 1997
X.Os
X.Dt SPPPCONTROL 8
X.Sh NAME
X.Nm spppcontrol
X.Nd display or set parameters for an sppp interface
X.Sh SYNOPSIS
X.Nm spppcontrol
X.Op Fl v
X.Ar ifname
X.Op Ar parameter Ns Op \&= Ns Ar value
X.Op Ar ...
X.Sh DESCRIPTION
XThe
X.Xr sppp 4
Xdriver might require a number of additional arguments or optional
Xparameters besides the settings that can be adjusted with
X.Xr ifconfig 8 .
XThese are things like authentication protocol parameters, but also
Xother tunable configuration variables.  The
X.Nm
Xutility can be used to display the current settings, or adjust these
Xparameters as required.
X.Pp
XFor whatever intent
X.Nm
Xis being called, at least the parameter
X.Ar ifname
Xneeds to be specified, naming the interface for which the settings
Xare to be performed or displayed.  Use
X.Xr ifconfig 8 ,
Xor
X.Xr netstat 1
Xto see which interfaces are available.
X.Pp
XIf no other parameter is given,
X.Nm
Xwill just list the current settings for
X.Ar ifname
Xand exit.  The reported settings include the current PPP phase the
Xinterface is in, which can be one of the names
X.Em dead ,
X.Em establish ,
X.Em authenticate ,
X.Em network ,
Xor
X.Em terminate .
XIf an authentication protocol is configured for the interface, the
Xname of the protocol to be used, as well as the system name to be used
Xor expected will be displayed, plus any possible options to the
Xauthentication protocol if applicable.  Note that the authentication
Xsecrets (sometimes also called
X.Em keys )
Xare not being returned by the underlying system call, and are thus not
Xdisplayed.
X.Pp
XIf any additional parameter is supplied, superuser privileges are
Xrequired, and the command works in
X.Ql set
Xmode.  This is normally done quietly, unless the option
X.Fl v
Xis also enabled, which will cause a final printout of the settings as
Xdescribed above once all other actions have been taken.  Use of this
Xmode will be rejected if the interface is currently in any other phase
Xthan
X.Em dead .
XNote that you can force an interface into
X.Em dead
Xphase by calling
X.Xr ifconfig 8
Xwith the parameter
X.Ql down .
X.Pp
XThe currently supported parameters include:
X.Bl -tag -offset indent -width indent
X.It Ar authproto Ns \&= Ns Em protoname
XSet both, his and my authentication protocol to
X.Em protoname .
XThe protocol name can be one of
X.Ql chap ,
X.Ql pap ,
Xor
X.Ql none .
XIn the latter case, the use of an authentication protocol will be
Xturned off for the named interface.  This has the side-effect of
Xclearing the other authentication-related parameters for this
Xinterface as well (i.e. system name and authentication secret will
Xbe forgotten).
X.It Ar myauthproto Ns \&= Ns Em protoname
XSame as above, but only for my end of the link. I.e. this is the
Xprotocol when remote is authenticator, and I am the peer required to
Xauthenticate.
X.It Ar hisauthproto Ns \&= Ns Em protoname
XSame as above, but only for his end of the link.
X.It Ar myauthname Ns \&= Ns Em name
XSet my system name for the authentication protocol.
X.It Ar hisauthname Ns \&= Ns Em name
XSet his system name for the authentication protocol.  For CHAP, this
Xwill only be used as a hint, causing a warning message if remote did
Xsupply a different name.  For PAP, it's the name remote must use to
Xauthenticate himself (in connection with his secret).
X.It Ar myauthsecret Ns \&= Ns Em secret
XSet my secret (key, password) for use in the authentication phase.
XFor CHAP, this will be used to compute the response hash value, based
Xon remote's challenge.  For PAP, it will be transmitted as plain text
Xtogether with the system name.  Don't forget to quote the secrets from
Xthe shell if they contain shell metacharacters (or white space).
X.It Ar myauthkey Ns \&= Ns Em secret
XSame as above.
X.It Ar hisauthsecret Ns \&= Ns Em secret
XSame as above, to be used if we are authenticator and the remote peer
Xneeds to authenticate.
X.It Ar hisauthkey Ns \&= Ns Em secret
XSame as above.
X.It Ar callin
XRequire remote to authenticate himself only when he's calling in, but
Xnot when we are caller.  This is required for some peers that do not
Ximplement the authentication protocols symmetrically (like Ascend
Xrouters, for example).
X.It Ar always
XThe opposite of
X.Ar callin .
XRequire remote to always authenticate, regardless of which side is
Xplacing the call.  This is the default, and will not be explicitly
Xdisplayed in
X.Ql list
Xmode.
X.It Ar norechallenge
XOnly meaningful with CHAP.  Do not re-challenge peer once the initial
XCHAP handshake was successful.  Used to work around broken peer
Ximplementations that can't grok being re-challenged once the
Xconnection is up.
X.It Ar rechallenge
XWith CHAP, send re-challenges at random intervals while the connection
Xis in network phase.  (The intervals are currently in the range of 300
Xthrough approximately 800 seconds.)  This is the default, and will not
Xbe explicitly displayed in
X.Ql list
Xmode.
X.El
X.Sh EXAMPLES
X.Bd -literal
X# spppcontrol bppp0
Xbppp0:	phase=dead
X	myauthproto=chap myauthname="uriah"
X	hisauthproto=chap hisauthname="ifb-gw" norechallenge
X.Ed
X.Pp
XDisplay the settings for bppp0.  The interface is currently in
X.Em dead
Xphase, i.e. the LCP layer is down, and no traffic is possible.  Both
Xends of the connection use the CHAP protocol, my end tells remote the
Xsystem name
X.Ql uriah ,
Xand remote is expected to authenticate by the name
X.Ql ifb-gw .
XOnce the initial CHAP handshake was successful, no further CHAP
Xchallenges will be transmitted.  There are supposedly some known CHAP
Xsecrets for both ends of the link which are not being shown.
X.Pp
X.Bd -literal
X# spppcontrol bppp0 \e
X	authproto=chap \e
X	myauthname=uriah myauthsecret='some secret' \e
X	hisauthname=ifb-gw hisauthsecret='another' \e
X	norechallenge
X.Ed
X.Pp
XA possible call to
X.Nm
Xthat could have been used to bring the interface into the state shown
Xby the previous example.
X.Sh SEE ALSO
X.Xr netstat 1 ,
X.Xr sppp 4 ,
X.Xr ifconfig 8
X.Rs
X.%A B. Lloyd,  W. Simpson
X.%T "PPP Authentication Protocols"
X.%O RFC 1334
X.Re
X.Rs
X.%A W. Simpson, Editor
X.%T "The Point-to-Point Protocol (PPP)"
X.%O RFC 1661
X.Re
X.Rs
X.%A W. Simpson
X.%T "PPP Challenge Handshake Authentication Protocol (CHAP)"
X.%O RFC 1994
X.Re
X.Sh HISTORY
XThe
X.Nm
Xutility appeared in
X.Fx 3.0 .
X.Sh AUTHORS
XThe program was written by
X.ie t J\(:org Wunsch,
X.el Joerg Wunsch,
XDresden.
END-of-./usr/src/sbin/spppcontrol/spppcontrol.1
echo x - ./usr/src/sbin/spppcontrol/spppcontrol.c
sed 's/^X//' >./usr/src/sbin/spppcontrol/spppcontrol.c << 'END-of-./usr/src/sbin/spppcontrol/spppcontrol.c'
X/*
X * Copyright (c) 1997 Joerg Wunsch
X *
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X *
X * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
X * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
X * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
X * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
X * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
X * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
X * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
X * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
X * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
X */
X
X#ifndef lint
Xstatic const char rcsid[] =
X	"$Id: spppcontrol.c,v 1.4 1998/08/03 06:24:59 charnier Exp $";
X#endif /* not lint */
X
X#include <sys/param.h>
X#include <sys/callout.h>
X#include <sys/ioctl.h>
X#include <sys/socket.h>
X
X#include <net/if.h>
X#include <net/if_var.h>
X#include <net/if_sppp.h>
X
X#include <err.h>
X#include <stdio.h>
X#include <string.h>
X#include <sysexits.h>
X#include <unistd.h>
X
Xstatic void usage(void);
Xvoid	print_vals(const char *ifname, struct spppreq *sp);
Xconst char *phase_name(enum ppp_phase phase);
Xconst char *proto_name(u_short proto);
Xconst char *authflags(u_short flags);
X
X#define PPP_PAP		0xc023
X#define PPP_CHAP	0xc223
X
Xint
Xmain(int argc, char **argv)
X{
X	int s, c;
X	int errs = 0, verbose = 0;
X	size_t off;
X	const char *ifname, *cp;
X	struct ifreq ifr;
X	struct spppreq spr;
X
X	while ((c = getopt(argc, argv, "v")) != -1)
X		switch (c) {
X		case 'v':
X			verbose++;
X			break;
X
X		default:
X			errs++;
X			break;
X		}
X	argv += optind;
X	argc -= optind;
X
X	if (errs || argc < 1)
X		usage();
X
X	ifname = argv[0];
X	strncpy(ifr.ifr_name, ifname, sizeof ifr.ifr_name);
X
X	/* use a random AF to create the socket */
X	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
X		err(EX_UNAVAILABLE, "ifconfig: socket");
X
X	argc--;
X	argv++;
X
X	spr.cmd = (int)SPPPIOGDEFS;
X	ifr.ifr_data = (caddr_t)&spr;
X
X	if (ioctl(s, SIOCGIFGENERIC, &ifr) == -1)
X		err(EX_OSERR, "SIOCGIFGENERIC(SPPPIOGDEFS)");
X
X	if (argc == 0) {
X		/* list only mode */
X		print_vals(ifname, &spr);
X		return 0;
X	}
X
X#define startswith(s) strncmp(argv[0], s, (off = strlen(s))) == 0
X
X	while (argc > 0) {
X		if (startswith("authproto=")) {
X			cp = argv[0] + off;
X			if (strcmp(cp, "pap") == 0)
X				spr.defs.myauth.proto =
X					spr.defs.hisauth.proto = PPP_PAP;
X			else if (strcmp(cp, "chap") == 0)
X				spr.defs.myauth.proto =
X					spr.defs.hisauth.proto = PPP_CHAP;
X			else if (strcmp(cp, "none") == 0)
X				spr.defs.myauth.proto =
X					spr.defs.hisauth.proto = 0;
X			else
X				errx(EX_DATAERR, "bad auth proto: %s", cp);
X		} else if (startswith("myauthproto=")) {
X			cp = argv[0] + off;
X			if (strcmp(cp, "pap") == 0)
X				spr.defs.myauth.proto = PPP_PAP;
X			else if (strcmp(cp, "chap") == 0)
X				spr.defs.myauth.proto = PPP_CHAP;
X			else if (strcmp(cp, "none") == 0)
X				spr.defs.myauth.proto = 0;
X			else
X				errx(EX_DATAERR, "bad auth proto: %s", cp);
X		} else if (startswith("myauthname="))
X			strncpy(spr.defs.myauth.name, argv[0] + off,
X				AUTHNAMELEN);
X		else if (startswith("myauthsecret=") ||
X			 startswith("myauthkey="))
X			strncpy(spr.defs.myauth.secret, argv[0] + off,
X				AUTHKEYLEN);
X		else if (startswith("hisauthproto=")) {
X			cp = argv[0] + off;
X			if (strcmp(cp, "pap") == 0)
X				spr.defs.hisauth.proto = PPP_PAP;
X			else if (strcmp(cp, "chap") == 0)
X				spr.defs.hisauth.proto = PPP_CHAP;
X			else if (strcmp(cp, "none") == 0)
X				spr.defs.hisauth.proto = 0;
X			else
X				errx(EX_DATAERR, "bad auth proto: %s", cp);
X		} else if (startswith("hisauthname="))
X			strncpy(spr.defs.hisauth.name, argv[0] + off,
X				AUTHNAMELEN);
X		else if (startswith("hisauthsecret=") ||
X			 startswith("hisauthkey="))
X			strncpy(spr.defs.hisauth.secret, argv[0] + off,
X				AUTHKEYLEN);
X		else if (strcmp(argv[0], "callin") == 0)
X			spr.defs.hisauth.flags |= AUTHFLAG_NOCALLOUT;
X		else if (strcmp(argv[0], "always") == 0)
X			spr.defs.hisauth.flags &= ~AUTHFLAG_NOCALLOUT;
X		else if (strcmp(argv[0], "norechallenge") == 0)
X			spr.defs.hisauth.flags |= AUTHFLAG_NORECHALLENGE;
X		else if (strcmp(argv[0], "rechallenge") == 0)
X			spr.defs.hisauth.flags &= ~AUTHFLAG_NORECHALLENGE;
X		else
X			errx(EX_DATAERR, "bad parameter: \"%s\"", argv[0]);
X
X		argv++;
X		argc--;
X	}
X
X	spr.cmd = (int)SPPPIOSDEFS;
X
X	if (ioctl(s, SIOCSIFGENERIC, &ifr) == -1)
X		err(EX_OSERR, "SIOCSIFGENERIC(SPPPIOSDEFS)");
X
X	if (verbose)
X		print_vals(ifname, &spr);
X
X	return 0;
X}
X
Xstatic void
Xusage(void)
X{
X	fprintf(stderr, "%s\n%s\n",
X	"usage: spppcontrol [-v] ifname [{my|his}auth{proto|name|secret}=...]",
X	"       spppcontrol [-v] ifname callin|always");
X	exit(EX_USAGE);
X}
X
Xvoid
Xprint_vals(const char *ifname, struct spppreq *sp)
X{
X	time_t send, recv;
X	printf("%s:\tphase=%s\n", ifname, phase_name(sp->defs.pp_phase));
X	if (sp->defs.myauth.proto) {
X		printf("\tmyauthproto=%s myauthname=\"%.*s\"\n",
X		       proto_name(sp->defs.myauth.proto),
X		       AUTHNAMELEN, sp->defs.myauth.name);
X	}
X	if (sp->defs.hisauth.proto) {
X		printf("\thisauthproto=%s hisauthname=\"%.*s\"%s\n",
X		       proto_name(sp->defs.hisauth.proto),
X		       AUTHNAMELEN, sp->defs.hisauth.name,
X		       authflags(sp->defs.hisauth.flags));
X	}
X	send = time(NULL) - sp->defs.pp_last_sent;
X	recv = time(NULL) - sp->defs.pp_last_recv;
X	printf("\tidle_time=%ld\n", (send<recv)? send : recv);
X}
X
Xconst char *
Xphase_name(enum ppp_phase phase)
X{
X	switch (phase) {
X	case PHASE_DEAD:	return "dead";
X	case PHASE_ESTABLISH:	return "establish";
X	case PHASE_TERMINATE:	return "terminate";
X	case PHASE_AUTHENTICATE: return "authenticate";
X	case PHASE_NETWORK:	return "network";
X	}
X	return "illegal";
X}
X
Xconst char *
Xproto_name(u_short proto)
X{
X	static char buf[12];
X	switch (proto) {
X	case PPP_PAP:	return "pap";
X	case PPP_CHAP:	return "chap";
X	}
X	sprintf(buf, "0x%x", (unsigned)proto);
X	return buf;
X}
X
Xconst char *
Xauthflags(u_short flags)
X{
X	static char buf[10];
X	buf[0] = '\0';
X	if (flags & AUTHFLAG_NOCALLOUT)
X		strcat(buf, " callin");
X	if (flags & AUTHFLAG_NORECHALLENGE)
X		strcat(buf, " norechallenge");
X	return buf;
X}
END-of-./usr/src/sbin/spppcontrol/spppcontrol.c
echo x - ./usr/src/sbin/spppcontrol/spppcontrol.c.patch
sed 's/^X//' >./usr/src/sbin/spppcontrol/spppcontrol.c.patch << 'END-of-./usr/src/sbin/spppcontrol/spppcontrol.c.patch'
X*** spppcontrol.c	Mon Mar 15 15:04:04 1999
X--- spppcontrol.c.new	Mon Mar 15 15:03:43 1999
X***************
X*** 209,212 ****
X--- 209,213 ----
X  print_vals(const char *ifname, struct spppreq *sp)
X  {
X+ 	time_t send, recv;
X  	printf("%s:\tphase=%s\n", ifname, phase_name(sp->defs.pp_phase));
X  	if (sp->defs.myauth.proto) {
X***************
X*** 221,224 ****
X--- 222,228 ----
X  		       authflags(sp->defs.hisauth.flags));
X  	}
X+ 	send = time(NULL) - sp->defs.pp_last_sent;
X+ 	recv = time(NULL) - sp->defs.pp_last_recv;
X+ 	printf("\tidle_time=%ld\n", (send<recv)? send : recv);
X  }
X  
END-of-./usr/src/sbin/spppcontrol/spppcontrol.c.patch
echo x - ./usr/src/sbin/spppcontrol/spppcontrol.c.orig
sed 's/^X//' >./usr/src/sbin/spppcontrol/spppcontrol.c.orig << 'END-of-./usr/src/sbin/spppcontrol/spppcontrol.c.orig'
X/*
X * Copyright (c) 1997 Joerg Wunsch
X *
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X *
X * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
X * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
X * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
X * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
X * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
X * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
X * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
X * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
X * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
X */
X
X#ifndef lint
Xstatic const char rcsid[] =
X	"$Id: spppcontrol.c,v 1.4 1998/08/03 06:24:59 charnier Exp $";
X#endif /* not lint */
X
X#include <sys/param.h>
X#include <sys/callout.h>
X#include <sys/ioctl.h>
X#include <sys/socket.h>
X
X#include <net/if.h>
X#include <net/if_var.h>
X#include <net/if_sppp.h>
X
X#include <err.h>
X#include <stdio.h>
X#include <string.h>
X#include <sysexits.h>
X#include <unistd.h>
X
Xstatic void usage(void);
Xvoid	print_vals(const char *ifname, struct spppreq *sp);
Xconst char *phase_name(enum ppp_phase phase);
Xconst char *proto_name(u_short proto);
Xconst char *authflags(u_short flags);
X
X#define PPP_PAP		0xc023
X#define PPP_CHAP	0xc223
X
Xint
Xmain(int argc, char **argv)
X{
X	int s, c;
X	int errs = 0, verbose = 0;
X	size_t off;
X	const char *ifname, *cp;
X	struct ifreq ifr;
X	struct spppreq spr;
X
X	while ((c = getopt(argc, argv, "v")) != -1)
X		switch (c) {
X		case 'v':
X			verbose++;
X			break;
X
X		default:
X			errs++;
X			break;
X		}
X	argv += optind;
X	argc -= optind;
X
X	if (errs || argc < 1)
X		usage();
X
X	ifname = argv[0];
X	strncpy(ifr.ifr_name, ifname, sizeof ifr.ifr_name);
X
X	/* use a random AF to create the socket */
X	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
X		err(EX_UNAVAILABLE, "ifconfig: socket");
X
X	argc--;
X	argv++;
X
X	spr.cmd = (int)SPPPIOGDEFS;
X	ifr.ifr_data = (caddr_t)&spr;
X
X	if (ioctl(s, SIOCGIFGENERIC, &ifr) == -1)
X		err(EX_OSERR, "SIOCGIFGENERIC(SPPPIOGDEFS)");
X
X	if (argc == 0) {
X		/* list only mode */
X		print_vals(ifname, &spr);
X		return 0;
X	}
X
X#define startswith(s) strncmp(argv[0], s, (off = strlen(s))) == 0
X
X	while (argc > 0) {
X		if (startswith("authproto=")) {
X			cp = argv[0] + off;
X			if (strcmp(cp, "pap") == 0)
X				spr.defs.myauth.proto =
X					spr.defs.hisauth.proto = PPP_PAP;
X			else if (strcmp(cp, "chap") == 0)
X				spr.defs.myauth.proto =
X					spr.defs.hisauth.proto = PPP_CHAP;
X			else if (strcmp(cp, "none") == 0)
X				spr.defs.myauth.proto =
X					spr.defs.hisauth.proto = 0;
X			else
X				errx(EX_DATAERR, "bad auth proto: %s", cp);
X		} else if (startswith("myauthproto=")) {
X			cp = argv[0] + off;
X			if (strcmp(cp, "pap") == 0)
X				spr.defs.myauth.proto = PPP_PAP;
X			else if (strcmp(cp, "chap") == 0)
X				spr.defs.myauth.proto = PPP_CHAP;
X			else if (strcmp(cp, "none") == 0)
X				spr.defs.myauth.proto = 0;
X			else
X				errx(EX_DATAERR, "bad auth proto: %s", cp);
X		} else if (startswith("myauthname="))
X			strncpy(spr.defs.myauth.name, argv[0] + off,
X				AUTHNAMELEN);
X		else if (startswith("myauthsecret=") ||
X			 startswith("myauthkey="))
X			strncpy(spr.defs.myauth.secret, argv[0] + off,
X				AUTHKEYLEN);
X		else if (startswith("hisauthproto=")) {
X			cp = argv[0] + off;
X			if (strcmp(cp, "pap") == 0)
X				spr.defs.hisauth.proto = PPP_PAP;
X			else if (strcmp(cp, "chap") == 0)
X				spr.defs.hisauth.proto = PPP_CHAP;
X			else if (strcmp(cp, "none") == 0)
X				spr.defs.hisauth.proto = 0;
X			else
X				errx(EX_DATAERR, "bad auth proto: %s", cp);
X		} else if (startswith("hisauthname="))
X			strncpy(spr.defs.hisauth.name, argv[0] + off,
X				AUTHNAMELEN);
X		else if (startswith("hisauthsecret=") ||
X			 startswith("hisauthkey="))
X			strncpy(spr.defs.hisauth.secret, argv[0] + off,
X				AUTHKEYLEN);
X		else if (strcmp(argv[0], "callin") == 0)
X			spr.defs.hisauth.flags |= AUTHFLAG_NOCALLOUT;
X		else if (strcmp(argv[0], "always") == 0)
X			spr.defs.hisauth.flags &= ~AUTHFLAG_NOCALLOUT;
X		else if (strcmp(argv[0], "norechallenge") == 0)
X			spr.defs.hisauth.flags |= AUTHFLAG_NORECHALLENGE;
X		else if (strcmp(argv[0], "rechallenge") == 0)
X			spr.defs.hisauth.flags &= ~AUTHFLAG_NORECHALLENGE;
X		else
X			errx(EX_DATAERR, "bad parameter: \"%s\"", argv[0]);
X
X		argv++;
X		argc--;
X	}
X
X	spr.cmd = (int)SPPPIOSDEFS;
X
X	if (ioctl(s, SIOCSIFGENERIC, &ifr) == -1)
X		err(EX_OSERR, "SIOCSIFGENERIC(SPPPIOSDEFS)");
X
X	if (verbose)
X		print_vals(ifname, &spr);
X
X	return 0;
X}
X
Xstatic void
Xusage(void)
X{
X	fprintf(stderr, "%s\n%s\n",
X	"usage: spppcontrol [-v] ifname [{my|his}auth{proto|name|secret}=...]",
X	"       spppcontrol [-v] ifname callin|always");
X	exit(EX_USAGE);
X}
X
Xvoid
Xprint_vals(const char *ifname, struct spppreq *sp)
X{
X	printf("%s:\tphase=%s\n", ifname, phase_name(sp->defs.pp_phase));
X	if (sp->defs.myauth.proto) {
X		printf("\tmyauthproto=%s myauthname=\"%.*s\"\n",
X		       proto_name(sp->defs.myauth.proto),
X		       AUTHNAMELEN, sp->defs.myauth.name);
X	}
X	if (sp->defs.hisauth.proto) {
X		printf("\thisauthproto=%s hisauthname=\"%.*s\"%s\n",
X		       proto_name(sp->defs.hisauth.proto),
X		       AUTHNAMELEN, sp->defs.hisauth.name,
X		       authflags(sp->defs.hisauth.flags));
X	}
X}
X
Xconst char *
Xphase_name(enum ppp_phase phase)
X{
X	switch (phase) {
X	case PHASE_DEAD:	return "dead";
X	case PHASE_ESTABLISH:	return "establish";
X	case PHASE_TERMINATE:	return "terminate";
X	case PHASE_AUTHENTICATE: return "authenticate";
X	case PHASE_NETWORK:	return "network";
X	}
X	return "illegal";
X}
X
Xconst char *
Xproto_name(u_short proto)
X{
X	static char buf[12];
X	switch (proto) {
X	case PPP_PAP:	return "pap";
X	case PPP_CHAP:	return "chap";
X	}
X	sprintf(buf, "0x%x", (unsigned)proto);
X	return buf;
X}
X
Xconst char *
Xauthflags(u_short flags)
X{
X	static char buf[10];
X	buf[0] = '\0';
X	if (flags & AUTHFLAG_NOCALLOUT)
X		strcat(buf, " callin");
X	if (flags & AUTHFLAG_NORECHALLENGE)
X		strcat(buf, " norechallenge");
X	return buf;
X}
END-of-./usr/src/sbin/spppcontrol/spppcontrol.c.orig
exit


>Release-Note:
>Audit-Trail:
State-Changed-From-To: open->suspended 
State-Changed-By: hm 
State-Changed-When: Mon Oct 18 07:32:00 PDT 1999 
State-Changed-Why:  
This fix is part of the  i4b distribution since 05/99 and has not been 
integrated into i4b because of a drawback in the very first connection 
handling when thisis patch was applied. 
According to some testers from the i4b development this patch in its 
current form is not the right way to solve the problem. 


Responsible-Changed-From-To: freebsd-bugs->hm 
Responsible-Changed-By: hm 
Responsible-Changed-When: Mon Oct 18 07:32:00 PDT 1999 
Responsible-Changed-Why:  
State-Changed-From-To: suspended->closed 
State-Changed-By: hm 
State-Changed-When: Thu Dec 30 00:36:40 PST 1999 
State-Changed-Why:  
This fix is part of the i4b distribution as /contrib/lcp-patch2.tar.uu. The 
README supplied with this patch says: 
KNOWN BUGS: 
* Initial idle time upon reboot is always very, very, very large.  
Everything is OK, after the first packet is sent.  But this causes 
a rough start. 
* Doesn't not yet work with shorthold mode.  (I didn't get around to 
it.) 

* Works only with FreeBSD-3.0 and higher (kernel variable time_seconds) 

* others (???) 

Because of this bugs this patch solves a problem by introducing at least two 
new bugs. This is IMHO not acceptable and i wovill not apply this patch  
until the known bugs are removed from this patch. 
>Unformatted:
