// -*- mode: c++; c-basic-offset: 4 -*-
#ifndef CLICK_IPRW_HH
#define CLICK_IPRW_HH
#include <click/element.hh>
#include <click/timer.hh>
#include <click/bighashmap.hh>
#include <click/ipflowid.hh>
#include <clicknet/ip.h>
CLICK_DECLS
class IPMapper;

class IPRw : public Element { public:

    class Pattern;
    class Mapping;
    typedef HashMap<IPFlowID, Mapping*> Map;
    enum InputSpecName {
	INPUT_SPEC_NOCHANGE, INPUT_SPEC_KEEP, INPUT_SPEC_DROP,
	INPUT_SPEC_PATTERN, INPUT_SPEC_MAPPER
    };
    struct InputSpec {
	int kind;
	union {
	    int output;
	    struct {
		Pattern* p;
		int fport;
		int rport;
	    } pattern;		/* also used for INPUT_SPEC_KEEP */
	    IPMapper* mapper;
	} u;
    };

    IPRw();
    ~IPRw();

    enum ConfigurePhase {
	CONFIGURE_PHASE_PATTERNS = CONFIGURE_PHASE_INFO,
	CONFIGURE_PHASE_REWRITER = CONFIGURE_PHASE_DEFAULT,
	CONFIGURE_PHASE_MAPPER = CONFIGURE_PHASE_REWRITER - 1,
	CONFIGURE_PHASE_USER = CONFIGURE_PHASE_REWRITER + 1
    };

    int configure_phase() const 	{ return CONFIGURE_PHASE_REWRITER; }

    int parse_input_spec(const String&, InputSpec&, String, ErrorHandler*);

    virtual int notify_pattern(Pattern*, ErrorHandler*);
    virtual Mapping* apply_pattern(Pattern*, int ip_p, const IPFlowID&, int, int) = 0;
    virtual Mapping* get_mapping(int ip_p, const IPFlowID&) const = 0;

  protected:

    Vector<Pattern*> _all_patterns;

    enum { GC_INTERVAL_SEC = 3600 };

    void take_state_map(Map&, Mapping** free_head, Mapping** free_tail,
			const Vector<Pattern*>&, const Vector<Pattern*>&);
    void clear_map(Map&);
    void clean_map(Map&, uint32_t last_jif);
    void clean_map_free_tracked(Map&, Mapping*& free_head, Mapping*& free_tail,
				uint32_t last_jif);
    void incr_clean_map_free_tracked(Map&, Mapping*& head, Mapping*& tail,
				     uint32_t last_jif);

};


class IPRw::Mapping { public:

    enum { F_REVERSE = 1, F_MARKED = 2, F_FLOW_OVER = 4, F_FREE_TRACKED = 8,
	   F_DST_ANNO = 16 };

    Mapping(bool dst_anno);

    void initialize(int ip_p, const IPFlowID& inf, const IPFlowID& outf, int output, uint16_t flags, Mapping* reverse);
    static void make_pair(int ip_p, const IPFlowID& inf, const IPFlowID& outf,
			  int foutput, int routput, Mapping* f, Mapping* r);

    const IPFlowID& flow_id() const	{ return _mapto; }
    Pattern* pattern() const		{ return _pat; }
    int output() const 			{ return _output; }
    bool is_primary() const		{ return !(_flags & F_REVERSE); }
    const Mapping* primary() const { return is_primary() ? this : _reverse; }
    Mapping* primary()		   { return is_primary() ? this : _reverse; }
    Mapping* reverse() const		{ return _reverse; }

    inline bool used_since(uint32_t) const;
    void mark_used()			{ _used = click_jiffies(); }

    bool marked() const			{ return (_flags & F_MARKED); }
    void mark()				{ _flags |= F_MARKED; }
    void unmark()			{ _flags &= ~F_MARKED; }

    uint16_t sport() const		{ return _mapto.sport(); } // network

    Mapping* free_next() const		{ return _free_next; }
    void set_free_next(Mapping* m)	{ _free_next = m; }

    bool session_over() const		{ return (_flags & F_FLOW_OVER) && (_reverse->_flags & F_FLOW_OVER); }
    void set_session_over()		{ _flags |= F_FLOW_OVER; _reverse->_flags |= F_FLOW_OVER; }
    void set_session_flow_over()	{ _flags |= F_FLOW_OVER; }
    void clear_session_flow_over()	{ _flags &= ~F_FLOW_OVER; }

