Logo Search packages:      
Sourcecode: openswan version File versions  Download package

ipv4.c

/*

Copyright (c) 2003,2004 Jeremy Kerr & Rusty Russell

This file is part of nfsim.

nfsim is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

nfsim is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with nfsim; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "ipv4.h"
#include "utils.h"
#include <core.h>
#include <field.h>

#if 0
#include <linux/netfilter_ipv4.h>
#endif


int route(struct sk_buff *skb);

LIST_HEAD(routes);

static struct notifier_block *inetaddr_chain;

static int __ip_route_output_key(struct rtable **rp, struct flowi *flp);

static int destroy_route(void *r)
{
      struct ipv4_route *route = r;
      list_del(&route->entry);
      return 0;
}

void add_route_for_device(struct in_device *indev)
{
      struct ipv4_route *route;

      route = talloc(indev, struct ipv4_route);
      talloc_set_destructor(route, destroy_route);
      route->netmask = indev->ifa_list->ifa_mask;
      route->network = indev->ifa_list->ifa_address & indev->ifa_list->ifa_mask;
      route->interface = indev->dev;
      route->gateway = indev->ifa_list->ifa_address;
      list_add_tail(&route->entry, &routes);
}

struct rtable *rcache;
static int destroy_rtable(void *r)
{
      struct rtable *rt = r, **p;

      /* Remove from cache if it's there (it should be, currently) */
      for (p = &rcache; *p; p = &(*p)->u.rt_next) {
            if (*p == rt) {
                  *p = rt->u.rt_next;
                  break;
            }
      }
      return 0;
}

#if 0
/* need the following:
    - interfaces
    - routes
 */
static void init(void)
{
      /* name our hooks */
      nf_hooknames[PF_INET][0] = "NF_IP_PRE_ROUTING";
      nf_hooknames[PF_INET][1] = "NF_IP_LOCAL_IN";
      nf_hooknames[PF_INET][2] = "NF_IP_FORWARD";
      nf_hooknames[PF_INET][3] = "NF_IP_LOCAL_OUT";
      nf_hooknames[PF_INET][4] = "NF_IP_POST_ROUTING";
}

init_call(init);
#endif

int register_inetaddr_notifier(struct notifier_block *nb)
{
      return notifier_chain_register(&inetaddr_chain, nb);
}

int unregister_inetaddr_notifier(struct notifier_block *nb)
{
      return notifier_chain_unregister(&inetaddr_chain, nb);
}

int __call_inetaddr_notifier(unsigned long val, struct in_ifaddr *ifa)
{
      struct notifier_block *nb = inetaddr_chain;

      return notifier_call_chain(&nb, val, ifa);
}

#if 0
static int ip_rcv_finish(struct sk_buff *skb)
{
      if (!skb->dst &&
          ip_route_input(skb, skb->nh.iph->daddr, skb->nh.iph->saddr,
                     skb->nh.iph->tos, skb->dev)) {
            kfree_skb(skb);
            return -1;
      }
            
      return dst_input(skb);
}
#endif

int ip_rcv(struct sk_buff *skb)
{
#if 0
      skb->dev->stats.rxpackets++;
      skb->dev->stats.rxbytes += skb->len;

      /* pull data to the start of the ip header */
      skb_pull(skb, skb->nh.raw - skb->data);

      log_packet(skb, "rcv:%s", skb->dev->name);

      return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
                     ip_rcv_finish);
#else
      return 0;
#endif
}


int ip_rcv_local(struct sk_buff *skb)
{
      struct rtable *rt;
      struct flowi fl;

      log_packet(skb, "rcv_local");

      /* pull data to the start of the ip header */
      skb_pull(skb, skb->nh.raw - skb->data);

      rt = (struct rtable *)skb->dst;
      if (rt)
            goto routed;


      memset(&fl, 0, sizeof(fl));
      
      fl.fl4_dst    = skb->nh.iph->daddr;
      fl.fl4_src    = skb->nh.iph->saddr;
      fl.fl4_tos    = skb->nh.iph->tos;
#ifdef CONFIG_IP_ROUTE_FWMARK
      fl.fl4_fwmark = skb->nfmark;
#endif

      if (__ip_route_output_key(&rt, &fl)) {
            log_route(skb, "no route");
            kfree_skb(skb);
            return 1;
      }
      
      skb->dst = (struct dst_entry *)rt;
      dst_hold(skb_dst);
routed:
      skb->dev = skb->dst->dev;
#if 0
      return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, skb->dev, dst_output);
