ip.c revision 30092
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 *
2030092Sbrian * $Id: ip.c,v 1.24 1997/09/03 00:40:49 brian Exp $
218857Srgrimes *
226059Samurai *	TODO:
236059Samurai *		o Return ICMP message for filterd packet
246059Samurai *		  and optionaly record it into log.
256059Samurai */
266059Samurai#include "fsm.h"
276059Samurai#include "lcpproto.h"
286059Samurai#include "hdlc.h"
296059Samurai#include <netinet/in_systm.h>
306059Samurai#include <netinet/ip.h>
316059Samurai#include <netinet/ip_icmp.h>
326059Samurai#include <netinet/udp.h>
336059Samurai#include <netinet/tcp.h>
3413389Sphk#include <arpa/inet.h>
3526031Sbrian#include <alias.h>
3630092Sbrian#include <errno.h>
3726142Sbrian#include "loadalias.h"
386059Samurai#include "vars.h"
396059Samurai#include "filter.h"
4026516Sbrian#include "mbuf.h"
4126516Sbrian#include "log.h"
4229043Sbrian#include "os.h"
436059Samurai
446735Samuraiextern void SendPppFrame();
456059Samuraiextern void LcpClose();
466059Samurai
476059Samuraistatic struct pppTimer IdleTimer;
486059Samurai
4928679Sbrianstatic void
5028679SbrianIdleTimeout()
516059Samurai{
5226516Sbrian  LogPrintf(LogPHASE, "Idle timer expired.\n");
5326098Sbrian  reconnect(RECON_FALSE);
546059Samurai  LcpClose();
556059Samurai}
566059Samurai
576059Samurai/*
586059Samurai *  Start Idle timer. If timeout is reached, we call LcpClose() to
596059Samurai *  close LCP and link.
606059Samurai */
616059Samuraivoid
626059SamuraiStartIdleTimer()
636059Samurai{
6428679Sbrian  if (!(mode & (MODE_DEDICATED | MODE_DDIAL))) {
656059Samurai    StopTimer(&IdleTimer);
666059Samurai    IdleTimer.func = IdleTimeout;
676059Samurai    IdleTimer.load = VarIdleTimeout * SECTICKS;
686059Samurai    IdleTimer.state = TIMER_STOPPED;
696059Samurai    StartTimer(&IdleTimer);
706059Samurai  }
716059Samurai}
726059Samurai
736059Samuraivoid
7426516SbrianUpdateIdleTimer()
7526516Sbrian{
7629043Sbrian  if (OsLinkIsUp())
7726516Sbrian    StartIdleTimer();
7826516Sbrian}
7926516Sbrian
8026516Sbrianvoid
816059SamuraiStopIdleTimer()
826059Samurai{
836059Samurai  StopTimer(&IdleTimer);
846059Samurai}
856059Samurai
866059Samurai/*
876059Samurai *  If any IP layer traffic is detected, refresh IdleTimer.
886059Samurai */
896059Samuraistatic void
906059SamuraiRestartIdleTimer()
916059Samurai{
9228679Sbrian  if (!(mode & (MODE_DEDICATED | MODE_DDIAL)) && ipKeepAlive) {
936059Samurai    StartTimer(&IdleTimer);
946059Samurai    ipIdleSecs = 0;
956059Samurai  }
966059Samurai}
976059Samurai
9813733Sdfrstatic u_short interactive_ports[32] = {
9928679Sbrian  544, 513, 514, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
10028679Sbrian  0, 0, 0, 0, 0, 21, 22, 23, 0, 0, 0, 0, 0, 0, 0, 543,
1016059Samurai};
1026059Samurai
10313733Sdfr#define	INTERACTIVE(p)	(interactive_ports[(p) & 0x1F] == (p))
1046059Samurai
1056059Samuraistatic char *TcpFlags[] = {
1066059Samurai  "FIN", "SYN", "RST", "PSH", "ACK", "URG",
1076059Samurai};
1086059Samurai
10928679Sbrianstatic char *Direction[] = {"INP", "OUT", "OUT", "IN/OUT"};
11028679Sbrianstatic struct filterent *Filters[] = {ifilters, ofilters, dfilters, afilters};
1116059Samurai
1126059Samuraistatic int
11328679SbrianPortMatch(int op, u_short pport, u_short rport)
1146059Samurai{
1156059Samurai  switch (op) {
11628679Sbrian    case OP_EQ:
11728679Sbrian    return (pport == rport);
1186059Samurai  case OP_GT:
11928679Sbrian    return (pport > rport);
1206059Samurai  case OP_LT:
12128679Sbrian    return (pport < rport);
1226059Samurai  default:
12328679Sbrian    return (0);
1246059Samurai  }
1256059Samurai}
1266059Samurai
1276059Samurai/*
1286059Samurai *  Check a packet against with defined filters
1296059Samurai */
1306059Samuraistatic int
13128679SbrianFilterCheck(struct ip * pip, int direction)
1326059Samurai{
1336059Samurai  struct filterent *fp = Filters[direction];
1346059Samurai  int gotinfo, cproto, estab, n;
1356059Samurai  struct tcphdr *th;
1366059Samurai  struct udphdr *uh;
1376059Samurai  struct icmp *ih;
1386059Samurai  char *ptop;
1396059Samurai  u_short sport, dport;
1406059Samurai
1416059Samurai  if (fp->action) {
1426059Samurai    cproto = gotinfo = estab = 0;
1436059Samurai    sport = dport = 0;
1446059Samurai    for (n = 0; n < MAXFILTERS; n++) {
1456059Samurai      if (fp->action) {
14628679Sbrian	/* permit fragments on in and out filter */
14728679Sbrian	if ((direction == FL_IN || direction == FL_OUT) &&
14828679Sbrian	    (ntohs(pip->ip_off) & IP_OFFMASK) != 0) {
14928679Sbrian	  return (A_PERMIT);
15028679Sbrian	}
15128679Sbrian	LogPrintf(LogDEBUG, "rule = %d\n", n);
1526059Samurai	if ((pip->ip_src.s_addr & fp->smask.s_addr) == fp->saddr.s_addr
1536059Samurai	    && (pip->ip_dst.s_addr & fp->dmask.s_addr) == fp->daddr.s_addr) {
1546059Samurai	  if (fp->proto) {
1556059Samurai	    if (!gotinfo) {
15628679Sbrian	      ptop = (char *) pip + (pip->ip_hl << 2);
1576059Samurai
1586059Samurai	      switch (pip->ip_p) {
1596059Samurai	      case IPPROTO_ICMP:
16028679Sbrian		cproto = P_ICMP;
16128679Sbrian		ih = (struct icmp *) ptop;
16228679Sbrian		sport = ih->icmp_type;
16328679Sbrian		estab = 1;
1646059Samurai		break;
1656059Samurai	      case IPPROTO_UDP:
16628679Sbrian		cproto = P_UDP;
16728679Sbrian		uh = (struct udphdr *) ptop;
16828679Sbrian		sport = ntohs(uh->uh_sport);
16928679Sbrian		dport = ntohs(uh->uh_dport);
1706059Samurai		estab = 1;
1716059Samurai		break;
1726059Samurai	      case IPPROTO_TCP:
17328679Sbrian		cproto = P_TCP;
17428679Sbrian		th = (struct tcphdr *) ptop;
17528679Sbrian		sport = ntohs(th->th_sport);
17628679Sbrian		dport = ntohs(th->th_dport);
1776059Samurai		estab = (th->th_flags & TH_ACK);
17828679Sbrian		if (estab == 0)
17928679Sbrian		  LogPrintf(LogDEBUG, "flag = %02x, sport = %d, dport = %d\n",
18028679Sbrian			    th->th_flags, sport, dport);
1816059Samurai		break;
1826059Samurai	      default:
18328679Sbrian		return (A_DENY);/* We'll block unknown type of packet */
1846059Samurai	      }
1856059Samurai	      gotinfo = 1;
18628679Sbrian	      LogPrintf(LogDEBUG, "dir = %d, proto = %d, srcop = %d,"
18728679Sbrian			" dstop = %d, estab = %d\n", direction, cproto,
18828679Sbrian			fp->opt.srcop, fp->opt.dstop, estab);
1896059Samurai	    }
19026516Sbrian	    LogPrintf(LogDEBUG, "check0: rule = %d, proto = %d, sport = %d,"
19128679Sbrian		      " dport = %d\n", n, cproto, sport, dport);
19226516Sbrian	    LogPrintf(LogDEBUG, "check0: action = %d\n", fp->action);
19326516Sbrian
1946059Samurai	    if (cproto == fp->proto) {
1956059Samurai	      if ((fp->opt.srcop == OP_NONE ||
19628679Sbrian		   PortMatch(fp->opt.srcop, sport, fp->opt.srcport))
19728679Sbrian		  &&
1986059Samurai		  (fp->opt.dstop == OP_NONE ||
19928679Sbrian		   PortMatch(fp->opt.dstop, dport, fp->opt.dstport))
20028679Sbrian		  &&
2016059Samurai		  (fp->opt.estab == 0 || estab)) {
20228679Sbrian		return (fp->action);
2036059Samurai	      }
2046059Samurai	    }
2056059Samurai	  } else {
2066059Samurai	    /* Address is mached. Make a decision. */
20726516Sbrian	    LogPrintf(LogDEBUG, "check1: action = %d\n", fp->action);
20828679Sbrian	    return (fp->action);
2096059Samurai	  }
2106059Samurai	}
2116059Samurai      }
2126059Samurai      fp++;
2136059Samurai    }
21428679Sbrian    return (A_DENY);		/* No rule is mached. Deny this packet */
2156059Samurai  }
21628679Sbrian  return (A_PERMIT);		/* No rule is given. Permit this packet */
2176059Samurai}
2186059Samurai
2196059Samuraistatic void
22028679SbrianIcmpError(struct ip * pip, int code)
2216059Samurai{
2226059Samurai#ifdef notdef
2236059Samurai  struct mbuf *bp;
2246059Samurai
2256059Samurai  if (pip->ip_p != IPPROTO_ICMP) {
2266059Samurai    bp = mballoc(cnt, MB_IPIN);
2276059Samurai    bcopy(ptr, MBUF_CTOP(bp), cnt);
22813733Sdfr    SendPppFrame(bp);
2296059Samurai    RestartIdleTimer();
2306059Samurai    ipOutOctets += cnt;
2316059Samurai  }
2326059Samurai#endif
2336059Samurai}
2346059Samurai
2356059Samurai/*
2366059Samurai *  For debugging aid.
2376059Samurai */
2386059Samuraiint
23928679SbrianPacketCheck(char *cp, int nb, int direction)
2406059Samurai{
2416059Samurai  struct ip *pip;
2426059Samurai  struct tcphdr *th;
2436059Samurai  struct udphdr *uh;
2446059Samurai  struct icmp *icmph;
2456059Samurai  char *ptop;
2466059Samurai  int mask, len, n;
2476059Samurai  int pri = PRI_NORMAL;
24826692Sbrian  int logit, loglen;
24926692Sbrian  static char logbuf[200];
2506059Samurai
25126516Sbrian  logit = LogIsKept(LogTCPIP);
25226692Sbrian  loglen = 0;
2536059Samurai
25428679Sbrian  pip = (struct ip *) cp;
2558857Srgrimes
25626692Sbrian  if (logit && loglen < sizeof logbuf) {
25728679Sbrian    snprintf(logbuf + loglen, sizeof logbuf - loglen, "%s ",
25828679Sbrian	     Direction[direction]);
25928679Sbrian    loglen += strlen(logbuf + loglen);
26026692Sbrian  }
2616059Samurai  ptop = (cp + (pip->ip_hl << 2));
2626059Samurai
2636059Samurai  switch (pip->ip_p) {
2646059Samurai  case IPPROTO_ICMP:
26526692Sbrian    if (logit && loglen < sizeof logbuf) {
26628679Sbrian      icmph = (struct icmp *) ptop;
26728679Sbrian      snprintf(logbuf + loglen, sizeof logbuf - loglen,
26828679Sbrian	     "ICMP: %s:%d ---> ", inet_ntoa(pip->ip_src), icmph->icmp_type);
26928679Sbrian      loglen += strlen(logbuf + loglen);
27028679Sbrian      snprintf(logbuf + loglen, sizeof logbuf - loglen,
27128679Sbrian	       "%s:%d", inet_ntoa(pip->ip_dst), icmph->icmp_type);
27228679Sbrian      loglen += strlen(logbuf + loglen);
2736059Samurai    }
2746059Samurai    break;
2756059Samurai  case IPPROTO_UDP:
27626692Sbrian    if (logit && loglen < sizeof logbuf) {
27728679Sbrian      uh = (struct udphdr *) ptop;
27828679Sbrian      snprintf(logbuf + loglen, sizeof logbuf - loglen,
27928679Sbrian	   "UDP: %s:%d ---> ", inet_ntoa(pip->ip_src), ntohs(uh->uh_sport));
28028679Sbrian      loglen += strlen(logbuf + loglen);
28128679Sbrian      snprintf(logbuf + loglen, sizeof logbuf - loglen,
28228679Sbrian	       "%s:%d", inet_ntoa(pip->ip_dst), ntohs(uh->uh_dport));
28328679Sbrian      loglen += strlen(logbuf + loglen);
2846059Samurai    }
2856059Samurai    break;
2866059Samurai  case IPPROTO_TCP:
28728679Sbrian    th = (struct tcphdr *) ptop;
2886059Samurai    if (pip->ip_tos == IPTOS_LOWDELAY)
2896059Samurai      pri = PRI_FAST;
29013733Sdfr    else if ((ntohs(pip->ip_off) & IP_OFFMASK) == 0) {
2916059Samurai      if (INTERACTIVE(ntohs(th->th_sport)) || INTERACTIVE(ntohs(th->th_dport)))
29228679Sbrian	pri = PRI_FAST;
2936059Samurai    }
29426692Sbrian    if (logit && loglen < sizeof logbuf) {
2956059Samurai      len = ntohs(pip->ip_len) - (pip->ip_hl << 2) - (th->th_off << 2);
29628679Sbrian      snprintf(logbuf + loglen, sizeof logbuf - loglen,
29728679Sbrian	   "TCP: %s:%d ---> ", inet_ntoa(pip->ip_src), ntohs(th->th_sport));
29828679Sbrian      loglen += strlen(logbuf + loglen);
29928679Sbrian      snprintf(logbuf + loglen, sizeof logbuf - loglen,
30028679Sbrian	       "%s:%d", inet_ntoa(pip->ip_dst), ntohs(th->th_dport));
30128679Sbrian      loglen += strlen(logbuf + loglen);
3026059Samurai      n = 0;
3036059Samurai      for (mask = TH_FIN; mask != 0x40; mask <<= 1) {
30426692Sbrian	if (th->th_flags & mask) {
30528679Sbrian	  snprintf(logbuf + loglen, sizeof logbuf - loglen, " %s", TcpFlags[n]);
30628679Sbrian	  loglen += strlen(logbuf + loglen);
30728679Sbrian	}
3086059Samurai	n++;
3096059Samurai      }
31028679Sbrian      snprintf(logbuf + loglen, sizeof logbuf - loglen,
31128679Sbrian	       "  seq:%x  ack:%x (%d/%d)",
31228679Sbrian	       ntohl(th->th_seq), ntohl(th->th_ack), len, nb);
31328679Sbrian      loglen += strlen(logbuf + loglen);
3146059Samurai      if ((th->th_flags & TH_SYN) && nb > 40) {
31528679Sbrian	u_short *sp;
3166059Samurai
3176059Samurai	ptop += 20;
31828679Sbrian	sp = (u_short *) ptop;
31926692Sbrian	if (ntohs(sp[0]) == 0x0204) {
32028679Sbrian	  snprintf(logbuf + loglen, sizeof logbuf - loglen,
32128679Sbrian		   " MSS = %d", ntohs(sp[1]));
32228679Sbrian	  loglen += strlen(logbuf + loglen);
32328679Sbrian	}
3246059Samurai      }
3256059Samurai    }
3266059Samurai    break;
3276059Samurai  }
32826692Sbrian
32926692Sbrian  if (logit)
33026692Sbrian    LogPrintf(LogTCPIP, "%s\n", logbuf);
33128679Sbrian
33213733Sdfr  if ((FilterCheck(pip, direction) & A_DENY)) {
33326516Sbrian    LogPrintf(LogDEBUG, "blocked.\n");
33428679Sbrian    if (direction == 0)
33528679Sbrian      IcmpError(pip, pri);
33628679Sbrian    return (-1);
3376059Samurai  } else {
33828679Sbrian    if (FilterCheck(pip, FL_KEEP) & A_DENY) {	/* Check Keep Alive filter */
33928679Sbrian      ipKeepAlive = FALSE;
3406735Samurai    } else {
34128679Sbrian      ipKeepAlive = TRUE;
3426735Samurai    }
34328679Sbrian    return (pri);
3446059Samurai  }
3456059Samurai}
3466059Samurai
3476059Samuraivoid
34828679SbrianIpInput(struct mbuf * bp)
34928679Sbrian{				/* IN: Pointer to IP pakcet */
3506059Samurai  u_char *cp;
3516059Samurai  struct mbuf *wp;
3526059Samurai  int nb, nw;
3536059Samurai  u_char tunbuff[MAX_MRU];
3546059Samurai
3556059Samurai  cp = tunbuff;
3566059Samurai  nb = 0;
35728679Sbrian  for (wp = bp; wp; wp = wp->next) {	/* Copy to contiguous region */
3586059Samurai    bcopy(MBUF_CTOP(wp), cp, wp->cnt);
3596059Samurai    cp += wp->cnt;
3606059Samurai    nb += wp->cnt;
3616059Samurai  }
3626059Samurai
36320365Sjkh  if (mode & MODE_ALIAS) {
36426031Sbrian    int iresult;
36526031Sbrian    char *fptr;
36626031Sbrian
36726142Sbrian    iresult = VarPacketAliasIn(tunbuff, sizeof tunbuff);
36826031Sbrian    nb = ntohs(((struct ip *) tunbuff)->ip_len);
36926031Sbrian
37026031Sbrian    if (nb > MAX_MRU) {
37126516Sbrian      LogPrintf(LogERROR, "IpInput: Problem with IP header length\n");
37226031Sbrian      pfree(bp);
37326031Sbrian      return;
37426031Sbrian    }
37526031Sbrian    if (iresult == PKT_ALIAS_OK
37628679Sbrian	|| iresult == PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
37728679Sbrian      if (PacketCheck(tunbuff, nb, FL_IN) < 0) {
37828679Sbrian	pfree(bp);
37928679Sbrian	return;
38026031Sbrian      }
38126031Sbrian      ipInOctets += nb;
38226031Sbrian
38326031Sbrian      nb = ntohs(((struct ip *) tunbuff)->ip_len);
38426031Sbrian      nw = write(tun_out, tunbuff, nb);
38526031Sbrian      if (nw != nb)
38630092Sbrian        if (nw == -1)
38730092Sbrian	  LogPrintf(LogERROR, "IpInput: wrote %d, got %s\n", nb,
38830092Sbrian                    strerror(errno));
38930092Sbrian        else
39030092Sbrian	  LogPrintf(LogERROR, "IpInput: wrote %d, got %d\n", nb, nw);
39126031Sbrian
39226031Sbrian      if (iresult == PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
39328679Sbrian	while ((fptr = VarPacketAliasGetFragment(tunbuff)) != NULL) {
39428679Sbrian	  VarPacketAliasFragmentIn(tunbuff, fptr);
39528679Sbrian	  nb = ntohs(((struct ip *) fptr)->ip_len);
39628679Sbrian	  nw = write(tun_out, fptr, nb);
39728679Sbrian	  if (nw != nb)
39830092Sbrian            if (nw == -1)
39930092Sbrian	      LogPrintf(LogERROR, "IpInput: wrote %d, got %s\n", nb,
40030092Sbrian                        strerror(errno));
40130092Sbrian            else
40230092Sbrian	      LogPrintf(LogERROR, "IpInput: wrote %d, got %d\n", nb, nw);
40328679Sbrian	  free(fptr);
40428679Sbrian	}
40526031Sbrian      }
40628679Sbrian    } else if (iresult == PKT_ALIAS_UNRESOLVED_FRAGMENT) {
40726031Sbrian      nb = ntohs(((struct ip *) tunbuff)->ip_len);
40826031Sbrian      fptr = malloc(nb);
40926516Sbrian      if (fptr == NULL)
41028679Sbrian	LogPrintf(LogALERT, "IpInput: Cannot allocate memory for fragment\n");
41126031Sbrian      else {
41228679Sbrian	memcpy(fptr, tunbuff, nb);
41328679Sbrian	VarPacketAliasSaveFragment(fptr);
41426031Sbrian      }
41526031Sbrian    }
41628679Sbrian  } else {			/* no aliasing */
41728679Sbrian    if (PacketCheck(tunbuff, nb, FL_IN) < 0) {
41826031Sbrian      pfree(bp);
41926031Sbrian      return;
42026031Sbrian    }
42126031Sbrian    ipInOctets += nb;
42226031Sbrian    nw = write(tun_out, tunbuff, nb);
42326031Sbrian    if (nw != nb)
42430092Sbrian      if (nw == -1)
42530092Sbrian	LogPrintf(LogERROR, "IpInput: wrote %d, got %s\n", nb, strerror(errno));
42630092Sbrian      else
42730092Sbrian        LogPrintf(LogERROR, "IpInput: wrote %d, got %d\n", nb, nw);
4286059Samurai  }
4296059Samurai  pfree(bp);
4306059Samurai
4316059Samurai  RestartIdleTimer();
4326059Samurai}
4336059Samurai
43428679Sbrianstatic struct mqueue IpOutputQueues[PRI_FAST + 1];
4356059Samurai
4366059Samuraivoid
43728679SbrianIpEnqueue(int pri, char *ptr, int count)
4386059Samurai{
4396059Samurai  struct mbuf *bp;
4406059Samurai
4416059Samurai  bp = mballoc(count, MB_IPQ);
4426059Samurai  bcopy(ptr, MBUF_CTOP(bp), count);
4436059Samurai  Enqueue(&IpOutputQueues[pri], bp);
4446059Samurai}
4456059Samurai
4468857Srgrimesint
4477001SamuraiIsIpEnqueued()
4487001Samurai{
4497001Samurai  struct mqueue *queue;
45028679Sbrian  int exist = FALSE;
45128679Sbrian
45213733Sdfr  for (queue = &IpOutputQueues[PRI_FAST]; queue >= IpOutputQueues; queue--) {
45328679Sbrian    if (queue->qlen > 0) {
45428679Sbrian      exist = TRUE;
45528679Sbrian      break;
45628679Sbrian    }
4577001Samurai  }
45828679Sbrian  return (exist);
4597001Samurai}
4607001Samurai
4616059Samuraivoid
4626059SamuraiIpStartOutput()
4636059Samurai{
4646059Samurai  struct mqueue *queue;
4656059Samurai  struct mbuf *bp;
46625630Sbrian  int cnt;
4676059Samurai
4686059Samurai  if (IpcpFsm.state != ST_OPENED)
4696059Samurai    return;
47013733Sdfr  for (queue = &IpOutputQueues[PRI_FAST]; queue >= IpOutputQueues; queue--) {
4716059Samurai    if (queue->top) {
4726059Samurai      bp = Dequeue(queue);
4736059Samurai      if (bp) {
4746059Samurai	cnt = plength(bp);
47513733Sdfr	SendPppFrame(bp);
4766059Samurai	RestartIdleTimer();
4776059Samurai	ipOutOctets += cnt;
47813733Sdfr	break;
47928679Sbrian      }
4806059Samurai    }
4816059Samurai  }
4826059Samurai}
483