    bool free_tracked() const		{ return (_flags & F_FREE_TRACKED); }
    inline void add_to_free_tracked_tail(Mapping*& head, Mapping*& tail);
    inline void clear_free_tracked();

    void apply(WritablePacket*);

    String unparse() const;

  protected:

    IPFlowID _mapto;

    uint16_t _ip_csum_delta;
    uint16_t _udp_csum_delta;

    Mapping* _reverse;

    uint16_t _flags;
    uint8_t _ip_p;
    uint8_t _output;
    unsigned _used;

    // these variables maintained by IPRw::Pattern
    Pattern* _pat;

    Mapping* _free_next;

    friend class IPRw;
    friend class IPRw::Pattern;

    inline Mapping* free_from_list(Map&, bool notify);
    inline void append_to_free(Mapping*& head, Mapping*& tail);

};


class IPRw::Pattern { public:

    // Pattern is <saddr/sport[-sport2]/daddr/dport>.
    // It is associated with a IPRewriter output port.
    // It can be applied to a specific IPFlowID (<saddr/sport/daddr/dport>)
    // to obtain a new IPFlowID rewritten according to the Pattern.
    // Any Pattern component can be '-', which means that the corresponding
    // IPFlowID component is left unchanged. 

    Pattern(const IPAddress&, int, const IPAddress&, int, bool is_napt, bool sequential, uint32_t variation);
    static int parse(const String&, Pattern**, Element*, ErrorHandler*);
    static int parse_with_ports(const String&, Pattern**,
				int*, int*, Element*, ErrorHandler*);

    void use()			{ _refcount++; }
    void unuse()		{ if (--_refcount <= 0) delete this; }

    int nmappings() const	{ return _nmappings; }
  
    operator bool() const	{ return _saddr || _sport || _daddr || _dport; }
    IPAddress daddr() const	{ return _daddr; }
    bool allow_nat() const	{ return !_is_napt || _variation_top == 0; }
    bool allow_napt() const	{ return _is_napt || _variation_top == 0; }

    bool can_accept_from(const Pattern&) const;

    bool create_mapping(int ip_p, const IPFlowID&, int fport, int rport, Mapping*, Mapping*, const Map&);
    void accept_mapping(Mapping*);
    inline void mapping_freed(Mapping*);

    String unparse() const;

  public:

    IPAddress _saddr;
    int _sport;			// host byte order
    IPAddress _daddr;
    int _dport;			// host byte order

    uint32_t _variation_top;
    uint32_t _variation_mask;
    uint32_t _next_variation;

    bool _is_napt;
    bool _sequential;

    int _refcount;
    int _nmappings;

    Pattern(const Pattern&);
    Pattern& operator=(const Pattern&);

    static int parse_napt(Vector<String>&, Pattern**, Element*, ErrorHandler*);
    static int parse_nat(Vector<String>&, Pattern**, Element*, ErrorHandler*);
    friend class Mapping;

};


class IPMapper { public:

    IPMapper()				{ }
    virtual ~IPMapper()			{ }

    void notify_rewriter(IPRw*, ErrorHandler*);
    virtual IPRw::Mapping* get_map(IPRw*, int ip_p, const IPFlowID&, Packet*);

};


inline void
IPRw::Mapping::append_to_free(Mapping*& head, Mapping*& tail)
{
    assert((!head && !tail)
	   || (head && tail && (head->_flags & F_FREE_TRACKED) && (tail->_flags & F_FREE_TRACKED)));
    assert(!_free_next && !_reverse->_free_next);
    if (tail)
	tail = tail->_free_next = this;
    else
	head = tail = this;
}

inline void
IPRw::Mapping::add_to_free_tracked_tail(Mapping*& head, Mapping*& tail)
{
    assert(!(_flags & F_FREE_TRACKED) && !(_reverse->_flags & F_FREE_TRACKED));
    _flags |= F_FREE_TRACKED;
    _reverse->_flags |= F_FREE_TRACKED;
    primary()->append_to_free(head, tail);
}

inline void
IPRw::Mapping::clear_free_tracked()
{
    _flags &= ~F_FREE_TRACKED;
    _reverse->_flags &= ~F_FREE_TRACKED;
    _free_next = 0;
    assert(_reverse->_free_next == 0);
}

inline bool
IPRw::Mapping::used_since(uint32_t t) const
{ 
    return ((int32_t)(_used - t)) >= 0 || ((int32_t)(_reverse->_used - t)) >= 0;
}

CLICK_ENDDECLS
#endif


syntax highlighted by Code2HTML, v. 0.9.1