aboutsummaryrefslogblamecommitdiff
path: root/usr.sbin/ppp/ip.c
blob: 82d3644df014c720325b9beb1ecfb271bc593d15 (plain) (tree)


















                                                                              
                                                        




                                                        
                      














                             

                   




                  
                

                      
                       
                   
                       
                 
                   
                       



                 
                   
                   


                








                                                                             


















                                               
                                                  
 
                                                           




                       
                                      
                  

                   
                                                          



                                                   
                                                                     
                            
 



                                                            












                                                      
                                          

                                                                     





                                            
                                          


                                                                         






                                                

                                                          



                                                                  

                      
                                                                               
               


                                        


                                                                 



                                                                
                          
             


















                                                                        


                                              
                                                                       
                                              



                                                                       




                                                     

                                                                               

                                

                                                                    







                                                                          
             


                                    





                                    
                           
   
 
      




                      
                                                                           










                          
                                               




                                        
                                                                           





































































                                                                                
                                            

                                                    
             

                          
      

                
                                 

                                                           
                                                           
          
                                            




                 

                                                



                      
                                         
















                                                                       
                       



                          
                                                           








                                                                      
                                                                      


                  



                                                              
                                              


                                                   
                                           







                                                                     

                                                                       



                                                          
                                               

















                                                                              
                                                




                                         
                                                                    


                



                                                            
                                            
 
                                       
                                         






                                                                                













                                                  
   
             

                       
                 
 



                                                                          
 
 

                                                    




                       
                                              
             

                                                                           


                          

                                                    
                          
                                    

                                                                
                                                  
                 

       
 
           
 
/*
 *		PPP IP Protocol Interface
 *
 *	    Written by Toshiharu OHNO (tony-o@iij.ad.jp)
 *
 *   Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the Internet Initiative Japan.  The name of the
 * IIJ may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * $Id: ip.c,v 1.38.2.21 1998/04/07 23:45:52 brian Exp $
 *
 *	TODO:
 *		o Return ICMP message for filterd packet
 *		  and optionaly record it into log.
 */
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>

#ifndef NOALIAS
#include <alias.h>
#endif
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "mbuf.h"
#include "log.h"
#include "defs.h"
#include "timer.h"
#include "fsm.h"
#include "lqr.h"
#include "hdlc.h"
#include "loadalias.h"
#include "throughput.h"
#include "iplist.h"
#include "slcompress.h"
#include "ipcp.h"
#include "filter.h"
#include "descriptor.h"
#include "lcp.h"
#include "ccp.h"
#include "link.h"
#include "mp.h"
#include "bundle.h"
#include "vjcomp.h"
#include "tun.h"
#include "ip.h"

static const u_short interactive_ports[32] = {
  544, 513, 514, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 21, 22, 23, 0, 0, 0, 0, 0, 0, 0, 543,
};

#define	INTERACTIVE(p)	(interactive_ports[(p) & 0x1F] == (p))

static const char *TcpFlags[] = { "FIN", "SYN", "RST", "PSH", "ACK", "URG" };

static int
PortMatch(int op, u_short pport, u_short rport)
{
  switch (op) {
    case OP_EQ:
    return (pport == rport);
  case OP_GT:
    return (pport > rport);
  case OP_LT:
    return (pport < rport);
  default:
    return (0);
  }
}

/*
 *  Check a packet against with defined filters
 */
