if_spppsubr.c revision 12495
14910Swollman/*
24910Swollman * Synchronous PPP/Cisco link level subroutines.
34910Swollman * Keepalive protocol implemented in both Cisco and PPP modes.
44910Swollman *
54910Swollman * Copyright (C) 1994 Cronyx Ltd.
64910Swollman * Author: Serge Vakulenko, <vak@zebub.msk.su>
74910Swollman *
84910Swollman * This software is distributed with NO WARRANTIES, not even the implied
94910Swollman * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
104910Swollman *
114910Swollman * Authors grant any other persons or organisations permission to use
124910Swollman * or modify this software as long as this message is kept with the software,
134910Swollman * all derivative works or modified versions.
144910Swollman *
1511189Sjkh * Version 1.9, Wed Oct  4 18:58:15 MSK 1995
164910Swollman */
174910Swollman#undef DEBUG
184910Swollman
194910Swollman#include <sys/param.h>
204952Sbde#include <sys/systm.h>
214952Sbde#include <sys/kernel.h>
224910Swollman#include <sys/ioctl.h>
234910Swollman#include <sys/socket.h>
244910Swollman#include <sys/mbuf.h>
254910Swollman
264910Swollman#include <net/if.h>
274910Swollman#include <net/netisr.h>
284910Swollman#include <net/if_types.h>
294910Swollman
304910Swollman#ifdef INET
314910Swollman#include <netinet/in.h>
324910Swollman#include <netinet/in_systm.h>
334910Swollman#include <netinet/in_var.h>
344910Swollman#include <netinet/ip.h>
354910Swollman#include <netinet/tcp.h>
364910Swollman#include <netinet/if_ether.h>
374910Swollman#endif
384910Swollman
3911819Sjulian#ifdef IPX
4011819Sjulian#include <netipx/ipx.h>
4111819Sjulian#include <netipx/ipx_if.h>
4211819Sjulian#endif
4311819Sjulian
444910Swollman#ifdef NS
454910Swollman#include <netns/ns.h>
464910Swollman#include <netns/ns_if.h>
474910Swollman#endif
484910Swollman
494910Swollman#ifdef ISO
504910Swollman#include <netiso/argo_debug.h>
514910Swollman#include <netiso/iso.h>
524910Swollman#include <netiso/iso_var.h>
534910Swollman#include <netiso/iso_snpac.h>
544910Swollman#endif
554910Swollman
564910Swollman#include <net/if_sppp.h>
574910Swollman
584910Swollman#ifdef DEBUG
594910Swollman#define print(s)        printf s
604910Swollman#else
6111189Sjkh#define print(s)        {/*void*/}
624910Swollman#endif
634910Swollman
644910Swollman#define MAXALIVECNT     3               /* max. alive packets */
654910Swollman
664910Swollman#define PPP_ALLSTATIONS 0xff            /* All-Stations broadcast address */
674910Swollman#define PPP_UI          0x03            /* Unnumbered Information */
684910Swollman#define PPP_IP          0x0021          /* Internet Protocol */
694910Swollman#define PPP_ISO         0x0023          /* ISO OSI Protocol */
704910Swollman#define PPP_XNS         0x0025          /* Xerox NS Protocol */
7112495Speter#define PPP_IPX         0x002b          /* Novell IPX Protocol */
724910Swollman#define PPP_LCP         0xc021          /* Link Control Protocol */
734910Swollman#define PPP_IPCP        0x8021          /* Internet Protocol Control Protocol */
744910Swollman
754910Swollman#define LCP_CONF_REQ    1               /* PPP LCP configure request */
764910Swollman#define LCP_CONF_ACK    2               /* PPP LCP configure acknowledge */
774910Swollman#define LCP_CONF_NAK    3               /* PPP LCP configure negative ack */
784910Swollman#define LCP_CONF_REJ    4               /* PPP LCP configure reject */
794910Swollman#define LCP_TERM_REQ    5               /* PPP LCP terminate request */
804910Swollman#define LCP_TERM_ACK    6               /* PPP LCP terminate acknowledge */
814910Swollman#define LCP_CODE_REJ    7               /* PPP LCP code reject */
824910Swollman#define LCP_PROTO_REJ   8               /* PPP LCP protocol reject */
834910Swollman#define LCP_ECHO_REQ    9               /* PPP LCP echo request */
844910Swollman#define LCP_ECHO_REPLY  10              /* PPP LCP echo reply */
854910Swollman#define LCP_DISC_REQ    11              /* PPP LCP discard request */
864910Swollman
874910Swollman#define LCP_OPT_MRU             1       /* maximum receive unit */
884910Swollman#define LCP_OPT_ASYNC_MAP       2       /* async control character map */
894910Swollman#define LCP_OPT_AUTH_PROTO      3       /* authentication protocol */
904910Swollman#define LCP_OPT_QUAL_PROTO      4       /* quality protocol */
914910Swollman#define LCP_OPT_MAGIC           5       /* magic number */
924910Swollman#define LCP_OPT_RESERVED        6       /* reserved */
934910Swollman#define LCP_OPT_PROTO_COMP      7       /* protocol field compression */
944910Swollman#define LCP_OPT_ADDR_COMP       8       /* address/control field compression */
954910Swollman
964910Swollman#define IPCP_CONF_REQ   LCP_CONF_REQ    /* PPP IPCP configure request */
974910Swollman#define IPCP_CONF_ACK   LCP_CONF_ACK    /* PPP IPCP configure acknowledge */
984910Swollman#define IPCP_CONF_NAK   LCP_CONF_NAK    /* PPP IPCP configure negative ack */
994910Swollman#define IPCP_CONF_REJ   LCP_CONF_REJ    /* PPP IPCP configure reject */
1004910Swollman#define IPCP_TERM_REQ   LCP_TERM_REQ    /* PPP IPCP terminate request */
1014910Swollman#define IPCP_TERM_ACK   LCP_TERM_ACK    /* PPP IPCP terminate acknowledge */
1024910Swollman#define IPCP_CODE_REJ   LCP_CODE_REJ    /* PPP IPCP code reject */
1034910Swollman
1044910Swollman#define CISCO_MULTICAST         0x8f    /* Cisco multicast address */
1054910Swollman#define CISCO_UNICAST           0x0f    /* Cisco unicast address */
1064910Swollman#define CISCO_KEEPALIVE         0x8035  /* Cisco keepalive protocol */
1074910Swollman#define CISCO_ADDR_REQ          0       /* Cisco address request */
1084910Swollman#define CISCO_ADDR_REPLY        1       /* Cisco address reply */
1094910Swollman#define CISCO_KEEPALIVE_REQ     2       /* Cisco keepalive request */
1104910Swollman
1114910Swollmanstruct ppp_header {
11211189Sjkh	u_char address;
11311189Sjkh	u_char control;
11411189Sjkh	u_short protocol;
1154910Swollman};
1164910Swollman#define PPP_HEADER_LEN          sizeof (struct ppp_header)
1174910Swollman
1184910Swollmanstruct lcp_header {
11911189Sjkh	u_char type;
12011189Sjkh	u_char ident;
12111189Sjkh	u_short len;
1224910Swollman};
1234910Swollman#define LCP_HEADER_LEN          sizeof (struct lcp_header)
1244910Swollman
1254910Swollmanstruct cisco_packet {
12611189Sjkh	u_long type;
12711189Sjkh	u_long par1;
12811189Sjkh	u_long par2;
12911189Sjkh	u_short rel;
13011189Sjkh	u_short time0;
13111189Sjkh	u_short time1;
1324910Swollman};
1334910Swollman#define CISCO_PACKET_LEN 18
1344910Swollman
1354910Swollmanstruct sppp *spppq;
1364910Swollman
1374910Swollman/*
1384910Swollman * The following disgusting hack gets around the problem that IP TOS
1394910Swollman * can't be set yet.  We want to put "interactive" traffic on a high
1404910Swollman * priority queue.  To decide if traffic is interactive, we check that
1414910Swollman * a) it is TCP and b) one of its ports is telnet, rlogin or ftp control.
1424910Swollman */
14311189Sjkhstatic u_short interactive_ports[8] = {
1444910Swollman	0,	513,	0,	0,
1454910Swollman	0,	21,	0,	23,
1464910Swollman};
1474910Swollman#define INTERACTIVE(p) (interactive_ports[(p) & 7] == (p))
1484910Swollman
14911189Sjkh/*
15011189Sjkh * Timeout routine activation macros.
15111189Sjkh */
15211189Sjkh#define TIMO(p,s) if (! ((p)->pp_flags & PP_TIMO)) { \
15311189Sjkh			timeout (sppp_cp_timeout, (void*) (p), (s)*hz); \
15411189Sjkh			(p)->pp_flags |= PP_TIMO; }
15511189Sjkh#define UNTIMO(p) if ((p)->pp_flags & PP_TIMO) { \
15611189Sjkh			untimeout (sppp_cp_timeout, (void*) (p)); \
15711189Sjkh			(p)->pp_flags &= ~PP_TIMO; }
15811189Sjkh
15911189Sjkhvoid sppp_keepalive (void *dummy);
16011189Sjkhvoid sppp_cp_send (struct sppp *sp, u_short proto, u_char type,
16111189Sjkh	u_char ident, u_short len, void *data);
1624910Swollmanvoid sppp_cisco_send (struct sppp *sp, int type, long par1, long par2);
1634910Swollmanvoid sppp_lcp_input (struct sppp *sp, struct mbuf *m);
1644910Swollmanvoid sppp_cisco_input (struct sppp *sp, struct mbuf *m);
1654910Swollmanvoid sppp_ipcp_input (struct sppp *sp, struct mbuf *m);
1664910Swollmanvoid sppp_lcp_open (struct sppp *sp);
1674910Swollmanvoid sppp_ipcp_open (struct sppp *sp);
16811189Sjkhint sppp_lcp_conf_parse_options (struct sppp *sp, struct lcp_header *h,
16911189Sjkh	int len, u_long *magic);
1706429Sjkhvoid sppp_cp_timeout (void *arg);
17111189Sjkhchar *sppp_lcp_type_name (u_char type);
17211189Sjkhchar *sppp_ipcp_type_name (u_char type);
17311189Sjkhvoid sppp_print_bytes (u_char *p, u_short len);
1744910Swollman
1754910Swollman/*
1764910Swollman * Flush interface queue.
1774910Swollman */
1784910Swollmanstatic void qflush (struct ifqueue *ifq)
1794910Swollman{
1804910Swollman	struct mbuf *m, *n;
1814910Swollman
1824910Swollman	n = ifq->ifq_head;
1834910Swollman	while ((m = n)) {
1844910Swollman		n = m->m_act;
1854910Swollman		m_freem (m);
1864910Swollman	}
1874910Swollman	ifq->ifq_head = 0;
1884910Swollman	ifq->ifq_tail = 0;
1894910Swollman	ifq->ifq_len = 0;
1904910Swollman}
1914910Swollman
1924910Swollman/*
1934910Swollman * Process the received packet.
1944910Swollman */
1954910Swollmanvoid sppp_input (struct ifnet *ifp, struct mbuf *m)
1964910Swollman{
1974910Swollman	struct ppp_header *h;
19811189Sjkh	struct sppp *sp = (struct sppp*) ifp;
1994910Swollman	struct ifqueue *inq = 0;
20011189Sjkh	int s;
2014910Swollman
2024910Swollman	ifp->if_lastchange = time;
2034910Swollman	if (ifp->if_flags & IFF_UP)
2044910Swollman		/* Count received bytes, add FCS and one flag */
2054910Swollman		ifp->if_ibytes += m->m_pkthdr.len + 3;
2064910Swollman
2074910Swollman	if (m->m_pkthdr.len <= PPP_HEADER_LEN) {
2084910Swollman		/* Too small packet, drop it. */
2094910Swollman		if (ifp->if_flags & IFF_DEBUG)
2104910Swollman			printf ("%s%d: input packet is too small, %d bytes\n",
2114910Swollman				ifp->if_name, ifp->if_unit, m->m_pkthdr.len);
2124910Swollmandrop:           ++ifp->if_iqdrops;
2134910Swollman		m_freem (m);
2144910Swollman		return;
2154910Swollman	}
2164910Swollman
2174910Swollman	/* Get PPP header. */
2184910Swollman	h = mtod (m, struct ppp_header*);
2194910Swollman	m_adj (m, PPP_HEADER_LEN);
2204910Swollman
2214910Swollman	switch (h->address) {
2224910Swollman	default:        /* Invalid PPP packet. */
2234910Swollmaninvalid:        if (ifp->if_flags & IFF_DEBUG)
2244910Swollman			printf ("%s%d: invalid input packet <0x%x 0x%x 0x%x>\n",
2254910Swollman				ifp->if_name, ifp->if_unit,
2264910Swollman				h->address, h->control, ntohs (h->protocol));
2274910Swollman		goto drop;
2284910Swollman	case PPP_ALLSTATIONS:
2294910Swollman		if (h->control != PPP_UI)
2304910Swollman			goto invalid;
23111189Sjkh		if (sp->pp_flags & PP_CISCO) {
23211189Sjkh			if (ifp->if_flags & IFF_DEBUG)
23311189Sjkh				printf ("%s%d: PPP packet in Cisco mode <0x%x 0x%x 0x%x>\n",
23411189Sjkh					ifp->if_name, ifp->if_unit,
23511189Sjkh					h->address, h->control, ntohs (h->protocol));
23611189Sjkh			goto drop;
23711189Sjkh		}
2384910Swollman		switch (ntohs (h->protocol)) {
2394910Swollman		default:
2404910Swollman			if (sp->lcp.state == LCP_STATE_OPENED)
2414910Swollman				sppp_cp_send (sp, PPP_LCP, LCP_PROTO_REJ,
24211189Sjkh					++sp->pp_seq, m->m_pkthdr.len + 2,
2434910Swollman					&h->protocol);
2444910Swollman			if (ifp->if_flags & IFF_DEBUG)
2454910Swollman				printf ("%s%d: invalid input protocol <0x%x 0x%x 0x%x>\n",
2464910Swollman					ifp->if_name, ifp->if_unit,
2474910Swollman					h->address, h->control, ntohs (h->protocol));
2484910Swollman			++ifp->if_noproto;
2494910Swollman			goto drop;
2504910Swollman		case PPP_LCP:
2514910Swollman			sppp_lcp_input ((struct sppp*) ifp, m);
2524910Swollman			m_freem (m);
2534910Swollman			return;
2544910Swollman#ifdef INET
2554910Swollman		case PPP_IPCP:
2564910Swollman			if (sp->lcp.state == LCP_STATE_OPENED)
2574910Swollman				sppp_ipcp_input ((struct sppp*) ifp, m);
2584910Swollman			m_freem (m);
2594910Swollman			return;
2604910Swollman		case PPP_IP:
2614910Swollman			if (sp->ipcp.state == IPCP_STATE_OPENED) {
2624910Swollman				schednetisr (NETISR_IP);
2634910Swollman				inq = &ipintrq;
2644910Swollman			}
2654910Swollman			break;
2664910Swollman#endif
26712495Speter#ifdef IPX
26812495Speter		case PPP_IPX:
26912495Speter			/* IPX IPXCP not implemented yet */
27012495Speter			if (sp->lcp.state == LCP_STATE_OPENED) {
27112495Speter				schednetisr (NETISR_IPX);
27212495Speter				inq = &ipxintrq;
27312495Speter			}
27412495Speter			break;
27512495Speter#endif
2764910Swollman#ifdef NS
2774910Swollman		case PPP_XNS:
2784910Swollman			/* XNS IDPCP not implemented yet */
2794910Swollman			if (sp->lcp.state == LCP_STATE_OPENED) {
2804910Swollman				schednetisr (NETISR_NS);
2814910Swollman				inq = &nsintrq;
2824910Swollman			}
2834910Swollman			break;
2844910Swollman#endif
2854910Swollman#ifdef ISO
2864910Swollman		case PPP_ISO:
2874910Swollman			/* OSI NLCP not implemented yet */
2884910Swollman			if (sp->lcp.state == LCP_STATE_OPENED) {
2894910Swollman				schednetisr (NETISR_ISO);
2904910Swollman				inq = &clnlintrq;
2914910Swollman			}
2924910Swollman			break;
2934910Swollman#endif
2944910Swollman		}
2954910Swollman		break;
2964910Swollman	case CISCO_MULTICAST:
2974910Swollman	case CISCO_UNICAST:
2984910Swollman		/* Don't check the control field here (RFC 1547). */
29911189Sjkh		if (! (sp->pp_flags & PP_CISCO)) {
30011189Sjkh			if (ifp->if_flags & IFF_DEBUG)
30111189Sjkh				printf ("%s%d: Cisco packet in PPP mode <0x%x 0x%x 0x%x>\n",
30211189Sjkh					ifp->if_name, ifp->if_unit,
30311189Sjkh					h->address, h->control, ntohs (h->protocol));
30411189Sjkh			goto drop;
30511189Sjkh		}
3064910Swollman		switch (ntohs (h->protocol)) {
3074910Swollman		default:
3084910Swollman			++ifp->if_noproto;
3094910Swollman			goto invalid;
3104910Swollman		case CISCO_KEEPALIVE:
3114910Swollman			sppp_cisco_input ((struct sppp*) ifp, m);
3124910Swollman			m_freem (m);
3134910Swollman			return;
3144910Swollman#ifdef INET
3154910Swollman		case ETHERTYPE_IP:
3164910Swollman			schednetisr (NETISR_IP);
3174910Swollman			inq = &ipintrq;
3184910Swollman			break;
3194910Swollman#endif
32012495Speter#ifdef IPX
32112495Speter		case ETHERTYPE_IPX:
32212495Speter			schednetisr (NETISR_IPX);
32312495Speter			inq = &ipxintrq;
32412495Speter			break;
32512495Speter#endif
3264910Swollman#ifdef NS
3274910Swollman		case ETHERTYPE_NS:
3284910Swollman			schednetisr (NETISR_NS);
3294910Swollman			inq = &nsintrq;
3304910Swollman			break;
3314910Swollman#endif
3324910Swollman		}
3334910Swollman		break;
3344910Swollman	}
3354910Swollman
3364910Swollman	if (! (ifp->if_flags & IFF_UP) || ! inq)
3374910Swollman		goto drop;
3384910Swollman
3394910Swollman	/* Check queue. */
34011189Sjkh	s = splimp ();
3414910Swollman	if (IF_QFULL (inq)) {
3424910Swollman		/* Queue overflow. */
34311189Sjkh		IF_DROP (inq);
34411189Sjkh		splx (s);
3454910Swollman		if (ifp->if_flags & IFF_DEBUG)
3464910Swollman			printf ("%s%d: protocol queue overflow\n",
3474910Swollman				ifp->if_name, ifp->if_unit);
3484910Swollman		goto drop;
3494910Swollman	}
3504910Swollman	IF_ENQUEUE (inq, m);
35111189Sjkh	splx (s);
3524910Swollman}
3534910Swollman
3544910Swollman/*
3554910Swollman * Enqueue transmit packet.
3564910Swollman */
3574910Swollmanint sppp_output (struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, struct rtentry *rt)
3584910Swollman{
3594910Swollman	struct sppp *sp = (struct sppp*) ifp;
3604910Swollman	struct ppp_header *h;
3614910Swollman	struct ifqueue *ifq;
3624910Swollman	int s = splimp ();
3634910Swollman
3644910Swollman	if (! (ifp->if_flags & IFF_UP) || ! (ifp->if_flags & IFF_RUNNING)) {
3654910Swollman		m_freem (m);
3664910Swollman		splx (s);
3674910Swollman		return (ENETDOWN);
3684910Swollman	}
3694910Swollman
3704910Swollman	ifq = &ifp->if_snd;
3714910Swollman#ifdef INET
3724910Swollman	/*
3734910Swollman	 * Put low delay, telnet, rlogin and ftp control packets
3744910Swollman	 * in front of the queue.
3754910Swollman	 */
37612436Speter	if (dst->sa_family == AF_INET) {
37712436Speter		struct ip *ip = mtod (m, struct ip*);
37812436Speter		struct tcphdr *tcp = (struct tcphdr*) ((long*)ip + ip->ip_hl);
3794910Swollman
38012436Speter		if (! IF_QFULL (&sp->pp_fastq) &&
38112436Speter		    ((ip->ip_tos & IPTOS_LOWDELAY) ||
38212436Speter	    	    ip->ip_p == IPPROTO_TCP &&
38312436Speter	    	    m->m_len >= sizeof (struct ip) + sizeof (struct tcphdr) &&
38412436Speter	    	    (INTERACTIVE (ntohs (tcp->th_sport)) ||
38512436Speter	    	    INTERACTIVE (ntohs (tcp->th_dport)))))
38612436Speter			ifq = &sp->pp_fastq;
3874910Swollman	}
3884910Swollman#endif
3894910Swollman
3904910Swollman	/*
3914910Swollman	 * Prepend general data packet PPP header. For now, IP only.
3924910Swollman	 */
3934910Swollman	M_PREPEND (m, PPP_HEADER_LEN, M_DONTWAIT);
3944910Swollman	if (! m) {
3954910Swollman		if (ifp->if_flags & IFF_DEBUG)
3964910Swollman			printf ("%s%d: no memory for transmit header\n",
3974910Swollman				ifp->if_name, ifp->if_unit);
3984910Swollman		splx (s);
3994910Swollman		return (ENOBUFS);
4004910Swollman	}
4014910Swollman	h = mtod (m, struct ppp_header*);
4024910Swollman	if (sp->pp_flags & PP_CISCO) {
4034910Swollman		h->address = CISCO_MULTICAST;        /* broadcast address */
4044910Swollman		h->control = 0;
4054910Swollman	} else {
4064910Swollman		h->address = PPP_ALLSTATIONS;        /* broadcast address */
4074910Swollman		h->control = PPP_UI;                 /* Unnumbered Info */
4084910Swollman	}
4094910Swollman
4104910Swollman	switch (dst->sa_family) {
4114910Swollman#ifdef INET
4124910Swollman	case AF_INET:   /* Internet Protocol */
41311189Sjkh		if (sp->pp_flags & PP_CISCO)
41411189Sjkh			h->protocol = htons (ETHERTYPE_IP);
41511189Sjkh		else if (sp->ipcp.state == IPCP_STATE_OPENED)
41611189Sjkh			h->protocol = htons (PPP_IP);
41711189Sjkh		else {
41811189Sjkh			m_freem (m);
41911189Sjkh			splx (s);
42011189Sjkh			return (ENETDOWN);
42111189Sjkh		}
4224910Swollman		break;
4234910Swollman#endif
4244910Swollman#ifdef NS
4254910Swollman	case AF_NS:     /* Xerox NS Protocol */
4264910Swollman		h->protocol = htons ((sp->pp_flags & PP_CISCO) ?
4274910Swollman			ETHERTYPE_NS : PPP_XNS);
4284910Swollman		break;
4294910Swollman#endif
43011819Sjulian#ifdef IPX
43112495Speter	case AF_IPX:     /* Novell IPX Protocol */
43211819Sjulian		h->protocol = htons ((sp->pp_flags & PP_CISCO) ?
43312495Speter			ETHERTYPE_IPX : PPP_IPX);
43411819Sjulian		break;
43511819Sjulian#endif
4364910Swollman#ifdef ISO
4374910Swollman	case AF_ISO:    /* ISO OSI Protocol */
4384910Swollman		if (sp->pp_flags & PP_CISCO)
4394910Swollman			goto nosupport;
4404910Swollman		h->protocol = htons (PPP_ISO);
4414910Swollman		break;
4424910Swollman#endif
4435099Swollmannosupport:
4444910Swollman	default:
4454910Swollman		m_freem (m);
4464910Swollman		splx (s);
4474910Swollman		return (EAFNOSUPPORT);
4484910Swollman	}
4494910Swollman
4504910Swollman	/*
4514910Swollman	 * Queue message on interface, and start output if interface
4524910Swollman	 * not yet active.
4534910Swollman	 */
4544910Swollman	if (IF_QFULL (ifq)) {
4554910Swollman		IF_DROP (&ifp->if_snd);
4564910Swollman		m_freem (m);
4574910Swollman		splx (s);
4584910Swollman		return (ENOBUFS);
4594910Swollman	}
4604910Swollman	IF_ENQUEUE (ifq, m);
4614910Swollman	if (! (ifp->if_flags & IFF_OACTIVE))
4624910Swollman		(*ifp->if_start) (ifp);
4634910Swollman
4644910Swollman	/*
4654910Swollman	 * Count output packets and bytes.
4664910Swollman	 * The packet length includes header, FCS and 1 flag,
4674910Swollman	 * according to RFC 1333.
4684910Swollman	 */
4694910Swollman	ifp->if_obytes += m->m_pkthdr.len + 3;
4704910Swollman	ifp->if_lastchange = time;
4714910Swollman	splx (s);
4724910Swollman	return (0);
4734910Swollman}
4744910Swollman
4754910Swollmanvoid sppp_attach (struct ifnet *ifp)
4764910Swollman{
4774910Swollman	struct sppp *sp = (struct sppp*) ifp;
4784910Swollman
4794910Swollman	/* Initialize keepalive handler. */
4804910Swollman	if (! spppq)
48111189Sjkh		timeout (sppp_keepalive, 0, hz * 10);
4824910Swollman
4834910Swollman	/* Insert new entry into the keepalive list. */
4844910Swollman	sp->pp_next = spppq;
4854910Swollman	spppq = sp;
4864910Swollman
4874910Swollman	sp->pp_if.if_type = IFT_PPP;
4884910Swollman	sp->pp_if.if_output = sppp_output;
4894910Swollman	sp->pp_fastq.ifq_maxlen = 32;
4904910Swollman	sp->pp_loopcnt = 0;
4914910Swollman	sp->pp_alivecnt = 0;
49211189Sjkh	sp->pp_seq = 0;
4934910Swollman	sp->pp_rseq = 0;
49411189Sjkh	sp->lcp.magic = 0;
49511189Sjkh	sp->lcp.state = LCP_STATE_CLOSED;
49611189Sjkh	sp->ipcp.state = IPCP_STATE_CLOSED;
4974910Swollman}
4984910Swollman
4994910Swollmanvoid sppp_detach (struct ifnet *ifp)
5004910Swollman{
5014910Swollman	struct sppp **q, *p, *sp = (struct sppp*) ifp;
5024910Swollman
5034910Swollman	/* Remove the entry from the keepalive list. */
5044910Swollman	for (q = &spppq; (p = *q); q = &p->pp_next)
5054910Swollman		if (p == sp) {
5064910Swollman			*q = p->pp_next;
5074910Swollman			break;
5084910Swollman		}
5094910Swollman
5104910Swollman	/* Stop keepalive handler. */
5114910Swollman	if (! spppq)
51211189Sjkh		untimeout (sppp_keepalive, 0);
51311189Sjkh	UNTIMO (sp);
5144910Swollman}
5154910Swollman
5164910Swollman/*
5174910Swollman * Flush the interface output queue.
5184910Swollman */
5194910Swollmanvoid sppp_flush (struct ifnet *ifp)
5204910Swollman{
5214910Swollman	struct sppp *sp = (struct sppp*) ifp;
5224910Swollman
5234910Swollman	qflush (&sp->pp_if.if_snd);
5244910Swollman	qflush (&sp->pp_fastq);
5254910Swollman}
5264910Swollman
5274910Swollman/*
52811189Sjkh * Check if the output queue is empty.
52911189Sjkh */
53011189Sjkhint sppp_isempty (struct ifnet *ifp)
53111189Sjkh{
53211189Sjkh	struct sppp *sp = (struct sppp*) ifp;
53311189Sjkh	int empty, s = splimp ();
53411189Sjkh
53511189Sjkh	empty = !sp->pp_fastq.ifq_head && !sp->pp_if.if_snd.ifq_head;
53611189Sjkh	splx (s);
53711189Sjkh	return (empty);
53811189Sjkh}
53911189Sjkh
54011189Sjkh/*
5414910Swollman * Get next packet to send.
5424910Swollman */
5434910Swollmanstruct mbuf *sppp_dequeue (struct ifnet *ifp)
5444910Swollman{
5454910Swollman	struct sppp *sp = (struct sppp*) ifp;
5464910Swollman	struct mbuf *m;
5474910Swollman	int s = splimp ();
5484910Swollman
5494910Swollman	IF_DEQUEUE (&sp->pp_fastq, m);
5504910Swollman	if (! m)
5514910Swollman		IF_DEQUEUE (&sp->pp_if.if_snd, m);
5524910Swollman	splx (s);
5534910Swollman	return (m);
5544910Swollman}
5554910Swollman
5564910Swollman/*
5574910Swollman * Send keepalive packets, every 10 seconds.
5584910Swollman */
55911189Sjkhvoid sppp_keepalive (void *dummy)
5604910Swollman{
5614910Swollman	struct sppp *sp;
5624910Swollman	int s = splimp ();
5634910Swollman
5644910Swollman	for (sp=spppq; sp; sp=sp->pp_next) {
5654910Swollman		struct ifnet *ifp = &sp->pp_if;
5664910Swollman
56711189Sjkh		/* Keepalive mode disabled or channel down? */
5684910Swollman		if (! (sp->pp_flags & PP_KEEPALIVE) ||
56911189Sjkh		    ! (ifp->if_flags & IFF_RUNNING))
57011189Sjkh			continue;
57111189Sjkh
57211189Sjkh		/* No keepalive in PPP mode if LCP not opened yet. */
57311189Sjkh		if (! (sp->pp_flags & PP_CISCO) &&
5744910Swollman		    sp->lcp.state != LCP_STATE_OPENED)
5754910Swollman			continue;
5764910Swollman
5774910Swollman		if (sp->pp_alivecnt == MAXALIVECNT) {
5784910Swollman			/* No keepalive packets got.  Stop the interface. */
5794910Swollman			printf ("%s%d: down\n", ifp->if_name, ifp->if_unit);
5804910Swollman			if_down (ifp);
5814910Swollman			qflush (&sp->pp_fastq);
58211189Sjkh			if (! (sp->pp_flags & PP_CISCO)) {
58311189Sjkh				/* Shut down the PPP link. */
58411189Sjkh				sp->lcp.state = LCP_STATE_CLOSED;
58511189Sjkh				sp->ipcp.state = IPCP_STATE_CLOSED;
58611189Sjkh				UNTIMO (sp);
58711189Sjkh				/* Initiate negotiation. */
58811189Sjkh				sppp_lcp_open (sp);
58911189Sjkh			}
5904910Swollman		}
5914910Swollman		if (sp->pp_alivecnt <= MAXALIVECNT)
5924910Swollman			++sp->pp_alivecnt;
5934910Swollman		if (sp->pp_flags & PP_CISCO)
5944910Swollman			sppp_cisco_send (sp, CISCO_KEEPALIVE_REQ, ++sp->pp_seq,
5954910Swollman				sp->pp_rseq);
5964910Swollman		else if (sp->lcp.state == LCP_STATE_OPENED) {
5974910Swollman			long nmagic = htonl (sp->lcp.magic);
59811189Sjkh			sp->lcp.echoid = ++sp->pp_seq;
5994910Swollman			sppp_cp_send (sp, PPP_LCP, LCP_ECHO_REQ,
60011189Sjkh				sp->lcp.echoid, 4, &nmagic);
6014910Swollman		}
6024910Swollman	}
6034910Swollman	splx (s);
60411189Sjkh	timeout (sppp_keepalive, 0, hz * 10);
6054910Swollman}
6064910Swollman
6074910Swollman/*
6084910Swollman * Handle incoming PPP Link Control Protocol packets.
6094910Swollman */
6104910Swollmanvoid sppp_lcp_input (struct sppp *sp, struct mbuf *m)
6114910Swollman{
6124910Swollman	struct lcp_header *h;
6134910Swollman	struct ifnet *ifp = &sp->pp_if;
6144910Swollman	int len = m->m_pkthdr.len;
61511189Sjkh	u_char *p, opt[6];
61611189Sjkh	u_long rmagic;
6174910Swollman
6184910Swollman	if (len < 4) {
6194910Swollman		if (ifp->if_flags & IFF_DEBUG)
6204910Swollman			printf ("%s%d: invalid lcp packet length: %d bytes\n",
6214910Swollman				ifp->if_name, ifp->if_unit, len);
6224910Swollman		return;
6234910Swollman	}
6244910Swollman	h = mtod (m, struct lcp_header*);
6254910Swollman	if (ifp->if_flags & IFF_DEBUG) {
62611189Sjkh		char state = '?';
62711189Sjkh		switch (sp->lcp.state) {
62811189Sjkh		case LCP_STATE_CLOSED:   state = 'C'; break;
62911189Sjkh		case LCP_STATE_ACK_RCVD: state = 'R'; break;
63011189Sjkh		case LCP_STATE_ACK_SENT: state = 'S'; break;
63111189Sjkh		case LCP_STATE_OPENED:   state = 'O'; break;
63211189Sjkh		}
63311189Sjkh		printf ("%s%d: lcp input(%c): %d bytes <%s id=%xh len=%xh",
63411189Sjkh			ifp->if_name, ifp->if_unit, state, len,
6354910Swollman			sppp_lcp_type_name (h->type), h->ident, ntohs (h->len));
6364910Swollman		if (len > 4)
63711189Sjkh			sppp_print_bytes ((u_char*) (h+1), len-4);
6384910Swollman		printf (">\n");
6394910Swollman	}
6404910Swollman	if (len > ntohs (h->len))
6414910Swollman		len = ntohs (h->len);
6424910Swollman	switch (h->type) {
6434910Swollman	default:
6444910Swollman		/* Unknown packet type -- send Code-Reject packet. */
64511189Sjkh		sppp_cp_send (sp, PPP_LCP, LCP_CODE_REJ, ++sp->pp_seq,
64611189Sjkh			m->m_pkthdr.len, h);
6474910Swollman		break;
6484910Swollman	case LCP_CONF_REQ:
6494910Swollman		if (len < 4) {
6504910Swollman			if (ifp->if_flags & IFF_DEBUG)
6514910Swollman				printf ("%s%d: invalid lcp configure request packet length: %d bytes\n",
6524910Swollman					ifp->if_name, ifp->if_unit, len);
65311189Sjkh			break;
6544910Swollman		}
65511189Sjkh		if (len>4 && !sppp_lcp_conf_parse_options (sp, h, len, &rmagic))
65611189Sjkh			goto badreq;
65711189Sjkh		if (rmagic == sp->lcp.magic) {
65811189Sjkh			/* Local and remote magics equal -- loopback? */
65911189Sjkh			if (sp->pp_loopcnt >= MAXALIVECNT*5) {
66011189Sjkh				printf ("%s%d: loopback\n",
66111189Sjkh					ifp->if_name, ifp->if_unit);
66211189Sjkh				sp->pp_loopcnt = 0;
66311189Sjkh				if (ifp->if_flags & IFF_UP) {
66411189Sjkh					if_down (ifp);
66511189Sjkh					qflush (&sp->pp_fastq);
66611189Sjkh				}
66711189Sjkh			} else if (ifp->if_flags & IFF_DEBUG)
66811189Sjkh				printf ("%s%d: conf req: magic glitch\n",
66911189Sjkh					ifp->if_name, ifp->if_unit);
67011189Sjkh			++sp->pp_loopcnt;
67111189Sjkh
67211189Sjkh			/* MUST send Conf-Nack packet. */
67311189Sjkh			rmagic = ~sp->lcp.magic;
67411189Sjkh			opt[0] = LCP_OPT_MAGIC;
67511189Sjkh			opt[1] = sizeof (opt);
67611189Sjkh			opt[2] = rmagic >> 24;
67711189Sjkh			opt[3] = rmagic >> 16;
67811189Sjkh			opt[4] = rmagic >> 8;
67911189Sjkh			opt[5] = rmagic;
68011189Sjkh			sppp_cp_send (sp, PPP_LCP, LCP_CONF_NAK,
68111189Sjkh				h->ident, sizeof (opt), &opt);
68211189Sjkhbadreq:
68311189Sjkh			switch (sp->lcp.state) {
68411189Sjkh			case LCP_STATE_OPENED:
6854910Swollman				/* Initiate renegotiation. */
6864910Swollman				sppp_lcp_open (sp);
68711189Sjkh				/* fall through... */
68811189Sjkh			case LCP_STATE_ACK_SENT:
6894910Swollman				/* Go to closed state. */
6904910Swollman				sp->lcp.state = LCP_STATE_CLOSED;
6914910Swollman				sp->ipcp.state = IPCP_STATE_CLOSED;
6924910Swollman			}
69311189Sjkh			break;
69412436Speter		}
69512436Speter		/* Send Configure-Ack packet. */
69612436Speter		sp->pp_loopcnt = 0;
69712436Speter		sppp_cp_send (sp, PPP_LCP, LCP_CONF_ACK,
69812436Speter				h->ident, len-4, h+1);
69912436Speter		/* Change the state. */
70011189Sjkh		switch (sp->lcp.state) {
70111189Sjkh		case LCP_STATE_CLOSED:
70211189Sjkh			sp->lcp.state = LCP_STATE_ACK_SENT;
70311189Sjkh			break;
70411189Sjkh		case LCP_STATE_ACK_RCVD:
70512436Speter			sp->lcp.state = LCP_STATE_OPENED;
70612436Speter			sppp_ipcp_open (sp);
70711189Sjkh			break;
70811189Sjkh		case LCP_STATE_OPENED:
70911189Sjkh			/* Remote magic changed -- close session. */
71011189Sjkh			sp->lcp.state = LCP_STATE_CLOSED;
71111189Sjkh			sp->ipcp.state = IPCP_STATE_CLOSED;
71211189Sjkh			/* Initiate renegotiation. */
71311189Sjkh			sppp_lcp_open (sp);
71412436Speter			/* An ACK has already been sent. */
71512436Speter			sp->lcp.state = LCP_STATE_ACK_SENT;
71611189Sjkh			break;
7174910Swollman		}
7184910Swollman		break;
7194910Swollman	case LCP_CONF_ACK:
72011189Sjkh		if (h->ident != sp->lcp.confid)
72111189Sjkh			break;
72211189Sjkh		UNTIMO (sp);
72311189Sjkh		if (! (ifp->if_flags & IFF_UP) &&
72411189Sjkh		    (ifp->if_flags & IFF_RUNNING)) {
72511189Sjkh			/* Coming out of loopback mode. */
72611189Sjkh			ifp->if_flags |= IFF_UP;
72711189Sjkh			printf ("%s%d: up\n", ifp->if_name, ifp->if_unit);
72811189Sjkh		}
7294910Swollman		switch (sp->lcp.state) {
7304910Swollman		case LCP_STATE_CLOSED:
7314910Swollman			sp->lcp.state = LCP_STATE_ACK_RCVD;
73211189Sjkh			TIMO (sp, 5);
7334910Swollman			break;
7344910Swollman		case LCP_STATE_ACK_SENT:
7354910Swollman			sp->lcp.state = LCP_STATE_OPENED;
7364910Swollman			sppp_ipcp_open (sp);
7374910Swollman			break;
7384910Swollman		}
7394910Swollman		break;
7404910Swollman	case LCP_CONF_NAK:
74111189Sjkh		if (h->ident != sp->lcp.confid)
74211189Sjkh			break;
74311189Sjkh		p = (u_char*) (h+1);
7444910Swollman		if (len>=10 && p[0] == LCP_OPT_MAGIC && p[1] >= 4) {
74511189Sjkh			rmagic = (u_long)p[2] << 24 |
74611189Sjkh				(u_long)p[3] << 16 | p[4] << 8 | p[5];
74711189Sjkh			if (rmagic == ~sp->lcp.magic) {
7484910Swollman				if (ifp->if_flags & IFF_DEBUG)
7494910Swollman					printf ("%s%d: conf nak: magic glitch\n",
7504910Swollman						ifp->if_name, ifp->if_unit);
75111189Sjkh				sp->lcp.magic += time.tv_sec + time.tv_usec;
75211189Sjkh			} else
75311189Sjkh				sp->lcp.magic = rmagic;
7544910Swollman			}
75511189Sjkh		if (sp->lcp.state != LCP_STATE_ACK_SENT) {
75611189Sjkh			/* Go to closed state. */
75711189Sjkh			sp->lcp.state = LCP_STATE_CLOSED;
75811189Sjkh			sp->ipcp.state = IPCP_STATE_CLOSED;
7594910Swollman		}
76011189Sjkh		/* The link will be renegotiated after timeout,
76111189Sjkh		 * to avoid endless req-nack loop. */
76211189Sjkh		UNTIMO (sp);
76311189Sjkh		TIMO (sp, 2);
76411189Sjkh		break;
7654910Swollman	case LCP_CONF_REJ:
76611189Sjkh		if (h->ident != sp->lcp.confid)
76711189Sjkh			break;
76811189Sjkh		UNTIMO (sp);
7694910Swollman		/* Initiate renegotiation. */
7704910Swollman		sppp_lcp_open (sp);
7714910Swollman		if (sp->lcp.state != LCP_STATE_ACK_SENT) {
7724910Swollman			/* Go to closed state. */
7734910Swollman			sp->lcp.state = LCP_STATE_CLOSED;
7744910Swollman			sp->ipcp.state = IPCP_STATE_CLOSED;
7754910Swollman		}
7764910Swollman		break;
7774910Swollman	case LCP_TERM_REQ:
77811189Sjkh		UNTIMO (sp);
7794910Swollman		/* Send Terminate-Ack packet. */
7804910Swollman		sppp_cp_send (sp, PPP_LCP, LCP_TERM_ACK, h->ident, 0, 0);
7814910Swollman		/* Go to closed state. */
7824910Swollman		sp->lcp.state = LCP_STATE_CLOSED;
7834910Swollman		sp->ipcp.state = IPCP_STATE_CLOSED;
78412436Speter		/* Initiate renegotiation. */
78512436Speter		sppp_lcp_open (sp);
7864910Swollman		break;
78711189Sjkh	case LCP_TERM_ACK:
7884910Swollman	case LCP_CODE_REJ:
7894910Swollman	case LCP_PROTO_REJ:
7904910Swollman		/* Ignore for now. */
7914910Swollman		break;
7924910Swollman	case LCP_DISC_REQ:
7934910Swollman		/* Discard the packet. */
7944910Swollman		break;
7954910Swollman	case LCP_ECHO_REQ:
79612436Speter		if (sp->lcp.state != LCP_STATE_OPENED)
79712436Speter			break;
7984910Swollman		if (len < 8) {
7994910Swollman			if (ifp->if_flags & IFF_DEBUG)
8004910Swollman				printf ("%s%d: invalid lcp echo request packet length: %d bytes\n",
8014910Swollman					ifp->if_name, ifp->if_unit, len);
80211189Sjkh			break;
8034910Swollman		}
8044910Swollman		if (ntohl (*(long*)(h+1)) == sp->lcp.magic) {
80511189Sjkh			/* Line loopback mode detected. */
80611189Sjkh			printf ("%s%d: loopback\n", ifp->if_name, ifp->if_unit);
80711189Sjkh			if_down (ifp);
80811189Sjkh			qflush (&sp->pp_fastq);
80911189Sjkh
81011189Sjkh			/* Shut down the PPP link. */
81111189Sjkh			sp->lcp.state = LCP_STATE_CLOSED;
81211189Sjkh			sp->ipcp.state = IPCP_STATE_CLOSED;
81311189Sjkh			UNTIMO (sp);
81411189Sjkh			/* Initiate negotiation. */
81511189Sjkh			sppp_lcp_open (sp);
81611189Sjkh			break;
8174910Swollman		}
8184910Swollman		*(long*)(h+1) = htonl (sp->lcp.magic);
8194910Swollman		sppp_cp_send (sp, PPP_LCP, LCP_ECHO_REPLY, h->ident, len-4, h+1);
8204910Swollman		break;
8214910Swollman	case LCP_ECHO_REPLY:
82211189Sjkh		if (h->ident != sp->lcp.echoid)
82311189Sjkh			break;
8244910Swollman		if (len < 8) {
8254910Swollman			if (ifp->if_flags & IFF_DEBUG)
8264910Swollman				printf ("%s%d: invalid lcp echo reply packet length: %d bytes\n",
8274910Swollman					ifp->if_name, ifp->if_unit, len);
82811189Sjkh			break;
8294910Swollman		}
83011189Sjkh		if (ntohl (*(long*)(h+1)) != sp->lcp.magic)
8314910Swollman		sp->pp_alivecnt = 0;
8324910Swollman		break;
8334910Swollman	}
8344910Swollman}
8354910Swollman
8364910Swollman/*
8374910Swollman * Handle incoming Cisco keepalive protocol packets.
8384910Swollman */
8394910Swollmanvoid sppp_cisco_input (struct sppp *sp, struct mbuf *m)
8404910Swollman{
8414910Swollman	struct cisco_packet *h;
8424910Swollman	struct ifaddr *ifa;
8434910Swollman	struct ifnet *ifp = &sp->pp_if;
8444910Swollman
8454910Swollman	if (m->m_pkthdr.len != CISCO_PACKET_LEN) {
8464910Swollman		if (ifp->if_flags & IFF_DEBUG)
8474910Swollman			printf ("%s%d: invalid cisco packet length: %d bytes\n",
8484910Swollman				ifp->if_name, ifp->if_unit, m->m_pkthdr.len);
8494910Swollman		return;
8504910Swollman	}
8514910Swollman	h = mtod (m, struct cisco_packet*);
8524910Swollman	if (ifp->if_flags & IFF_DEBUG)
85311189Sjkh		printf ("%s%d: cisco input: %d bytes <%lxh %lxh %lxh %xh %xh-%xh>\n",
8544910Swollman			ifp->if_name, ifp->if_unit, m->m_pkthdr.len,
8554910Swollman			ntohl (h->type), h->par1, h->par2, h->rel,
8564910Swollman			h->time0, h->time1);
8574910Swollman	switch (ntohl (h->type)) {
8584910Swollman	default:
8594910Swollman		if (ifp->if_flags & IFF_DEBUG)
8608456Srgrimes			printf ("%s%d: unknown cisco packet type: 0x%lx\n",
8614910Swollman				ifp->if_name, ifp->if_unit, ntohl (h->type));
8624910Swollman		break;
8634910Swollman	case CISCO_ADDR_REPLY:
8644910Swollman		/* Reply on address request, ignore */
8654910Swollman		break;
8664910Swollman	case CISCO_KEEPALIVE_REQ:
8674910Swollman		sp->pp_alivecnt = 0;
8684910Swollman		sp->pp_rseq = ntohl (h->par1);
8694910Swollman		if (sp->pp_seq == sp->pp_rseq) {
8704910Swollman			/* Local and remote sequence numbers are equal.
8714910Swollman			 * Probably, the line is in loopback mode. */
87211189Sjkh			if (sp->pp_loopcnt >= MAXALIVECNT) {
87311189Sjkh				printf ("%s%d: loopback\n",
87411189Sjkh					ifp->if_name, ifp->if_unit);
87511189Sjkh				sp->pp_loopcnt = 0;
87611189Sjkh				if (ifp->if_flags & IFF_UP) {
87711189Sjkh					if_down (ifp);
87811189Sjkh					qflush (&sp->pp_fastq);
87911189Sjkh				}
88011189Sjkh			}
8814910Swollman			++sp->pp_loopcnt;
8824910Swollman
8834910Swollman			/* Generate new local sequence number */
8844910Swollman			sp->pp_seq ^= time.tv_sec ^ time.tv_usec;
88511189Sjkh			break;
88611189Sjkh		}
8874910Swollman			sp->pp_loopcnt = 0;
88811189Sjkh		if (! (ifp->if_flags & IFF_UP) &&
88911189Sjkh		    (ifp->if_flags & IFF_RUNNING)) {
89011189Sjkh			ifp->if_flags |= IFF_UP;
89111189Sjkh			printf ("%s%d: up\n", ifp->if_name, ifp->if_unit);
89211189Sjkh		}
8934910Swollman		break;
8944910Swollman	case CISCO_ADDR_REQ:
8954910Swollman		for (ifa=ifp->if_addrlist; ifa; ifa=ifa->ifa_next)
8964910Swollman			if (ifa->ifa_addr->sa_family == AF_INET)
8974910Swollman				break;
8984910Swollman		if (! ifa) {
8994910Swollman			if (ifp->if_flags & IFF_DEBUG)
9004910Swollman				printf ("%s%d: unknown address for cisco request\n",
9014910Swollman					ifp->if_name, ifp->if_unit);
9024910Swollman			return;
9034910Swollman		}
9044910Swollman		sppp_cisco_send (sp, CISCO_ADDR_REPLY,
9054910Swollman			ntohl (((struct sockaddr_in*)ifa->ifa_addr)->sin_addr.s_addr),
9064910Swollman			ntohl (((struct sockaddr_in*)ifa->ifa_netmask)->sin_addr.s_addr));
9074910Swollman		break;
9084910Swollman	}
9094910Swollman}
9104910Swollman
9114910Swollman/*
9124910Swollman * Send PPP LCP packet.
9134910Swollman */
91411189Sjkhvoid sppp_cp_send (struct sppp *sp, u_short proto, u_char type,
91511189Sjkh	u_char ident, u_short len, void *data)
9164910Swollman{
9174910Swollman	struct ppp_header *h;
9184910Swollman	struct lcp_header *lh;
9194910Swollman	struct mbuf *m;
9204910Swollman	struct ifnet *ifp = &sp->pp_if;
9214910Swollman
9224910Swollman	if (len > MHLEN - PPP_HEADER_LEN - LCP_HEADER_LEN)
9234910Swollman		len = MHLEN - PPP_HEADER_LEN - LCP_HEADER_LEN;
9244910Swollman	MGETHDR (m, M_DONTWAIT, MT_DATA);
9254910Swollman	if (! m)
9264910Swollman		return;
9274910Swollman	m->m_pkthdr.len = m->m_len = PPP_HEADER_LEN + LCP_HEADER_LEN + len;
9284910Swollman	m->m_pkthdr.rcvif = 0;
9294910Swollman
9304910Swollman	h = mtod (m, struct ppp_header*);
9314910Swollman	h->address = PPP_ALLSTATIONS;        /* broadcast address */
9324910Swollman	h->control = PPP_UI;                 /* Unnumbered Info */
9334910Swollman	h->protocol = htons (proto);         /* Link Control Protocol */
9344910Swollman
9354910Swollman	lh = (struct lcp_header*) (h + 1);
9364910Swollman	lh->type = type;
9374910Swollman	lh->ident = ident;
9384910Swollman	lh->len = htons (LCP_HEADER_LEN + len);
9394910Swollman	if (len)
9404910Swollman		bcopy (data, lh+1, len);
9414910Swollman
9424910Swollman	if (ifp->if_flags & IFF_DEBUG) {
9434910Swollman		printf ("%s%d: %s output <%s id=%xh len=%xh",
9444910Swollman			ifp->if_name, ifp->if_unit,
9454910Swollman			proto==PPP_LCP ? "lcp" : "ipcp",
9464910Swollman			proto==PPP_LCP ? sppp_lcp_type_name (lh->type) :
9474910Swollman			sppp_ipcp_type_name (lh->type), lh->ident,
9484910Swollman			ntohs (lh->len));
9494910Swollman		if (len)
95011189Sjkh			sppp_print_bytes ((u_char*) (lh+1), len);
9514910Swollman		printf (">\n");
9524910Swollman	}
9534910Swollman	if (IF_QFULL (&sp->pp_fastq)) {
9544910Swollman		IF_DROP (&ifp->if_snd);
9554910Swollman		m_freem (m);
9564910Swollman	} else
9574910Swollman		IF_ENQUEUE (&sp->pp_fastq, m);
9584910Swollman	if (! (ifp->if_flags & IFF_OACTIVE))
9594910Swollman		(*ifp->if_start) (ifp);
9604910Swollman	ifp->if_obytes += m->m_pkthdr.len + 3;
9614910Swollman}
9624910Swollman
9634910Swollman/*
9644910Swollman * Send Cisco keepalive packet.
9654910Swollman */
9664910Swollmanvoid sppp_cisco_send (struct sppp *sp, int type, long par1, long par2)
9674910Swollman{
9684910Swollman	struct ppp_header *h;
9694910Swollman	struct cisco_packet *ch;
9704910Swollman	struct mbuf *m;
9714910Swollman	struct ifnet *ifp = &sp->pp_if;
97211189Sjkh	u_long t = (time.tv_sec - boottime.tv_sec) * 1000;
9734910Swollman
9744910Swollman	MGETHDR (m, M_DONTWAIT, MT_DATA);
9754910Swollman	if (! m)
9764910Swollman		return;
9774910Swollman	m->m_pkthdr.len = m->m_len = PPP_HEADER_LEN + CISCO_PACKET_LEN;
9784910Swollman	m->m_pkthdr.rcvif = 0;
9794910Swollman
9804910Swollman	h = mtod (m, struct ppp_header*);
9814910Swollman	h->address = CISCO_MULTICAST;
9824910Swollman	h->control = 0;
9834910Swollman	h->protocol = htons (CISCO_KEEPALIVE);
9844910Swollman
9854910Swollman	ch = (struct cisco_packet*) (h + 1);
9864910Swollman	ch->type = htonl (type);
9874910Swollman	ch->par1 = htonl (par1);
9884910Swollman	ch->par2 = htonl (par2);
9894910Swollman	ch->rel = -1;
99011189Sjkh	ch->time0 = htons ((u_short) (t >> 16));
99111189Sjkh	ch->time1 = htons ((u_short) t);
9924910Swollman
9934910Swollman	if (ifp->if_flags & IFF_DEBUG)
9948456Srgrimes		printf ("%s%d: cisco output: <%lxh %lxh %lxh %xh %xh-%xh>\n",
9954910Swollman			ifp->if_name, ifp->if_unit, ntohl (ch->type), ch->par1,
9964910Swollman			ch->par2, ch->rel, ch->time0, ch->time1);
9974910Swollman
9984910Swollman	if (IF_QFULL (&sp->pp_fastq)) {
9994910Swollman		IF_DROP (&ifp->if_snd);
10004910Swollman		m_freem (m);
10014910Swollman	} else
10024910Swollman		IF_ENQUEUE (&sp->pp_fastq, m);
10034910Swollman	if (! (ifp->if_flags & IFF_OACTIVE))
10044910Swollman		(*ifp->if_start) (ifp);
10054910Swollman	ifp->if_obytes += m->m_pkthdr.len + 3;
10064910Swollman}
10074910Swollman
10084910Swollman/*
10094910Swollman * Process an ioctl request.  Called on low priority level.
10104910Swollman */
101111189Sjkhint sppp_ioctl (struct ifnet *ifp, int cmd, void *data)
10124910Swollman{
10134910Swollman	struct ifreq *ifr = (struct ifreq*) data;
101411189Sjkh	struct sppp *sp = (struct sppp*) ifp;
101511189Sjkh	int s, going_up, going_down;
10164910Swollman
10174910Swollman	switch (cmd) {
10184910Swollman	default:
10194910Swollman		return (EINVAL);
10204910Swollman
10214910Swollman	case SIOCAIFADDR:
10224910Swollman	case SIOCSIFDSTADDR:
10234910Swollman		break;
10244910Swollman
102511189Sjkh	case SIOCSIFADDR:
102611189Sjkh		ifp->if_flags |= IFF_UP;
102711189Sjkh		/* fall through... */
102811189Sjkh
10294910Swollman	case SIOCSIFFLAGS:
10304910Swollman		s = splimp ();
103111189Sjkh		going_up   = (ifp->if_flags & IFF_UP) &&
103211189Sjkh			     ! (ifp->if_flags & IFF_RUNNING);
103311189Sjkh		going_down = ! (ifp->if_flags & IFF_UP) &&
103411189Sjkh			      (ifp->if_flags & IFF_RUNNING);
103511189Sjkh		if (going_up || going_down) {
103611189Sjkh			/* Shut down the PPP link. */
103711189Sjkh			ifp->if_flags &= ~IFF_RUNNING;
10384910Swollman			sp->lcp.state = LCP_STATE_CLOSED;
10394910Swollman			sp->ipcp.state = IPCP_STATE_CLOSED;
104011189Sjkh			UNTIMO (sp);
104111189Sjkh		}
104211189Sjkh		if (going_up) {
104311189Sjkh			/* Interface is starting -- initiate negotiation. */
104411189Sjkh			ifp->if_flags |= IFF_RUNNING;
104512436Speter			if (!(sp->pp_flags & PP_CISCO))
104612436Speter				sppp_lcp_open (sp);
10474910Swollman		}
10484910Swollman		splx (s);
10494910Swollman		break;
10504910Swollman
10514910Swollman#ifdef SIOCSIFMTU
10524910Swollman#ifndef ifr_mtu
10534910Swollman#define ifr_mtu ifr_metric
10544910Swollman#endif
10554910Swollman	case SIOCSIFMTU:
10564910Swollman		if (ifr->ifr_mtu < 128 || ifr->ifr_mtu > PP_MTU)
10574910Swollman			return (EINVAL);
10584910Swollman		ifp->if_mtu = ifr->ifr_mtu;
10594910Swollman		break;
10604910Swollman#endif
10614910Swollman#ifdef SLIOCSETMTU
10624910Swollman	case SLIOCSETMTU:
10634910Swollman		if (*(short*)data < 128 || *(short*)data > PP_MTU)
10644910Swollman			return (EINVAL);
10654910Swollman		ifp->if_mtu = *(short*)data;
10664910Swollman		break;
10674910Swollman#endif
10684910Swollman#ifdef SIOCGIFMTU
10694910Swollman	case SIOCGIFMTU:
10704910Swollman		ifr->ifr_mtu = ifp->if_mtu;
10714910Swollman		break;
10724910Swollman#endif
10734910Swollman#ifdef SLIOCGETMTU
10744910Swollman	case SLIOCGETMTU:
10754910Swollman		*(short*)data = ifp->if_mtu;
10764910Swollman		break;
10774910Swollman#endif
10784910Swollman#ifdef MULTICAST
10794910Swollman	case SIOCADDMULTI:
10804910Swollman	case SIOCDELMULTI:
10814910Swollman		break;
10824910Swollman#endif
10834910Swollman	}
10844910Swollman	return (0);
10854910Swollman}
10864910Swollman
108711189Sjkh/*
108811189Sjkh * Analyze the LCP Configure-Request options list
108911189Sjkh * for the presence of unknown options.
109011189Sjkh * If the request contains unknown options, build and
109111189Sjkh * send Configure-reject packet, containing only unknown options.
109211189Sjkh */
109311189Sjkhint sppp_lcp_conf_parse_options (struct sppp *sp, struct lcp_header *h,
109411189Sjkh	int len, u_long *magic)
10954910Swollman{
109611189Sjkh	u_char *buf, *r, *p;
109711189Sjkh	int rlen;
10984910Swollman
109911189Sjkh	len -= 4;
110011189Sjkh	buf = r = malloc (len, M_TEMP, M_NOWAIT);
110111189Sjkh	if (! buf)
110211189Sjkh		return (0);
11034910Swollman
110411189Sjkh	p = (void*) (h+1);
110511189Sjkh	for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) {
110611189Sjkh		switch (*p) {
110711189Sjkh		case LCP_OPT_MAGIC:
110811189Sjkh			/* Magic number -- extract. */
110911189Sjkh			if (len >= 6 && p[1] == 6) {
111011189Sjkh				*magic = (u_long)p[2] << 24 |
111111189Sjkh					(u_long)p[3] << 16 | p[4] << 8 | p[5];
111211189Sjkh				continue;
111311189Sjkh			}
111411189Sjkh			break;
111511189Sjkh		case LCP_OPT_ASYNC_MAP:
111611189Sjkh			/* Async control character map -- check to be zero. */
111711189Sjkh			if (len >= 6 && p[1] == 6 && ! p[2] && ! p[3] &&
111811189Sjkh			    ! p[4] && ! p[5])
111911189Sjkh				continue;
112011189Sjkh			break;
112111189Sjkh		case LCP_OPT_MRU:
112211189Sjkh			/* Maximum receive unit -- always OK. */
112311189Sjkh			continue;
112411189Sjkh		default:
112511189Sjkh			/* Others not supported. */
112611189Sjkh			break;
112711189Sjkh		}
112811189Sjkh		/* Add the option to rejected list. */
11294910Swollman			bcopy (p, r, p[1]);
11304910Swollman			r += p[1];
113111189Sjkh		rlen += p[1];
113212436Speter	}
113311189Sjkh	if (rlen)
11344910Swollman	sppp_cp_send (sp, PPP_LCP, LCP_CONF_REJ, h->ident, rlen, buf);
113511189Sjkh	free (buf, M_TEMP);
113611189Sjkh	return (rlen == 0);
11374910Swollman}
11384910Swollman
11394910Swollmanvoid sppp_ipcp_input (struct sppp *sp, struct mbuf *m)
11404910Swollman{
11414910Swollman	struct lcp_header *h;
11424910Swollman	struct ifnet *ifp = &sp->pp_if;
11434910Swollman	int len = m->m_pkthdr.len;
11444910Swollman
11454910Swollman	if (len < 4) {
11464910Swollman		/* if (ifp->if_flags & IFF_DEBUG) */
11474910Swollman			printf ("%s%d: invalid ipcp packet length: %d bytes\n",
11484910Swollman				ifp->if_name, ifp->if_unit, len);
11494910Swollman		return;
11504910Swollman	}
11514910Swollman	h = mtod (m, struct lcp_header*);
11524910Swollman	if (ifp->if_flags & IFF_DEBUG) {
11534910Swollman		printf ("%s%d: ipcp input: %d bytes <%s id=%xh len=%xh",
11544910Swollman			ifp->if_name, ifp->if_unit, len,
11554910Swollman			sppp_ipcp_type_name (h->type), h->ident, ntohs (h->len));
11564910Swollman		if (len > 4)
115711189Sjkh			sppp_print_bytes ((u_char*) (h+1), len-4);
11584910Swollman		printf (">\n");
11594910Swollman	}
11604910Swollman	if (len > ntohs (h->len))
11614910Swollman		len = ntohs (h->len);
11624910Swollman	switch (h->type) {
11634910Swollman	default:
11644910Swollman		/* Unknown packet type -- send Code-Reject packet. */
11654910Swollman		sppp_cp_send (sp, PPP_IPCP, IPCP_CODE_REJ, ++sp->pp_seq, len, h);
11664910Swollman		break;
11674910Swollman	case IPCP_CONF_REQ:
11684910Swollman		if (len < 4) {
11694910Swollman			if (ifp->if_flags & IFF_DEBUG)
11704910Swollman				printf ("%s%d: invalid ipcp configure request packet length: %d bytes\n",
11714910Swollman					ifp->if_name, ifp->if_unit, len);
11724910Swollman			return;
11734910Swollman		}
11744910Swollman		if (len > 4) {
11754910Swollman			sppp_cp_send (sp, PPP_IPCP, LCP_CONF_REJ, h->ident,
11764910Swollman				len-4, h+1);
117711189Sjkh
117811189Sjkh			switch (sp->ipcp.state) {
117911189Sjkh			case IPCP_STATE_OPENED:
11804910Swollman				/* Initiate renegotiation. */
11814910Swollman				sppp_ipcp_open (sp);
118211189Sjkh				/* fall through... */
118311189Sjkh			case IPCP_STATE_ACK_SENT:
11844910Swollman				/* Go to closed state. */
11854910Swollman				sp->ipcp.state = IPCP_STATE_CLOSED;
118611189Sjkh			}
11874910Swollman		} else {
11884910Swollman			/* Send Configure-Ack packet. */
11894910Swollman			sppp_cp_send (sp, PPP_IPCP, IPCP_CONF_ACK, h->ident,
11904910Swollman				0, 0);
11914910Swollman			/* Change the state. */
119211189Sjkh			if (sp->ipcp.state == IPCP_STATE_ACK_RCVD)
119311189Sjkh				sp->ipcp.state = IPCP_STATE_OPENED;
119411189Sjkh			else
119511189Sjkh				sp->ipcp.state = IPCP_STATE_ACK_SENT;
11964910Swollman		}
11974910Swollman		break;
11984910Swollman	case IPCP_CONF_ACK:
119911189Sjkh		if (h->ident != sp->ipcp.confid)
120011189Sjkh			break;
120111189Sjkh		UNTIMO (sp);
12024910Swollman		switch (sp->ipcp.state) {
12034910Swollman		case IPCP_STATE_CLOSED:
12044910Swollman			sp->ipcp.state = IPCP_STATE_ACK_RCVD;
120511189Sjkh			TIMO (sp, 5);
12064910Swollman			break;
12074910Swollman		case IPCP_STATE_ACK_SENT:
12084910Swollman			sp->ipcp.state = IPCP_STATE_OPENED;
12094910Swollman			break;
12104910Swollman		}
12114910Swollman		break;
12124910Swollman	case IPCP_CONF_NAK:
12134910Swollman	case IPCP_CONF_REJ:
121411189Sjkh		if (h->ident != sp->ipcp.confid)
121511189Sjkh			break;
121611189Sjkh		UNTIMO (sp);
12174910Swollman			/* Initiate renegotiation. */
12184910Swollman			sppp_ipcp_open (sp);
12194910Swollman		if (sp->ipcp.state != IPCP_STATE_ACK_SENT)
12204910Swollman			/* Go to closed state. */
12214910Swollman			sp->ipcp.state = IPCP_STATE_CLOSED;
12224910Swollman		break;
12234910Swollman	case IPCP_TERM_REQ:
12244910Swollman		/* Send Terminate-Ack packet. */
12254910Swollman		sppp_cp_send (sp, PPP_IPCP, IPCP_TERM_ACK, h->ident, 0, 0);
12264910Swollman		/* Go to closed state. */
12274910Swollman		sp->ipcp.state = IPCP_STATE_CLOSED;
12284910Swollman			/* Initiate renegotiation. */
12294910Swollman			sppp_ipcp_open (sp);
12304910Swollman		break;
123111189Sjkh	case IPCP_TERM_ACK:
123211189Sjkh		/* Ignore for now. */
12334910Swollman	case IPCP_CODE_REJ:
12344910Swollman		/* Ignore for now. */
12354910Swollman		break;
12364910Swollman	}
12374910Swollman}
12384910Swollman
12394910Swollmanvoid sppp_lcp_open (struct sppp *sp)
12404910Swollman{
12414910Swollman	char opt[6];
12424910Swollman
124311189Sjkh	if (! sp->lcp.magic)
12444910Swollman	sp->lcp.magic = time.tv_sec + time.tv_usec;
12454910Swollman	opt[0] = LCP_OPT_MAGIC;
12464910Swollman	opt[1] = sizeof (opt);
12474910Swollman	opt[2] = sp->lcp.magic >> 24;
12484910Swollman	opt[3] = sp->lcp.magic >> 16;
12494910Swollman	opt[4] = sp->lcp.magic >> 8;
12504910Swollman	opt[5] = sp->lcp.magic;
125111189Sjkh	sp->lcp.confid = ++sp->pp_seq;
125211189Sjkh	sppp_cp_send (sp, PPP_LCP, LCP_CONF_REQ, sp->lcp.confid,
12534910Swollman		sizeof (opt), &opt);
125411189Sjkh	TIMO (sp, 2);
12554910Swollman}
12564910Swollman
12574910Swollmanvoid sppp_ipcp_open (struct sppp *sp)
12584910Swollman{
125911189Sjkh	sp->ipcp.confid = ++sp->pp_seq;
126011189Sjkh	sppp_cp_send (sp, PPP_IPCP, IPCP_CONF_REQ, sp->ipcp.confid, 0, 0);
126111189Sjkh	TIMO (sp, 2);
12624910Swollman}
12634910Swollman
12644910Swollman/*
12654910Swollman * Process PPP control protocol timeouts.
12664910Swollman */
126711189Sjkhvoid sppp_cp_timeout (void *arg)
12684910Swollman{
12694910Swollman	struct sppp *sp = (struct sppp*) arg;
12704910Swollman	int s = splimp ();
12714910Swollman
127211189Sjkh	sp->pp_flags &= ~PP_TIMO;
127311189Sjkh	if (! (sp->pp_if.if_flags & IFF_RUNNING) || (sp->pp_flags & PP_CISCO)) {
127411189Sjkh		splx (s);
127511189Sjkh		return;
127611189Sjkh	}
12774910Swollman	switch (sp->lcp.state) {
12784910Swollman	case LCP_STATE_CLOSED:
12794910Swollman		/* No ACK for Configure-Request, retry. */
12804910Swollman		sppp_lcp_open (sp);
12814910Swollman		break;
12824910Swollman	case LCP_STATE_ACK_RCVD:
12834910Swollman		/* ACK got, but no Configure-Request for peer, retry. */
12844910Swollman		sppp_lcp_open (sp);
12854910Swollman		sp->lcp.state = LCP_STATE_CLOSED;
12864910Swollman		break;
12874910Swollman	case LCP_STATE_ACK_SENT:
12884910Swollman		/* ACK sent but no ACK for Configure-Request, retry. */
12894910Swollman		sppp_lcp_open (sp);
12904910Swollman		break;
12914910Swollman	case LCP_STATE_OPENED:
12924910Swollman		/* LCP is already OK, try IPCP. */
12934910Swollman		switch (sp->ipcp.state) {
12944910Swollman		case IPCP_STATE_CLOSED:
12954910Swollman			/* No ACK for Configure-Request, retry. */
12964910Swollman			sppp_ipcp_open (sp);
12974910Swollman			break;
12984910Swollman		case IPCP_STATE_ACK_RCVD:
12994910Swollman			/* ACK got, but no Configure-Request for peer, retry. */
13004910Swollman			sppp_ipcp_open (sp);
13014910Swollman			sp->ipcp.state = IPCP_STATE_CLOSED;
13024910Swollman			break;
13034910Swollman		case IPCP_STATE_ACK_SENT:
13044910Swollman			/* ACK sent but no ACK for Configure-Request, retry. */
13054910Swollman			sppp_ipcp_open (sp);
13064910Swollman			break;
13074910Swollman		case IPCP_STATE_OPENED:
13084910Swollman			/* IPCP is OK. */
13094910Swollman			break;
13104910Swollman		}
13114910Swollman		break;
13124910Swollman	}
13134910Swollman	splx (s);
13144910Swollman}
13154910Swollman
131611189Sjkhchar *sppp_lcp_type_name (u_char type)
13174910Swollman{
13184910Swollman	static char buf [8];
13194910Swollman	switch (type) {
13204910Swollman	case LCP_CONF_REQ:   return ("conf-req");
13214910Swollman	case LCP_CONF_ACK:   return ("conf-ack");
13224910Swollman	case LCP_CONF_NAK:   return ("conf-nack");
13234910Swollman	case LCP_CONF_REJ:   return ("conf-rej");
13244910Swollman	case LCP_TERM_REQ:   return ("term-req");
13254910Swollman	case LCP_TERM_ACK:   return ("term-ack");
13264910Swollman	case LCP_CODE_REJ:   return ("code-rej");
13274910Swollman	case LCP_PROTO_REJ:  return ("proto-rej");
13284910Swollman	case LCP_ECHO_REQ:   return ("echo-req");
13294910Swollman	case LCP_ECHO_REPLY: return ("echo-reply");
13304910Swollman	case LCP_DISC_REQ:   return ("discard-req");
13314910Swollman	}
13324910Swollman	sprintf (buf, "%xh", type);
13334910Swollman	return (buf);
13344910Swollman}
13354910Swollman
133611189Sjkhchar *sppp_ipcp_type_name (u_char type)
13374910Swollman{
13384910Swollman	static char buf [8];
13394910Swollman	switch (type) {
13404910Swollman	case IPCP_CONF_REQ:   return ("conf-req");
13414910Swollman	case IPCP_CONF_ACK:   return ("conf-ack");
13424910Swollman	case IPCP_CONF_NAK:   return ("conf-nack");
13434910Swollman	case IPCP_CONF_REJ:   return ("conf-rej");
13444910Swollman	case IPCP_TERM_REQ:   return ("term-req");
13454910Swollman	case IPCP_TERM_ACK:   return ("term-ack");
13464910Swollman	case IPCP_CODE_REJ:   return ("code-rej");
13474910Swollman	}
13484910Swollman	sprintf (buf, "%xh", type);
13494910Swollman	return (buf);
13504910Swollman}
13514910Swollman
135211189Sjkhvoid sppp_print_bytes (u_char *p, u_short len)
13534910Swollman{
13544910Swollman	printf (" %x", *p++);
13554910Swollman	while (--len > 0)
13564910Swollman		printf ("-%x", *p++);
13574910Swollman}
1358