ip.c revision 28679
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 *
2028679Sbrian * $Id: ip.c,v 1.22 1997/06/16 21:20:00 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>
3626142Sbrian#include "loadalias.h"
376059Samurai#include "vars.h"
386059Samurai#include "filter.h"
3926516Sbrian#include "mbuf.h"
4026516Sbrian#include "log.h"
416059Samurai
426735Samuraiextern void SendPppFrame();
436059Samuraiextern void LcpClose();
446059Samurai
456059Samuraistatic struct pppTimer IdleTimer;
466059Samurai
4728679Sbrianstatic void
4828679SbrianIdleTimeout()
496059Samurai{
5026516Sbrian  LogPrintf(LogPHASE, "Idle timer expired.\n");
5126098Sbrian  reconnect(RECON_FALSE);
526059Samurai  LcpClose();
536059Samurai}
546059Samurai
556059Samurai/*
566059Samurai *  Start Idle timer. If timeout is reached, we call LcpClose() to
576059Samurai *  close LCP and link.
586059Samurai */
596059Samuraivoid
606059SamuraiStartIdleTimer()
616059Samurai{
6228679Sbrian  if (!(mode & (MODE_DEDICATED | MODE_DDIAL))) {
636059Samurai    StopTimer(&IdleTimer);
646059Samurai    IdleTimer.func = IdleTimeout;
656059Samurai    IdleTimer.load = VarIdleTimeout * SECTICKS;
666059Samurai    IdleTimer.state = TIMER_STOPPED;
676059Samurai    StartTimer(&IdleTimer);
686059Samurai  }
696059Samurai}
706059Samurai
716059Samuraivoid
7226516SbrianUpdateIdleTimer()
7326516Sbrian{
7426516Sbrian  if (IdleTimer.state == TIMER_RUNNING)
7526516Sbrian    StartIdleTimer();
7626516Sbrian}
7726516Sbrian
7826516Sbrianvoid
796059SamuraiStopIdleTimer()
806059Samurai{
816059Samurai  StopTimer(&IdleTimer);
826059Samurai}
836059Samurai
846059Samurai/*
856059Samurai *  If any IP layer traffic is detected, refresh IdleTimer.
866059Samurai */
876059Samuraistatic void
886059SamuraiRestartIdleTimer()
896059Samurai{
9028679Sbrian  if (!(mode & (MODE_DEDICATED | MODE_DDIAL)) && ipKeepAlive) {
916059Samurai    StartTimer(&IdleTimer);
926059Samurai    ipIdleSecs = 0;
936059Samurai  }
946059Samurai}
956059Samurai
9613733Sdfrstatic u_short interactive_ports[32] = {
9728679Sbrian  544, 513, 514, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
9828679Sbrian  0, 0, 0, 0, 0, 21, 22, 23, 0, 0, 0, 0, 0, 0, 0, 543,
996059Samurai};
1006059Samurai
10113733Sdfr#define	INTERACTIVE(p)	(interactive_ports[(p) & 0x1F] == (p))
1026059Samurai
1036059Samuraistatic char *TcpFlags[] = {
1046059Samurai  "FIN", "SYN", "RST", "PSH", "ACK", "URG",
1056059Samurai};
1066059Samurai
10728679Sbrianstatic char *Direction[] = {"INP", "OUT", "OUT", "IN/OUT"};
10828679Sbrianstatic struct filterent *Filters[] = {ifilters, ofilters, dfilters, afilters};
1096059Samurai
1106059Samuraistatic int
11128679SbrianPortMatch(int op, u_short pport, u_short rport)
1126059Samurai{
1136059Samurai  switch (op) {
11428679Sbrian    case OP_EQ:
11528679Sbrian    return (pport == rport);
1166059Samurai  case OP_GT:
11728679Sbrian    return (pport > rport);
1186059Samurai  case OP_LT:
11928679Sbrian    return (pport < rport);
1206059Samurai  default:
12128679Sbrian    return (0);
1226059Samurai  }
1236059Samurai}
1246059Samurai
1256059Samurai/*
1266059Samurai *  Check a packet against with defined filters
1276059Samurai */
1286059Samuraistatic int
12928679SbrianFilterCheck(struct ip * pip, int direction)
1306059Samurai{
1316059Samurai  struct filterent *fp = Filters[direction];
1326059Samurai  int gotinfo, cproto, estab, n;
1336059Samurai  struct tcphdr *th;
1346059Samurai  struct udphdr *uh;
1356059Samurai  struct icmp *ih;
1366059Samurai  char *ptop;
1376059Samurai  u_short sport, dport;
1386059Samurai
1396059Samurai  if (fp->action) {
1406059Samurai    cproto = gotinfo = estab = 0;
1416059Samurai    sport = dport = 0;
1426059Samurai    for (n = 0; n < MAXFILTERS; n++) {
1436059Samurai      if (fp->action) {
14428679Sbrian	/* permit fragments on in and out filter */
14528679Sbrian	if ((direction == FL_IN || direction == FL_OUT) &&
14628679Sbrian	    (ntohs(pip->ip_off) & IP_OFFMASK) != 0) {
14728679Sbrian	  return (A_PERMIT);
14828679Sbrian	}
14928679Sbrian	LogPrintf(LogDEBUG, "rule = %d\n", n);
1506059Samurai	if ((pip->ip_src.s_addr & fp->smask.s_addr) == fp->saddr.s_addr
1516059Samurai	    && (pip->ip_dst.s_addr & fp->dmask.s_addr) == fp->daddr.s_addr) {
1526059Samurai	  if (fp->proto) {
1536059Samurai	    if (!gotinfo) {
15428679Sbrian	      ptop = (char *) pip + (pip->ip_hl << 2);
1556059Samurai
1566059Samurai	      switch (pip->ip_p) {
1576059Samurai	      case IPPROTO_ICMP:
15828679Sbrian		cproto = P_ICMP;
15928679Sbrian		ih = (struct icmp *) ptop;
16028679Sbrian		sport = ih->icmp_type;
16128679Sbrian		estab = 1;
1626059Samurai		break;
1636059Samurai	      case IPPROTO_UDP:
16428679Sbrian		cproto = P_UDP;
16528679Sbrian		uh = (struct udphdr *) ptop;
16628679Sbrian		sport = ntohs(uh->uh_sport);
16728679Sbrian		dport = ntohs(uh->uh_dport);
1686059Samurai		estab = 1;
1696059Samurai		break;
1706059Samurai	      case IPPROTO_TCP:
17128679Sbrian		cproto = P_TCP;
17228679Sbrian		th = (struct tcphdr *) ptop;
17328679Sbrian		sport = ntohs(th->th_sport);
17428679Sbrian		dport = ntohs(th->th_dport);
1756059Samurai		estab = (th->th_flags & TH_ACK);
17628679Sbrian		if (estab == 0)
17728679Sbrian		  LogPrintf(LogDEBUG, "flag = %02x, sport = %d, dport = %d\n",
17828679Sbrian			    th->th_flags, sport, dport);
1796059Samurai		break;
1806059Samurai	      default:
18128679Sbrian		return (A_DENY);/* We'll block unknown type of packet */
1826059Samurai	      }
1836059Samurai	      gotinfo = 1;
18428679Sbrian	      LogPrintf(LogDEBUG, "dir = %d, proto = %d, srcop = %d,"
18528679Sbrian			" dstop = %d, estab = %d\n", direction, cproto,
18628679Sbrian			fp->opt.srcop, fp->opt.dstop, estab);
1876059Samurai	    }
18826516Sbrian	    LogPrintf(LogDEBUG, "check0: rule = %d, proto = %d, sport = %d,"
18928679Sbrian		      " dport = %d\n", n, cproto, sport, dport);
19026516Sbrian	    LogPrintf(LogDEBUG, "check0: action = %d\n", fp->action);
19126516Sbrian
1926059Samurai	    if (cproto == fp->proto) {
1936059Samurai	      if ((fp->opt.srcop == OP_NONE ||
19428679Sbrian		   PortMatch(fp->opt.srcop, sport, fp->opt.srcport))
19528679Sbrian		  &&
1966059Samurai		  (fp->opt.dstop == OP_NONE ||
19728679Sbrian		   PortMatch(fp->opt.dstop, dport, fp->opt.dstport))
19828679Sbrian		  &&
1996059Samurai		  (fp->opt.estab == 0 || estab)) {
20028679Sbrian		return (fp->action);
2016059Samurai	      }
2026059Samurai	    }
2036059Samurai	  } else {
2046059Samurai	    /* Address is mached. Make a decision. */
20526516Sbrian	    LogPrintf(LogDEBUG, "check1: action = %d\n", fp->action);
20628679Sbrian	    return (fp->action);
2076059Samurai	  }
2086059Samurai	}
2096059Samurai      }
2106059Samurai      fp++;
2116059Samurai    }
21228679Sbrian    return (A_DENY);		/* No rule is mached. Deny this packet */
2136059Samurai  }
21428679Sbrian  return (A_PERMIT);		/* No rule is given. Permit this packet */
2156059Samurai}
2166059Samurai
2176059Samuraistatic void
21828679SbrianIcmpError(struct ip * pip, int code)
2196059Samurai{
2206059Samurai#ifdef notdef
2216059Samurai  struct mbuf *bp;
2226059Samurai
2236059Samurai  if (pip->ip_p != IPPROTO_ICMP) {
2246059Samurai    bp = mballoc(cnt, MB_IPIN);
2256059Samurai    bcopy(ptr, MBUF_CTOP(bp), cnt);
22613733Sdfr    SendPppFrame(bp);
2276059Samurai    RestartIdleTimer();
2286059Samurai    ipOutOctets += cnt;
2296059Samurai  }
2306059Samurai#endif
2316059Samurai}
2326059Samurai
2336059Samurai/*
2346059Samurai *  For debugging aid.
2356059Samurai */
2366059Samuraiint
23728679SbrianPacketCheck(char *cp, int nb, int direction)
2386059Samurai{
2396059Samurai  struct ip *pip;
2406059Samurai  struct tcphdr *th;
2416059Samurai  struct udphdr *uh;
2426059Samurai  struct icmp *icmph;
2436059Samurai  char *ptop;
2446059Samurai  int mask, len, n;
2456059Samurai  int pri = PRI_NORMAL;
24626692Sbrian  int logit, loglen;
24726692Sbrian  static char logbuf[200];
2486059Samurai
24926516Sbrian  logit = LogIsKept(LogTCPIP);
25026692Sbrian  loglen = 0;
2516059Samurai
25228679Sbrian  pip = (struct ip *) cp;
2538857Srgrimes
25426692Sbrian  if (logit && loglen < sizeof logbuf) {
25528679Sbrian    snprintf(logbuf + loglen, sizeof logbuf - loglen, "%s ",
25628679Sbrian	     Direction[direction]);
25728679Sbrian    loglen += strlen(logbuf + loglen);
25826692Sbrian  }
2596059Samurai  ptop = (cp + (pip->ip_hl << 2));
2606059Samurai
2616059Samurai  switch (pip->ip_p) {
2626059Samurai  case IPPROTO_ICMP:
26326692Sbrian    if (logit && loglen < sizeof logbuf) {
26428679Sbrian      icmph = (struct icmp *) ptop;
26528679Sbrian      snprintf(logbuf + loglen, sizeof logbuf - loglen,
26628679Sbrian	     "ICMP: %s:%d ---> ", inet_ntoa(pip->ip_src), icmph->icmp_type);
26728679Sbrian      loglen += strlen(logbuf + loglen);
26828679Sbrian      snprintf(logbuf + loglen, sizeof logbuf - loglen,
26928679Sbrian	       "%s:%d", inet_ntoa(pip->ip_dst), icmph->icmp_type);
27028679Sbrian      loglen += strlen(logbuf + loglen);
2716059Samurai    }
2726059Samurai    break;
2736059Samurai  case IPPROTO_UDP:
27426692Sbrian    if (logit && loglen < sizeof logbuf) {
27528679Sbrian      uh = (struct udphdr *) ptop;
27628679Sbrian      snprintf(logbuf + loglen, sizeof logbuf - loglen,
27728679Sbrian	   "UDP: %s:%d ---> ", inet_ntoa(pip->ip_src), ntohs(uh->uh_sport));
27828679Sbrian      loglen += strlen(logbuf + loglen);
27928679Sbrian      snprintf(logbuf + loglen, sizeof logbuf - loglen,
28028679Sbrian	       "%s:%d", inet_ntoa(pip->ip_dst), ntohs(uh->uh_dport));
28128679Sbrian      loglen += strlen(logbuf + loglen);
2826059Samurai    }
2836059Samurai    break;
2846059Samurai  case IPPROTO_TCP:
28528679Sbrian    th = (struct tcphdr *) ptop;
2866059Samurai    if (pip->ip_tos == IPTOS_LOWDELAY)
2876059Samurai      pri = PRI_FAST;
28813733Sdfr    else if ((ntohs(pip->ip_off) & IP_OFFMASK) == 0) {
2896059Samurai      if (INTERACTIVE(ntohs(th->th_sport)) || INTERACTIVE(ntohs(th->th_dport)))
29028679Sbrian	pri = PRI_FAST;
2916059Samurai    }
29226692Sbrian    if (logit && loglen < sizeof logbuf) {
2936059Samurai      len = ntohs(pip->ip_len) - (pip->ip_hl << 2) - (th->th_off << 2);
29428679Sbrian      snprintf(logbuf + loglen, sizeof logbuf - loglen,
29528679Sbrian	   "TCP: %s:%d ---> ", inet_ntoa(pip->ip_src), ntohs(th->th_sport));
29628679Sbrian      loglen += strlen(logbuf + loglen);
29728679Sbrian      snprintf(logbuf + loglen, sizeof logbuf - loglen,
29828679Sbrian	       "%s:%d", inet_ntoa(pip->ip_dst), ntohs(th->th_dport));
29928679Sbrian      loglen += strlen(logbuf + loglen);
3006059Samurai      n = 0;
3016059Samurai      for (mask = TH_FIN; mask != 0x40; mask <<= 1) {
30226692Sbrian	if (th->th_flags & mask) {
30328679Sbrian	  snprintf(logbuf + loglen, sizeof logbuf - loglen, " %s", TcpFlags[n]);
30428679Sbrian	  loglen += strlen(logbuf + loglen);
30528679Sbrian	}
3066059Samurai	n++;
3076059Samurai      }
30828679Sbrian      snprintf(logbuf + loglen, sizeof logbuf - loglen,
30928679Sbrian	       "  seq:%x  ack:%x (%d/%d)",
31028679Sbrian	       ntohl(th->th_seq), ntohl(th->th_ack), len, nb);
31128679Sbrian      loglen += strlen(logbuf + loglen);
3126059Samurai      if ((th->th_flags & TH_SYN) && nb > 40) {
31328679Sbrian	u_short *sp;
3146059Samurai
3156059Samurai	ptop += 20;
31628679Sbrian	sp = (u_short *) ptop;
31726692Sbrian	if (ntohs(sp[0]) == 0x0204) {
31828679Sbrian	  snprintf(logbuf + loglen, sizeof logbuf - loglen,
31928679Sbrian		   " MSS = %d", ntohs(sp[1]));
32028679Sbrian	  loglen += strlen(logbuf + loglen);
32128679Sbrian	}
3226059Samurai      }
3236059Samurai    }
3246059Samurai    break;
3256059Samurai  }
32626692Sbrian
32726692Sbrian  if (logit)
32826692Sbrian    LogPrintf(LogTCPIP, "%s\n", logbuf);
32928679Sbrian
33013733Sdfr  if ((FilterCheck(pip, direction) & A_DENY)) {
33126516Sbrian    LogPrintf(LogDEBUG, "blocked.\n");
33228679Sbrian    if (direction == 0)
33328679Sbrian      IcmpError(pip, pri);
33428679Sbrian    return (-1);
3356059Samurai  } else {
33628679Sbrian    if (FilterCheck(pip, FL_KEEP) & A_DENY) {	/* Check Keep Alive filter */
33728679Sbrian      ipKeepAlive = FALSE;
3386735Samurai    } else {
33928679Sbrian      ipKeepAlive = TRUE;
3406735Samurai    }
34128679Sbrian    return (pri);
3426059Samurai  }
3436059Samurai}
3446059Samurai
3456059Samuraivoid
34628679SbrianIpInput(struct mbuf * bp)
34728679Sbrian{				/* IN: Pointer to IP pakcet */
3486059Samurai  u_char *cp;
3496059Samurai  struct mbuf *wp;
3506059Samurai  int nb, nw;
3516059Samurai  u_char tunbuff[MAX_MRU];
3526059Samurai
3536059Samurai  cp = tunbuff;
3546059Samurai  nb = 0;
35528679Sbrian  for (wp = bp; wp; wp = wp->next) {	/* Copy to contiguous region */
3566059Samurai    bcopy(MBUF_CTOP(wp), cp, wp->cnt);
3576059Samurai    cp += wp->cnt;
3586059Samurai    nb += wp->cnt;
3596059Samurai  }
3606059Samurai
36120365Sjkh  if (mode & MODE_ALIAS) {
36226031Sbrian    int iresult;
36326031Sbrian    char *fptr;
36426031Sbrian
36526142Sbrian    iresult = VarPacketAliasIn(tunbuff, sizeof tunbuff);
36626031Sbrian    nb = ntohs(((struct ip *) tunbuff)->ip_len);
36726031Sbrian
36826031Sbrian    if (nb > MAX_MRU) {
36926516Sbrian      LogPrintf(LogERROR, "IpInput: Problem with IP header length\n");
37026031Sbrian      pfree(bp);
37126031Sbrian      return;
37226031Sbrian    }
37326031Sbrian    if (iresult == PKT_ALIAS_OK
37428679Sbrian	|| iresult == PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
37528679Sbrian      if (PacketCheck(tunbuff, nb, FL_IN) < 0) {
37628679Sbrian	pfree(bp);
37728679Sbrian	return;
37826031Sbrian      }
37926031Sbrian      ipInOctets += nb;
38026031Sbrian
38126031Sbrian      nb = ntohs(((struct ip *) tunbuff)->ip_len);
38226031Sbrian      nw = write(tun_out, tunbuff, nb);
38326031Sbrian      if (nw != nb)
38428679Sbrian	LogPrintf(LogERROR, "IpInput: wrote %d, got %d\n", nb, nw);
38526031Sbrian
38626031Sbrian      if (iresult == PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
38728679Sbrian	while ((fptr = VarPacketAliasGetFragment(tunbuff)) != NULL) {
38828679Sbrian	  VarPacketAliasFragmentIn(tunbuff, fptr);
38928679Sbrian	  nb = ntohs(((struct ip *) fptr)->ip_len);
39028679Sbrian	  nw = write(tun_out, fptr, nb);
39128679Sbrian	  if (nw != nb)
39228679Sbrian	    LogPrintf(LogERROR, "IpInput: wrote %d, got %d\n", nb, nw);
39328679Sbrian	  free(fptr);
39428679Sbrian	}
39526031Sbrian      }
39628679Sbrian    } else if (iresult == PKT_ALIAS_UNRESOLVED_FRAGMENT) {
39726031Sbrian      nb = ntohs(((struct ip *) tunbuff)->ip_len);
39826031Sbrian      fptr = malloc(nb);
39926516Sbrian      if (fptr == NULL)
40028679Sbrian	LogPrintf(LogALERT, "IpInput: Cannot allocate memory for fragment\n");
40126031Sbrian      else {
40228679Sbrian	memcpy(fptr, tunbuff, nb);
40328679Sbrian	VarPacketAliasSaveFragment(fptr);
40426031Sbrian      }
40526031Sbrian    }
40628679Sbrian  } else {			/* no aliasing */
40728679Sbrian    if (PacketCheck(tunbuff, nb, FL_IN) < 0) {
40826031Sbrian      pfree(bp);
40926031Sbrian      return;
41026031Sbrian    }
41126031Sbrian    ipInOctets += nb;
41226031Sbrian    nw = write(tun_out, tunbuff, nb);
41326031Sbrian    if (nw != nb)
41426516Sbrian      LogPrintf(LogERROR, "IpInput: wrote %d, got %d\n", nb, nw);
4156059Samurai  }
4166059Samurai  pfree(bp);
4176059Samurai
4186059Samurai  RestartIdleTimer();
4196059Samurai}
4206059Samurai
42128679Sbrianstatic struct mqueue IpOutputQueues[PRI_FAST + 1];
4226059Samurai
4236059Samuraivoid
42428679SbrianIpEnqueue(int pri, char *ptr, int count)
4256059Samurai{
4266059Samurai  struct mbuf *bp;
4276059Samurai
4286059Samurai  bp = mballoc(count, MB_IPQ);
4296059Samurai  bcopy(ptr, MBUF_CTOP(bp), count);
4306059Samurai  Enqueue(&IpOutputQueues[pri], bp);
4316059Samurai}
4326059Samurai
4338857Srgrimesint
4347001SamuraiIsIpEnqueued()
4357001Samurai{
4367001Samurai  struct mqueue *queue;
43728679Sbrian  int exist = FALSE;
43828679Sbrian
43913733Sdfr  for (queue = &IpOutputQueues[PRI_FAST]; queue >= IpOutputQueues; queue--) {
44028679Sbrian    if (queue->qlen > 0) {
44128679Sbrian      exist = TRUE;
44228679Sbrian      break;
44328679Sbrian    }
4447001Samurai  }
44528679Sbrian  return (exist);
4467001Samurai}
4477001Samurai
4486059Samuraivoid
4496059SamuraiIpStartOutput()
4506059Samurai{
4516059Samurai  struct mqueue *queue;
4526059Samurai  struct mbuf *bp;
45325630Sbrian  int cnt;
4546059Samurai
4556059Samurai  if (IpcpFsm.state != ST_OPENED)
4566059Samurai    return;
45713733Sdfr  for (queue = &IpOutputQueues[PRI_FAST]; queue >= IpOutputQueues; queue--) {
4586059Samurai    if (queue->top) {
4596059Samurai      bp = Dequeue(queue);
4606059Samurai      if (bp) {
4616059Samurai	cnt = plength(bp);
46213733Sdfr	SendPppFrame(bp);
4636059Samurai	RestartIdleTimer();
4646059Samurai	ipOutOctets += cnt;
46513733Sdfr	break;
46628679Sbrian      }
4676059Samurai    }
4686059Samurai  }
4696059Samurai}
470