diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/ar/if_ar.c | 9 | ||||
-rw-r--r-- | sys/dev/ar/if_ar_isa.c | 9 | ||||
-rw-r--r-- | sys/dev/sr/if_sr.c | 20 | ||||
-rw-r--r-- | sys/dev/sr/if_sr_isa.c | 20 | ||||
-rw-r--r-- | sys/i386/isa/if_ar.c | 9 | ||||
-rw-r--r-- | sys/i386/isa/if_cx.c | 8 | ||||
-rw-r--r-- | sys/i386/isa/if_sr.c | 20 | ||||
-rw-r--r-- | sys/net/if_sppp.h | 80 | ||||
-rw-r--r-- | sys/net/if_spppsubr.c | 2730 |
9 files changed, 2119 insertions, 786 deletions
diff --git a/sys/dev/ar/if_ar.c b/sys/dev/ar/if_ar.c index a0fad0f..8e5c384 100644 --- a/sys/dev/ar/if_ar.c +++ b/sys/dev/ar/if_ar.c @@ -28,7 +28,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id$ + * $Id: if_ar.c,v 1.14 1997/02/22 09:36:15 peter Exp $ */ /* @@ -349,6 +349,13 @@ arattach(struct isa_device *id) sppp_attach((struct ifnet *)&sc->ifsppp); if_attach(ifp); + /* + * Shortcut the sppp tls/tlf actions to up/down events + * since our lower layer is always ready. + */ + sc->ifsppp.pp_tls = sc->ifsppp.pp_up; + sc->ifsppp.pp_tlf = sc->ifsppp.pp_down; + #if NBPFILTER > 0 bpfattach(ifp, DLT_PPP, PPP_HEADER_LEN); #endif diff --git a/sys/dev/ar/if_ar_isa.c b/sys/dev/ar/if_ar_isa.c index a0fad0f..8e5c384 100644 --- a/sys/dev/ar/if_ar_isa.c +++ b/sys/dev/ar/if_ar_isa.c @@ -28,7 +28,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id$ + * $Id: if_ar.c,v 1.14 1997/02/22 09:36:15 peter Exp $ */ /* @@ -349,6 +349,13 @@ arattach(struct isa_device *id) sppp_attach((struct ifnet *)&sc->ifsppp); if_attach(ifp); + /* + * Shortcut the sppp tls/tlf actions to up/down events + * since our lower layer is always ready. + */ + sc->ifsppp.pp_tls = sc->ifsppp.pp_up; + sc->ifsppp.pp_tlf = sc->ifsppp.pp_down; + #if NBPFILTER > 0 bpfattach(ifp, DLT_PPP, PPP_HEADER_LEN); #endif diff --git a/sys/dev/sr/if_sr.c b/sys/dev/sr/if_sr.c index 21c0f48..a9abd8f 100644 --- a/sys/dev/sr/if_sr.c +++ b/sys/dev/sr/if_sr.c @@ -27,7 +27,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id$ + * $Id: if_sr.c,v 1.6 1997/02/22 09:36:35 peter Exp $ */ /* @@ -1304,6 +1304,14 @@ srioctl(struct ifnet *ifp, int cmd, caddr_t data) default: sc->ifsppp.pp_flags = PP_KEEPALIVE; sppp_attach(&sc->ifsppp.pp_if); + + /* + * Shortcut the sppp tls/tlf actions to + * up/down events since our lower layer is + * always ready. + */ + sc->ifsppp.pp_tls = sc->ifsppp.pp_up; + sc->ifsppp.pp_tlf = sc->ifsppp.pp_down; } sc->attached = sc->protocol; @@ -1487,7 +1495,15 @@ sr_up(struct sr_softc *sc) default: sc->ifsppp.pp_flags = PP_KEEPALIVE; sppp_attach(&sc->ifsppp.pp_if); - } + + /* + * Shortcut the sppp tls/tlf actions to + * up/down events since our lower layer is + * always ready. + */ + sc->ifsppp.pp_tls = sc->ifsppp.pp_up; + sc->ifsppp.pp_tlf = sc->ifsppp.pp_down; + } sc->attached = sc->protocol; } diff --git a/sys/dev/sr/if_sr_isa.c b/sys/dev/sr/if_sr_isa.c index 21c0f48..a9abd8f 100644 --- a/sys/dev/sr/if_sr_isa.c +++ b/sys/dev/sr/if_sr_isa.c @@ -27,7 +27,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id$ + * $Id: if_sr.c,v 1.6 1997/02/22 09:36:35 peter Exp $ */ /* @@ -1304,6 +1304,14 @@ srioctl(struct ifnet *ifp, int cmd, caddr_t data) default: sc->ifsppp.pp_flags = PP_KEEPALIVE; sppp_attach(&sc->ifsppp.pp_if); + + /* + * Shortcut the sppp tls/tlf actions to + * up/down events since our lower layer is + * always ready. + */ + sc->ifsppp.pp_tls = sc->ifsppp.pp_up; + sc->ifsppp.pp_tlf = sc->ifsppp.pp_down; } sc->attached = sc->protocol; @@ -1487,7 +1495,15 @@ sr_up(struct sr_softc *sc) default: sc->ifsppp.pp_flags = PP_KEEPALIVE; sppp_attach(&sc->ifsppp.pp_if); - } + + /* + * Shortcut the sppp tls/tlf actions to + * up/down events since our lower layer is + * always ready. + */ + sc->ifsppp.pp_tls = sc->ifsppp.pp_up; + sc->ifsppp.pp_tlf = sc->ifsppp.pp_down; + } sc->attached = sc->protocol; } diff --git a/sys/i386/isa/if_ar.c b/sys/i386/isa/if_ar.c index a0fad0f..8e5c384 100644 --- a/sys/i386/isa/if_ar.c +++ b/sys/i386/isa/if_ar.c @@ -28,7 +28,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id$ + * $Id: if_ar.c,v 1.14 1997/02/22 09:36:15 peter Exp $ */ /* @@ -349,6 +349,13 @@ arattach(struct isa_device *id) sppp_attach((struct ifnet *)&sc->ifsppp); if_attach(ifp); + /* + * Shortcut the sppp tls/tlf actions to up/down events + * since our lower layer is always ready. + */ + sc->ifsppp.pp_tls = sc->ifsppp.pp_up; + sc->ifsppp.pp_tlf = sc->ifsppp.pp_down; + #if NBPFILTER > 0 bpfattach(ifp, DLT_PPP, PPP_HEADER_LEN); #endif diff --git a/sys/i386/isa/if_cx.c b/sys/i386/isa/if_cx.c index d57480b..c7dd4e4 100644 --- a/sys/i386/isa/if_cx.c +++ b/sys/i386/isa/if_cx.c @@ -224,6 +224,7 @@ cxattach (struct isa_device *id) int drq = id->id_drq; cx_board_t *b = cxboard + unit; int i; + struct sppp *sp; /* Initialize the board structure. */ cx_init (b, unit, iobase, ffs(irq)-1, drq); @@ -276,6 +277,13 @@ cxattach (struct isa_device *id) /* Init routine is never called by upper level? */ sppp_attach (c->ifp); if_attach (c->ifp); + /* + * Shortcut the sppp tls/tlf actions to up/down + * events since our lower layer is always ready. + */ + sp = (struct sppp*) c->ifp; + sp->pp_tls = sp->pp_up; + sp->pp_tlf = sp->pp_down; #if NBPFILTER > 0 /* If BPF is in the kernel, call the attach for it. */ bpfattach (c->ifp, DLT_PPP, PPP_HEADER_LEN); diff --git a/sys/i386/isa/if_sr.c b/sys/i386/isa/if_sr.c index 21c0f48..a9abd8f 100644 --- a/sys/i386/isa/if_sr.c +++ b/sys/i386/isa/if_sr.c @@ -27,7 +27,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id$ + * $Id: if_sr.c,v 1.6 1997/02/22 09:36:35 peter Exp $ */ /* @@ -1304,6 +1304,14 @@ srioctl(struct ifnet *ifp, int cmd, caddr_t data) default: sc->ifsppp.pp_flags = PP_KEEPALIVE; sppp_attach(&sc->ifsppp.pp_if); + + /* + * Shortcut the sppp tls/tlf actions to + * up/down events since our lower layer is + * always ready. + */ + sc->ifsppp.pp_tls = sc->ifsppp.pp_up; + sc->ifsppp.pp_tlf = sc->ifsppp.pp_down; } sc->attached = sc->protocol; @@ -1487,7 +1495,15 @@ sr_up(struct sr_softc *sc) default: sc->ifsppp.pp_flags = PP_KEEPALIVE; sppp_attach(&sc->ifsppp.pp_if); - } + + /* + * Shortcut the sppp tls/tlf actions to + * up/down events since our lower layer is + * always ready. + */ + sc->ifsppp.pp_tls = sc->ifsppp.pp_up; + sc->ifsppp.pp_tlf = sc->ifsppp.pp_down; + } sc->attached = sc->protocol; } diff --git a/sys/net/if_sppp.h b/sys/net/if_sppp.h index 378fdd3..d40b591 100644 --- a/sys/net/if_sppp.h +++ b/sys/net/if_sppp.h @@ -2,7 +2,10 @@ * Defines for synchronous PPP/Cisco link level subroutines. * * Copyright (C) 1994 Cronyx Ltd. - * Author: Serge Vakulenko, <vak@zebub.msk.su> + * Author: Serge Vakulenko, <vak@cronyx.ru> + * + * Heavily revamped to conform to RFC 1661. + * Copyright (C) 1997, Joerg Wunsch. * * This software is distributed with NO WARRANTIES, not even the implied * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. @@ -11,25 +14,53 @@ * or modify this software as long as this message is kept with the software, * all derivative works or modified versions. * - * Version 1.7, Wed Jun 7 22:12:02 MSD 1995 + * From: Version 1.7, Wed Jun 7 22:12:02 MSD 1995 + * + * $Id$ */ #ifndef _NET_IF_HDLC_H_ #define _NET_IF_HDLC_H_ 1 +#define IDX_LCP 0 /* idx into state table */ + struct slcp { - u_short state; /* state machine */ + u_long opts; /* LCP options to send (bitfield) */ u_long magic; /* local magic number */ + u_long mru; /* our max receive unit */ + u_long their_mru; /* their max receive unit */ + u_long protos; /* bitmask of protos that are started */ u_char echoid; /* id of last keepalive echo request */ - u_char confid; /* id of last configuration request */ + /* restart max values, see RFC 1661 */ + int timeout; + int max_terminate; + int max_configure; + int max_failure; }; +#define IDX_IPCP 1 /* idx into state table */ + struct sipcp { - u_short state; /* state machine */ - u_char confid; /* id of last configuration request */ + u_long opts; /* IPCP options to send (bitfield) */ + u_int flags; +#define IPCP_HISADDR_SEEN 1 /* have seen his address already */ +#define IPCP_MYADDR_DYN 2 /* my address is dynamically assigned */ +}; + +#define IDX_COUNT (IDX_IPCP + 1) /* bump this when adding cp's! */ + +/* + * Don't change the order of this. Ordering the phases this way allows + * for a comparision of ``pp_phase >= PHASE_AUTHENTICATE'' in order to + * know whether LCP is up. + */ +enum ppp_phase { + PHASE_DEAD, PHASE_ESTABLISH, PHASE_TERMINATE, + PHASE_AUTHENTICATE, PHASE_NETWORK }; struct sppp { + /* NB: pp_if _must_ be first */ struct ifnet pp_if; /* network interface data */ struct ifqueue pp_fastq; /* fast output queue */ struct sppp *pp_next; /* next interface in keepalive list */ @@ -38,25 +69,38 @@ struct sppp { u_short pp_loopcnt; /* loopback detection counter */ u_long pp_seq; /* local sequence number */ u_long pp_rseq; /* remote sequence number */ + enum ppp_phase pp_phase; /* phase we're currently in */ + int state[IDX_COUNT]; /* state machine */ + u_char confid[IDX_COUNT]; /* id of last configuration request */ + int rst_counter[IDX_COUNT]; /* restart counter */ + int fail_counter[IDX_COUNT]; /* negotiation failure counter */ struct slcp lcp; /* LCP params */ struct sipcp ipcp; /* IPCP params */ + /* + * These functions are filled in by sppp_attach(), and are + * expected to be used by the lower layer (hardware) drivers + * in order to communicate the (un)availability of the + * communication link. Lower layer drivers that are always + * ready to communicate (like hardware HDLC) can shortcut + * pp_up from pp_tls, and pp_down from pp_tlf. + */ + void (*pp_up)(struct sppp *sp); + void (*pp_down)(struct sppp *sp); + /* + * These functions need to be filled in by the lower layer + * (hardware) drivers if they request notification from the + * PPP layer whether the link is actually required. They + * correspond to the tls and tlf actions. + */ + void (*pp_tls)(struct sppp *sp); + void (*pp_tlf)(struct sppp *sp); }; #define PP_KEEPALIVE 0x01 /* use keepalive protocol */ #define PP_CISCO 0x02 /* use Cisco protocol instead of PPP */ -#define PP_TIMO 0x04 /* cp_timeout routine active */ - -#define PP_MTU 1500 /* max. transmit unit */ - -#define LCP_STATE_CLOSED 0 /* LCP state: closed (conf-req sent) */ -#define LCP_STATE_ACK_RCVD 1 /* LCP state: conf-ack received */ -#define LCP_STATE_ACK_SENT 2 /* LCP state: conf-ack sent */ -#define LCP_STATE_OPENED 3 /* LCP state: opened */ -#define IPCP_STATE_CLOSED 0 /* IPCP state: closed (conf-req sent) */ -#define IPCP_STATE_ACK_RCVD 1 /* IPCP state: conf-ack received */ -#define IPCP_STATE_ACK_SENT 2 /* IPCP state: conf-ack sent */ -#define IPCP_STATE_OPENED 3 /* IPCP state: opened */ +#define PP_MTU 1500 /* default/minimal MRU */ +#define PP_MAX_MRU 2048 /* maximal MRU we want to negotiate */ #ifdef KERNEL void sppp_attach (struct ifnet *ifp); diff --git a/sys/net/if_spppsubr.c b/sys/net/if_spppsubr.c index bc40e32..5b10f3a 100644 --- a/sys/net/if_spppsubr.c +++ b/sys/net/if_spppsubr.c @@ -3,7 +3,10 @@ * Keepalive protocol implemented in both Cisco and PPP modes. * * Copyright (C) 1994 Cronyx Ltd. - * Author: Serge Vakulenko, <vak@zebub.msk.su> + * Author: Serge Vakulenko, <vak@cronyx.ru> + * + * Heavily revamped to conform to RFC 1661. + * Copyright (C) 1997, Joerg Wunsch. * * This software is distributed with NO WARRANTIES, not even the implied * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. @@ -12,9 +15,9 @@ * or modify this software as long as this message is kept with the software, * all derivative works or modified versions. * - * Version 1.9, Wed Oct 4 18:58:15 MSK 1995 + * From: Version 1.9, Wed Oct 4 18:58:15 MSK 1995 * - * $Id: if_spppsubr.c,v 1.17 1997/03/24 11:33:16 bde Exp $ + * $Id: if_spppsubr.c,v 1.18 1997/05/11 10:04:24 joerg Exp $ */ #include <sys/param.h> @@ -59,6 +62,21 @@ #define MAXALIVECNT 3 /* max. alive packets */ +/* + * Interface flags that can be set in an ifconfig command. + * + * Setting link0 will cause the link to auto-dial only as packets + * arrive to be sent. + * + * Setting link1 will make the link passive, i.e. it will be marked + * as being administrative openable, but won't be opened to begin + * with. Incoming calls will be answered, or subsequent calls with + * -link1 will cause the administrative open of the LCP layer. + */ + +#define IFF_AUTO IFF_LINK0 /* auto-dial on output */ +#define IFF_PASSIVE IFF_LINK1 /* wait passively for connection */ + #define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */ #define PPP_UI 0x03 /* Unnumbered Information */ #define PPP_IP 0x0021 /* Internet Protocol */ @@ -68,17 +86,17 @@ #define PPP_LCP 0xc021 /* Link Control Protocol */ #define PPP_IPCP 0x8021 /* Internet Protocol Control Protocol */ -#define LCP_CONF_REQ 1 /* PPP LCP configure request */ -#define LCP_CONF_ACK 2 /* PPP LCP configure acknowledge */ -#define LCP_CONF_NAK 3 /* PPP LCP configure negative ack */ -#define LCP_CONF_REJ 4 /* PPP LCP configure reject */ -#define LCP_TERM_REQ 5 /* PPP LCP terminate request */ -#define LCP_TERM_ACK 6 /* PPP LCP terminate acknowledge */ -#define LCP_CODE_REJ 7 /* PPP LCP code reject */ -#define LCP_PROTO_REJ 8 /* PPP LCP protocol reject */ -#define LCP_ECHO_REQ 9 /* PPP LCP echo request */ -#define LCP_ECHO_REPLY 10 /* PPP LCP echo reply */ -#define LCP_DISC_REQ 11 /* PPP LCP discard request */ +#define CONF_REQ 1 /* PPP configure request */ +#define CONF_ACK 2 /* PPP configure acknowledge */ +#define CONF_NAK 3 /* PPP configure negative ack */ +#define CONF_REJ 4 /* PPP configure reject */ +#define TERM_REQ 5 /* PPP terminate request */ +#define TERM_ACK 6 /* PPP terminate acknowledge */ +#define CODE_REJ 7 /* PPP code reject */ +#define PROTO_REJ 8 /* PPP protocol reject */ +#define ECHO_REQ 9 /* PPP echo request */ +#define ECHO_REPLY 10 /* PPP echo reply */ +#define DISC_REQ 11 /* PPP discard request */ #define LCP_OPT_MRU 1 /* maximum receive unit */ #define LCP_OPT_ASYNC_MAP 2 /* async control character map */ @@ -89,13 +107,9 @@ #define LCP_OPT_PROTO_COMP 7 /* protocol field compression */ #define LCP_OPT_ADDR_COMP 8 /* address/control field compression */ -#define IPCP_CONF_REQ LCP_CONF_REQ /* PPP IPCP configure request */ -#define IPCP_CONF_ACK LCP_CONF_ACK /* PPP IPCP configure acknowledge */ -#define IPCP_CONF_NAK LCP_CONF_NAK /* PPP IPCP configure negative ack */ -#define IPCP_CONF_REJ LCP_CONF_REJ /* PPP IPCP configure reject */ -#define IPCP_TERM_REQ LCP_TERM_REQ /* PPP IPCP terminate request */ -#define IPCP_TERM_ACK LCP_TERM_ACK /* PPP IPCP terminate acknowledge */ -#define IPCP_CODE_REJ LCP_CODE_REJ /* PPP IPCP code reject */ +#define IPCP_OPT_ADDRESSES 1 /* both IP addresses; deprecated */ +#define IPCP_OPT_COMPRESSION 2 /* IP compression protocol (VJ) */ +#define IPCP_OPT_ADDRESS 3 /* local IP address */ #define CISCO_MULTICAST 0x8f /* Cisco multicast address */ #define CISCO_UNICAST 0x0f /* Cisco unicast address */ @@ -104,6 +118,18 @@ #define CISCO_ADDR_REPLY 1 /* Cisco address reply */ #define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */ +/* states are named and numbered according to RFC 1661 */ +#define STATE_INITIAL 0 +#define STATE_STARTING 1 +#define STATE_CLOSED 2 +#define STATE_STOPPED 3 +#define STATE_CLOSING 4 +#define STATE_STOPPING 5 +#define STATE_REQ_SENT 6 +#define STATE_ACK_RCVD 7 +#define STATE_ACK_SENT 8 +#define STATE_OPENED 9 + struct ppp_header { u_char address; u_char control; @@ -128,6 +154,38 @@ struct cisco_packet { }; #define CISCO_PACKET_LEN 18 +/* + * We follow the spelling and capitalization of RFC 1661 here, to make + * it easier comparing with the standard. Please refer to this RFC in + * case you can't make sense out of these abbreviation; it will also + * explain the semantics related to the various events and actions. + */ +struct cp { + u_short proto; /* PPP control protocol number */ + u_char protoidx; /* index into state table in struct sppp */ + u_char flags; +#define CP_LCP 0x01 /* this is the LCP */ +#define CP_AUTH 0x02 /* this is an authentication protocol */ +#define CP_NCP 0x04 /* this is a NCP */ +#define CP_QUAL 0x08 /* this is a quality reporting protocol */ + const char *name; /* name of this control protocol */ + /* event handlers */ + void (*Up)(struct sppp *sp); + void (*Down)(struct sppp *sp); + void (*Open)(struct sppp *sp); + void (*Close)(struct sppp *sp); + void (*TO)(void *sp); + int (*RCR)(struct sppp *sp, struct lcp_header *h, int len); + void (*RCN_rej)(struct sppp *sp, struct lcp_header *h, int len); + void (*RCN_nak)(struct sppp *sp, struct lcp_header *h, int len); + /* actions */ + void (*tlu)(struct sppp *sp); + void (*tld)(struct sppp *sp); + void (*tls)(struct sppp *sp); + void (*tlf)(struct sppp *sp); + void (*scr)(struct sppp *sp); +}; + static struct sppp *spppq; /* @@ -142,51 +200,103 @@ static u_short interactive_ports[8] = { }; #define INTERACTIVE(p) (interactive_ports[(p) & 7] == (p)) -/* - * Timeout routine activation macros. - */ -#define TIMO(p,s) if (! ((p)->pp_flags & PP_TIMO)) { \ - timeout (sppp_cp_timeout, (void*) (p), (s)*hz); \ - (p)->pp_flags |= PP_TIMO; } -#define UNTIMO(p) if ((p)->pp_flags & PP_TIMO) { \ - untimeout (sppp_cp_timeout, (void*) (p)); \ - (p)->pp_flags &= ~PP_TIMO; } - -static void sppp_keepalive (void *dummy); -static void sppp_cp_send (struct sppp *sp, u_short proto, u_char type, - u_char ident, u_short len, void *data); -static void sppp_cisco_send (struct sppp *sp, int type, long par1, long par2); -static void sppp_lcp_input (struct sppp *sp, struct mbuf *m); -static void sppp_cisco_input (struct sppp *sp, struct mbuf *m); -static void sppp_ipcp_input (struct sppp *sp, struct mbuf *m); -static void sppp_lcp_open (struct sppp *sp); -static void sppp_ipcp_open (struct sppp *sp); -static int sppp_lcp_conf_parse_options (struct sppp *sp, struct lcp_header *h, - int len, u_long *magic); -static void sppp_cp_timeout (void *arg); -static const char *sppp_lcp_type_name (u_char type); -static const char *sppp_ipcp_type_name (u_char type); -static void sppp_print_bytes (u_char *p, u_short len); -static int sppp_output (struct ifnet *ifp, struct mbuf *m, - struct sockaddr *dst, struct rtentry *rt); +/* almost every function needs these */ +#define STDDCL \ + struct ifnet *ifp = &sp->pp_if; \ + int debug = ifp->if_flags & IFF_DEBUG + +static int sppp_output(struct ifnet *ifp, struct mbuf *m, + struct sockaddr *dst, struct rtentry *rt); + +static void sppp_cisco_send(struct sppp *sp, int type, long par1, long par2); +static void sppp_cisco_input(struct sppp *sp, struct mbuf *m); + +static void sppp_cp_input(const struct cp *cp, struct sppp *sp, + struct mbuf *m); +static void sppp_cp_send(struct sppp *sp, u_short proto, u_char type, + u_char ident, u_short len, void *data); +static void sppp_cp_timeout(void *arg); +static void sppp_cp_change_state(const struct cp *cp, struct sppp *sp, + int newstate); + +static void sppp_up_event(const struct cp *cp, struct sppp *sp); +static void sppp_down_event(const struct cp *cp, struct sppp *sp); +static void sppp_open_event(const struct cp *cp, struct sppp *sp); +static void sppp_close_event(const struct cp *cp, struct sppp *sp); +static void sppp_to_event(const struct cp *cp, struct sppp *sp); + +static void sppp_lcp_init(struct sppp *sp); +static void sppp_lcp_up(struct sppp *sp); +static void sppp_lcp_down(struct sppp *sp); +static void sppp_lcp_open(struct sppp *sp); +static void sppp_lcp_close(struct sppp *sp); +static void sppp_lcp_TO(void *sp); +static int sppp_lcp_RCR(struct sppp *sp, struct lcp_header *h, int len); +static void sppp_lcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len); +static void sppp_lcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len); +static void sppp_lcp_tlu(struct sppp *sp); +static void sppp_lcp_tld(struct sppp *sp); +static void sppp_lcp_tls(struct sppp *sp); +static void sppp_lcp_tlf(struct sppp *sp); +static void sppp_lcp_scr(struct sppp *sp); +static void sppp_lcp_check(struct sppp *sp); + +static void sppp_ipcp_init(struct sppp *sp); +static void sppp_ipcp_up(struct sppp *sp); +static void sppp_ipcp_down(struct sppp *sp); +static void sppp_ipcp_open(struct sppp *sp); +static void sppp_ipcp_close(struct sppp *sp); +static void sppp_ipcp_TO(void *sp); +static int sppp_ipcp_RCR(struct sppp *sp, struct lcp_header *h, int len); +static void sppp_ipcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len); +static void sppp_ipcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len); +static void sppp_ipcp_tlu(struct sppp *sp); +static void sppp_ipcp_tld(struct sppp *sp); +static void sppp_ipcp_tls(struct sppp *sp); +static void sppp_ipcp_tlf(struct sppp *sp); +static void sppp_ipcp_scr(struct sppp *sp); + +static const char *sppp_cp_type_name(u_char type); +static const char *sppp_lcp_opt_name(u_char opt); +static const char *sppp_ipcp_opt_name(u_char opt); +static const char *sppp_state_name(int state); +static const char *sppp_phase_name(enum ppp_phase phase); +static const char *sppp_proto_name(u_short proto); + +static void sppp_keepalive(void *dummy); +static void sppp_qflush(struct ifqueue *ifq); + +static void sppp_get_ip_addrs(struct sppp *sp, u_long *src, u_long *dst); +static void sppp_set_ip_addr(struct sppp *sp, u_long src); + +static void sppp_print_bytes(u_char *p, u_short len); + +/* our control protocol descriptors */ +const struct cp lcp = { + PPP_LCP, IDX_LCP, CP_LCP, "lcp", + sppp_lcp_up, sppp_lcp_down, sppp_lcp_open, sppp_lcp_close, + sppp_lcp_TO, sppp_lcp_RCR, sppp_lcp_RCN_rej, sppp_lcp_RCN_nak, + sppp_lcp_tlu, sppp_lcp_tld, sppp_lcp_tls, sppp_lcp_tlf, + sppp_lcp_scr +}; -/* - * Flush interface queue. - */ -static void -qflush(struct ifqueue *ifq) -{ - struct mbuf *m, *n; +const struct cp ipcp = { + PPP_IPCP, IDX_IPCP, CP_NCP, "ipcp", + sppp_ipcp_up, sppp_ipcp_down, sppp_ipcp_open, sppp_ipcp_close, + sppp_ipcp_TO, sppp_ipcp_RCR, sppp_ipcp_RCN_rej, sppp_ipcp_RCN_nak, + sppp_ipcp_tlu, sppp_ipcp_tld, sppp_ipcp_tls, sppp_ipcp_tlf, + sppp_ipcp_scr +}; - n = ifq->ifq_head; - while ((m = n)) { - n = m->m_act; - m_freem (m); - } - ifq->ifq_head = 0; - ifq->ifq_tail = 0; - ifq->ifq_len = 0; -} +const struct cp *cps[IDX_COUNT] = { + &lcp, /* IDX_LCP */ + &ipcp, /* IDX_IPCP */ +}; + + +/* + * Exported functions, comprising our interface to the lower layer. + */ /* * Process the received packet. @@ -195,9 +305,10 @@ void sppp_input(struct ifnet *ifp, struct mbuf *m) { struct ppp_header *h; - struct sppp *sp = (struct sppp*) ifp; struct ifqueue *inq = 0; int s; + struct sppp *sp = (struct sppp *)ifp; + int debug = ifp->if_flags & IFF_DEBUG; if (ifp->if_flags & IFF_UP) /* Count received bytes, add FCS and one flag */ @@ -205,11 +316,13 @@ sppp_input(struct ifnet *ifp, struct mbuf *m) if (m->m_pkthdr.len <= PPP_HEADER_LEN) { /* Too small packet, drop it. */ - if (ifp->if_flags & IFF_DEBUG) + if (debug) log(LOG_DEBUG, "%s%d: input packet is too small, %d bytes\n", ifp->if_name, ifp->if_unit, m->m_pkthdr.len); -drop: ++ifp->if_iqdrops; + drop: + ++ifp->if_ierrors; + ++ifp->if_iqdrops; m_freem (m); return; } @@ -219,19 +332,11 @@ drop: ++ifp->if_iqdrops; m_adj (m, PPP_HEADER_LEN); switch (h->address) { - default: /* Invalid PPP packet. */ -invalid: if (ifp->if_flags & IFF_DEBUG) - log(LOG_DEBUG, - "%s%d: invalid input packet " - "<addr=0x%x ctrl=0x%x proto=0x%x>\n", - ifp->if_name, ifp->if_unit, - h->address, h->control, ntohs(h->protocol)); - goto drop; case PPP_ALLSTATIONS: if (h->control != PPP_UI) goto invalid; if (sp->pp_flags & PP_CISCO) { - if (ifp->if_flags & IFF_DEBUG) + if (debug) log(LOG_DEBUG, "%s%d: PPP packet in Cisco mode " "<addr=0x%x ctrl=0x%x proto=0x%x>\n", @@ -241,11 +346,11 @@ invalid: if (ifp->if_flags & IFF_DEBUG) } switch (ntohs (h->protocol)) { default: - if (sp->lcp.state == LCP_STATE_OPENED) - sppp_cp_send (sp, PPP_LCP, LCP_PROTO_REJ, + if (sp->state[IDX_LCP] == STATE_OPENED) + sppp_cp_send (sp, PPP_LCP, PROTO_REJ, ++sp->pp_seq, m->m_pkthdr.len + 2, &h->protocol); - if (ifp->if_flags & IFF_DEBUG) + if (debug) log(LOG_DEBUG, "%s%d: invalid input protocol " "<addr=0x%x ctrl=0x%x proto=0x%x>\n", @@ -254,17 +359,17 @@ invalid: if (ifp->if_flags & IFF_DEBUG) ++ifp->if_noproto; goto drop; case PPP_LCP: - sppp_lcp_input ((struct sppp*) ifp, m); + sppp_cp_input(&lcp, (struct sppp*)ifp, m); m_freem (m); return; #ifdef INET case PPP_IPCP: - if (sp->lcp.state == LCP_STATE_OPENED) - sppp_ipcp_input ((struct sppp*) ifp, m); + if (sp->pp_phase == PHASE_NETWORK) + sppp_cp_input(&ipcp, (struct sppp*) ifp, m); m_freem (m); return; case PPP_IP: - if (sp->ipcp.state == IPCP_STATE_OPENED) { + if (sp->state[IDX_IPCP] == STATE_OPENED) { schednetisr (NETISR_IP); inq = &ipintrq; } @@ -273,7 +378,7 @@ invalid: if (ifp->if_flags & IFF_DEBUG) #ifdef IPX case PPP_IPX: /* IPX IPXCP not implemented yet */ - if (sp->lcp.state == LCP_STATE_OPENED) { + if (sp->pp_phase == PHASE_NETWORK) { schednetisr (NETISR_IPX); inq = &ipxintrq; } @@ -282,7 +387,7 @@ invalid: if (ifp->if_flags & IFF_DEBUG) #ifdef NS case PPP_XNS: /* XNS IDPCP not implemented yet */ - if (sp->lcp.state == LCP_STATE_OPENED) { + if (sp->pp_phase == PHASE_NETWORK) { schednetisr (NETISR_NS); inq = &nsintrq; } @@ -291,7 +396,7 @@ invalid: if (ifp->if_flags & IFF_DEBUG) #ifdef ISO case PPP_ISO: /* OSI NLCP not implemented yet */ - if (sp->lcp.state == LCP_STATE_OPENED) { + if (sp->pp_phase == PHASE_NETWORK) { schednetisr (NETISR_ISO); inq = &clnlintrq; } @@ -303,7 +408,7 @@ invalid: if (ifp->if_flags & IFF_DEBUG) case CISCO_UNICAST: /* Don't check the control field here (RFC 1547). */ if (! (sp->pp_flags & PP_CISCO)) { - if (ifp->if_flags & IFF_DEBUG) + if (debug) log(LOG_DEBUG, "%s%d: Cisco packet in PPP mode " "<addr=0x%x ctrl=0x%x proto=0x%x>\n", @@ -339,24 +444,33 @@ invalid: if (ifp->if_flags & IFF_DEBUG) #endif } break; + default: /* Invalid PPP packet. */ + invalid: + if (debug) + log(LOG_DEBUG, + "%s%d: invalid input packet " + "<addr=0x%x ctrl=0x%x proto=0x%x>\n", + ifp->if_name, ifp->if_unit, + h->address, h->control, ntohs(h->protocol)); + goto drop; } if (! (ifp->if_flags & IFF_UP) || ! inq) goto drop; /* Check queue. */ - s = splimp (); + s = splimp(); if (IF_QFULL (inq)) { /* Queue overflow. */ - IF_DROP (inq); - splx (s); - if (ifp->if_flags & IFF_DEBUG) + IF_DROP(inq); + splx(s); + if (debug) log(LOG_DEBUG, "%s%d: protocol queue overflow\n", ifp->if_name, ifp->if_unit); goto drop; } - IF_ENQUEUE (inq, m); - splx (s); + IF_ENQUEUE(inq, m); + splx(s); } /* @@ -369,14 +483,28 @@ sppp_output(struct ifnet *ifp, struct mbuf *m, struct sppp *sp = (struct sppp*) ifp; struct ppp_header *h; struct ifqueue *ifq; - int s = splimp (); + int s; + + s = splimp(); - if (! (ifp->if_flags & IFF_UP) || ! (ifp->if_flags & IFF_RUNNING)) { + if ((ifp->if_flags & IFF_UP) == 0 || + (ifp->if_flags & (IFF_RUNNING | IFF_AUTO)) == 0) { m_freem (m); splx (s); return (ENETDOWN); } + if ((ifp->if_flags & (IFF_RUNNING | IFF_AUTO)) == IFF_AUTO) { + /* + * Interface is not yet running, but auto-dial. Need + * to start LCP for it. + */ + ifp->if_flags |= IFF_RUNNING; + splx(s); + lcp.Open(sp); + s = splimp(); + } + ifq = &ifp->if_snd; #ifdef INET /* @@ -405,6 +533,7 @@ sppp_output(struct ifnet *ifp, struct mbuf *m, if (ifp->if_flags & IFF_DEBUG) log(LOG_DEBUG, "%s%d: no memory for transmit header\n", ifp->if_name, ifp->if_unit); + ++ifp->if_oerrors; splx (s); return (ENOBUFS); } @@ -422,10 +551,11 @@ sppp_output(struct ifnet *ifp, struct mbuf *m, case AF_INET: /* Internet Protocol */ if (sp->pp_flags & PP_CISCO) h->protocol = htons (ETHERTYPE_IP); - else if (sp->ipcp.state == IPCP_STATE_OPENED) + else if (sp->state[IDX_IPCP] == STATE_OPENED) h->protocol = htons (PPP_IP); else { m_freem (m); + ++ifp->if_oerrors; splx (s); return (ENETDOWN); } @@ -453,6 +583,7 @@ nosupport: #endif default: m_freem (m); + ++ifp->if_oerrors; splx (s); return (EAFNOSUPPORT); } @@ -464,6 +595,7 @@ nosupport: if (IF_QFULL (ifq)) { IF_DROP (&ifp->if_snd); m_freem (m); + ++ifp->if_oerrors; splx (s); return (ENOBUFS); } @@ -501,15 +633,19 @@ sppp_attach(struct ifnet *ifp) sp->pp_alivecnt = 0; sp->pp_seq = 0; sp->pp_rseq = 0; - sp->lcp.magic = 0; - sp->lcp.state = LCP_STATE_CLOSED; - sp->ipcp.state = IPCP_STATE_CLOSED; + sp->pp_phase = PHASE_DEAD; + sp->pp_up = lcp.Up; + sp->pp_down = lcp.Down; + + sppp_lcp_init(sp); + sppp_ipcp_init(sp); } void sppp_detach(struct ifnet *ifp) { struct sppp **q, *p, *sp = (struct sppp*) ifp; + int i; /* Remove the entry from the keepalive list. */ for (q = &spppq; (p = *q); q = &p->pp_next) @@ -521,7 +657,9 @@ sppp_detach(struct ifnet *ifp) /* Stop keepalive handler. */ if (! spppq) untimeout (sppp_keepalive, 0); - UNTIMO (sp); + + for (i = 0; i < IDX_COUNT; i++) + untimeout((cps[i])->TO, (void *)sp); } /* @@ -532,8 +670,8 @@ sppp_flush(struct ifnet *ifp) { struct sppp *sp = (struct sppp*) ifp; - qflush (&sp->pp_if.if_snd); - qflush (&sp->pp_fastq); + sppp_qflush (&sp->pp_if.if_snd); + sppp_qflush (&sp->pp_fastq); } /* @@ -543,10 +681,11 @@ int sppp_isempty(struct ifnet *ifp) { struct sppp *sp = (struct sppp*) ifp; - int empty, s = splimp (); + int empty, s; + s = splimp(); empty = !sp->pp_fastq.ifq_head && !sp->pp_if.if_snd.ifq_head; - splx (s); + splx(s); return (empty); } @@ -558,8 +697,9 @@ sppp_dequeue(struct ifnet *ifp) { struct sppp *sp = (struct sppp*) ifp; struct mbuf *m; - int s = splimp (); + int s; + s = splimp(); IF_DEQUEUE (&sp->pp_fastq, m); if (! m) IF_DEQUEUE (&sp->pp_if.if_snd, m); @@ -568,349 +708,102 @@ sppp_dequeue(struct ifnet *ifp) } /* - * Send keepalive packets, every 10 seconds. + * Process an ioctl request. Called on low priority level. */ -static void -sppp_keepalive(void *dummy) +int +sppp_ioctl(struct ifnet *ifp, int cmd, void *data) { - struct sppp *sp; - int s = splimp (); - - for (sp=spppq; sp; sp=sp->pp_next) { - struct ifnet *ifp = &sp->pp_if; + struct ifreq *ifr = (struct ifreq*) data; + struct sppp *sp = (struct sppp*) ifp; + int s, going_up, going_down, newmode; - /* Keepalive mode disabled or channel down? */ - if (! (sp->pp_flags & PP_KEEPALIVE) || - ! (ifp->if_flags & IFF_RUNNING)) - continue; + s = splimp(); + switch (cmd) { + case SIOCAIFADDR: + case SIOCSIFDSTADDR: + break; - /* No keepalive in PPP mode if LCP not opened yet. */ - if (! (sp->pp_flags & PP_CISCO) && - sp->lcp.state != LCP_STATE_OPENED) - continue; + case SIOCSIFADDR: + if_up(ifp); + /* fall through... */ - if (sp->pp_alivecnt == MAXALIVECNT) { - /* No keepalive packets got. Stop the interface. */ - printf ("%s%d: down\n", ifp->if_name, ifp->if_unit); - if_down (ifp); - qflush (&sp->pp_fastq); - if (! (sp->pp_flags & PP_CISCO)) { - /* Shut down the PPP link. */ - sp->lcp.state = LCP_STATE_CLOSED; - sp->ipcp.state = IPCP_STATE_CLOSED; - UNTIMO (sp); - /* Initiate negotiation. */ - sppp_lcp_open (sp); - } - } - if (sp->pp_alivecnt <= MAXALIVECNT) - ++sp->pp_alivecnt; - if (sp->pp_flags & PP_CISCO) - sppp_cisco_send (sp, CISCO_KEEPALIVE_REQ, ++sp->pp_seq, - sp->pp_rseq); - else if (sp->lcp.state == LCP_STATE_OPENED) { - long nmagic = htonl (sp->lcp.magic); - sp->lcp.echoid = ++sp->pp_seq; - sppp_cp_send (sp, PPP_LCP, LCP_ECHO_REQ, - sp->lcp.echoid, 4, &nmagic); + case SIOCSIFFLAGS: + going_up = ifp->if_flags & IFF_UP && + (ifp->if_flags & IFF_RUNNING) == 0; + going_down = (ifp->if_flags & IFF_UP) == 0 && + ifp->if_flags & IFF_RUNNING; + newmode = ifp->if_flags & (IFF_AUTO | IFF_PASSIVE); + if (newmode == (IFF_AUTO | IFF_PASSIVE)) { + /* sanity */ + newmode = IFF_PASSIVE; + ifp->if_flags &= ~IFF_AUTO; } - } - splx (s); - timeout (sppp_keepalive, 0, hz * 10); -} -/* - * Handle incoming PPP Link Control Protocol packets. - */ -static void -sppp_lcp_input(struct sppp *sp, struct mbuf *m) -{ - struct lcp_header *h; - struct ifnet *ifp = &sp->pp_if; - int len = m->m_pkthdr.len, debug = ifp->if_flags & IFF_DEBUG; - u_char *p, opt[6]; - u_long rmagic; + if (going_up || going_down) + lcp.Close(sp); + if (going_up && newmode == 0) { + /* neither auto-dial nor passive */ + ifp->if_flags |= IFF_RUNNING; + if (!(sp->pp_flags & PP_CISCO)) + lcp.Open(sp); + } else if (going_down) + ifp->if_flags &= ~IFF_RUNNING; - if (len < 4) { - if (debug) - log(LOG_DEBUG, - "%s%d: invalid lcp packet length: %d bytes\n", - ifp->if_name, ifp->if_unit, len); - return; - } - h = mtod (m, struct lcp_header*); - if (debug) { - const char *state = "unknown"; - switch (sp->lcp.state) { - case LCP_STATE_CLOSED: state = "closed"; break; - case LCP_STATE_ACK_RCVD: state = "ack-rcvd"; break; - case LCP_STATE_ACK_SENT: state = "ack-sent"; break; - case LCP_STATE_OPENED: state = "opened"; break; - } - log(LOG_DEBUG, - "%s%d: lcp input(%s): %d bytes <%s id=0x%x len=0x%x", - ifp->if_name, ifp->if_unit, state, len, - sppp_lcp_type_name (h->type), h->ident, ntohs (h->len)); - if (len > 4) - sppp_print_bytes ((u_char*) (h+1), len-4); - addlog(">\n"); - } - if (len > ntohs (h->len)) - len = ntohs (h->len); - switch (h->type) { - default: - /* Unknown packet type -- send Code-Reject packet. */ - if (debug) - addlog("%s%d: sending lcp code rej\n", - ifp->if_name, ifp->if_unit); - sppp_cp_send (sp, PPP_LCP, LCP_CODE_REJ, ++sp->pp_seq, - m->m_pkthdr.len, h); break; - case LCP_CONF_REQ: - if (len < 4) { - if (debug) - addlog("%s%d: invalid lcp configure request " - "packet length: %d bytes\n", - ifp->if_name, ifp->if_unit, len); - break; - } - rmagic = 0xd00ddeed; /* in case LCP fails */ - if (len > 4 && - !sppp_lcp_conf_parse_options (sp, h, len, &rmagic)) - goto badreq; - if (rmagic == sp->lcp.magic) { - /* Local and remote magics equal -- loopback? */ - if (sp->pp_loopcnt >= MAXALIVECNT*5) { - printf ("%s%d: loopback\n", - ifp->if_name, ifp->if_unit); - sp->pp_loopcnt = 0; - if (ifp->if_flags & IFF_UP) { - if_down (ifp); - qflush (&sp->pp_fastq); - } - } else if (debug) { - addlog("%s%d: conf req: magic glitch, " - "sending config nak\n", - ifp->if_name, ifp->if_unit); - } - ++sp->pp_loopcnt; - /* MUST send Conf-Nack packet. */ - rmagic = ~sp->lcp.magic; - opt[0] = LCP_OPT_MAGIC; - opt[1] = sizeof (opt); - opt[2] = rmagic >> 24; - opt[3] = rmagic >> 16; - opt[4] = rmagic >> 8; - opt[5] = rmagic; - sppp_cp_send (sp, PPP_LCP, LCP_CONF_NAK, - h->ident, sizeof (opt), &opt); - badreq: - switch (sp->lcp.state) { - case LCP_STATE_OPENED: - /* Initiate renegotiation. */ - sppp_lcp_open (sp); - /* fall through... */ - case LCP_STATE_ACK_SENT: - /* Go to closed state. */ - sp->lcp.state = LCP_STATE_CLOSED; - sp->ipcp.state = IPCP_STATE_CLOSED; - } - break; - } - /* Send Configure-Ack packet. */ - if (debug) - addlog("%s%d: sending configure ack\n", - ifp->if_name, ifp->if_unit); - sp->pp_loopcnt = 0; - sppp_cp_send (sp, PPP_LCP, LCP_CONF_ACK, - h->ident, len-4, h+1); - /* Change the state. */ - switch (sp->lcp.state) { - case LCP_STATE_CLOSED: - sp->lcp.state = LCP_STATE_ACK_SENT; - break; - case LCP_STATE_ACK_RCVD: - sp->lcp.state = LCP_STATE_OPENED; - sppp_ipcp_open (sp); - break; - case LCP_STATE_OPENED: - /* Remote magic changed -- close session. */ - sp->lcp.state = LCP_STATE_CLOSED; - sp->ipcp.state = IPCP_STATE_CLOSED; - /* Initiate renegotiation. */ - sppp_lcp_open (sp); - /* An ACK has already been sent. */ - sp->lcp.state = LCP_STATE_ACK_SENT; - break; - } - break; - case LCP_CONF_ACK: - if (h->ident != sp->lcp.confid) { - if (debug) - addlog("%s%d: lcp id mismatch 0x%x != 0x%x\n", - ifp->if_name, ifp->if_unit, - h->ident, sp->lcp.confid); - break; - } - UNTIMO (sp); - if (! (ifp->if_flags & IFF_UP) && - (ifp->if_flags & IFF_RUNNING)) { - /* Coming out of loopback mode. */ - ifp->if_flags |= IFF_UP; - printf ("%s%d: up\n", ifp->if_name, ifp->if_unit); - } - switch (sp->lcp.state) { - case LCP_STATE_CLOSED: - sp->lcp.state = LCP_STATE_ACK_RCVD; - TIMO (sp, 5); - break; - case LCP_STATE_ACK_SENT: - sp->lcp.state = LCP_STATE_OPENED; - sppp_ipcp_open (sp); - break; - } - break; - case LCP_CONF_NAK: - if (h->ident != sp->lcp.confid) { - if (debug) - addlog("%s%d: lcp id mismatch 0x%x != 0x%x\n", - ifp->if_name, ifp->if_unit, - h->ident, sp->lcp.confid); - break; - } - p = (u_char*) (h+1); - if (len>=10 && p[0] == LCP_OPT_MAGIC && p[1] >= 4) { - rmagic = (u_long)p[2] << 24 | - (u_long)p[3] << 16 | p[4] << 8 | p[5]; - if (rmagic == ~sp->lcp.magic) { - if (debug) - addlog("%s%d: conf nak: magic glitch\n", - ifp->if_name, ifp->if_unit); - sp->lcp.magic += time.tv_sec + time.tv_usec; - } else - sp->lcp.magic = rmagic; - } - if (sp->lcp.state != LCP_STATE_ACK_SENT) { - /* Go to closed state. */ - sp->lcp.state = LCP_STATE_CLOSED; - sp->ipcp.state = IPCP_STATE_CLOSED; - } - /* The link will be renegotiated after timeout, - * to avoid endless req-nack loop. */ - if (debug) - addlog("%s%d: sleeping due to nak\n", - ifp->if_name, ifp->if_unit); - UNTIMO (sp); - TIMO (sp, 2); +#ifdef SIOCSIFMTU +#ifndef ifr_mtu +#define ifr_mtu ifr_metric +#endif + case SIOCSIFMTU: + if (ifr->ifr_mtu < 128 || ifr->ifr_mtu > sp->lcp.their_mru) + return (EINVAL); + ifp->if_mtu = ifr->ifr_mtu; break; - case LCP_CONF_REJ: - if (h->ident != sp->lcp.confid) { - if (debug) - addlog("%s%d: lcp id mismatch 0x%x != 0x%x\n", - ifp->if_name, ifp->if_unit, - h->ident, sp->lcp.confid); - break; - } - UNTIMO (sp); - /* Initiate renegotiation. */ - if (debug) - addlog("%s%d: reopening lcp\n", - ifp->if_name, ifp->if_unit); - sppp_lcp_open (sp); - if (sp->lcp.state != LCP_STATE_ACK_SENT) { - /* Go to closed state. */ - sp->lcp.state = LCP_STATE_CLOSED; - sp->ipcp.state = IPCP_STATE_CLOSED; - } +#endif +#ifdef SLIOCSETMTU + case SLIOCSETMTU: + if (*(short*)data < 128 || *(short*)data > sp->lcp.their_mru) + return (EINVAL); + ifp->if_mtu = *(short*)data; break; - case LCP_TERM_REQ: - UNTIMO (sp); - /* Send Terminate-Ack packet. */ - if (debug) - addlog("%s%d: sending terminate-ack\n", - ifp->if_name, ifp->if_unit); - sppp_cp_send (sp, PPP_LCP, LCP_TERM_ACK, h->ident, 0, 0); - /* Go to closed state. */ - sp->lcp.state = LCP_STATE_CLOSED; - sp->ipcp.state = IPCP_STATE_CLOSED; - /* Initiate renegotiation. */ - sppp_lcp_open (sp); +#endif +#ifdef SIOCGIFMTU + case SIOCGIFMTU: + ifr->ifr_mtu = ifp->if_mtu; break; - case LCP_TERM_ACK: - case LCP_CODE_REJ: - case LCP_PROTO_REJ: - if (debug) - addlog("%s%d: ignoring lcp code 0x%x\n", - ifp->if_name, ifp->if_unit, h->type); - /* Ignore for now. */ +#endif +#ifdef SLIOCGETMTU + case SLIOCGETMTU: + *(short*)data = ifp->if_mtu; break; - case LCP_DISC_REQ: - /* Discard the packet. */ +#endif + case SIOCADDMULTI: + case SIOCDELMULTI: break; - case LCP_ECHO_REQ: - if (sp->lcp.state != LCP_STATE_OPENED) { - if (debug) - addlog("%s%d: lcp echo req but lcp close\n", - ifp->if_name, ifp->if_unit); - break; - } - if (len < 8) { - if (debug) - addlog("%s%d: invalid lcp echo request " - "packet length: %d bytes\n", - ifp->if_name, ifp->if_unit, len); - break; - } - if (ntohl (*(long*)(h+1)) == sp->lcp.magic) { - /* Line loopback mode detected. */ - printf ("%s%d: loopback\n", ifp->if_name, ifp->if_unit); - if_down (ifp); - qflush (&sp->pp_fastq); - /* Shut down the PPP link. */ - sp->lcp.state = LCP_STATE_CLOSED; - sp->ipcp.state = IPCP_STATE_CLOSED; - UNTIMO (sp); - /* Initiate negotiation. */ - sppp_lcp_open (sp); - break; - } - *(long*)(h+1) = htonl (sp->lcp.magic); - if (debug) - addlog("%s%d: got lcp echo req, sending echo rep\n", - ifp->if_name, ifp->if_unit); - sppp_cp_send (sp, PPP_LCP, LCP_ECHO_REPLY, h->ident, len-4, h+1); - break; - case LCP_ECHO_REPLY: - if (h->ident != sp->lcp.echoid) - break; - if (len < 8) { - if (debug) - addlog("%s%d: invalid lcp echo reply " - "packet length: %d bytes\n", - ifp->if_name, ifp->if_unit, len); - break; - } - if (debug) - addlog("%s%d: got lcp echo rep\n", - ifp->if_name, ifp->if_unit); - if (ntohl (*(long*)(h+1)) != sp->lcp.magic) - sp->pp_alivecnt = 0; - break; + default: + splx(s); + return (ENOTTY); } + splx(s); + return (0); } + +/* + * Cisco framing implementation. + */ + /* * Handle incoming Cisco keepalive protocol packets. */ static void sppp_cisco_input(struct sppp *sp, struct mbuf *m) { + STDDCL; struct cisco_packet *h; struct ifaddr *ifa; - struct ifnet *ifp = &sp->pp_if; - int debug = ifp->if_flags & IFF_DEBUG; if (m->m_pkthdr.len != CISCO_PACKET_LEN) { if (debug) @@ -948,7 +841,7 @@ sppp_cisco_input(struct sppp *sp, struct mbuf *m) sp->pp_loopcnt = 0; if (ifp->if_flags & IFF_UP) { if_down (ifp); - qflush (&sp->pp_fastq); + sppp_qflush (&sp->pp_fastq); } } ++sp->pp_loopcnt; @@ -983,17 +876,67 @@ sppp_cisco_input(struct sppp *sp, struct mbuf *m) } /* - * Send PPP LCP packet. + * Send Cisco keepalive packet. + */ +static void +sppp_cisco_send(struct sppp *sp, int type, long par1, long par2) +{ + STDDCL; + struct ppp_header *h; + struct cisco_packet *ch; + struct mbuf *m; + u_long t = (time.tv_sec - boottime.tv_sec) * 1000; + + MGETHDR (m, M_DONTWAIT, MT_DATA); + if (! m) + return; + m->m_pkthdr.len = m->m_len = PPP_HEADER_LEN + CISCO_PACKET_LEN; + m->m_pkthdr.rcvif = 0; + + h = mtod (m, struct ppp_header*); + h->address = CISCO_MULTICAST; + h->control = 0; + h->protocol = htons (CISCO_KEEPALIVE); + + ch = (struct cisco_packet*) (h + 1); + ch->type = htonl (type); + ch->par1 = htonl (par1); + ch->par2 = htonl (par2); + ch->rel = -1; + ch->time0 = htons ((u_short) (t >> 16)); + ch->time1 = htons ((u_short) t); + + if (debug) + log(LOG_DEBUG, + "%s%d: cisco output: <0x%lx 0x%lx 0x%lx 0x%x 0x%x-0x%x>\n", + ifp->if_name, ifp->if_unit, ntohl (ch->type), ch->par1, + ch->par2, ch->rel, ch->time0, ch->time1); + + if (IF_QFULL (&sp->pp_fastq)) { + IF_DROP (&ifp->if_snd); + m_freem (m); + } else + IF_ENQUEUE (&sp->pp_fastq, m); + if (! (ifp->if_flags & IFF_OACTIVE)) + (*ifp->if_start) (ifp); + ifp->if_obytes += m->m_pkthdr.len + 3; +} + +/* + * PPP protocol implementation. + */ + +/* + * Send PPP control protocol packet. */ static void sppp_cp_send(struct sppp *sp, u_short proto, u_char type, u_char ident, u_short len, void *data) { + STDDCL; struct ppp_header *h; struct lcp_header *lh; struct mbuf *m; - struct ifnet *ifp = &sp->pp_if; - int debug = ifp->if_flags & IFF_DEBUG; if (len > MHLEN - PPP_HEADER_LEN - LCP_HEADER_LEN) len = MHLEN - PPP_HEADER_LEN - LCP_HEADER_LEN; @@ -1016,12 +959,11 @@ sppp_cp_send(struct sppp *sp, u_short proto, u_char type, bcopy (data, lh+1, len); if (debug) { - log(LOG_DEBUG, "%s%d: %s output <%s id=%xh len=%xh", - ifp->if_name, ifp->if_unit, - proto==PPP_LCP ? "lcp" : "ipcp", - proto==PPP_LCP ? sppp_lcp_type_name (lh->type) : - sppp_ipcp_type_name (lh->type), lh->ident, - ntohs (lh->len)); + log(LOG_DEBUG, "%s%d: %s output <%s id=0x%x len=%d", + ifp->if_name, ifp->if_unit, + sppp_proto_name(proto), + sppp_cp_type_name (lh->type), lh->ident, + ntohs (lh->len)); if (len) sppp_print_bytes ((u_char*) (lh+1), len); addlog(">\n"); @@ -1029,6 +971,7 @@ sppp_cp_send(struct sppp *sp, u_short proto, u_char type, if (IF_QFULL (&sp->pp_fastq)) { IF_DROP (&ifp->if_snd); m_freem (m); + ++ifp->if_oerrors; } else IF_ENQUEUE (&sp->pp_fastq, m); if (! (ifp->if_flags & IFF_OACTIVE)) @@ -1037,461 +980,1722 @@ sppp_cp_send(struct sppp *sp, u_short proto, u_char type, } /* - * Send Cisco keepalive packet. + * Handle incoming PPP control protocol packets. */ static void -sppp_cisco_send(struct sppp *sp, int type, long par1, long par2) +sppp_cp_input(const struct cp *cp, struct sppp *sp, struct mbuf *m) { - struct ppp_header *h; - struct cisco_packet *ch; - struct mbuf *m; - struct ifnet *ifp = &sp->pp_if; - u_long t = (time.tv_sec - boottime.tv_sec) * 1000; - int debug = ifp->if_flags & IFF_DEBUG; + STDDCL; + struct lcp_header *h; + int len = m->m_pkthdr.len; + int rv; + u_char *p; - MGETHDR (m, M_DONTWAIT, MT_DATA); - if (! m) + if (len < 4) { + if (debug) + log(LOG_DEBUG, + "%s%d: %s invalid packet length: %d bytes\n", + ifp->if_name, ifp->if_unit, cp->name, len); return; - m->m_pkthdr.len = m->m_len = PPP_HEADER_LEN + CISCO_PACKET_LEN; - m->m_pkthdr.rcvif = 0; - - h = mtod (m, struct ppp_header*); - h->address = CISCO_MULTICAST; - h->control = 0; - h->protocol = htons (CISCO_KEEPALIVE); - - ch = (struct cisco_packet*) (h + 1); - ch->type = htonl (type); - ch->par1 = htonl (par1); - ch->par2 = htonl (par2); - ch->rel = -1; - ch->time0 = htons ((u_short) (t >> 16)); - ch->time1 = htons ((u_short) t); - - if (debug) + } + h = mtod (m, struct lcp_header*); + if (debug) { log(LOG_DEBUG, - "%s%d: cisco output: <0x%lx 0x%lx 0x%lx 0x%x 0x%x-0x%x>\n", - ifp->if_name, ifp->if_unit, ntohl (ch->type), ch->par1, - ch->par2, ch->rel, ch->time0, ch->time1); + "%s%d: %s input(%s): <%s id=0x%x len=%d", + ifp->if_name, ifp->if_unit, cp->name, + sppp_state_name(sp->state[cp->protoidx]), + sppp_cp_type_name (h->type), h->ident, ntohs (h->len)); + if (len > 4) + sppp_print_bytes ((u_char*) (h+1), len-4); + addlog(">\n"); + } + if (len > ntohs (h->len)) + len = ntohs (h->len); + switch (h->type) { + case CONF_REQ: + if (len < 4) { + if (debug) + addlog("%s%d: %s invalid conf-req length %d\n", + ifp->if_name, ifp->if_unit, cp->name, + len); + ++ifp->if_ierrors; + break; + } + rv = (cp->RCR)(sp, h, len); + switch (sp->state[cp->protoidx]) { + case STATE_OPENED: + (cp->tld)(sp); + (cp->scr)(sp); + /* fall through... */ + case STATE_ACK_SENT: + case STATE_REQ_SENT: + sppp_cp_change_state(cp, sp, rv? + STATE_ACK_SENT: STATE_REQ_SENT); + break; + case STATE_CLOSING: + case STATE_STOPPING: + break; + case STATE_STOPPED: + sp->rst_counter[cp->protoidx] = sp->lcp.max_configure; + (cp->scr)(sp); + sppp_cp_change_state(cp, sp, rv? + STATE_ACK_SENT: STATE_REQ_SENT); + break; + case STATE_CLOSED: + sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident, + 0, 0); + break; + case STATE_ACK_RCVD: + if (rv) { + sppp_cp_change_state(cp, sp, STATE_OPENED); + if (debug) + addlog("%s%d: %s tlu\n", + ifp->if_name, ifp->if_unit, + cp->name); + (cp->tlu)(sp); + } else + sppp_cp_change_state(cp, sp, STATE_ACK_RCVD); + break; + default: + printf("%s%d: %s illegal %s in state %s\n", + ifp->if_name, ifp->if_unit, cp->name, + sppp_cp_type_name(h->type), + sppp_state_name(sp->state[cp->protoidx])); + ++ifp->if_ierrors; + } + break; + case CONF_ACK: + if (h->ident != sp->confid[cp->protoidx]) { + if (debug) + addlog("%s%d: %s id mismatch 0x%x != 0x%x\n", + ifp->if_name, ifp->if_unit, cp->name, + h->ident, sp->confid[cp->protoidx]); + ++ifp->if_ierrors; + break; + } + switch (sp->state[cp->protoidx]) { + case STATE_CLOSED: + case STATE_STOPPED: + sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident, 0, 0); + break; + case STATE_CLOSING: + case STATE_STOPPING: + break; + case STATE_REQ_SENT: + sp->rst_counter[cp->protoidx] = sp->lcp.max_configure; + sppp_cp_change_state(cp, sp, STATE_ACK_RCVD); + break; + case STATE_OPENED: + (cp->tld)(sp); + /* fall through */ + case STATE_ACK_RCVD: + (cp->scr)(sp); + sppp_cp_change_state(cp, sp, STATE_REQ_SENT); + break; + case STATE_ACK_SENT: + sp->rst_counter[cp->protoidx] = sp->lcp.max_configure; + sppp_cp_change_state(cp, sp, STATE_OPENED); + if (debug) + addlog("%s%d: %s tlu\n", + ifp->if_name, ifp->if_unit, cp->name); + (cp->tlu)(sp); + break; + default: + printf("%s%d: %s illegal %s in state %s\n", + ifp->if_name, ifp->if_unit, cp->name, + sppp_cp_type_name(h->type), + sppp_state_name(sp->state[cp->protoidx])); + ++ifp->if_ierrors; + } + break; + case CONF_NAK: + case CONF_REJ: + if (h->ident != sp->confid[cp->protoidx]) { + if (debug) + addlog("%s%d: %s id mismatch 0x%x != 0x%x\n", + ifp->if_name, ifp->if_unit, cp->name, + h->ident, sp->confid[cp->protoidx]); + ++ifp->if_ierrors; + break; + } + if (h->type == CONF_NAK) + (cp->RCN_nak)(sp, h, len); + else /* CONF_REJ */ + (cp->RCN_rej)(sp, h, len); + + switch (sp->state[cp->protoidx]) { + case STATE_CLOSED: + case STATE_STOPPED: + sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident, 0, 0); + break; + case STATE_REQ_SENT: + case STATE_ACK_SENT: + sp->rst_counter[cp->protoidx] = sp->lcp.max_configure; + (cp->scr)(sp); + break; + case STATE_OPENED: + (cp->tld)(sp); + /* fall through */ + case STATE_ACK_RCVD: + sppp_cp_change_state(cp, sp, STATE_ACK_SENT); + (cp->scr)(sp); + break; + case STATE_CLOSING: + case STATE_STOPPING: + break; + default: + printf("%s%d: %s illegal %s in state %s\n", + ifp->if_name, ifp->if_unit, cp->name, + sppp_cp_type_name(h->type), + sppp_state_name(sp->state[cp->protoidx])); + ++ifp->if_ierrors; + } + break; - if (IF_QFULL (&sp->pp_fastq)) { - IF_DROP (&ifp->if_snd); - m_freem (m); - } else - IF_ENQUEUE (&sp->pp_fastq, m); - if (! (ifp->if_flags & IFF_OACTIVE)) - (*ifp->if_start) (ifp); - ifp->if_obytes += m->m_pkthdr.len + 3; + case TERM_REQ: + switch (sp->state[cp->protoidx]) { + case STATE_ACK_RCVD: + case STATE_ACK_SENT: + sppp_cp_change_state(cp, sp, STATE_REQ_SENT); + /* fall through */ + case STATE_CLOSED: + case STATE_STOPPED: + case STATE_CLOSING: + case STATE_STOPPING: + case STATE_REQ_SENT: + sta: + /* Send Terminate-Ack packet. */ + if (debug) + log(LOG_DEBUG, "%s%d: %s send terminate-ack\n", + ifp->if_name, ifp->if_unit, cp->name); + sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident, 0, 0); + break; + case STATE_OPENED: + (cp->tld)(sp); + sp->rst_counter[cp->protoidx] = 0; + sppp_cp_change_state(cp, sp, STATE_STOPPING); + goto sta; + break; + default: + printf("%s%d: %s illegal %s in state %s\n", + ifp->if_name, ifp->if_unit, cp->name, + sppp_cp_type_name(h->type), + sppp_state_name(sp->state[cp->protoidx])); + ++ifp->if_ierrors; + } + break; + case TERM_ACK: + switch (sp->state[cp->protoidx]) { + case STATE_CLOSED: + case STATE_STOPPED: + case STATE_REQ_SENT: + case STATE_ACK_SENT: + break; + case STATE_CLOSING: + (cp->tlf)(sp); + sppp_cp_change_state(cp, sp, STATE_CLOSED); + break; + case STATE_STOPPING: + (cp->tlf)(sp); + sppp_cp_change_state(cp, sp, STATE_STOPPED); + break; + case STATE_ACK_RCVD: + sppp_cp_change_state(cp, sp, STATE_REQ_SENT); + break; + case STATE_OPENED: + (cp->tld)(sp); + (cp->scr)(sp); + sppp_cp_change_state(cp, sp, STATE_ACK_RCVD); + break; + default: + printf("%s%d: %s illegal %s in state %s\n", + ifp->if_name, ifp->if_unit, cp->name, + sppp_cp_type_name(h->type), + sppp_state_name(sp->state[cp->protoidx])); + ++ifp->if_ierrors; + } + break; + case CODE_REJ: + case PROTO_REJ: + /* XXX catastrophic rejects (RXJ-) aren't handled yet. */ + switch (sp->state[cp->protoidx]) { + case STATE_CLOSED: + case STATE_STOPPED: + case STATE_REQ_SENT: + case STATE_ACK_SENT: + case STATE_CLOSING: + case STATE_STOPPING: + case STATE_OPENED: + break; + case STATE_ACK_RCVD: + sppp_cp_change_state(cp, sp, STATE_REQ_SENT); + break; + default: + printf("%s%d: %s illegal %s in state %s\n", + ifp->if_name, ifp->if_unit, cp->name, + sppp_cp_type_name(h->type), + sppp_state_name(sp->state[cp->protoidx])); + ++ifp->if_ierrors; + } + break; + case DISC_REQ: + if (cp->proto != PPP_LCP) + goto illegal; + /* Discard the packet. */ + break; + case ECHO_REQ: + if (cp->proto != PPP_LCP) + goto illegal; + if (sp->state[cp->protoidx] != STATE_OPENED) { + if (debug) + addlog("%s%d: lcp echo req but lcp closed\n", + ifp->if_name, ifp->if_unit); + ++ifp->if_ierrors; + break; + } + if (len < 8) { + if (debug) + addlog("%s%d: invalid lcp echo request " + "packet length: %d bytes\n", + ifp->if_name, ifp->if_unit, len); + break; + } + if (ntohl (*(long*)(h+1)) == sp->lcp.magic) { + /* Line loopback mode detected. */ + printf("%s%d: loopback\n", ifp->if_name, ifp->if_unit); + if_down (ifp); + sppp_qflush (&sp->pp_fastq); + + /* Shut down the PPP link. */ + /* XXX */ + lcp.Down(sp); + lcp.Up(sp); + break; + } + *(long*)(h+1) = htonl (sp->lcp.magic); + if (debug) + addlog("%s%d: got lcp echo req, sending echo rep\n", + ifp->if_name, ifp->if_unit); + sppp_cp_send (sp, PPP_LCP, ECHO_REPLY, h->ident, len-4, h+1); + break; + case ECHO_REPLY: + if (cp->proto != PPP_LCP) + goto illegal; + if (h->ident != sp->lcp.echoid) { + ++ifp->if_ierrors; + break; + } + if (len < 8) { + if (debug) + addlog("%s%d: lcp invalid echo reply " + "packet length: %d bytes\n", + ifp->if_name, ifp->if_unit, len); + break; + } + if (debug) + addlog("%s%d: lcp got echo rep\n", + ifp->if_name, ifp->if_unit); + if (ntohl (*(long*)(h+1)) != sp->lcp.magic) + sp->pp_alivecnt = 0; + break; + default: + /* Unknown packet type -- send Code-Reject packet. */ + illegal: + if (debug) + addlog("%s%d: %c send code-rej for 0x%x\n", + ifp->if_name, ifp->if_unit, cp->name, h->type); + sppp_cp_send(sp, cp->proto, CODE_REJ, ++sp->pp_seq, + m->m_pkthdr.len, h); + ++ifp->if_ierrors; + } } + /* - * Process an ioctl request. Called on low priority level. + * The generic part of all Up/Down/Open/Close/TO event handlers. + * Basically, the state transition handling in the automaton. */ -int -sppp_ioctl(struct ifnet *ifp, int cmd, void *data) +static void +sppp_up_event(const struct cp *cp, struct sppp *sp) { - struct ifreq *ifr = (struct ifreq*) data; - struct sppp *sp = (struct sppp*) ifp; - int s, going_up, going_down; + STDDCL; - switch (cmd) { + if (debug) + log(LOG_DEBUG, "%s%d: %s up(%s)\n", + ifp->if_name, ifp->if_unit, cp->name, + sppp_state_name(sp->state[cp->protoidx])); + + switch (sp->state[cp->protoidx]) { + case STATE_INITIAL: + sppp_cp_change_state(cp, sp, STATE_CLOSED); + break; + case STATE_STARTING: + sp->rst_counter[cp->protoidx] = sp->lcp.max_configure; + (cp->scr)(sp); + sppp_cp_change_state(cp, sp, STATE_REQ_SENT); + break; default: - return (ENOTTY); + printf("%s%d: %s illegal up in state %s\n", + ifp->if_name, ifp->if_unit, cp->name, + sppp_state_name(sp->state[cp->protoidx])); + } +} - case SIOCAIFADDR: - case SIOCSIFDSTADDR: +static void +sppp_down_event(const struct cp *cp, struct sppp *sp) +{ + STDDCL; + + if (debug) + log(LOG_DEBUG, "%s%d: %s down(%s)\n", + ifp->if_name, ifp->if_unit, cp->name, + sppp_state_name(sp->state[cp->protoidx])); + + switch (sp->state[cp->protoidx]) { + case STATE_CLOSED: + case STATE_CLOSING: + sppp_cp_change_state(cp, sp, STATE_INITIAL); + break; + case STATE_STOPPED: + (cp->tls)(sp); + /* fall through */ + case STATE_STOPPING: + case STATE_REQ_SENT: + case STATE_ACK_RCVD: + case STATE_ACK_SENT: + sppp_cp_change_state(cp, sp, STATE_STARTING); + break; + case STATE_OPENED: + (cp->tld)(sp); + sppp_cp_change_state(cp, sp, STATE_STARTING); break; + default: + printf("%s%d: %s illegal down in state %s\n", + ifp->if_name, ifp->if_unit, cp->name, + sppp_state_name(sp->state[cp->protoidx])); + } +} - case SIOCSIFADDR: - ifp->if_flags |= IFF_UP; - /* fall through... */ - case SIOCSIFFLAGS: - s = splimp (); - going_up = (ifp->if_flags & IFF_UP) && - ! (ifp->if_flags & IFF_RUNNING); - going_down = ! (ifp->if_flags & IFF_UP) && - (ifp->if_flags & IFF_RUNNING); - if (going_up || going_down) { - /* Shut down the PPP link. */ - ifp->if_flags &= ~IFF_RUNNING; - sp->lcp.state = LCP_STATE_CLOSED; - sp->ipcp.state = IPCP_STATE_CLOSED; - UNTIMO (sp); - } - if (going_up) { - /* Interface is starting -- initiate negotiation. */ - ifp->if_flags |= IFF_RUNNING; - if (!(sp->pp_flags & PP_CISCO)) - sppp_lcp_open (sp); - } - splx (s); +static void +sppp_open_event(const struct cp *cp, struct sppp *sp) +{ + STDDCL; + + if (debug) + log(LOG_DEBUG, "%s%d: %s open(%s)\n", + ifp->if_name, ifp->if_unit, cp->name, + sppp_state_name(sp->state[cp->protoidx])); + + switch (sp->state[cp->protoidx]) { + case STATE_INITIAL: + (cp->tls)(sp); + sppp_cp_change_state(cp, sp, STATE_STARTING); + break; + case STATE_STARTING: + break; + case STATE_CLOSED: + sp->rst_counter[cp->protoidx] = sp->lcp.max_configure; + (cp->scr)(sp); + sppp_cp_change_state(cp, sp, STATE_REQ_SENT); + break; + case STATE_STOPPED: + case STATE_STOPPING: + case STATE_REQ_SENT: + case STATE_ACK_RCVD: + case STATE_ACK_SENT: + case STATE_OPENED: + break; + case STATE_CLOSING: + sppp_cp_change_state(cp, sp, STATE_STOPPING); break; + } +} -#ifdef SIOCSIFMTU -#ifndef ifr_mtu -#define ifr_mtu ifr_metric -#endif - case SIOCSIFMTU: - if (ifr->ifr_mtu < 128 || ifr->ifr_mtu > PP_MTU) - return (EINVAL); - ifp->if_mtu = ifr->ifr_mtu; + +static void +sppp_close_event(const struct cp *cp, struct sppp *sp) +{ + STDDCL; + + if (debug) + log(LOG_DEBUG, "%s%d: %s close(%s)\n", + ifp->if_name, ifp->if_unit, cp->name, + sppp_state_name(sp->state[cp->protoidx])); + + switch (sp->state[cp->protoidx]) { + case STATE_INITIAL: + case STATE_CLOSED: + case STATE_CLOSING: break; -#endif -#ifdef SLIOCSETMTU - case SLIOCSETMTU: - if (*(short*)data < 128 || *(short*)data > PP_MTU) - return (EINVAL); - ifp->if_mtu = *(short*)data; + case STATE_STARTING: + (cp->tlf)(sp); + sppp_cp_change_state(cp, sp, STATE_INITIAL); break; -#endif -#ifdef SIOCGIFMTU - case SIOCGIFMTU: - ifr->ifr_mtu = ifp->if_mtu; + case STATE_STOPPED: + sppp_cp_change_state(cp, sp, STATE_CLOSED); break; -#endif -#ifdef SLIOCGETMTU - case SLIOCGETMTU: - *(short*)data = ifp->if_mtu; + case STATE_STOPPING: + sppp_cp_change_state(cp, sp, STATE_CLOSING); break; -#endif - case SIOCADDMULTI: - case SIOCDELMULTI: + case STATE_OPENED: + (cp->tld)(sp); + /* fall through */ + case STATE_REQ_SENT: + case STATE_ACK_RCVD: + case STATE_ACK_SENT: + sp->rst_counter[cp->protoidx] = sp->lcp.max_terminate; + sppp_cp_send(sp, cp->proto, TERM_REQ, ++sp->pp_seq, 0, 0); + sppp_cp_change_state(cp, sp, STATE_CLOSING); break; } - return (0); +} + +static void +sppp_to_event(const struct cp *cp, struct sppp *sp) +{ + STDDCL; + int s; + + s = splimp(); + if (debug) + log(LOG_DEBUG, "%s%d: %s TO(%s) rst_counter = %d\n", + ifp->if_name, ifp->if_unit, cp->name, + sppp_state_name(sp->state[cp->protoidx]), + sp->rst_counter[cp->protoidx]); + + if (--sp->rst_counter[cp->protoidx] < 0) + /* TO- event */ + switch (sp->state[cp->protoidx]) { + case STATE_CLOSING: + (cp->tlf)(sp); + sppp_cp_change_state(cp, sp, STATE_CLOSED); + break; + case STATE_STOPPING: + (cp->tlf)(sp); + sppp_cp_change_state(cp, sp, STATE_STOPPED); + break; + case STATE_REQ_SENT: + case STATE_ACK_RCVD: + case STATE_ACK_SENT: + (cp->tlf)(sp); + sppp_cp_change_state(cp, sp, STATE_STOPPED); + break; + } + else + /* TO+ event */ + switch (sp->state[cp->protoidx]) { + case STATE_CLOSING: + case STATE_STOPPING: + sppp_cp_send(sp, cp->proto, TERM_REQ, ++sp->pp_seq, + 0, 0); + timeout(cp->TO, (void *)sp, sp->lcp.timeout); + break; + case STATE_REQ_SENT: + case STATE_ACK_RCVD: + (cp->scr)(sp); + /* sppp_cp_change_state() will restart the timer */ + sppp_cp_change_state(cp, sp, STATE_REQ_SENT); + break; + case STATE_ACK_SENT: + (cp->scr)(sp); + timeout(cp->TO, (void *)sp, sp->lcp.timeout); + break; + } + + splx(s); } /* - * Analyze the LCP Configure-Request options list - * for the presence of unknown options. - * If the request contains unknown options, build and - * send Configure-reject packet, containing only unknown options. + * Change the state of a control protocol in the state automaton. + * Takes care of starting/stopping the restart timer. + */ +void +sppp_cp_change_state(const struct cp *cp, struct sppp *sp, int newstate) +{ + sp->state[cp->protoidx] = newstate; + + untimeout(cp->TO, (void *)sp); + switch (newstate) { + case STATE_INITIAL: + case STATE_STARTING: + case STATE_CLOSED: + case STATE_STOPPED: + case STATE_OPENED: + break; + case STATE_CLOSING: + case STATE_STOPPING: + case STATE_REQ_SENT: + case STATE_ACK_RCVD: + case STATE_ACK_SENT: + timeout(cp->TO, (void *)sp, sp->lcp.timeout); + break; + } +} +/* + *--------------------------------------------------------------------------* + * * + * The LCP implementation. * + * * + *--------------------------------------------------------------------------* + */ +static void +sppp_lcp_init(struct sppp *sp) +{ + sp->lcp.opts = (1 << LCP_OPT_MAGIC); + sp->lcp.magic = 0; + sp->state[IDX_LCP] = STATE_INITIAL; + sp->fail_counter[IDX_LCP] = 0; + sp->lcp.protos = 0; + sp->lcp.mru = sp->lcp.their_mru = PP_MTU; + + /* + * Initialize counters and timeout values. Note that we don't + * use the 3 seconds suggested in RFC 1661 since we are likely + * running on a fast link. XXX We should probably implement + * the exponential backoff option. Note that these values are + * relevant for all control protocols, not just LCP only. + */ + sp->lcp.timeout = 1 * hz; + sp->lcp.max_terminate = 2; + sp->lcp.max_configure = 10; + sp->lcp.max_failure = 10; +} + +static void +sppp_lcp_up(struct sppp *sp) +{ + STDDCL; + + /* + * If this interface is passive or dial-on-demand, it means + * we've got in incoming call. Activate the interface. + */ + if ((ifp->if_flags & (IFF_AUTO | IFF_PASSIVE)) != 0) { + if (debug) + log(LOG_DEBUG, + "%s%d: Up event (incoming call)\n", + ifp->if_name, ifp->if_unit); + ifp->if_flags |= IFF_RUNNING; + lcp.Open(sp); + } + + sppp_up_event(&lcp, sp); +} + +static void +sppp_lcp_down(struct sppp *sp) +{ + STDDCL; + + sppp_down_event(&lcp, sp); + + /* + * If this is neither a dial-on-demand nor a passive + * interface, simulate an ``ifconfig down'' action, so the + * administrator can force a redial by another ``ifconfig + * up''. XXX For leased line operation, should we immediately + * try to reopen the connection here? + */ + if ((ifp->if_flags & (IFF_AUTO | IFF_PASSIVE)) == 0) { + log(LOG_INFO, + "%s%d: Down event (carrier loss), taking interface down.\n", + ifp->if_name, ifp->if_unit); + if_down(ifp); + } else { + if (debug) + log(LOG_DEBUG, + "%s%d: Down event (carrier loss)\n", + ifp->if_name, ifp->if_unit); + } + lcp.Close(sp); + ifp->if_flags &= ~IFF_RUNNING; +} + +static void +sppp_lcp_open(struct sppp *sp) +{ + sppp_open_event(&lcp, sp); +} + +static void +sppp_lcp_close(struct sppp *sp) +{ + sppp_close_event(&lcp, sp); +} + +static void +sppp_lcp_TO(void *cookie) +{ + sppp_to_event(&lcp, (struct sppp *)cookie); +} + +/* + * Analyze a configure request. Return true if it was agreeable, and + * caused action sca, false if it has been rejected or nak'ed, and + * caused action scn. (The return value is used to make the state + * transition decision in the state automaton.) */ static int -sppp_lcp_conf_parse_options(struct sppp *sp, struct lcp_header *h, - int len, u_long *magic) +sppp_lcp_RCR(struct sppp *sp, struct lcp_header *h, int len) { + STDDCL; u_char *buf, *r, *p; - struct ifnet *ifp = &sp->pp_if; - int rlen, debug = ifp->if_flags & IFF_DEBUG; + int origlen, rlen; + u_long nmagic; len -= 4; + origlen = len; buf = r = malloc (len, M_TEMP, M_NOWAIT); if (! buf) return (0); if (debug) - addlog("%s%d: lcp parse opts: ", ifp->if_name, ifp->if_unit); + log(LOG_DEBUG, "%s%d: lcp parse opts: ", + ifp->if_name, ifp->if_unit); + /* pass 1: check for things that need to be rejected */ p = (void*) (h+1); for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) { + if (debug) + addlog(" %s ", sppp_lcp_opt_name(*p)); switch (*p) { case LCP_OPT_MAGIC: - /* Magic number -- extract. */ + /* Magic number. */ + /* fall through, both are same length */ + case LCP_OPT_ASYNC_MAP: + /* Async control character map. */ + if (len >= 6 || p[1] == 6) + continue; if (debug) - addlog(" magicnum: "); - if (len >= 6 && p[1] == 6) { - *magic = (u_long)p[2] << 24 | - (u_long)p[3] << 16 | p[4] << 8 | p[5]; + addlog("[invalid] "); + break; + case LCP_OPT_MRU: + /* Maximum receive unit. */ + if (len >= 4 && p[1] == 4) + continue; + if (debug) + addlog("[invalid] "); + break; + default: + /* Others not supported. */ + if (debug) + addlog("[rej] "); + break; + } + /* Add the option to rejected list. */ + bcopy (p, r, p[1]); + r += p[1]; + rlen += p[1]; + } + if (rlen) { + if (debug) + addlog(" send conf-rej\n"); + sppp_cp_send (sp, PPP_LCP, CONF_REJ, h->ident, rlen, buf); + return 0; + } else if (debug) + addlog("\n"); + + /* + * pass 2: check for option values that are unacceptable and + * thus require to be nak'ed. + */ + if (debug) + addlog("%s%d: lcp parse opt values: ", + ifp->if_name, ifp->if_unit); + + p = (void*) (h+1); + len = origlen; + for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) { + if (debug) + addlog(" %s ", sppp_lcp_opt_name(*p)); + switch (*p) { + case LCP_OPT_MAGIC: + /* Magic number -- extract. */ + nmagic = (u_long)p[2] << 24 | + (u_long)p[3] << 16 | p[4] << 8 | p[5]; + if (nmagic != sp->lcp.magic) { if (debug) - addlog("0x%x", *magic); + addlog("0x%x ", nmagic); continue; } - if (debug) - addlog("invalid"); + /* + * Local and remote magics equal -- loopback? + */ + if (sp->pp_loopcnt >= MAXALIVECNT*5) { + printf ("\n%s%d: loopback\n", + ifp->if_name, ifp->if_unit); + sp->pp_loopcnt = 0; + if (ifp->if_flags & IFF_UP) { + if_down(ifp); + sppp_qflush(&sp->pp_fastq); + /* XXX ? */ + lcp.Down(sp); + lcp.Up(sp); + } + } else if (debug) + addlog("[glitch] "); + ++sp->pp_loopcnt; + /* + * We negate our magic here, and NAK it. If + * we see it later in an NAK packet, we + * suggest a new one. + */ + nmagic = ~sp->lcp.magic; + /* Gonna NAK it. */ + p[2] = nmagic >> 24; + p[3] = nmagic >> 16; + p[4] = nmagic >> 8; + p[5] = nmagic; break; + case LCP_OPT_ASYNC_MAP: /* Async control character map -- check to be zero. */ - if (debug) - addlog(" asyncmap: "); - if (len >= 6 && p[1] == 6 && ! p[2] && ! p[3] && - ! p[4] && ! p[5]) { + if (! p[2] && ! p[3] && ! p[4] && ! p[5]) { if (debug) - addlog("empty (ack)"); + addlog("[empty] "); continue; } if (debug) - addlog("non-empty (rej)"); + addlog("[non-empty] "); + /* suggest a zero one */ + p[2] = p[3] = p[4] = p[5] = 0; break; + case LCP_OPT_MRU: - /* Maximum receive unit -- always OK. */ + /* + * Maximum receive unit. Always agreeable, + * but ignored by now. + */ + sp->lcp.their_mru = p[2] * 256 + p[3]; if (debug) - addlog(" mru (ignored)"); + addlog("%d ", sp->lcp.their_mru); continue; - default: - /* Others not supported. */ - if (debug) - addlog(" unknown 0x%x", *p); - break; } - /* Add the option to rejected list. */ + /* Add the option to nak'ed list. */ bcopy (p, r, p[1]); r += p[1]; rlen += p[1]; } if (rlen) { if (debug) - addlog(", send lcp configure nak\n"); - sppp_cp_send (sp, PPP_LCP, LCP_CONF_NAK, h->ident, rlen, buf); - } else if (debug) - addlog("\n"); + addlog(" send conf-nak\n"); + sppp_cp_send (sp, PPP_LCP, CONF_NAK, h->ident, rlen, buf); + return 0; + } else { + if (debug) + addlog(" send conf-ack\n"); + sp->pp_loopcnt = 0; + sppp_cp_send (sp, PPP_LCP, CONF_ACK, + h->ident, origlen, h+1); + } + free (buf, M_TEMP); return (rlen == 0); } +/* + * Analyze the LCP Configure-Reject option list, and adjust our + * negotiation. + */ static void -sppp_ipcp_input(struct sppp *sp, struct mbuf *m) +sppp_lcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len) { - struct lcp_header *h; - struct ifnet *ifp = &sp->pp_if; - int len = m->m_pkthdr.len, debug = ifp->if_flags & IFF_DEBUG;; + STDDCL; + u_char *buf, *p; - if (len < 4) { - printf ("%s%d: invalid ipcp packet length: %d bytes\n", - ifp->if_name, ifp->if_unit, len); + len -= 4; + buf = malloc (len, M_TEMP, M_NOWAIT); + if (!buf) return; - } - h = mtod (m, struct lcp_header*); - if (debug) { - log(LOG_DEBUG, - "%s%d: ipcp input: %d bytes <%s id=0x%x len=0x%x", - ifp->if_name, ifp->if_unit, len, - sppp_ipcp_type_name (h->type), h->ident, ntohs (h->len)); - if (len > 4) - sppp_print_bytes ((u_char*) (h+1), len-4); - addlog(">\n"); - } - if (len > ntohs (h->len)) - len = ntohs (h->len); - switch (h->type) { - default: - /* Unknown packet type -- send Code-Reject packet. */ + + if (debug) + log(LOG_DEBUG, "%s%d: lcp rej opts: ", + ifp->if_name, ifp->if_unit); + + p = (void*) (h+1); + for (; len > 1 && p[1]; len -= p[1], p += p[1]) { if (debug) - addlog("%s%d: ipcp unknown type 0x%x, sending code rej\n", - ifp->if_name, ifp->if_unit, h->type); - sppp_cp_send (sp, PPP_IPCP, IPCP_CODE_REJ, ++sp->pp_seq, len, h); - break; - case IPCP_CONF_REQ: - if (len < 4) { - if (debug) - addlog("%s%d: invalid ipcp configure " - "request packet length: %d bytes\n", - ifp->if_name, ifp->if_unit, len); - return; + addlog(" %s ", sppp_lcp_opt_name(*p)); + switch (*p) { + case LCP_OPT_MAGIC: + /* Magic number -- can't use it, use 0 */ + sp->lcp.opts &= ~(1 << LCP_OPT_MAGIC); + sp->lcp.magic = 0; + break; + case LCP_OPT_MRU: + /* + * Should not be rejected anyway, since we only + * negotiate a MRU if explicitly requested by + * peer. + */ + sp->lcp.opts &= ~(1 << LCP_OPT_MRU); + break; } - if (len > 4) { - if (debug) - addlog("%s%d: rejecting ipcp conf req len=%d\n", - ifp->if_name, ifp->if_unit, len); - sppp_cp_send (sp, PPP_IPCP, LCP_CONF_REJ, h->ident, - len-4, h+1); - - switch (sp->ipcp.state) { - case IPCP_STATE_OPENED: - /* Initiate renegotiation. */ - sppp_ipcp_open (sp); - /* fall through... */ - case IPCP_STATE_ACK_SENT: - /* Go to closed state. */ - sp->ipcp.state = IPCP_STATE_CLOSED; + } + if (debug) + addlog("\n"); + free (buf, M_TEMP); + return; +} + +/* + * Analyze the LCP Configure-NAK option list, and adjust our + * negotiation. + */ +static void +sppp_lcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len) +{ + STDDCL; + u_char *buf, *p; + u_long magic; + + len -= 4; + buf = malloc (len, M_TEMP, M_NOWAIT); + if (!buf) + return; + + if (debug) + log(LOG_DEBUG, "%s%d: lcp nak opts: ", + ifp->if_name, ifp->if_unit); + + p = (void*) (h+1); + for (; len > 1 && p[1]; len -= p[1], p += p[1]) { + if (debug) + addlog(" %s ", sppp_lcp_opt_name(*p)); + switch (*p) { + case LCP_OPT_MAGIC: + /* Magic number -- renegotiate */ + if ((sp->lcp.opts & (1 << LCP_OPT_MAGIC)) && + len >= 6 && p[1] == 6) { + magic = (u_long)p[2] << 24 | + (u_long)p[3] << 16 | p[4] << 8 | p[5]; + /* + * If the remote magic is our negated one, + * this looks like a loopback problem. + * Suggest a new magic to make sure. + */ + if (magic == ~sp->lcp.magic) { + if (debug) + addlog("magic glitch "); + sp->lcp.magic += time.tv_sec + time.tv_usec; + } else { + sp->lcp.magic = magic; + if (debug) + addlog("%d "); + } } - } else { - if (debug) - addlog("%s%d: sending ipcp conf ack\n", - ifp->if_name, ifp->if_unit); - /* Send Configure-Ack packet. */ - sppp_cp_send (sp, PPP_IPCP, IPCP_CONF_ACK, h->ident, - 0, 0); - /* Change the state. */ - if (sp->ipcp.state == IPCP_STATE_ACK_RCVD) - sp->ipcp.state = IPCP_STATE_OPENED; - else - sp->ipcp.state = IPCP_STATE_ACK_SENT; + break; + case LCP_OPT_MRU: + /* + * Peer wants to advise us to negotiate an MRU. + * Agree on it if it's reasonable, or use + * default otherwise. + */ + if (len >= 4 && p[1] == 4) { + u_int mru = p[2] * 256 + p[3]; + if (debug) + addlog("%d ", mru); + if (mru < PP_MTU || mru > PP_MAX_MRU) + mru = PP_MTU; + sp->lcp.mru = mru; + sp->lcp.opts |= (1 << LCP_OPT_MRU); + } + break; } - break; - case IPCP_CONF_ACK: - if (h->ident != sp->ipcp.confid) { + } + if (debug) + addlog("\n"); + free (buf, M_TEMP); + return; +} + +static void +sppp_lcp_tlu(struct sppp *sp) +{ + STDDCL; + int i; + u_long mask; + + /* XXX ? */ + if (! (ifp->if_flags & IFF_UP) && + (ifp->if_flags & IFF_RUNNING)) { + /* Coming out of loopback mode. */ + if_up(ifp); + printf ("%s%d: up\n", ifp->if_name, ifp->if_unit); + } + + for (i = 0; i < IDX_COUNT; i++) + if ((cps[i])->flags & CP_QUAL) + (cps[i])->Open(sp); + + if (/* require authentication XXX */ 0) + sp->pp_phase = PHASE_AUTHENTICATE; + else + sp->pp_phase = PHASE_NETWORK; + + log(LOG_INFO, "%s%d: phase %s\n", ifp->if_name, ifp->if_unit, + sppp_phase_name(sp->pp_phase)); + + if (sp->pp_phase == PHASE_AUTHENTICATE) { + for (i = 0; i < IDX_COUNT; i++) + if ((cps[i])->flags & CP_AUTH) + (cps[i])->Open(sp); + } else { + /* Notify all NCPs. */ + for (i = 0; i < IDX_COUNT; i++) + if ((cps[i])->flags & CP_NCP) + (cps[i])->Open(sp); + } + + /* Send Up events to all started protos. */ + for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1) + if (sp->lcp.protos & mask && ((cps[i])->flags & CP_LCP) == 0) + (cps[i])->Up(sp); + + if (sp->pp_phase == PHASE_NETWORK) + /* if no NCP is starting, close down */ + sppp_lcp_check(sp); +} + +static void +sppp_lcp_tld(struct sppp *sp) +{ + STDDCL; + int i; + u_long mask; + + sp->pp_phase = PHASE_TERMINATE; + + log(LOG_INFO, "%s%d: phase %s\n", ifp->if_name, ifp->if_unit, + sppp_phase_name(sp->pp_phase)); + + /* + * Take upper layers down. We send the Down event first and + * the Close second to prevent the upper layers from sending + * ``a flurry of terminate-request packets'', as the RFC + * describes it. + */ + for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1) + if (sp->lcp.protos & mask && ((cps[i])->flags & CP_LCP) == 0) { + (cps[i])->Down(sp); + (cps[i])->Close(sp); + } +} + +static void +sppp_lcp_tls(struct sppp *sp) +{ + STDDCL; + + sp->pp_phase = PHASE_ESTABLISH; + + log(LOG_INFO, "%s%d: phase %s\n", ifp->if_name, ifp->if_unit, + sppp_phase_name(sp->pp_phase)); + + /* Notify lower layer if desired. */ + if (sp->pp_tls) + (sp->pp_tls)(sp); +} + +static void +sppp_lcp_tlf(struct sppp *sp) +{ + STDDCL; + + sp->pp_phase = PHASE_DEAD; + log(LOG_INFO, "%s%d: phase %s\n", ifp->if_name, ifp->if_unit, + sppp_phase_name(sp->pp_phase)); + + /* Notify lower layer if desired. */ + if (sp->pp_tlf) + (sp->pp_tlf)(sp); +} + +static void +sppp_lcp_scr(struct sppp *sp) +{ + char opt[6 /* magicnum */ + 4 /* mru */]; + int i = 0; + + if (sp->lcp.opts & (1 << LCP_OPT_MAGIC)) { + if (! sp->lcp.magic) + sp->lcp.magic = time.tv_sec + time.tv_usec; + opt[i++] = LCP_OPT_MAGIC; + opt[i++] = 6; + opt[i++] = sp->lcp.magic >> 24; + opt[i++] = sp->lcp.magic >> 16; + opt[i++] = sp->lcp.magic >> 8; + opt[i++] = sp->lcp.magic; + } + + if (sp->lcp.opts & (1 << LCP_OPT_MRU)) { + opt[i++] = LCP_OPT_MRU; + opt[i++] = 4; + opt[i++] = sp->lcp.mru >> 8; + opt[i++] = sp->lcp.mru; + } + + sp->confid[IDX_LCP] = ++sp->pp_seq; + sppp_cp_send (sp, PPP_LCP, CONF_REQ, sp->confid[IDX_LCP], i, &opt); +} + +/* + * Re-check the open NCPs and see if we should terminate the link. + * Called by the NCPs during their tlf action handling. + */ +static void +sppp_lcp_check(struct sppp *sp) +{ + int i, mask; + + for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1) + if (sp->lcp.protos & mask && (cps[i])->flags & CP_NCP) + return; + lcp.Close(sp); +} +/* + *--------------------------------------------------------------------------* + * * + * The IPCP implementation. * + * * + *--------------------------------------------------------------------------* + */ + +static void +sppp_ipcp_init(struct sppp *sp) +{ + sp->ipcp.opts = 0; + sp->ipcp.flags = 0; + sp->state[IDX_IPCP] = STATE_INITIAL; + sp->fail_counter[IDX_IPCP] = 0; +} + +static void +sppp_ipcp_up(struct sppp *sp) +{ + sppp_up_event(&ipcp, sp); +} + +static void +sppp_ipcp_down(struct sppp *sp) +{ + sppp_down_event(&ipcp, sp); +} + +static void +sppp_ipcp_open(struct sppp *sp) +{ + STDDCL; + u_long myaddr, hisaddr; + + sppp_get_ip_addrs(sp, &myaddr, &hisaddr); + /* + * If we don't have his address, this probably means our + * interface doesn't want to talk IP at all. (This could + * be the case if somebody wants to speak only IPX, for + * example.) Don't open IPCP in this case. + */ + if (hisaddr == 0L) { + /* XXX this message should go away */ + if (debug) + log(LOG_DEBUG, "%s%d: ipcp_open(): no IP interface\n", + ifp->if_name, ifp->if_unit); + return; + } + + if (myaddr == 0L) { + /* + * I don't have an assigned address, so i need to + * negotiate my address. + */ + sp->ipcp.flags |= IPCP_MYADDR_DYN; + sp->ipcp.opts |= (1 << IPCP_OPT_ADDRESS); + } + sppp_open_event(&ipcp, sp); +} + +static void +sppp_ipcp_close(struct sppp *sp) +{ + sppp_close_event(&ipcp, sp); + if (sp->ipcp.flags & IPCP_MYADDR_DYN) + /* + * My address was dynamic, clear it again. + */ + sppp_set_ip_addr(sp, 0L); +} + +static void +sppp_ipcp_TO(void *cookie) +{ + sppp_to_event(&ipcp, (struct sppp *)cookie); +} + +/* + * Analyze a configure request. Return true if it was agreeable, and + * caused action sca, false if it has been rejected or nak'ed, and + * caused action scn. (The return value is used to make the state + * transition decision in the state automaton.) + */ +static int +sppp_ipcp_RCR(struct sppp *sp, struct lcp_header *h, int len) +{ + u_char *buf, *r, *p; + struct ifnet *ifp = &sp->pp_if; + int rlen, origlen, debug = ifp->if_flags & IFF_DEBUG; + u_long hisaddr, desiredaddr; + + len -= 4; + origlen = len; + /* + * Make sure to allocate a buf that can at least hold a + * conf-nak with an `address' option. We might need it below. + */ + buf = r = malloc ((len < 6? 6: len), M_TEMP, M_NOWAIT); + if (! buf) + return (0); + + /* pass 1: see if we can recognize them */ + if (debug) + log(LOG_DEBUG, "%s%d: ipcp parse opts: ", + ifp->if_name, ifp->if_unit); + p = (void*) (h+1); + for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) { + if (debug) + addlog(" %s ", sppp_ipcp_opt_name(*p)); + switch (*p) { +#ifdef notyet + case IPCP_OPT_COMPRESSION: + if (len >= 6 && p[1] >= 6) { + /* correctly formed compress option */ + continue; + } if (debug) - addlog("%s%d: ipcp id mismatch 0x%x != 0x%x\n", - ifp->if_name, ifp->if_unit, - h->ident, sp->ipcp.confid); + addlog("[invalid] "); break; - } - UNTIMO (sp); - switch (sp->ipcp.state) { - case IPCP_STATE_CLOSED: - sp->ipcp.state = IPCP_STATE_ACK_RCVD; - TIMO (sp, 5); +#endif + case IPCP_OPT_ADDRESS: + if (len >= 6 && p[1] == 6) { + /* correctly formed address option */ + continue; + } + if (debug) + addlog("[invalid] "); break; - case IPCP_STATE_ACK_SENT: - sp->ipcp.state = IPCP_STATE_OPENED; + default: + /* Others not supported. */ + if (debug) + addlog("[rej] "); break; } - break; - case IPCP_CONF_NAK: - case IPCP_CONF_REJ: - if (h->ident != sp->ipcp.confid) { - if (debug) - addlog("%s%d: ipcp id mismatch 0x%x != 0x%x\n", - ifp->if_name, ifp->if_unit, - h->ident, sp->ipcp.confid); + /* Add the option to rejected list. */ + bcopy (p, r, p[1]); + r += p[1]; + rlen += p[1]; + } + if (rlen) { + if (debug) + addlog(" send conf-rej\n"); + sppp_cp_send (sp, PPP_IPCP, CONF_REJ, h->ident, rlen, buf); + return 0; + } else if (debug) + addlog("\n"); + + /* pass 2: parse option values */ + sppp_get_ip_addrs(sp, 0, &hisaddr); + if (debug) + addlog("%s%d: ipcp parse opt values: ", ifp->if_name, ifp->if_unit); + p = (void*) (h+1); + len = origlen; + for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) { + if (debug) + addlog(" %s ", sppp_ipcp_opt_name(*p)); + switch (*p) { +#ifdef notyet + case IPCP_OPT_COMPRESSION: + continue; +#endif + case IPCP_OPT_ADDRESS: + desiredaddr = p[2] << 24 | p[3] << 16 | + p[4] << 8 | p[5]; + if (desiredaddr == hisaddr) { + /* + * Peer's address is same as our value, + * this is agreeable. Gonna conf-ack + * it. + */ + if (debug) + addlog("0x%x [ack] ", hisaddr); + /* record that we've seen it already */ + sp->ipcp.flags |= IPCP_HISADDR_SEEN; + continue; + } + /* + * The address wasn't agreeable. This is either + * he sent us 0.0.0.0, asking to assign him an + * address, or he send us another address not + * matching our value. Either case, we gonna + * conf-nak it with our value. + */ + if (debug) { + if (desiredaddr == 0) + addlog("[addr requested] "); + else + addlog("0x%x [not agreed] ", + desiredaddr); + + p[2] = hisaddr >> 24; + p[3] = hisaddr >> 16; + p[4] = hisaddr >> 8; + p[5] = hisaddr; + } break; } + /* Add the option to nak'ed list. */ + bcopy (p, r, p[1]); + r += p[1]; + rlen += p[1]; + } + + /* + * If we are about to conf-ack the request, but haven't seen + * his address so far, gonna conf-nak it instead, with the + * `address' option present and our idea of his address being + * filled in there, to request negotiation of both addresses. + * + * XXX This can result in an endless req - nak loop if peer + * doesn't want to send us his address. Q: What should we do + * about it? XXX A: implement the max-failure counter. + */ + if (rlen == 0 && !(sp->ipcp.flags & IPCP_HISADDR_SEEN)) { + buf[0] = IPCP_OPT_ADDRESS; + buf[1] = 6; + buf[2] = hisaddr >> 24; + buf[3] = hisaddr >> 16; + buf[4] = hisaddr >> 8; + buf[5] = hisaddr; + rlen = 6; if (debug) - addlog("%s%d: got ipcp conf %s, reopening ipcp\n", - ifp->if_name, ifp->if_unit, - (h->type == IPCP_CONF_NAK? "nak": "rej")); - UNTIMO (sp); - /* Initiate renegotiation. */ - sppp_ipcp_open (sp); - if (sp->ipcp.state != IPCP_STATE_ACK_SENT) - /* Go to closed state. */ - sp->ipcp.state = IPCP_STATE_CLOSED; - break; - case IPCP_TERM_REQ: - /* Send Terminate-Ack packet. */ + addlog("still need hisaddr "); + } + + if (rlen) { if (debug) - addlog("%s%d: got ipcp term req\n", - ifp->if_name, ifp->if_unit); - sppp_cp_send (sp, PPP_IPCP, IPCP_TERM_ACK, h->ident, 0, 0); - /* Go to closed state. */ - sp->ipcp.state = IPCP_STATE_CLOSED; - /* Initiate renegotiation. */ - sppp_ipcp_open (sp); - break; - case IPCP_TERM_ACK: - /* Ignore for now. */ + addlog(" send conf-nak\n"); + sppp_cp_send (sp, PPP_IPCP, CONF_NAK, h->ident, rlen, buf); + } else { if (debug) - addlog("%s%d: ignoring ipcp term ack\n", - ifp->if_name, ifp->if_unit); - case IPCP_CODE_REJ: - /* Ignore for now. */ + addlog(" send conf-ack\n"); + sppp_cp_send (sp, PPP_IPCP, CONF_ACK, + h->ident, origlen, h+1); + } + + free (buf, M_TEMP); + return (rlen == 0); +} + +/* + * Analyze the IPCP Configure-Reject option list, and adjust our + * negotiation. + */ +static void +sppp_ipcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len) +{ + u_char *buf, *p; + struct ifnet *ifp = &sp->pp_if; + int debug = ifp->if_flags & IFF_DEBUG; + + len -= 4; + buf = malloc (len, M_TEMP, M_NOWAIT); + if (!buf) + return; + + if (debug) + log(LOG_DEBUG, "%s%d: ipcp rej opts: ", + ifp->if_name, ifp->if_unit); + + p = (void*) (h+1); + for (; len > 1 && p[1]; len -= p[1], p += p[1]) { if (debug) - addlog("%s%d: ignoring ipcp code rej\n", - ifp->if_name, ifp->if_unit); - break; + addlog(" %s ", sppp_ipcp_opt_name(*p)); + switch (*p) { + case IPCP_OPT_ADDRESS: + /* + * Peer doesn't grok address option. This is + * bad. XXX Should we better give up here? + */ + sp->ipcp.opts &= ~(1 << IPCP_OPT_ADDRESS); + break; +#ifdef notyet + case IPCP_OPT_COMPRESS: + sp->ipcp.opts &= ~(1 << IPCP_OPT_COMPRESS); + break; +#endif + } } + if (debug) + addlog("\n"); + free (buf, M_TEMP); + return; } +/* + * Analyze the IPCP Configure-NAK option list, and adjust our + * negotiation. + */ static void -sppp_lcp_open(struct sppp *sp) +sppp_ipcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len) +{ + u_char *buf, *p; + struct ifnet *ifp = &sp->pp_if; + int debug = ifp->if_flags & IFF_DEBUG; + u_long wantaddr; + + len -= 4; + buf = malloc (len, M_TEMP, M_NOWAIT); + if (!buf) + return; + + if (debug) + log(LOG_DEBUG, "%s%d: ipcp nak opts: ", + ifp->if_name, ifp->if_unit); + + p = (void*) (h+1); + for (; len > 1 && p[1]; len -= p[1], p += p[1]) { + if (debug) + addlog(" %s ", sppp_ipcp_opt_name(*p)); + switch (*p) { + case IPCP_OPT_ADDRESS: + /* + * Peer doesn't like our local IP address. See + * if we can do something for him. We'll drop + * him our address then. + */ + if (len >= 6 && p[1] == 6) { + wantaddr = p[2] << 24 | p[3] << 16 | + p[4] << 8 | p[5]; + sp->ipcp.opts |= (1 << IPCP_OPT_ADDRESS); + if (debug) + addlog("[wantaddr 0x%x] ", wantaddr); + /* + * When doing dynamic address assignment, + * we accept his offer. Otherwise, we + * ignore it and thus continue to negotiate + * our already existing value. + */ + if (sp->ipcp.flags & IPCP_MYADDR_DYN) { + sppp_set_ip_addr(sp, wantaddr); + if (debug) + addlog("[agree] "); + } + } + break; +#ifdef notyet + case IPCP_OPT_COMPRESS: + /* + * Peer wants different compression parameters. + */ + break; +#endif + } + } + if (debug) + addlog("\n"); + free (buf, M_TEMP); + return; +} + +static void +sppp_ipcp_tlu(struct sppp *sp) { - char opt[6]; - - if (! sp->lcp.magic) - sp->lcp.magic = time.tv_sec + time.tv_usec; - opt[0] = LCP_OPT_MAGIC; - opt[1] = sizeof (opt); - opt[2] = sp->lcp.magic >> 24; - opt[3] = sp->lcp.magic >> 16; - opt[4] = sp->lcp.magic >> 8; - opt[5] = sp->lcp.magic; - sp->lcp.confid = ++sp->pp_seq; - sppp_cp_send (sp, PPP_LCP, LCP_CONF_REQ, sp->lcp.confid, - sizeof (opt), &opt); - TIMO (sp, 2); } static void -sppp_ipcp_open(struct sppp *sp) +sppp_ipcp_tld(struct sppp *sp) { - sp->ipcp.confid = ++sp->pp_seq; - sppp_cp_send (sp, PPP_IPCP, IPCP_CONF_REQ, sp->ipcp.confid, 0, 0); - TIMO (sp, 2); } +static void +sppp_ipcp_tls(struct sppp *sp) +{ + /* indicate to LCP that it must stay alive */ + sp->lcp.protos |= (1 << IDX_IPCP); +} + +static void +sppp_ipcp_tlf(struct sppp *sp) +{ + /* we no longer need LCP */ + sp->lcp.protos &= ~(1 << IDX_IPCP); + sppp_lcp_check(sp); +} + +static void +sppp_ipcp_scr(struct sppp *sp) +{ + char opt[6 /* compression */ + 6 /* address */]; + u_long ouraddr; + int i = 0; + +#ifdef notyet + if (sp->ipcp.opts & (1 << IPCP_OPT_COMPRESSION)) { + opt[i++] = IPCP_OPT_COMPRESSION; + opt[i++] = 6; + opt[i++] = 0; /* VJ header compression */ + opt[i++] = 0x2d; /* VJ header compression */ + opt[i++] = max_slot_id; + opt[i++] = comp_slot_id; + } +#endif + + if (sp->ipcp.opts & (1 << IPCP_OPT_ADDRESS)) { + sppp_get_ip_addrs(sp, &ouraddr, 0); + opt[i++] = IPCP_OPT_ADDRESS; + opt[i++] = 6; + opt[i++] = ouraddr >> 24; + opt[i++] = ouraddr >> 16; + opt[i++] = ouraddr >> 8; + opt[i++] = ouraddr; + } + + sp->confid[IDX_IPCP] = ++sp->pp_seq; + sppp_cp_send(sp, PPP_IPCP, CONF_REQ, sp->confid[IDX_IPCP], i, &opt); +} + + +/* + * Random miscellaneous functions. + */ + /* - * Process PPP control protocol timeouts. + * Flush interface queue. */ static void -sppp_cp_timeout(void *arg) +sppp_qflush(struct ifqueue *ifq) { - struct sppp *sp = (struct sppp*) arg; - int s = splimp (); + struct mbuf *m, *n; - sp->pp_flags &= ~PP_TIMO; - if (! (sp->pp_if.if_flags & IFF_RUNNING) || (sp->pp_flags & PP_CISCO)) { - splx (s); - return; + n = ifq->ifq_head; + while ((m = n)) { + n = m->m_act; + m_freem (m); } - switch (sp->lcp.state) { - case LCP_STATE_CLOSED: - /* No ACK for Configure-Request, retry. */ - sppp_lcp_open (sp); - break; - case LCP_STATE_ACK_RCVD: - /* ACK got, but no Configure-Request for peer, retry. */ - sppp_lcp_open (sp); - sp->lcp.state = LCP_STATE_CLOSED; - break; - case LCP_STATE_ACK_SENT: - /* ACK sent but no ACK for Configure-Request, retry. */ - sppp_lcp_open (sp); - break; - case LCP_STATE_OPENED: - /* LCP is already OK, try IPCP. */ - switch (sp->ipcp.state) { - case IPCP_STATE_CLOSED: - /* No ACK for Configure-Request, retry. */ - sppp_ipcp_open (sp); - break; - case IPCP_STATE_ACK_RCVD: - /* ACK got, but no Configure-Request for peer, retry. */ - sppp_ipcp_open (sp); - sp->ipcp.state = IPCP_STATE_CLOSED; - break; - case IPCP_STATE_ACK_SENT: - /* ACK sent but no ACK for Configure-Request, retry. */ - sppp_ipcp_open (sp); - break; - case IPCP_STATE_OPENED: - /* IPCP is OK. */ - break; + ifq->ifq_head = 0; + ifq->ifq_tail = 0; + ifq->ifq_len = 0; +} + +/* + * Send keepalive packets, every 10 seconds. + */ +static void +sppp_keepalive(void *dummy) +{ + struct sppp *sp; + int s; + + s = splimp(); + for (sp=spppq; sp; sp=sp->pp_next) { + struct ifnet *ifp = &sp->pp_if; + + /* Keepalive mode disabled or channel down? */ + if (! (sp->pp_flags & PP_KEEPALIVE) || + ! (ifp->if_flags & IFF_RUNNING)) + continue; + + /* No keepalive in PPP mode if LCP not opened yet. */ + if (! (sp->pp_flags & PP_CISCO) && + sp->pp_phase < PHASE_AUTHENTICATE) + continue; + + if (sp->pp_alivecnt == MAXALIVECNT) { + /* No keepalive packets got. Stop the interface. */ + printf ("%s%d: down\n", ifp->if_name, ifp->if_unit); + if_down (ifp); + sppp_qflush (&sp->pp_fastq); + if (! (sp->pp_flags & PP_CISCO)) { + /* XXX */ + /* Shut down the PPP link. */ + lcp.Down(sp); + /* Initiate negotiation. XXX */ + lcp.Up(sp); + } + } + if (sp->pp_alivecnt <= MAXALIVECNT) + ++sp->pp_alivecnt; + if (sp->pp_flags & PP_CISCO) + sppp_cisco_send (sp, CISCO_KEEPALIVE_REQ, ++sp->pp_seq, + sp->pp_rseq); + else if (sp->pp_phase >= PHASE_AUTHENTICATE) { + long nmagic = htonl (sp->lcp.magic); + sp->lcp.echoid = ++sp->pp_seq; + sppp_cp_send (sp, PPP_LCP, ECHO_REQ, + sp->lcp.echoid, 4, &nmagic); } - break; } - splx (s); + splx(s); + timeout(sppp_keepalive, 0, hz * 10); +} + +/* + * Get both IP addresses. + */ +static void +sppp_get_ip_addrs(struct sppp *sp, u_long *src, u_long *dst) +{ + struct ifnet *ifp = &sp->pp_if; + struct ifaddr *ifa; + struct sockaddr_in *si; + u_long ssrc, ddst; + + ssrc = ddst = 0L; + /* + * Pick the first AF_INET address from the list, + * aliases don't make any sense on a p2p link anyway. + */ + for (ifa = ifp->if_addrhead.tqh_first, si = 0; + ifa; + ifa = ifa->ifa_link.tqe_next) + if (ifa->ifa_addr->sa_family == AF_INET) { + si = (struct sockaddr_in *)ifa->ifa_addr; + if (si) + break; + } + if (ifa) { + if (si && si->sin_addr.s_addr) + ssrc = si->sin_addr.s_addr; + + si = (struct sockaddr_in *)ifa->ifa_dstaddr; + if (si && si->sin_addr.s_addr) + ddst = si->sin_addr.s_addr; + } + + if (dst) *dst = ntohl(ddst); + if (src) *src = ntohl(ssrc); +} + +/* + * Set my IP address. Must be called at splimp. + */ +static void +sppp_set_ip_addr(struct sppp *sp, u_long src) +{ + struct ifnet *ifp = &sp->pp_if; + struct ifaddr *ifa; + struct sockaddr_in *si; + u_long ssrc, ddst; + + /* + * Pick the first AF_INET address from the list, + * aliases don't make any sense on a p2p link anyway. + */ + for (ifa = ifp->if_addrhead.tqh_first, si = 0; + ifa; + ifa = ifa->ifa_link.tqe_next) + if (ifa->ifa_addr->sa_family == AF_INET) { + si = (struct sockaddr_in *)ifa->ifa_addr; + if (si) + break; + } + if (ifa && si) + si->sin_addr.s_addr = htonl(src); } static const char * -sppp_lcp_type_name(u_char type) +sppp_cp_type_name(u_char type) { static char buf [12]; switch (type) { - case LCP_CONF_REQ: return ("conf-req"); - case LCP_CONF_ACK: return ("conf-ack"); - case LCP_CONF_NAK: return ("conf-nack"); - case LCP_CONF_REJ: return ("conf-rej"); - case LCP_TERM_REQ: return ("term-req"); - case LCP_TERM_ACK: return ("term-ack"); - case LCP_CODE_REJ: return ("code-rej"); - case LCP_PROTO_REJ: return ("proto-rej"); - case LCP_ECHO_REQ: return ("echo-req"); - case LCP_ECHO_REPLY: return ("echo-reply"); - case LCP_DISC_REQ: return ("discard-req"); + case CONF_REQ: return ("conf-req"); + case CONF_ACK: return ("conf-ack"); + case CONF_NAK: return ("conf-nak"); + case CONF_REJ: return ("conf-rej"); + case TERM_REQ: return ("term-req"); + case TERM_ACK: return ("term-ack"); + case CODE_REJ: return ("code-rej"); + case PROTO_REJ: return ("proto-rej"); + case ECHO_REQ: return ("echo-req"); + case ECHO_REPLY: return ("echo-reply"); + case DISC_REQ: return ("discard-req"); } sprintf (buf, "0x%x", type); return (buf); } static const char * -sppp_ipcp_type_name(u_char type) +sppp_lcp_opt_name(u_char opt) { static char buf [12]; - switch (type) { - case IPCP_CONF_REQ: return ("conf-req"); - case IPCP_CONF_ACK: return ("conf-ack"); - case IPCP_CONF_NAK: return ("conf-nack"); - case IPCP_CONF_REJ: return ("conf-rej"); - case IPCP_TERM_REQ: return ("term-req"); - case IPCP_TERM_ACK: return ("term-ack"); - case IPCP_CODE_REJ: return ("code-rej"); + switch (opt) { + case LCP_OPT_MRU: return ("mru"); + case LCP_OPT_ASYNC_MAP: return ("async-map"); + case LCP_OPT_AUTH_PROTO: return ("auth-proto"); + case LCP_OPT_QUAL_PROTO: return ("qual-proto"); + case LCP_OPT_MAGIC: return ("magic"); + case LCP_OPT_PROTO_COMP: return ("proto-comp"); + case LCP_OPT_ADDR_COMP: return ("addr-comp"); } - sprintf (buf, "0x%x", type); + sprintf (buf, "0x%x", opt); return (buf); } +static const char * +sppp_ipcp_opt_name(u_char opt) +{ + static char buf [12]; + switch (opt) { + case IPCP_OPT_ADDRESSES: return ("addresses"); + case IPCP_OPT_COMPRESSION: return ("compression"); + case IPCP_OPT_ADDRESS: return ("address"); + } + sprintf (buf, "0x%x", opt); + return (buf); +} + +static const char * +sppp_state_name(int state) +{ + switch (state) { + case STATE_INITIAL: return "initial"; + case STATE_STARTING: return "starting"; + case STATE_CLOSED: return "closed"; + case STATE_STOPPED: return "stopped"; + case STATE_CLOSING: return "closing"; + case STATE_STOPPING: return "stopping"; + case STATE_REQ_SENT: return "req-sent"; + case STATE_ACK_RCVD: return "ack-rcvd"; + case STATE_ACK_SENT: return "ack-sent"; + case STATE_OPENED: return "opened"; + } + return "illegal"; +} + +static const char * +sppp_phase_name(enum ppp_phase phase) +{ + switch (phase) { + case PHASE_DEAD: return "dead"; + case PHASE_ESTABLISH: return "establish"; + case PHASE_TERMINATE: return "terminate"; + case PHASE_AUTHENTICATE: return "authenticate"; + case PHASE_NETWORK: return "network"; + } + return "illegal"; +} + +static const char * +sppp_proto_name(u_short proto) +{ + static char buf[12]; + switch (proto) { + case PPP_LCP: return "lcp"; + case PPP_IPCP: return "ipcp"; + } + sprintf(buf, "0x%x", (unsigned)proto); + return buf; +} + static void sppp_print_bytes(u_char *p, u_short len) { @@ -1499,3 +2703,11 @@ sppp_print_bytes(u_char *p, u_short len) while (--len > 0) addlog("-%x", *p++); } + +/* + * This file is large. Tell emacs to highlight it nevertheless. + * + * Local Variables: + * hilit-auto-highlight-maxout: 100000 + * End: + */ |