ip.c revision 30715
16059Samurai/*
26059Samurai *		PPP IP Protocol Interface
36059Samurai *
46059Samurai *	    Written by Toshiharu OHNO (tony-o@iij.ad.jp)
56059Samurai *
66059Samurai *   Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
76059Samurai *
86059Samurai * Redistribution and use in source and binary forms are permitted
96059Samurai * provided that the above copyright notice and this paragraph are
106059Samurai * duplicated in all such forms and that any documentation,
116059Samurai * advertising materials, and other materials related to such
126059Samurai * distribution and use acknowledge that the software was developed
136059Samurai * by the Internet Initiative Japan.  The name of the
146059Samurai * IIJ may not be used to endorse or promote products derived
156059Samurai * from this software without specific prior written permission.
166059Samurai * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
176059Samurai * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
186059Samurai * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
198857Srgrimes *
2030715Sbrian * $Id: ip.c,v 1.25 1997/10/04 00:14:39 brian Exp $
218857Srgrimes *
226059Samurai *	TODO:
236059Samurai *		o Return ICMP message for filterd packet
246059Samurai *		  and optionaly record it into log.
256059Samurai */
2630715Sbrian#include <sys/param.h>
2730715Sbrian#include <netinet/in.h>
286059Samurai#include <netinet/in_systm.h>
296059Samurai#include <netinet/ip.h>
306059Samurai#include <netinet/ip_icmp.h>
316059Samurai#include <netinet/udp.h>
326059Samurai#include <netinet/tcp.h>
3313389Sphk#include <arpa/inet.h>
3430715Sbrian
3526031Sbrian#include <alias.h>
3630092Sbrian#include <errno.h>
3730715Sbrian#include <stdio.h>
3830715Sbrian#include <stdlib.h>
3930715Sbrian#include <string.h>
4030715Sbrian#include <unistd.h>
4130715Sbrian
4230715Sbrian#include "mbuf.h"
4330715Sbrian#include "log.h"
4430715Sbrian#include "defs.h"
4530715Sbrian#include "timer.h"
4630715Sbrian#include "fsm.h"
4730715Sbrian#include "lcpproto.h"
4830715Sbrian#include "hdlc.h"
4926142Sbrian#include "loadalias.h"
5030715Sbrian#include "command.h"
516059Samurai#include "vars.h"
526059Samurai#include "filter.h"
5326516Sbrian#include "log.h"
5429043Sbrian#include "os.h"
5530715Sbrian#include "ipcp.h"
5630715Sbrian#include "vjcomp.h"
5730715Sbrian#include "lcp.h"
5830715Sbrian#include "ip.h"
596059Samurai
606059Samuraistatic struct pppTimer IdleTimer;
616059Samurai
6228679Sbrianstatic void
6328679SbrianIdleTimeout()
646059Samurai{
6526516Sbrian  LogPrintf(LogPHASE, "Idle timer expired.\n");
6626098Sbrian  reconnect(RECON_FALSE);
676059Samurai  LcpClose();
686059Samurai}
696059Samurai
706059Samurai/*
716059Samurai *  Start Idle timer. If timeout is reached, we call LcpClose() to
726059Samurai *  close LCP and link.
736059Samurai */
746059Samuraivoid
756059SamuraiStartIdleTimer()
766059Samurai{
7728679Sbrian  if (!(mode & (MODE_DEDICATED | MODE_DDIAL))) {
786059Samurai    StopTimer(&IdleTimer);
796059Samurai    IdleTimer.func = IdleTimeout;
806059Samurai    IdleTimer.load = VarIdleTimeout * SECTICKS;
816059Samurai    IdleTimer.state = TIMER_STOPPED;
826059Samurai    StartTimer(&IdleTimer);
836059Samurai  }
846059Samurai}
856059Samurai
866059Samuraivoid
8726516SbrianUpdateIdleTimer()
8826516Sbrian{
8929043Sbrian  if (OsLinkIsUp())
9026516Sbrian    StartIdleTimer();
9126516Sbrian}
9226516Sbrian
9326516Sbrianvoid
946059SamuraiStopIdleTimer()
956059Samurai{
966059Samurai  StopTimer(&IdleTimer);
976059Samurai}
986059Samurai
996059Samurai/*
1006059Samurai *  If any IP layer traffic is detected, refresh IdleTimer.
1016059Samurai */
1026059Samuraistatic void
1036059SamuraiRestartIdleTimer()
1046059Samurai{
10528679Sbrian  if (!(mode & (MODE_DEDICATED | MODE_DDIAL)) && ipKeepAlive) {
1066059Samurai    StartTimer(&IdleTimer);
1076059Samurai    ipIdleSecs = 0;
1086059Samurai  }
1096059Samurai}
1106059Samurai
11113733Sdfrstatic u_short interactive_ports[32] = {
11228679Sbrian  544, 513, 514, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
11328679Sbrian  0, 0, 0, 0, 0, 21, 22, 23, 0, 0, 0, 0, 0, 0, 0, 543,
1146059Samurai};
1156059Samurai
11613733Sdfr#define	INTERACTIVE(p)	(interactive_ports[(p) & 0x1F] == (p))
1176059Samurai
1186059Samuraistatic char *TcpFlags[] = {
1196059Samurai  "FIN", "SYN", "RST", "PSH", "ACK", "URG",
1206059Samurai};
1216059Samurai
12228679Sbrianstatic char *Direction[] = {"INP", "OUT", "OUT", "IN/OUT"};
12328679Sbrianstatic struct filterent *Filters[] = {ifilters, ofilters, dfilters, afilters};
1246059Samurai
1256059Samuraistatic int
12628679SbrianPortMatch(int op, u_short pport, u_short rport)
1276059Samurai{
1286059Samurai  switch (op) {
12928679Sbrian    case OP_EQ:
13028679Sbrian    return (pport == rport);
1316059Samurai  case OP_GT:
13228679Sbrian    return (pport > rport);
1336059Samurai  case OP_LT:
13428679Sbrian    return (pport < rport);
1356059Samurai  default:
13628679Sbrian    return (0);
1376059Samurai  }
1386059Samurai}
1396059Samurai
1406059Samurai/*
1416059Samurai *  Check a packet against with defined filters
1426059Samurai */
1436059Samuraistatic int
14428679SbrianFilterCheck(struct ip * pip, int direction)
1456059Samurai{
1466059Samurai  struct filterent *fp = Filters[direction];
1476059Samurai  int gotinfo, cproto, estab, n;
1486059Samurai  struct tcphdr *th;
1496059Samurai  struct udphdr *uh;
1506059Samurai  struct icmp *ih;
1516059Samurai  char *ptop;
1526059Samurai  u_short sport, dport;
1536059Samurai
1546059Samurai  if (fp->action) {
1556059Samurai    cproto = gotinfo = estab = 0;
1566059Samurai    sport = dport = 0;
1576059Samurai    for (n = 0; n < MAXFILTERS; n++) {
1586059Samurai      if (fp->action) {
15928679Sbrian	/* permit fragments on in and out filter */
16028679Sbrian	if ((direction == FL_IN || direction == FL_OUT) &&
16128679Sbrian	    (ntohs(pip->ip_off) & IP_OFFMASK) != 0) {
16228679Sbrian	  return (A_PERMIT);
16328679Sbrian	}
16428679Sbrian	LogPrintf(LogDEBUG, "rule = %d\n", n);
1656059Samurai	if ((pip->ip_src.s_addr & fp->smask.s_addr) == fp->saddr.s_addr
1666059Samurai	    && (pip->ip_dst.s_addr & fp->dmask.s_addr) == fp->daddr.s_addr) {
1676059Samurai	  if (fp->proto) {
1686059Samurai	    if (!gotinfo) {
16928679Sbrian	      ptop = (char *) pip + (pip->ip_hl << 2);
1706059Samurai
1716059Samurai	      switch (pip->ip_p) {
1726059Samurai	      case IPPROTO_ICMP:
17328679Sbrian		cproto = P_ICMP;
17428679Sbrian		ih = (struct icmp *) ptop;
17528679Sbrian		sport = ih->icmp_type;
17628679Sbrian		estab = 1;
1776059Samurai		break;
1786059Samurai	      case IPPROTO_UDP:
17928679Sbrian		cproto = P_UDP;
18028679Sbrian		uh = (struct udphdr *) ptop;
18128679Sbrian		sport = ntohs(uh->uh_sport);
18228679Sbrian		dport = ntohs(uh->uh_dport);
1836059Samurai		estab = 1;
1846059Samurai		break;
1856059Samurai	      case IPPROTO_TCP:
18628679Sbrian		cproto = P_TCP;
18728679Sbrian		th = (struct tcphdr *) ptop;
18828679Sbrian		sport = ntohs(th->th_sport);
18928679Sbrian		dport = ntohs(th->th_dport);
1906059Samurai		estab = (th->th_flags & TH_ACK);
19128679Sbrian		if (estab == 0)
19228679Sbrian		  LogPrintf(LogDEBUG, "flag = %02x, sport = %d, dport = %d\n",
19328679Sbrian			    th->th_flags, sport, dport);
1946059Samurai		break;
1956059Samurai	      default:
19628679Sbrian		return (A_DENY);/* We'll block unknown type of packet */
1976059Samurai	      }
1986059Samurai	      gotinfo = 1;
19928679Sbrian	      LogPrintf(LogDEBUG, "dir = %d, proto = %d, srcop = %d,"
20028679Sbrian			" dstop = %d, estab = %d\n", direction, cproto,
20128679Sbrian			fp->opt.srcop, fp->opt.dstop, estab);
2026059Samurai	    }
20326516Sbrian	    LogPrintf(LogDEBUG, "check0: rule = %d, proto = %d, sport = %d,"
20428679Sbrian		      " dport = %d\n", n, cproto, sport, dport);
20526516Sbrian	    LogPrintf(LogDEBUG, "check0: action = %d\n", fp->action);
20626516Sbrian
2076059Samurai	    if (cproto == fp->proto) {
2086059Samurai	      if ((fp->opt.srcop == OP_NONE ||
20928679Sbrian		   PortMatch(fp->opt.srcop, sport, fp->opt.srcport))
21028679Sbrian		  &&
2116059Samurai		  (fp->opt.dstop == OP_NONE ||
21228679Sbrian		   PortMatch(fp->opt.dstop, dport, fp->opt.dstport))
21328679Sbrian		  &&
2146059Samurai		  (fp->opt.estab == 0 || estab)) {
21528679Sbrian		return (fp->action);
2166059Samurai	      }
2176059Samurai	    }
2186059Samurai	  } else {
2196059Samurai	    /* Address is mached. Make a decision. */
22026516Sbrian	    LogPrintf(LogDEBUG, "check1: action = %d\n", fp->action);
22128679Sbrian	    return (fp->action);
2226059Samurai	  }
2236059Samurai	}
2246059Samurai      }
2256059Samurai      fp++;
2266059Samurai    }
22728679Sbrian    return (A_DENY);		/* No rule is mached. Deny this packet */
2286059Samurai  }
22928679Sbrian  return (A_PERMIT);		/* No rule is given. Permit this packet */
2306059Samurai}
2316059Samurai
2326059Samuraistatic void
23328679SbrianIcmpError(struct ip * pip, int code)
2346059Samurai{
2356059Samurai#ifdef notdef
2366059Samurai  struct mbuf *bp;
2376059Samurai
2386059Samurai  if (pip->ip_p != IPPROTO_ICMP) {
2396059Samurai    bp = mballoc(cnt, MB_IPIN);
24030715Sbrian    memcpy(MBUF_CTOP(bp), ptr, cnt);
24113733Sdfr    SendPppFrame(bp);
2426059Samurai    RestartIdleTimer();
2436059Samurai    ipOutOctets += cnt;
2446059Samurai  }
2456059Samurai#endif
2466059Samurai}
2476059Samurai
2486059Samurai/*
2496059Samurai *  For debugging aid.
2506059Samurai */
2516059Samuraiint
25228679SbrianPacketCheck(char *cp, int nb, int direction)
2536059Samurai{
2546059Samurai  struct ip *pip;
2556059Samurai  struct tcphdr *th;
2566059Samurai  struct udphdr *uh;
2576059Samurai  struct icmp *icmph;
2586059Samurai  char *ptop;
2596059Samurai  int mask, len, n;
2606059Samurai  int pri = PRI_NORMAL;
26126692Sbrian  int logit, loglen;
26226692Sbrian  static char logbuf[200];
2636059Samurai
26426516Sbrian  logit = LogIsKept(LogTCPIP);
26526692Sbrian  loglen = 0;
2666059Samurai
26728679Sbrian  pip = (struct ip *) cp;
2688857Srgrimes
26926692Sbrian  if (logit && loglen < sizeof logbuf) {
27028679Sbrian    snprintf(logbuf + loglen, sizeof logbuf - loglen, "%s ",
27128679Sbrian	     Direction[direction]);
27228679Sbrian    loglen += strlen(logbuf + loglen);
27326692Sbrian  }
2746059Samurai  ptop = (cp + (pip->ip_hl << 2));
2756059Samurai
2766059Samurai  switch (pip->ip_p) {
2776059Samurai  case IPPROTO_ICMP:
27826692Sbrian    if (logit && loglen < sizeof logbuf) {
27928679Sbrian      icmph = (struct icmp *) ptop;
28028679Sbrian      snprintf(logbuf + loglen, sizeof logbuf - loglen,
28128679Sbrian	     "ICMP: %s:%d ---> ", inet_ntoa(pip->ip_src), icmph->icmp_type);
28228679Sbrian      loglen += strlen(logbuf + loglen);
28328679Sbrian      snprintf(logbuf + loglen, sizeof logbuf - loglen,
28428679Sbrian	       "%s:%d", inet_ntoa(pip->ip_dst), icmph->icmp_type);
28528679Sbrian      loglen += strlen(logbuf + loglen);
2866059Samurai    }
2876059Samurai    break;
2886059Samurai  case IPPROTO_UDP:
28926692Sbrian    if (logit && loglen < sizeof logbuf) {
29028679Sbrian      uh = (struct udphdr *) ptop;
29128679Sbrian      snprintf(logbuf + loglen, sizeof logbuf - loglen,
29228679Sbrian	   "UDP: %s:%d ---> ", inet_ntoa(pip->ip_src), ntohs(uh->uh_sport));
29328679Sbrian      loglen += strlen(logbuf + loglen);
29428679Sbrian      snprintf(logbuf + loglen, sizeof logbuf - loglen,
29528679Sbrian	       "%s:%d", inet_ntoa(pip->ip_dst), ntohs(uh->uh_dport));
29628679Sbrian      loglen += strlen(logbuf + loglen);
2976059Samurai    }
2986059Samurai    break;
2996059Samurai  case IPPROTO_TCP:
30028679Sbrian    th = (struct tcphdr *) ptop;
3016059Samurai    if (pip->ip_tos == IPTOS_LOWDELAY)
3026059Samurai      pri = PRI_FAST;
30313733Sdfr    else if ((ntohs(pip->ip_off) & IP_OFFMASK) == 0) {
3046059Samurai      if (INTERACTIVE(ntohs(th->th_sport)) || INTERACTIVE(ntohs(th->th_dport)))
30528679Sbrian	pri = PRI_FAST;
3066059Samurai    }
30726692Sbrian    if (logit && loglen < sizeof logbuf) {
3086059Samurai      len = ntohs(pip->ip_len) - (pip->ip_hl << 2) - (th->th_off << 2);
30928679Sbrian      snprintf(logbuf + loglen, sizeof logbuf - loglen,
31028679Sbrian	   "TCP: %s:%d ---> ", inet_ntoa(pip->ip_src), ntohs(th->th_sport));
31128679Sbrian      loglen += strlen(logbuf + loglen);
31228679Sbrian      snprintf(logbuf + loglen, sizeof logbuf - loglen,
31328679Sbrian	       "%s:%d", inet_ntoa(pip->ip_dst), ntohs(th->th_dport));
31428679Sbrian      loglen += strlen(logbuf + loglen);
3156059Samurai      n = 0;
3166059Samurai      for (mask = TH_FIN; mask != 0x40; mask <<= 1) {
31726692Sbrian	if (th->th_flags & mask) {
31828679Sbrian	  snprintf(logbuf + loglen, sizeof logbuf - loglen, " %s", TcpFlags[n]);
31928679Sbrian	  loglen += strlen(logbuf + loglen);
32028679Sbrian	}
3216059Samurai	n++;
3226059Samurai      }
32328679Sbrian      snprintf(logbuf + loglen, sizeof logbuf - loglen,
32428679Sbrian	       "  seq:%x  ack:%x (%d/%d)",
32528679Sbrian	       ntohl(th->th_seq), ntohl(th->th_ack), len, nb);
32628679Sbrian      loglen += strlen(logbuf + loglen);
3276059Samurai      if ((th->th_flags & TH_SYN) && nb > 40) {
32828679Sbrian	u_short *sp;
3296059Samurai
3306059Samurai	ptop += 20;
33128679Sbrian	sp = (u_short *) ptop;
33226692Sbrian	if (ntohs(sp[0]) == 0x0204) {
33328679Sbrian	  snprintf(logbuf + loglen, sizeof logbuf - loglen,
33428679Sbrian		   " MSS = %d", ntohs(sp[1]));
33528679Sbrian	  loglen += strlen(logbuf + loglen);
33628679Sbrian	}
3376059Samurai      }
3386059Samurai    }
3396059Samurai    break;
3406059Samurai  }
34126692Sbrian
34226692Sbrian  if (logit)
34326692Sbrian    LogPrintf(LogTCPIP, "%s\n", logbuf);
34428679Sbrian
34513733Sdfr  if ((FilterCheck(pip, direction) & A_DENY)) {
34626516Sbrian    LogPrintf(LogDEBUG, "blocked.\n");
34728679Sbrian    if (direction == 0)
34828679Sbrian      IcmpError(pip, pri);
34928679Sbrian    return (-1);
3506059Samurai  } else {
35128679Sbrian    if (FilterCheck(pip, FL_KEEP) & A_DENY) {	/* Check Keep Alive filter */
35230715Sbrian      ipKeepAlive = 0;
3536735Samurai    } else {
35430715Sbrian      ipKeepAlive = 1;
3556735Samurai    }
35628679Sbrian    return (pri);
3576059Samurai  }
3586059Samurai}
3596059Samurai
3606059Samuraivoid
36128679SbrianIpInput(struct mbuf * bp)
36228679Sbrian{				/* IN: Pointer to IP pakcet */
3636059Samurai  u_char *cp;
3646059Samurai  struct mbuf *wp;
3656059Samurai  int nb, nw;
3666059Samurai  u_char tunbuff[MAX_MRU];
3676059Samurai
3686059Samurai  cp = tunbuff;
3696059Samurai  nb = 0;
37028679Sbrian  for (wp = bp; wp; wp = wp->next) {	/* Copy to contiguous region */
37130715Sbrian    memcpy(cp, MBUF_CTOP(wp), wp->cnt);
3726059Samurai    cp += wp->cnt;
3736059Samurai    nb += wp->cnt;
3746059Samurai  }
3756059Samurai
37620365Sjkh  if (mode & MODE_ALIAS) {
37726031Sbrian    int iresult;
37826031Sbrian    char *fptr;
37926031Sbrian
38026142Sbrian    iresult = VarPacketAliasIn(tunbuff, sizeof tunbuff);
38126031Sbrian    nb = ntohs(((struct ip *) tunbuff)->ip_len);
38226031Sbrian
38326031Sbrian    if (nb > MAX_MRU) {
38426516Sbrian      LogPrintf(LogERROR, "IpInput: Problem with IP header length\n");
38526031Sbrian      pfree(bp);
38626031Sbrian      return;
38726031Sbrian    }
38826031Sbrian    if (iresult == PKT_ALIAS_OK
38928679Sbrian	|| iresult == PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
39028679Sbrian      if (PacketCheck(tunbuff, nb, FL_IN) < 0) {
39128679Sbrian	pfree(bp);
39228679Sbrian	return;
39326031Sbrian      }
39426031Sbrian      ipInOctets += nb;
39526031Sbrian
39626031Sbrian      nb = ntohs(((struct ip *) tunbuff)->ip_len);
39726031Sbrian      nw = write(tun_out, tunbuff, nb);
39826031Sbrian      if (nw != nb)
39930092Sbrian        if (nw == -1)
40030092Sbrian	  LogPrintf(LogERROR, "IpInput: wrote %d, got %s\n", nb,
40130092Sbrian                    strerror(errno));
40230092Sbrian        else
40330092Sbrian	  LogPrintf(LogERROR, "IpInput: wrote %d, got %d\n", nb, nw);
40426031Sbrian
40526031Sbrian      if (iresult == PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
40628679Sbrian	while ((fptr = VarPacketAliasGetFragment(tunbuff)) != NULL) {
40728679Sbrian	  VarPacketAliasFragmentIn(tunbuff, fptr);
40828679Sbrian	  nb = ntohs(((struct ip *) fptr)->ip_len);
40928679Sbrian	  nw = write(tun_out, fptr, nb);
41028679Sbrian	  if (nw != nb)
41130092Sbrian            if (nw == -1)
41230092Sbrian	      LogPrintf(LogERROR, "IpInput: wrote %d, got %s\n", nb,
41330092Sbrian                        strerror(errno));
41430092Sbrian            else
41530092Sbrian	      LogPrintf(LogERROR, "IpInput: wrote %d, got %d\n", nb, nw);
41628679Sbrian	  free(fptr);
41728679Sbrian	}
41826031Sbrian      }
41928679Sbrian    } else if (iresult == PKT_ALIAS_UNRESOLVED_FRAGMENT) {
42026031Sbrian      nb = ntohs(((struct ip *) tunbuff)->ip_len);
42126031Sbrian      fptr = malloc(nb);
42226516Sbrian      if (fptr == NULL)
42328679Sbrian	LogPrintf(LogALERT, "IpInput: Cannot allocate memory for fragment\n");
42426031Sbrian      else {
42528679Sbrian	memcpy(fptr, tunbuff, nb);
42628679Sbrian	VarPacketAliasSaveFragment(fptr);
42726031Sbrian      }
42826031Sbrian    }
42928679Sbrian  } else {			/* no aliasing */
43028679Sbrian    if (PacketCheck(tunbuff, nb, FL_IN) < 0) {
43126031Sbrian      pfree(bp);
43226031Sbrian      return;
43326031Sbrian    }
43426031Sbrian    ipInOctets += nb;
43526031Sbrian    nw = write(tun_out, tunbuff, nb);
43626031Sbrian    if (nw != nb)
43730092Sbrian      if (nw == -1)
43830092Sbrian	LogPrintf(LogERROR, "IpInput: wrote %d, got %s\n", nb, strerror(errno));
43930092Sbrian      else
44030092Sbrian        LogPrintf(LogERROR, "IpInput: wrote %d, got %d\n", nb, nw);
4416059Samurai  }
4426059Samurai  pfree(bp);
4436059Samurai
4446059Samurai  RestartIdleTimer();
4456059Samurai}
4466059Samurai
44728679Sbrianstatic struct mqueue IpOutputQueues[PRI_FAST + 1];
4486059Samurai
4496059Samuraivoid
45028679SbrianIpEnqueue(int pri, char *ptr, int count)
4516059Samurai{
4526059Samurai  struct mbuf *bp;
4536059Samurai
4546059Samurai  bp = mballoc(count, MB_IPQ);
45530715Sbrian  memcpy(MBUF_CTOP(bp), ptr, count);
4566059Samurai  Enqueue(&IpOutputQueues[pri], bp);
4576059Samurai}
4586059Samurai
45930715Sbrian#if 0
4608857Srgrimesint
4617001SamuraiIsIpEnqueued()
4627001Samurai{
4637001Samurai  struct mqueue *queue;
46430715Sbrian  int exist = 0;
46528679Sbrian
46613733Sdfr  for (queue = &IpOutputQueues[PRI_FAST]; queue >= IpOutputQueues; queue--) {
46728679Sbrian    if (queue->qlen > 0) {
46830715Sbrian      exist = 1;
46928679Sbrian      break;
47028679Sbrian    }
4717001Samurai  }
47228679Sbrian  return (exist);
4737001Samurai}
47430715Sbrian#endif
4757001Samurai
4766059Samuraivoid
4776059SamuraiIpStartOutput()
4786059Samurai{
4796059Samurai  struct mqueue *queue;
4806059Samurai  struct mbuf *bp;
48125630Sbrian  int cnt;
4826059Samurai
4836059Samurai  if (IpcpFsm.state != ST_OPENED)
4846059Samurai    return;
48513733Sdfr  for (queue = &IpOutputQueues[PRI_FAST]; queue >= IpOutputQueues; queue--) {
4866059Samurai    if (queue->top) {
4876059Samurai      bp = Dequeue(queue);
4886059Samurai      if (bp) {
4896059Samurai	cnt = plength(bp);
49013733Sdfr	SendPppFrame(bp);
4916059Samurai	RestartIdleTimer();
4926059Samurai	ipOutOctets += cnt;
49313733Sdfr	break;
49428679Sbrian      }
4956059Samurai    }
4966059Samurai  }
4976059Samurai}
498