#else
      return 0;
#endif
}

static int ip_local_deliver(struct sk_buff *skb)
{
#if 0
      return NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL, nf_send_local);
#else
      return 0;
#endif
}

int ip_finish_output(struct sk_buff *skb)
{
      struct net_device *dev = skb->dst->dev;
      
      skb->dev = dev;
      skb->protocol = htons(ETH_P_IP);
      /*
      if (skb->mac.ethernet)
            skb->mac.ethernet->h_proto = skb->protocol;
      */

#if 0
      return NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL, dev,
            nf_send);
#else
      return 0;
#endif      
}

static int ip_output(struct sk_buff *skb)
{
      /* FIXME: fragment? */
      return ip_finish_output(skb);
}

unsigned short ip_compute_csum(unsigned char * buff, int len)
{
    return csum_fold (csum_partial(buff, len, 0));
}

static int __ip_route_output_key(struct rtable **rp, struct flowi *flp)
{
      struct rtable *rth;
      struct net_device *dev;
      struct ipv4_route *route;

      if (should_i_fail(__func__))
            return -ENOMEM;

      /* check for a cached route */
      for (rth = rcache; rth; rth = rth->u.rt_next) {
            if (rth->fl.fl4_dst == flp->fl4_dst &&
                rth->fl.fl4_src == flp->fl4_src &&
                rth->fl.iif     == 0 &&
                rth->fl.oif     == flp->oif &&
#ifdef CONFIG_IP_ROUTE_FWMARK
                rth->fl.fl4_fwmark == flp->fl4_fwmark &&
#endif
                rth->fl.fl4_tos == flp->fl4_tos) {
                  dst_hold(&rth->u.dst)
                  *rp = rth;
                  return 0;
            }
      }

      list_for_each_entry(dev, &interfaces, entry) {
            struct in_ifaddr *ifaddr;
            
            if (!dev->ip_ptr)
                  continue;

            ifaddr = ((struct in_device *)(dev->ip_ptr))->ifa_list;
            while (ifaddr) {
                  if (flp->fl4_dst == ifaddr->ifa_local) {
                        rth = talloc_zero(dev->ip_ptr, struct rtable);
                        talloc_set_destructor(rth, destroy_rtable);

                        rth->u.dst.output = ip_output;
                        rth->u.dst.input  = ip_local_deliver;
                        rth->u.dst.dev    = &loopback_dev;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
                        rth->u.dst.pmtu     = 1500;
#endif
                        rth->rt_src       = rth->fl.fl4_src
                                          = flp->fl4_src;
                        rth->rt_dst       = rth->fl.fl4_dst
                                          = flp->fl4_dst;
                        rth->rt_gateway   = flp->fl4_dst;
                        rth->rt_iif       = rth->fl.iif = flp->iif;

                        rth->fl.fl4_tos   = flp->fl4_tos;
#ifdef CONFIG_IP_ROUTE_FWMARK
                        rth->fl.fl4_fwmark = flp->fl4_fwmark;
#endif

                        rth->u.rt_next = rcache;
                        rcache = rth;

                        *rp = rth;
                        return 0;
                  }
                  ifaddr = ifaddr->ifa_next;
            }
            
      }

      /* otherwise, find the appropriate route & create an rcache entry */
      list_for_each_entry(route, &routes, entry) {
            if ((flp->fl4_dst & route->netmask) ==
                       route->network) {
                  rth = talloc_zero(route, struct rtable);
                  talloc_set_destructor(rth, destroy_rtable);

                  rth->u.dst.dev    =  route->interface;
                  rth->u.dst.output = ip_output;
                  rth->u.dst.input  = NULL;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
                  rth->u.dst.pmtu     = 1500;
#endif
                  rth->rt_src       = rth->fl.fl4_src = flp->fl4_src;
                  if (!rth->rt_src)
                        rth->rt_src
                              = inet_select_addr(route->interface,
                                             flp->fl4_dst,
                                             RT_SCOPE_UNIVERSE);
                  rth->rt_dst       = rth->fl.fl4_dst = flp->fl4_dst;
                  rth->rt_gateway   = route->gateway;
                  rth->fl.oif       = route->interface->ifindex;
                  rth->fl.oif = flp->oif;
                  rth->fl.fl4_tos   = flp->fl4_tos;
#ifdef CONFIG_IP_ROUTE_FWMARK
                  rth->fl.fl4_fwmark = flp->fl4_fwmark;
#endif
                  /* add to rcache list */
                  rth->u.rt_next = rcache;
                  rcache = rth;

                  *rp = rth;

                  return 0;
            }
      }

      return 1;
      
}