static int
FilterCheck(struct ip *pip, struct filter *filter)
{
  int gotinfo, cproto, estab, syn, finrst, n, len, didname;
  struct tcphdr *th;
  struct udphdr *uh;
  struct icmp *ih;
  char *ptop;
  u_short sport, dport;
  struct filterent *fp = filter->rule;
  char dbuff[100];

  if (fp->action) {
    cproto = gotinfo = estab = syn = finrst = didname = 0;
    sport = dport = 0;
    for (n = 0; n < MAXFILTERS; n++) {
      if (fp->action) {
	/* permit fragments on in and out filter */
        if (filter->fragok && (ntohs(pip->ip_off) & IP_OFFMASK) != 0)
	  return (A_PERMIT);

        if (!didname)
          LogPrintf(LogDEBUG, "%s filter:\n", filter->name);
        didname = 1;

	if ((pip->ip_src.s_addr & fp->smask.s_addr) ==
	    (fp->saddr.s_addr & fp->smask.s_addr) &&
	    (pip->ip_dst.s_addr & fp->dmask.s_addr) ==
	    (fp->daddr.s_addr & fp->dmask.s_addr)) {
	  if (fp->proto) {
	    if (!gotinfo) {
	      ptop = (char *) pip + (pip->ip_hl << 2);

	      switch (pip->ip_p) {
	      case IPPROTO_ICMP:
		cproto = P_ICMP;
		ih = (struct icmp *) ptop;
		sport = ih->icmp_type;
		estab = syn = finrst = -1;
                if (LogIsKept(LogDEBUG))
		  snprintf(dbuff, sizeof dbuff, "sport = %d", sport);
		break;
	      case IPPROTO_UDP:
		cproto = P_UDP;
		uh = (struct udphdr *) ptop;
		sport = ntohs(uh->uh_sport);
		dport = ntohs(uh->uh_dport);
		estab = syn = finrst = -1;
                if (LogIsKept(LogDEBUG))
		  snprintf(dbuff, sizeof dbuff, "sport = %d, dport = %d",
                           sport, dport);
		break;
	      case IPPROTO_TCP:
		cproto = P_TCP;
		th = (struct tcphdr *) ptop;
		sport = ntohs(th->th_sport);
		dport = ntohs(th->th_dport);
		estab = (th->th_flags & TH_ACK);
		syn = (th->th_flags & TH_SYN);
		finrst = (th->th_flags & (TH_FIN|TH_RST));
                if (LogIsKept(LogDEBUG) && !estab)
		  snprintf(dbuff, sizeof dbuff,
                           "flags = %02x, sport = %d, dport = %d",
                           th->th_flags, sport, dport);
		break;
	      default:
		return (A_DENY);       /* We'll block unknown type of packet */
	      }
              if (LogIsKept(LogDEBUG)) {
                if (estab != -1) {
                  len = strlen(dbuff);
                  snprintf(dbuff + len, sizeof dbuff - len,
                           ", estab = %d, syn = %d, finrst = %d",
                           estab, syn, finrst);
                }
	        LogPrintf(LogDEBUG, " Filter: proto = %s, %s\n",
                          filter_Proto2Nam(cproto), dbuff);
              }
	      gotinfo = 1;
	    }
            if (LogIsKept(LogDEBUG)) {
	      if (fp->opt.srcop != OP_NONE) {
                snprintf(dbuff, sizeof dbuff, ", src %s %d",
                         filter_Op2Nam(fp->opt.srcop), fp->opt.srcport);
                len = strlen(dbuff);
              } else
                len = 0;
	      if (fp->opt.dstop != OP_NONE) {
                snprintf(dbuff + len, sizeof dbuff - len,
                         ", dst %s %d", filter_Op2Nam(fp->opt.dstop),
                         fp->opt.dstport);
              } else if (!len)
                *dbuff = '\0';

	      LogPrintf(LogDEBUG, "  rule = %d: Address match, "
                        "check against proto %s%s, action = %s\n",
                        n, filter_Proto2Nam(fp->proto),
                        dbuff, filter_Action2Nam(fp->action));
            }

	    if (cproto == fp->proto) {
	      if ((fp->opt.srcop == OP_NONE ||
		   PortMatch(fp->opt.srcop, sport, fp->opt.srcport)) &&
		  (fp->opt.dstop == OP_NONE ||
		   PortMatch(fp->opt.dstop, dport, fp->opt.dstport)) &&
		  (fp->opt.estab == 0 || estab) &&
		  (fp->opt.syn == 0 || syn) &&
		  (fp->opt.finrst == 0 || finrst)) {
		return (fp->action);
	      }
	    }
	  } else {
	    /* Address is mached. Make a decision. */
	    LogPrintf(LogDEBUG, "  rule = %d: Address match, action = %s\n", n,
                      filter_Action2Nam(fp->action));
	    return (fp->action);
	  }
	} else
	  LogPrintf(LogDEBUG, "  rule = %d: Address mismatch\n", n);
      }
      fp++;
    }
    return (A_DENY);		/* No rule is mached. Deny this packet */
  }
  return (A_PERMIT);		/* No rule is given. Permit this packet */
}

