//-----------------------------------------------------------------------------
// File:  icmp.hh
// Date: Tue Mar 25 16:04:55 PST 2003
// Author: Norman Franke, Martin Casado
//
// Description:
//
// ICMP header construction
//
// ----------------------------------------------------------------------------
//
//    Copyright 2003. The Regents of the University of California.
//    All rights reserved.
//
//    This work was produced at the University of California, Lawrence Livermore
//    National Laboratory (UC LLNL) under contract no. W-7405-ENG-48 (Contract
//    48) between the U.S. Department of Energy (DOE) and The Regents of the
//    University of California (University) for the operation of UC LLNL.
//    Copyright is reserved to the University for purposes of controlled
//    dissemination, commercialization through formal licensing, or other
//    disposition under terms of Contract 48; DOE policies, regulations and
//    orders; and U.S. statutes. The rights of the Federal Government are
//    reserved under Contract 48 subject to the restrictions agreed upon by the
//    DOE and University as allowed under DOE Acquisition Letter 91-7.
//
//    DISCLAIMER
//    This software was prepared as an account of work sponsored by an
//    agency of the United States Government. Neither the United States
//    Government nor the University of California nor any of their employees,
//    makes any warranty, express or implied, or assumes any liability or
//    responsibility for the accuracy, completeness, or usefulness of any
//    information, apparatus, product, or process disclosed, or represents that
//    its use would not infringe protectedly-owned rights. Reference herein to any
//    specific commercial products, process, or service by trade name, trademark,
//    manufacturer, or otherwise, does not necessarily constitute or imply its
//    endorsement, recommendation, or favoring by the United States Government or
//    the University of California. The views and opinions of authors expressed
//    herein do not necessarily represent those of the United States
//    Government or the University of California, and shall not be used for
//    advertising or product endorsement purposes.
//
//-----------------------------------------------------------------------------

#ifndef ICMP_HH
#define ICMP_HH

#include "static_type_inference.hh"
#include "ip.hh"

extern "C"
{
#include <stdint.h>
#include <assert.h>
}

namespace pacman
{

//-----------------------------------------------------------------------------
struct icmp_base : public ip_proto, public icmp_proto
{
    // So IP knows our protocol
    static const uint8_t IPPROTO = ip_base::ICMP;
    static const uint8_t DEFPROTO = 0;

    // ICMP types
    static const uint8_t ECHO_REPLY =      0; // Echo Reply
    static const uint8_t DEST_UNREACH =    3; // Destination Unreachable
    static const uint8_t SOURCE_QUENCH =   4; // Source Quench
    static const uint8_t REDIRECT =        5; // Redirect (change route)
    static const uint8_t ECHO_REQUEST =    8; // Echo Request
    static const uint8_t ROUTER_ADVERT =   9; // Router Advertisement
    static const uint8_t ROUTER_SOLICIT =  10; // Router Solicitation
    static const uint8_t TIME_EXCEEDED =   11; // Time Exceeded
    static const uint8_t PARAMETERPROB =   12; // Parameter Problem
    static const uint8_t TIMESTAMP_REQ =   13; // Timestamp Request
    static const uint8_t TIMESTAMP_REPLY = 14; // Timestamp Reply
    static const uint8_t INFO_REQUEST =    15; // Information Request
    static const uint8_t INFO_REPLY =      16; // Information Reply
    static const uint8_t ADDRESS =         17; // Address Mask Request
    static const uint8_t ADDRESSREPLY =    18; // Address Mask Reply
    static const uint8_t TRACEROUTE =      30; // Traceroute Reply