#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
int ip_route_output_key(struct rtable **rp, struct rt_key *key)
{
      struct flowi fl;

      fl.fl4_dst = key->dst;
      fl.fl4_src = key->src;
      fl.iif = 0;
      fl.oif = key->oif;
#ifdef CONFIG_IP_ROUTE_FWMARK
      fl.fl4_fwmark = key->fwmark;
#endif
      fl.fl4_tos = key->tos;

      return __ip_route_output_key(rp, &fl);
}
#else
int ip_route_output_key(struct rtable **rp, struct flowi *flp)
{
      return __ip_route_output_key(rp, flp);
}
#endif

/* 2.6.10-rc3 added this. */
int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
            struct sock *sk, int flags)
{
      struct rtable **rp = (void *)dst_p;

      return __ip_route_output_key(rp, fl);
}

#if 0
static int ip_forward(struct sk_buff *skb)
{
      u32 check;

      if (!(--skb->nh.iph->ttl)) {
            log_route(skb, "ip_forward:ttl expired");
            icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);
            kfree_skb(skb);
            return 1;
      }
      nfsim_update_skb(skb, &skb->nh.iph->ttl, sizeof(skb->nh.iph->ttl));

      check = skb->nh.iph->check;
      check += htons(0x0100);
      skb->nh.iph->check = check + (check>=0xFFFF);

      /* Tell nfsim it's me changing data here. */
      nfsim_update_skb(skb, &skb->nh.iph->check, sizeof(skb->nh.iph->check));

      /* FIXME: strict source routing... */

#if 0
      return NF_HOOK(PF_INET, NF_IP_FORWARD, skb, skb->dev, skb->dst->dev,
            dst_output);
#else
      return 0;
#endif
}
#endif