#ifdef notdef
static void
IcmpError(struct ip * pip, int code)
{
  struct mbuf *bp;

  if (pip->ip_p != IPPROTO_ICMP) {
    bp = mballoc(cnt, MB_IPIN);
    memcpy(MBUF_CTOP(bp), ptr, cnt);
    SendPppFrame(bp);
    ipcp_AddOutOctets(cnt);
  }
}
#endif

/*
 *  For debugging aid.
 */
int
PacketCheck(struct bundle *bundle, char *cp, int nb, struct filter *filter)
{
  struct ip *pip;
  struct tcphdr *th;
  struct udphdr *uh;
  struct icmp *icmph;
  char *ptop;
  int mask, len, n;
  int pri = PRI_NORMAL;
  int logit, loglen;
  static char logbuf[200];

  logit = LogIsKept(LogTCPIP) && filter->logok;
  loglen = 0;

  pip = (struct ip *) cp;

  if (logit && loglen < sizeof logbuf) {
    snprintf(logbuf + loglen, sizeof logbuf - loglen, "%s ", filter->name);
    loglen += strlen(logbuf + loglen);
  }
  ptop = (cp + (pip->ip_hl << 2));

  switch (pip->ip_p) {
  case IPPROTO_ICMP:
    if (logit && loglen < sizeof logbuf) {
      icmph = (struct icmp *) ptop;
      snprintf(logbuf + loglen, sizeof logbuf - loglen,
	     "ICMP: %s:%d ---> ", inet_ntoa(pip->ip_src), icmph->icmp_type);
      loglen += strlen(logbuf + loglen);
      snprintf(logbuf + loglen, sizeof logbuf - loglen,
	       "%s:%d", inet_ntoa(pip->ip_dst), icmph->icmp_type);
      loglen += strlen(logbuf + loglen);
    }
    break;
  case IPPROTO_UDP:
    if (logit && loglen < sizeof logbuf) {
      uh = (struct udphdr *) ptop;
      snprintf(logbuf + loglen, sizeof logbuf - loglen,
	   "UDP: %s:%d ---> ", inet_ntoa(pip->ip_src), ntohs(uh->uh_sport));
      loglen += strlen(logbuf + loglen);
      snprintf(logbuf + loglen, sizeof logbuf - loglen,
	       "%s:%d", inet_ntoa(pip->ip_dst), ntohs(uh->uh_dport));
      loglen += strlen(logbuf + loglen);
    }
    break;
  case IPPROTO_TCP:
    th = (struct tcphdr *) ptop;
    if (pip->ip_tos == IPTOS_LOWDELAY)
      pri = PRI_FAST;
    else if ((ntohs(pip->ip_off) & IP_OFFMASK) == 0) {
      if (INTERACTIVE(ntohs(th->th_sport)) || INTERACTIVE(ntohs(th->th_dport)))
	pri = PRI_FAST;
    }
    if (logit && loglen < sizeof logbuf) {
      len = ntohs(pip->ip_len) - (pip->ip_hl << 2) - (th->th_off << 2);
      snprintf(logbuf + loglen, sizeof logbuf - loglen,
	   "TCP: %s:%d ---> ", inet_ntoa(pip->ip_src), ntohs(th->th_sport));
      loglen += strlen(logbuf + loglen);
      snprintf(logbuf + loglen, sizeof logbuf - loglen,
	       "%s:%d", inet_ntoa(pip->ip_dst), ntohs(th->th_dport));
      loglen += strlen(logbuf + loglen);
      n = 0;
      for (mask = TH_FIN; mask != 0x40; mask <<= 1) {
	if (th->th_flags & mask) {
	  snprintf(logbuf + loglen, sizeof logbuf - loglen, " %s", TcpFlags[n]);
	  loglen += strlen(logbuf + loglen);
	}
	n++;
      }
      snprintf(logbuf + loglen, sizeof logbuf - loglen,
	       "  seq:%x  ack:%x (%d/%d)",
	       ntohl(th->th_seq), ntohl(th->th_ack), len, nb);
      loglen += strlen(logbuf + loglen);
      if ((th->th_flags & TH_SYN) && nb > 40) {
	u_short *sp;

	ptop += 20;
	sp = (u_short *) ptop;
	if (ntohs(sp[0]) == 0x0204) {
	  snprintf(logbuf + loglen, sizeof logbuf - loglen,
		   " MSS = %d", ntohs(sp[1]));
	  loglen += strlen(logbuf + loglen);
	}
      }
    }
    break;
  }

  if ((FilterCheck(pip, filter) & A_DENY)) {
    if (logit)
      LogPrintf(LogTCPIP, "%s - BLOCKED\n", logbuf);
#ifdef notdef
    if (direction == 0)
      IcmpError(pip, pri);
#endif
    return (-1);
  } else {
    /* Check Keep Alive filter */
    if (logit)
      if (FilterCheck(pip, &bundle->filter.alive) & A_DENY)
        LogPrintf(LogTCPIP, "%s - NO KEEPALIVE\n", logbuf);
      else
        LogPrintf(LogTCPIP, "%s\n", logbuf);
    return (pri);
  }
}