    // Codes for UNREACH
    static const uint8_t NET_UNREACH =    0; // Network Unreachable
    static const uint8_t HOST_UNREACH =   1; // Host Unreachable
    static const uint8_t PROT_UNREACH =   2; // Protocol Unreachable
    static const uint8_t PORT_UNREACH =   3; // Port Unreachable
    static const uint8_t FRAG_NEEDED =    4; // Fragmentation Needed/DF set
    static const uint8_t SR_FAILED =      5; // Source Route failed
    static const uint8_t NET_UNKNOWN =    6; // Unknown net
    static const uint8_t HOST_UNKNOWN =   7; // Unknown host
    static const uint8_t HOST_ISOLATED =  8; // Src host isolated
    static const uint8_t NET_ANO =        9; // Destination network admin prohibit
    static const uint8_t HOST_ANO =       10; // Destination host admin prohibit
    static const uint8_t NET_UNR_TOS =    11; // Network unreachable for TOS
    static const uint8_t HOST_UNR_TOS =   12; // Host unreachable for TOS
    static const uint8_t PKT_FILTERED =   13; // Packet filtered
    static const uint8_t PREC_VIOLATION = 14; // Precedence violation
    static const uint8_t PREC_CUTOFF =    15; // Precedence cut off

    // Codes for REDIRECT
    static const uint8_t REDIR_NET =    0; // Redirect Net
    static const uint8_t REDIR_HOST =   1; // Redirect Host
    static const uint8_t REDIR_NETTOS =   2; // Redirect Net for TOS
    static const uint8_t REDIR_HOSTTOS =  3; // Redirect Host for TOS

    // Codes for TIME_EXCEEDED
    static const uint8_t EXC_TTL =        0; // TTL count exceeded
    static const uint8_t EXC_FRAGTIME =   1; // Fragment Reass time exceeded

    // Code for PARAMPROB
    static const uint8_t BAD_IPHEADER =   0; // ip header is invalid
    static const uint8_t OPTION_ABSENT =  1; // req. opt. absent

    // Code for TRACEROUTE
    static const uint8_t OUT_FWD_OK =   0; // Outbound pkt successfully forwarded
    static const uint8_t OUT_NO_RTE =   1; // No route; packet discarded

    template<typename T>
    static uint16_t icmp_base::cksum(T & in);

}__attribute__ ((__packed__));
//-----------------------------------------------------------------------------

template<typename T> struct icmp;
template<typename T> struct echo_request;
template<typename T> struct echo_reply;

//-----------------------------------------------------------------------------
template <>
struct icmp<empty> : public icmp_base
{
    typedef empty PayloadType;

    uint8_t type;   // type
    uint8_t code;   // code
    uint16_t check;   // checksum

}__attribute__ ((__packed__));
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
template <typename T = empty>
struct icmp : public icmp<empty>
{
    typedef T PayloadType;

    T        data;

    icmp()
    {
        type  = get_icmp_type<T, icmp_base::DEFPROTO>::val;
        code  = 0;
        check = 0;
    }

}__attribute__ ((__packed__));
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
template <>
struct echo_request<empty>: public icmp_base
{
    static const uint8_t ICMPTYPE = icmp<>::ECHO_REQUEST;
    typedef empty PayloadType;

    uint16_t id; // identifier
    uint16_t seq; // sequence

    echo_request()
    {
        id = 0;
        seq = 0;
    }
}__attribute__ ((__packed__));

template <typename T = empty>
struct echo_request: public echo_request<>
{
    typedef T PayloadType;

    T        data;
}__attribute__ ((__packed__));
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
template <>
struct echo_reply<empty>: public icmp_base
{
    static const uint8_t ICMPTYPE = icmp<>::ECHO_REPLY;
    typedef empty PayloadType;

    uint16_t id; // identifier
    uint16_t seq; // sequence

    echo_reply()
    {
        id = 0;
        seq = 0;
    }
}__attribute__ ((__packed__));

template <typename T = empty>
struct echo_reply: public echo_reply<>
{
    typedef T PayloadType;

    T        data;
}__attribute__ ((__packed__));
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
struct dest_unreach: public icmp_base
{
    static const uint8_t ICMPTYPE = icmp<>::DEST_UNREACH;
    typedef empty PayloadType;

    uint16_t unused; // MBZ
    uint16_t mtu; // Next-Hop MTU
    ip<empty, uint8_t[8]> ip; // IP header + 8 bytes of data

    dest_unreach()
    {
        unused = 0;
        mtu = 0;
    }
}__attribute__ ((__packed__));
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
struct source_quench: public icmp_base
{
    static const uint8_t ICMPTYPE = icmp<>::SOURCE_QUENCH;
    typedef empty PayloadType;

    uint32_t unused; // MBZ
    ip<empty, uint8_t[64]> ip; // IP header + 64 bytes of data