#if 0
int ip_route_input(struct sk_buff *skb, u32 daddr, u32 saddr,
               u8 tos, struct net_device *dev)
{
      struct rtable *rth;
      struct ipv4_route *route;
      int iif = dev->ifindex;

      for (rth = rcache; rth; rth = rth->u.rt_next) {
            if (rth->fl.fl4_dst == daddr &&
                rth->fl.fl4_src == saddr &&
                rth->fl.iif == iif &&
                rth->fl.oif == 0 &&
#ifdef CONFIG_IP_ROUTE_FWMARK
                rth->fl.fl4_fwmark == skb->nfmark &&
#endif
                rth->fl.fl4_tos == tos) {
                  dst_hold(&rth->u.dst);
                  skb->dst = (struct dst_entry*)rth;
                  return 0;
            }
      }

      /* is this a local packet ? */
      list_for_each_entry(dev, &interfaces, entry) {
            struct in_ifaddr *ifaddr;
            
            if (!dev->ip_ptr)
                  continue;

            ifaddr = ((struct in_device *)(dev->ip_ptr))->ifa_list;
            while (ifaddr) {
                  if (skb->nh.iph->daddr == ifaddr->ifa_local) {
                        log_route(skb,
                              "route:local packet (%s)",
                              dev->name);
                        rth = talloc_zero(dev->ip_ptr, struct rtable);
                        talloc_set_destructor(rth, destroy_rtable);

                        rth->u.dst.output = NULL;
                        rth->u.dst.input  = ip_local_deliver;
                        rth->u.dst.dev    = &loopback_dev;
                        rth->rt_src       = rth->fl.fl4_src = saddr;
                        rth->rt_dst       = rth->fl.fl4_dst = daddr;
                        rth->rt_gateway   = daddr;
                        rth->rt_iif       = rth->fl.iif = dev->ifindex;

                        rth->fl.fl4_tos   = tos;
#ifdef CONFIG_IP_ROUTE_FWMARK
                        rth->fl.fl4_fwmark = skb->nfmark;
#endif
                        skb->dst = &rth->u.dst;

                        return 0;
                  }
                  ifaddr = ifaddr->ifa_next;
            }
            
      }

      /* otherwise, find the appropriate route & create an rcache entry */
      list_for_each_entry(route, &routes, entry) {
            if ((skb->nh.iph->daddr & route->netmask) ==
                       route->network) {
                  rth = talloc_zero(route, struct rtable);
                  talloc_set_destructor(rth, destroy_rtable);

                  rth->u.dst.dev = route->interface;

                  rth->u.dst.output = ip_output;
                  rth->u.dst.input  = ip_forward;
                  rth->rt_src       = rth->fl.fl4_src = saddr;
                  rth->rt_dst       = rth->fl.fl4_dst = daddr;
                  rth->rt_gateway   = route->gateway;
                  rth->rt_iif       = rth->fl.iif =
                                    route->interface->ifindex;
                  rth->fl.oif = 0;
                  rth->fl.fl4_tos   = tos;
#ifdef CONFIG_IP_ROUTE_FWMARK
                  rth->fl.fl4_fwmark = skb->nfmark;
#endif
                  /* add to rcache list */
                  rth->u.rt_next = rcache;
                  rcache = rth;
                  
                  skb->dst = &rth->u.dst;

                  return 0;
            }
      }


      log_route(skb, "ERROR: packet is not local and no matching "
            "route (dst=%u.%u.%u.%u)", NIPQUAD(daddr));

      return 1;

}
#endif

int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
{
      log_packet(skb, "ip_fragment");
      (*output)(skb);
      return 0;
}

00501 struct fraglist
{
      struct list_head list;
      struct sk_buff *frags[20];
};
static struct list_head fraglist[IP_DEFRAG_VS_FWD];

static void init_fraglist(void)
{
      unsigned int i;

      for (i = 0; i < ARRAY_SIZE(fraglist); i++)
            INIT_LIST_HEAD(&fraglist[i]);
}
init_call(init_fraglist);

#if 0
static struct sk_buff *gather_frag(struct fraglist *f, struct sk_buff *skb)
{
      unsigned int i, len, off, max = 0;
      bool ended = false;
      char filled[70000] = { 0 };
      char data[70000];

      for (i = 0; i < ARRAY_SIZE(f->frags); i++) {
            if (!f->frags[i]) {
                  if (!skb)
                        break;
                  f->frags[i] = skb;
                  talloc_steal(f, skb);
                  skb = NULL;
            }

            off = (ntohs(f->frags[i]->nh.iph->frag_off) & IP_OFFSET)*8;
            len = ntohs(f->frags[i]->nh.iph->tot_len) 
                  - f->frags[i]->nh.iph->ihl * 4;
            if (len + off > max)
                  max = len + off;

            memset(filled + off, 1, len);
            skb_copy_bits(f->frags[i], f->frags[i]->nh.iph->ihl * 4,
                        data + off, len);
            if (!(ntohs(f->frags[i]->nh.iph->frag_off) & IP_MF))
                  ended = true;
      }
      if (skb)
            barf("Can't handle %i frags!\n", i);

      /* Not a complete packet? */
      if (!ended || memchr(filled, 0, max) != NULL)
            return NULL;

      /* Copy header and data. */
      suppress_failtest++;
      skb = skb_copy_expand(f->frags[0], 0, max, GFP_KERNEL);
      suppress_failtest--;