void
IpInput(struct bundle *bundle, struct mbuf * bp)
{
  u_char *cp;
  struct mbuf *wp;
  int nb, nw;
  struct tun_data tun;
  struct ip *pip = (struct ip *)tun.data;

  tun_fill_header(tun, AF_INET);
  cp = tun.data;
  nb = 0;
  for (wp = bp; wp; wp = wp->next) {	/* Copy to contiguous region */
    if (sizeof tun.data - (cp - tun.data) < wp->cnt) {
      LogPrintf(LogERROR, "IpInput: Packet too large (%d) - dropped\n",
                plength(bp));
      pfree(bp);
      return;
    }
    memcpy(cp, MBUF_CTOP(wp), wp->cnt);
    cp += wp->cnt;
    nb += wp->cnt;
  }

#ifndef NOALIAS
  if (AliasEnabled()) {
    struct tun_data *frag;
    int iresult;
    char *fptr;

    iresult = (*PacketAlias.In)(tun.data, sizeof tun.data);
    nb = ntohs(((struct ip *) tun.data)->ip_len);

    if (nb > MAX_MRU) {
      LogPrintf(LogERROR, "IpInput: Problem with IP header length\n");
      pfree(bp);
      return;
    }
    if (iresult == PKT_ALIAS_OK
	|| iresult == PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
      if (PacketCheck(bundle, tun.data, nb, &bundle->filter.in) < 0) {
	pfree(bp);
	return;
      }

      if (!(FilterCheck(pip, &bundle->filter.alive) & A_DENY))
        bundle_StartIdleTimer(bundle);

      ipcp_AddInOctets(&bundle->ncp.ipcp, nb);

      nb = ntohs(((struct ip *) tun.data)->ip_len);
      nb += sizeof tun - sizeof tun.data;
      nw = write(bundle->tun_fd, &tun, nb);
      if (nw != nb)
        if (nw == -1)
	  LogPrintf(LogERROR, "IpInput: wrote %d, got %s\n", nb,
                    strerror(errno));
        else
	  LogPrintf(LogERROR, "IpInput: wrote %d, got %d\n", nb, nw);

      if (iresult == PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
	while ((fptr = (*PacketAlias.GetFragment)(tun.data)) != NULL) {
	  (*PacketAlias.FragmentIn)(tun.data, fptr);
	  nb = ntohs(((struct ip *) fptr)->ip_len);
          frag = (struct tun_data *)
	    ((char *)fptr - sizeof tun + sizeof tun.data);
          nb += sizeof tun - sizeof tun.data;
	  nw = write(bundle->tun_fd, frag, nb);
	  if (nw != nb)
            if (nw == -1)
	      LogPrintf(LogERROR, "IpInput: wrote %d, got %s\n", nb,
                        strerror(errno));
            else
	      LogPrintf(LogERROR, "IpInput: wrote %d, got %d\n", nb, nw);
	  free(frag);
	}
      }
    } else if (iresult == PKT_ALIAS_UNRESOLVED_FRAGMENT) {
      nb = ntohs(((struct ip *) tun.data)->ip_len);
      nb += sizeof tun - sizeof tun.data;
      frag = (struct tun_data *)malloc(nb);
      if (frag == NULL)
	LogPrintf(LogALERT, "IpInput: Cannot allocate memory for fragment\n");
      else {
        tun_fill_header(*frag, AF_INET);
	memcpy(frag->data, tun.data, nb - sizeof tun + sizeof tun.data);
	(*PacketAlias.SaveFragment)(frag->data);
      }
    }
  } else
#endif /* #ifndef NOALIAS */
  {			/* no aliasing */
    if (PacketCheck(bundle, tun.data, nb, &bundle->filter.in) < 0) {
      pfree(bp);
      return;
    }

    if (!(FilterCheck(pip, &bundle->filter.alive) & A_DENY))
      bundle_StartIdleTimer(bundle);

    ipcp_AddInOctets(&bundle->ncp.ipcp, nb);

    nb += sizeof tun - sizeof tun.data;
    nw = write(bundle->tun_fd, &tun, nb);
    if (nw != nb)
      if (nw == -1)
	LogPrintf(LogERROR, "IpInput: wrote %d, got %s\n", nb, strerror(errno));
      else
        LogPrintf(LogERROR, "IpInput: wrote %d, got %d\n", nb, nw);
  }
  pfree(bp);
}