    source_quench()
    {
        unused = 0;
    }
}__attribute__ ((__packed__));
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
struct redirect: public icmp_base
{
    static const uint8_t ICMPTYPE = icmp<>::REDIRECT;
    typedef empty PayloadType;

    ipaddr addr; // IP gateway
    ip<empty, uint8_t[64]> iphdr; // IP header + 64 bytes of data

    redirect()
    {
    }
}__attribute__ ((__packed__));
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
struct time_exceeded: public icmp_base
{
    static const uint8_t ICMPTYPE = icmp<>::TIME_EXCEEDED;
    typedef empty PayloadType;

    uint32_t unused; // MBZ
    ip<empty, uint8_t[64]> iphdr; // IP header + 64 bytes of data

    time_exceeded()
    {
        unused = 0;
    }
}__attribute__ ((__packed__));
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
struct parameterprob: public icmp_base
{
    static const uint8_t ICMPTYPE = icmp<>::PARAMETERPROB;
    typedef empty PayloadType;

    uint8_t ptr; // byte offset of error message
    uint8_t unused1; // MBZ
    uint16_t unused2; // MBZ
    ip<empty, uint8_t[8]> iphdr; // IP header + 8 bytes of data

    parameterprob()
    {
        ptr = 0;
        unused1 = 0;
        unused2 = 0;
    }
}__attribute__ ((__packed__));
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
struct timestamp_req: public icmp_base
{
    static const uint8_t ICMPTYPE = icmp<>::TIMESTAMP_REQ;
    typedef empty PayloadType;

    uint16_t id; // identifier to match requests
    uint16_t seq; // sequence number
    uint32_t orig_ts; // originate timestamp
    uint32_t recv_ts; // originate timestamp
    uint32_t xmit_ts; // originate timestamp

    timestamp_req()
    {
        id = seq = 0;
        orig_ts = recv_ts = xmit_ts = 0;
    }
}__attribute__ ((__packed__));
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
struct timestamp_reply: public icmp_base
{
    static const uint8_t ICMPTYPE = icmp<>::TIMESTAMP_REPLY;
    typedef empty PayloadType;

    uint16_t id; // identifier to match requests
    uint16_t seq; // sequence number
    uint32_t orig_ts; // originate timestamp
    uint32_t recv_ts; // originate timestamp
    uint32_t xmit_ts; // originate timestamp

    timestamp_reply()
    {
        id = seq = 0;
        orig_ts = recv_ts = xmit_ts = 0;
    }
}__attribute__ ((__packed__));
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
struct traceroute : public icmp_base
{
    static const uint8_t ICMPTYPE = icmp<>::TRACEROUTE;
    typedef empty PayloadType;

    uint16_t id; // identifier to match requests
    uint16_t unused; // MBZ
    uint16_t out_hops; // outbound hop count
    uint16_t ret_hops; // return hop count
    uint32_t speed; // output link speed
    uint32_t mtu; // output MTU

    traceroute()
    {
        id = unused = out_hops = ret_hops = 0;
        speed = mtu = 0;
    }
}__attribute__ ((__packed__));
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
struct icmp_csum
{
    template <typename T>
    static void func(T & in)
    {
        in.check = ip_base::cksum_impl(&in, sizeof(in));
    }
};
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
template <typename T>
inline
uint16_t 
icmp_base::cksum(T & in)
{
    return ip_base::cksum_impl(&in, sizeof(in));
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
template <class T>
struct which_csum <icmp<T> >
{
    typedef icmp_csum Type;
};
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
typedef pacman::ip< empty, pacman::icmp< uint8_t[1500] > > icmp_mtu_t;
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Functor for Ping's F parameter.
template <typename T> class icmp_inc_seq;
template <typename O, typename T>
class icmp_inc_seq< pacman::ip< O, pacman::icmp< pacman::echo_request < T > > > >
{
  public:
    void operator()
      (pacman::ip< O, pacman::icmp< pacman::echo_request <T> > > & inPkt)
    {
      inPkt.data.data.seq++;
    };
};
//-----------------------------------------------------------------------------


} // -- namespace pacman


#endif  //-- ICMP_HH