      skb->nh.iph = (void *)skb->data;
      memcpy(skb->nh.iph, f->frags[0]->nh.iph, f->frags[0]->nh.iph->ihl*4);
      memcpy(skb_put(skb, max), data, max);

      /* Except we're not a fragment, and we're longer. */
      skb->nh.iph->frag_off = 0;
      skb->nh.iph->tot_len = htons(max + skb->nh.iph->ihl*4);

      list_del(&f->list);
      talloc_free(f);
      return skb;
}

static struct sk_buff *ip_defrag_user(struct sk_buff *skb, u32 user)
{
      struct fraglist *i;

      list_for_each_entry(i, &fraglist[user], list) {
            if (i->frags[0]->nh.iph->saddr != skb->nh.iph->saddr
                || i->frags[0]->nh.iph->daddr != skb->nh.iph->daddr
                || i->frags[0]->nh.iph->protocol!=skb->nh.iph->protocol)
                  continue;
            return gather_frag(i, skb);
      }
      i = talloc(NULL, struct fraglist);
      memset(i->frags, 0, sizeof(i->frags));
      i->frags[0] = skb;
      list_add(&i->list, &fraglist[user]);
      talloc_steal(i, skb);
      return NULL;
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
struct sk_buff *ip_defrag(struct sk_buff *skb)
{
      return ip_defrag_user(skb, 0);
}
#else
struct sk_buff *ip_defrag(struct sk_buff *skb, u32 user)
{
      return ip_defrag_user(skb, user);
}
#endif

void ipfrag_flush(void)
{
}
#endif

void icmp_send(struct sk_buff *skb_in, int type, int code, u32 info)
{
      log_packet(skb_in, "icmp_send:type=%d, code=%d, info=%d",
            type, code, info);
}

void ip_send_check(struct iphdr *iph)
{
      iph->check = 0;
      iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
}

static inline unsigned short from32to16(unsigned long x)
{
      /* add up 16-bit and 16-bit for 16+c bit */
      x = (x & 0xffff) + (x >> 16);
      /* add up carry.. */
      x = (x & 0xffff) + (x >> 16);
      return x;
}

static unsigned long do_csum(const unsigned char * buff, int len)
{
      int odd, count;
      unsigned long result = 0;

      if (len <= 0)
            return 0;

      odd = 1 & (unsigned long) buff;
      if (odd) {
            result = *buff;
            len--;
            buff++;
      }
      count = len >> 1;       /* nr of 16-bit words.. */
      if (count) {
            if (2 & (unsigned long) buff) {
                  result += *(const unsigned short *) buff;
                  count--;
                  len -= 2;
                  buff += 2;
            }
            count >>= 1;            /* nr of 32-bit words.. */
            if (count) {
                    unsigned long carry = 0;
                  do {
                        unsigned int w = *(const unsigned int *) buff;
                        count--;
                        buff += 4;
                        result += carry;
                        result += w;
                        carry = (w > result);
                  } while (count);
                  result += carry;
                  result = (result & 0xffff) + (result >> 16);
            }
            if (len & 2) {
                  result += *(const unsigned short *) buff;
                  buff += 2;
            }
      }
      if (len & 1)
            result += (*buff << 8);
      result = from32to16(result);
      if (odd)
            result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);

      return result;
}

unsigned short ip_fast_csum(void * iph, unsigned int ihl)
{
      return ~do_csum(iph, ihl*4);
}

unsigned int csum_fold(unsigned int sum)
{
      return ~from32to16(sum);
}

#if 0
unsigned int csum_partial(const void * buff, int len, unsigned int sum)
{
      unsigned int result = do_csum(buff, len);

      /* add in old sum, and carry.. */
      result += sum;
      if (sum > result)
            result += 1;
      return result;
}
#endif

u32 csum_tcpudp_nofold(unsigned long saddr,
                   unsigned long daddr,
                   unsigned short len,
                   unsigned short proto,
                   unsigned int sum)
{
      struct {
            u32 srcip, dstip;
            u8 mbz, protocol;
            u16 proto_len;
      } pseudo_header = { saddr, daddr, 0, proto, htons(len) };

      return csum_partial(&pseudo_header, sizeof(pseudo_header), sum);
}