static struct mqueue IpOutputQueues[PRI_FAST + 1];

void
IpEnqueue(int pri, char *ptr, int count)
{
  struct mbuf *bp;

  bp = mballoc(count, MB_IPQ);
  memcpy(MBUF_CTOP(bp), ptr, count);
  Enqueue(&IpOutputQueues[pri], bp);
}

int
ip_QueueLen()
{
  struct mqueue *queue;
  int result = 0;

  for (queue = &IpOutputQueues[PRI_MAX]; queue >= IpOutputQueues; queue--)
    result += queue->qlen;

  return result;
}

int
IpFlushPacket(struct link *l, struct bundle *bundle)
{
  struct mqueue *queue;
  struct mbuf *bp;
  int cnt;

  if (bundle->ncp.ipcp.fsm.state != ST_OPENED)
    return 0;

  for (queue = &IpOutputQueues[PRI_FAST]; queue >= IpOutputQueues; queue--)
    if (queue->top) {
      bp = Dequeue(queue);
      if (bp) {
        struct ip *pip = (struct ip *)MBUF_CTOP(bp);

	cnt = plength(bp);
	SendPppFrame(l, bp, bundle);
        if (!(FilterCheck(pip, &bundle->filter.alive) & A_DENY))
          bundle_StartIdleTimer(bundle);
        ipcp_AddOutOctets(&bundle->ncp.ipcp, cnt);
	return 1;
      }
    }

  return 0;
}