/*
 * ipencap.{cc,hh} -- element encapsulates packet in IP header
 * Robert Morris, Eddie Kohler, Alex Snoeren
 *
 * Copyright (c) 1999-2000 Massachusetts Institute of Technology
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, subject to the conditions
 * listed in the Click LICENSE file. These conditions include: you must
 * preserve this copyright notice, and you cannot mention the copyright
 * holders in advertising related to the Software without their permission.
 * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
 * notice is a summary of the Click LICENSE file; the license in that file is
 * legally binding.
 */

#include <click/config.h>
#include "ipencap.hh"
#include <click/nameinfo.hh>
#include <click/confparse.hh>
#include <click/error.hh>
#include <click/glue.hh>
#include <click/standard/alignmentinfo.hh>
CLICK_DECLS

IPEncap::IPEncap()
{
}

IPEncap::~IPEncap()
{
}

int
IPEncap::configure(Vector<String> &conf, ErrorHandler *errh)
{
  click_ip iph;
  memset(&iph, 0, sizeof(click_ip));
  iph.ip_v = 4;
  iph.ip_hl = sizeof(click_ip) >> 2;
  iph.ip_tos = 0;
  iph.ip_off = 0;
  iph.ip_ttl = 250;
  iph.ip_sum = 0;
  int proto, tos = -1, dscp = -1;
  bool ce = false, df = false;
  String ect_str;
  
  if (cp_va_parse(conf, this, errh,
		  cpNamedInteger, "protocol", NameInfo::T_IP_PROTO, &proto,
		  cpIPAddress, "source address", &iph.ip_src,
		  cpIPAddress, "destination address", &iph.ip_dst,
		  cpKeywords,
		  "TOS", cpUnsigned, "TOS", &tos,
		  "TTL", cpByte, "time-to-live", &iph.ip_ttl,
		  "DSCP", cpUnsigned, "DSCP", &dscp,
		  "ECT", cpKeyword, "ECN capable transport", &ect_str,
		  "CE", cpBool, "ECN congestion experienced", &ce,
		  "DF", cpBool, "don't fragment", &df,
		  cpEnd) < 0)
    return -1;

  if (proto < 0 || proto > 255)
      return errh->error("bad IP protocol");
  iph.ip_p = proto;
  
  int ect = 0;
  if (ect_str) {
    bool x;
    if (cp_bool(ect_str, &x))
      ect = x;
    else if (ect_str == "2")
      ect = 2;
    else
      return errh->error("bad ECT value '%s'", ect_str.c_str());
  }
  
  if (tos >= 0 && dscp >= 0)
    return errh->error("cannot set both TOS and DSCP");
  else if (tos >= 0 && (ect || ce))
    return errh->error("cannot set both TOS and ECN bits");
  if (tos >= 256 || tos < -1)
    return errh->error("TOS too large; max 255");
  else if (dscp >= 63 || dscp < -1)
    return errh->error("DSCP too large; max 63");
  if (ect && ce)
    return errh->error("can set at most one ECN option");

  if (tos >= 0)
    iph.ip_tos = tos;
  else if (dscp >= 0)
    iph.ip_tos = (dscp << 2);
  if (ect)
    iph.ip_tos |= (ect == 1 ? IP_ECN_ECT1 : IP_ECN_ECT2);
  if (ce)
    iph.ip_tos |= IP_ECN_CE;
  if (df)
    iph.ip_off |= htons(IP_DF);
  _iph = iph;
  
#if HAVE_FAST_CHECKSUM && FAST_CHECKSUM_ALIGNED
  // check alignment
  {
    int ans, c, o;
    ans = AlignmentInfo::query(this, 0, c, o);
    _aligned = (ans && c == 4 && o == 0);
    if (!_aligned)
      errh->warning("IP header unaligned, cannot use fast IP checksum");
    if (!ans)
      errh->message("(Try passing the configuration through 'click-align'.)");
  }
#endif
  
  return 0;
}

int
IPEncap::initialize(ErrorHandler *)
{
  _id = 0;
  return 0;
}

Packet *
IPEncap::simple_action(Packet *p_in)
{
  WritablePacket *p = p_in->push(sizeof(click_ip));
  if (!p) return 0;
  
  click_ip *ip = reinterpret_cast<click_ip *>(p->data());
  memcpy(ip, &_iph, sizeof(click_ip));
  ip->ip_len = htons(p->length());
  ip->ip_id = htons(_id.fetch_and_add(1));

#if HAVE_FAST_CHECKSUM && FAST_CHECKSUM_ALIGNED
  if (_aligned)
    ip->ip_sum = ip_fast_csum((unsigned char *)ip, sizeof(click_ip) >> 2);
  else
    ip->ip_sum = click_in_cksum((unsigned char *)ip, sizeof(click_ip));
#elif HAVE_FAST_CHECKSUM
  ip->ip_sum = ip_fast_csum((unsigned char *)ip, sizeof(click_ip) >> 2);
#else
  ip->ip_sum = click_in_cksum((unsigned char *)ip, sizeof(click_ip));
#endif
  
  p->set_dst_ip_anno(IPAddress(ip->ip_dst));
  p->set_ip_header(ip, sizeof(click_ip));
  
  return p;
}

String
IPEncap::read_handler(Element *e, void *thunk)
{
  IPEncap *ipe = static_cast<IPEncap *>(e);
  switch ((intptr_t)thunk) {
   case 0:	return IPAddress(ipe->_iph.ip_src).s();
   case 1:	return IPAddress(ipe->_iph.ip_dst).s();
   default:	return "<error>";
  }
}

void
IPEncap::add_handlers()
{
  add_read_handler("src", read_handler, (void *)0);  
  add_write_handler("src", reconfigure_positional_handler, (void *)1);
  add_read_handler("dst", read_handler, (void *)1);  
  add_write_handler("dst", reconfigure_positional_handler, (void *)2);
}

CLICK_ENDDECLS
EXPORT_ELEMENT(IPEncap)
ELEMENT_MT_SAFE(IPEncap)


syntax highlighted by Code2HTML, v. 0.9.1