unsigned short csum_tcpudp_magic(unsigned long saddr,
                                       unsigned long daddr,
                                       unsigned short len,
                                       unsigned short proto,
                                       unsigned int sum)
{
      return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
}

uint16_t tcp_v4_check(struct tcphdr *th, int len,
                           unsigned long saddr, unsigned long daddr,
                           unsigned long base)
{
      return csum_tcpudp_magic(saddr,daddr,len,IPPROTO_TCP,base);
}


unsigned inet_addr_type(u32 addr)
{
      struct net_device *dev;

      if (ZERONET(addr) || BADCLASS(addr))
            return RTN_BROADCAST;
      if (MULTICAST(addr))
            return RTN_MULTICAST;


      list_for_each_entry(dev, &interfaces, entry) {
            struct in_ifaddr *ifaddr;
            
            if (!dev->ip_ptr)
                  continue;

            
            for (ifaddr = ((struct in_device *)(dev->ip_ptr))->ifa_list;
                        ifaddr; ifaddr = ifaddr->ifa_next)
                  if (ifaddr->ifa_local == addr)
                        return RTN_LOCAL;
      }

      return RTN_UNICAST;

}

static __inline__ int inet_ifa_match(u32 addr, struct in_ifaddr *ifa)
{
      return !((addr^ifa->ifa_address)&ifa->ifa_mask);
}

/* Not strictly correct for loopback packets: they ignore device if it
 * doesn't match. */
u32 inet_select_addr(const struct net_device *dev, u32 dst, int scope)
{
      u32 addr = 0;
      struct in_device *in_dev;
      struct in_ifaddr *ifa;

      in_dev = dev->ip_ptr;
      if (!in_dev)
            return 0;

      for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
            if (!dst || inet_ifa_match(dst, ifa)) {
                  addr = ifa->ifa_local;
                  break;
            }
            if (!addr)
                  addr = ifa->ifa_local;
      };

      return addr;
}

int lastid=4;
void __ip_select_ident(struct iphdr *iph,
                   struct dst_entry *dst, struct sock *sk)
{
      iph->id = ++lastid;
}

void ip_select_ident(struct iphdr *iph,
                   struct dst_entry *dst, struct sock *sk)
{
  __ip_select_ident(iph, dst, sk);
}

static char *print_data(char *ptr, const char *data, int len)
{
      static const char hexbuf[]= "0123456789abcdef";
      int i;

      ptr += sprintf(ptr, "DATA ");
      for (i = 0; i < len; i++) {
            if (data[i] == '\n') {
                  *ptr++ = '\\';
                  *ptr++ = 'n';
            } else if (data[i] == '\r') {
                  *ptr++ = '\\';
                  *ptr++ = 'r';
            } else if (data[i] == '\0') {
                  *ptr++ = '\\';
                  *ptr++ = '0';
            } else if (data[i] == '\t') {
                  *ptr++ = '\\';
                  *ptr++ = 't';
            } else if (data[i] == '\\') {
                  *ptr++ = '\\';
                  *ptr++ = '\\';
            } else if (data[i] & 0x80 || (data[i] & 0xe0) == 0) {
                  *ptr++ = '\\';
                  *ptr++ = 'x';
                  *ptr++ = hexbuf[(data[i] >> 4) & 0xf];
                  *ptr++ = hexbuf[data[i] & 0xf];
            } else
                  *ptr++ = data[i];
      }
      *ptr = '\0';
      return ptr;
}

static char *describe(char *ptr, int len, struct iphdr *iph, char *dump_flags)
{
      /* the length of the packet reported by the ipv4 header */
      int iplen;
      struct icmphdr *icmph;
      struct tcphdr *tcph;
      struct udphdr *udph;
      char *p;

      *ptr = '\0';

      if (len < sizeof(struct iphdr)) {
            strcat(ptr, "-TRUNCATED-");
            goto out;
      }

      if (csum_partial(iph, sizeof(struct iphdr), 0)
                != 0xFFFF) {
            strcat(ptr, "-BAD IP CSUM-");
            goto out;
      }

      if (ntohs(iph->frag_off) & IP_OFFSET)
            ptr += sprintf(ptr, "FRAG=%u ",
                  (ntohs(iph->frag_off) & IP_OFFSET)*8);
      if (ntohs(iph->frag_off) & IP_DF)
            ptr += sprintf(ptr, "DF ");
      if (ntohs(iph->frag_off) & IP_MF)
            ptr += sprintf(ptr, "MF ");
      if (ntohs(iph->frag_off) & IP_CE)
            ptr += sprintf(ptr, "CE ");
      if (dump_flags && strstr(dump_flags, "ttl"))
            ptr += sprintf(ptr, "TTL=%i ", iph->ttl);
      if (dump_flags && strstr(dump_flags, "tos"))
            ptr += sprintf(ptr, "TOS=%i ", iph->tos);
      if (dump_flags && strstr(dump_flags, "dscp"))
            ptr += sprintf(ptr, "DSCP=0x%x ",
                         (iph->tos >> IPT_DSCP_SHIFT));
      if (dump_flags && strstr(dump_flags, "ect"))
            ptr += sprintf(ptr, "ECT=0x%x ", (iph->tos & 3));
      ptr += sprintf(ptr, "%u.%u.%u.%u %u.%u.%u.%u ",
                   NIPQUAD(iph->saddr),
                   NIPQUAD(iph->daddr));

      if (ntohs(iph->frag_off) & IP_OFFSET)
            goto out;
      
      iplen = htons(iph->tot_len) - sizeof(struct iphdr);
      len -= sizeof(struct iphdr);
      
      switch (iph->protocol) {
      case IPPROTO_ICMP:
            icmph = (struct icmphdr *)(iph + 1);
            ptr += sprintf(ptr, "%Zu %u ",
                   iplen - sizeof(struct icmphdr), iph->protocol);

            if (len < sizeof(struct icmphdr)) {
                  ptr += sprintf(ptr, "-TRUNCATED-");
                  goto out;
            }
            ptr += sprintf(ptr, "%u %u ", icmph->type, icmph->code);

            if (len < iplen) {
                  ptr += sprintf(ptr, "-TRUNCATED-");
                  goto out;
            }

            if (!(ntohs(iph->frag_off) & IP_MF)
                && csum_partial((char *)icmph, iplen, 0) != 0xFFFF) {
                        ptr += sprintf(ptr, "-BAD ICMP CSUM-");
                        goto out;
                        
            }

            if (icmph->type == 0 || icmph->type == 8) {
                  ptr += sprintf(ptr, "%u %u ",
                        ntohs(icmph->un.echo.id),
                        ntohs(icmph->un.echo.sequence));
            } else {
                  /* Print out packet inside it. */
                  ptr += sprintf(ptr, "CONTAINS ");
                  ptr = describe(ptr,
                               iplen - sizeof(struct icmphdr),
                               (struct iphdr *)(icmph + 1), dump_flags);
            }
            break;

      case IPPROTO_UDP:
            udph = (struct udphdr *)(iph + 1);
            ptr += sprintf(ptr, "%Zu %u ",
                   iplen - sizeof(struct udphdr), iph->protocol);

            if (len < sizeof(struct udphdr)) {
                  ptr += sprintf(ptr, "-TRUNCATED-");
                  goto out;
            }

            ptr += sprintf(ptr, "%u %u ",
                  ntohs(udph->source),
                  ntohs(udph->dest));

            if (len < iplen) {
                  ptr += sprintf(ptr, "-TRUNCATED-");
                  goto out;
            }

            if (!(ntohs(iph->frag_off) & IP_MF)
                && udph->check
                && csum_tcpudp_magic(iph->saddr, iph->daddr,
                               iplen, IPPROTO_UDP, 
                               csum_partial(udph, iplen, 0))) {
                  ptr += sprintf(ptr, "-BAD UDP CSUM- (%04x)", 
                               udph->check);
                  goto out;
            }

            if (dump_flags && strstr(dump_flags, "data"))
                  ptr = print_data(ptr, (char *)(udph + 1),
                               iplen - sizeof(*udph));
            break;

      case IPPROTO_TCP:
            tcph = (struct tcphdr *)(iph + 1);
            if (len < sizeof(struct tcphdr)) {
                  /* Assume no tcp options... */
                  ptr += sprintf(ptr, "%Zu %u ",
                               iplen - sizeof(*tcph), iph->protocol);

                  ptr += sprintf(ptr, "-TRUNCATED-");
                  goto out;
            }
            ptr += sprintf(ptr, "%u %u ",
                   iplen - tcph->doff*4, iph->protocol);

            ptr += sprintf(ptr, "%u %u ",
                         ntohs(tcph->source), ntohs(tcph->dest));

            if (len < iplen) {
                  ptr += sprintf(ptr, "-TRUNCATED-");
                  goto out;
            }
            p = ptr;

            if (tcph->syn)
                  ptr += sprintf(ptr, "SYN");
            if (tcph->fin)
                  ptr += sprintf(ptr, "%sFIN", ptr == p ? "" : "/");
            if (tcph->rst)
                  ptr += sprintf(ptr, "%sRST", ptr == p ? "" : "/");
            if (tcph->ack)
                  ptr += sprintf(ptr, "%sACK", ptr == p ? "" : "/");
            if (tcph->urg)
                  ptr += sprintf(ptr, "%sURG", ptr == p ? "" : "/");
            if (tcph->psh)
                  ptr += sprintf(ptr, "%sPSH", ptr == p ? "" : "/");
            if (tcph->cwr)
                  ptr += sprintf(ptr, "%sCWR", ptr == p ? "" : "/");
            if (tcph->ece)
                  ptr += sprintf(ptr, "%sECE", ptr == p ? "" : "/");
            if (ptr == p)
                  ptr += sprintf(ptr, "NONE");
            ptr += sprintf(ptr, " ");

            if (tcph->seq)
                  ptr += sprintf(ptr, "SEQ=%u ", ntohl(tcph->seq));

            if (tcph->ack_seq)
                  ptr += sprintf(ptr, "ACK=%u ", ntohl(tcph->ack_seq));

            if (tcph->window)
                  ptr += sprintf(ptr, "WIN=%u ", ntohs(tcph->window));

            if (tcph->doff*4 != sizeof(struct tcphdr)) {
                  char sep = '=';
                  int i;
                  ptr += sprintf(ptr, "OPT");
                  for (i = sizeof(struct tcphdr); i < tcph->doff*4; i++){
                        ptr += sprintf(ptr, "%c%u",
                                     sep,
                                     ((u_int8_t *)tcph)[i]);
                        sep = ',';
                  }
                  ptr += sprintf(ptr, " ");
            }

            if (!(ntohs(iph->frag_off) & IP_MF)
                && csum_tcpudp_magic(iph->saddr, iph->daddr,
                               iplen, IPPROTO_TCP, 
                               csum_partial(tcph, iplen, 0))) {
                  ptr += sprintf(ptr, "-BAD TCP CSUM- (%04x)",
                               tcph->check);
                  goto out;
            }

            if (dump_flags && strstr(dump_flags, "data"))
                  ptr = print_data(ptr, (char *)tcph + tcph->doff*4,
                               iplen - tcph->doff*4);
            break;
                  
      default:
            ptr += sprintf(ptr, "%u %u", iplen, iph->protocol);
            if (len < iplen) {
                  ptr += sprintf(ptr, "-TRUNCATED-");
                  goto out;
            }
      }

out:
      if (ptr[-1] == ' ') {
            ptr--;
            *ptr = '\0';
      }
      return ptr;
}

#if 0
char *ipv4_describe_packet(struct sk_buff *skb)
{
      static char ipv4_pbuf[1024];
      char packet[skb->len];

      if (skb_copy_bits(skb, 0, packet, skb->len) != 0)
            barf("skb_copy_bits failed");
      describe(ipv4_pbuf, skb->len, (struct iphdr *)packet,
             field_value(skb, "dump_flags"));
      return ipv4_pbuf;
}
#endif

Generated by  Doxygen 1.6.0   Back to index