// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2005 International Computer Science Institute // // 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 XORP 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 XORP LICENSE file; the license in that file is // legally binding. // $XORP: xorp/rip/packet_assembly.hh,v 1.9 2006/02/10 03:47:30 pavlin Exp $ #ifndef __RIP_PACKET_ASSEMBLY_HH__ #define __RIP_PACKET_ASSEMBLY_HH__ #include "rip_module.h" #include "libxorp/xlog.h" #include "auth.hh" #include "packets.hh" #include "port.hh" /** * @short Internal specialized state for PacketAssembler classes. * * Completely specialized implementations exist for IPv4 and IPv6 template * arguments. */ template <typename A> class PacketAssemblerSpecState {}; /** * @short IPv4 specialized PacketAssembler state. * * This class just holder the authentication handler that IPv4 packet * assembly requires. */ template <> class PacketAssemblerSpecState<IPv4> { private: AuthHandlerBase& _ah; public: /** * IPv4 Specific Constructor. */ PacketAssemblerSpecState(Port<IPv4>& port) : _ah(*(port.af_state().auth_handler())) {} /** * IPv4 Specific Constructor. */ PacketAssemblerSpecState(AuthHandlerBase& auth_handler) : _ah(auth_handler) {} /** * IPv4 Specific authentication handler accessor. */ inline AuthHandlerBase& ah() { return _ah; } /** * IPv4 Specific authentication handler accessor. */ inline const AuthHandlerBase& ah() const { return _ah; } }; /** * @short IPv6 specialized PacketAssembler state. * * This provides a means to query the RIP port and query the * configured maximum entries per packet. XXX At present it's a * placeholder and returns a fixed value. * * It also stores the last used nexthop value since nexthops are only * packed when they change. */ template <> class PacketAssemblerSpecState<IPv6> { private: uint32_t _max_entries; IPv6 _lnh; public: inline PacketAssemblerSpecState(Port<IPv6>& ) : _max_entries(25), _lnh(IPv6::ALL_ONES()) {} inline PacketAssemblerSpecState() : _max_entries(25), _lnh(IPv6::ALL_ONES()) {} inline uint32_t max_entries() const; inline void reset_last_nexthop(); inline void set_last_nexthop(const IPv6& ip6); inline const IPv6& last_nexthop() const; }; inline uint32_t PacketAssemblerSpecState<IPv6>::max_entries() const { return _max_entries; } inline void PacketAssemblerSpecState<IPv6>::reset_last_nexthop() { _lnh = IPv6::ALL_ONES(); } inline void PacketAssemblerSpecState<IPv6>::set_last_nexthop(const IPv6& ip6) { _lnh = ip6; } inline const IPv6& PacketAssemblerSpecState<IPv6>::last_nexthop() const { return _lnh; } /** * @short Class for RIP Response Packet Assemblers. * * Both RIPv2 and RIPng have some oddities in packing and this interface * provides a consistent interface for that packing. * * This class has specialized IPv4 and IPv6 implementations. */ template <typename A> class ResponsePacketAssembler { public: typedef A Addr; typedef IPNet<A> Net; typedef PacketAssemblerSpecState<A> SpState; public: /** * Constructor. * * @param port Port to take configuration information from. */ inline ResponsePacketAssembler(Port<A>& port); /** * Constructor. * * @param sp Specialized state. */ inline ResponsePacketAssembler(SpState& sp); /** * Destructor. */ inline ~ResponsePacketAssembler(); /** * Start assembling RIP response packet. */ inline void packet_start(RipPacket<A>* pkt); /** * Add a route to RIP response packet. * * @return true if route was added, false if packet is full and would * have indicated this if only @ref packet_full was called. */ inline bool packet_add_route(const Net& net, const Addr& nexthop, uint16_t cost, uint16_t tag); /** * Ready-to-go accessor. * * @return true if packet has no more space for route entries. */ inline bool packet_full() const; /** * Finish packet. Some packet types require final stage processing * and this method gives that processing a chance to happen. Common * usage is RIPv2 authentication. * * @param auth_packets a return-by-reference list with the * authenticated RIP packets (one copy for each valid authentication key). * @return true on success, false if a failure is detected. */ inline bool packet_finish(list<RipPacket<A>* >& auth_packets); private: /** * Copy Constructor (Disabled). */ ResponsePacketAssembler(const ResponsePacketAssembler&); /** * Assignment Operator (Disabled). */ ResponsePacketAssembler& operator=(const ResponsePacketAssembler&); protected: RipPacket<A>* _pkt; uint32_t _pos; SpState _sp_state; }; /** * @short Class to configure a RIP packet to be a table request. * * This class has specialized IPv4 and IPv6 implementations to cater for * address family differences. */ template <typename A> class RequestTablePacketAssembler { public: typedef A Addr; typedef IPNet<A> Net; typedef PacketAssemblerSpecState<A> SpState; public: inline RequestTablePacketAssembler(Port<A>& port) : _sp_state(port) {} /** * Take RipPacket packet and make it into a table request packet. * * @param auth_packets a return-by-reference list with the * authenticated RIP packets (one copy for each valid authentication key). * @return true on success, false if an error is encountered. Should * an error be encountered the reason is written to the xlog facility. */ inline bool prepare(RipPacket<A>* pkt, list<RipPacket<A>* >& auth_packets); protected: SpState _sp_state; }; // ---------------------------------------------------------------------------- // ResponsePacketAssembler<IPv4> implementation template <> inline ResponsePacketAssembler<IPv4>::ResponsePacketAssembler(Port<IPv4>& port) : _pkt(0), _pos(0), _sp_state(port) { } template <> inline ResponsePacketAssembler<IPv4>::ResponsePacketAssembler(SpState& sp) : _pkt(0), _pos(0), _sp_state(sp) { } template <> inline ResponsePacketAssembler<IPv4>::~ResponsePacketAssembler() { } template <> inline void ResponsePacketAssembler<IPv4>::packet_start(RipPacket<IPv4>* pkt) { _pkt = pkt; const AuthHandlerBase& ah = _sp_state.ah(); _pos = ah.head_entries(); _pkt->set_max_entries(ah.head_entries() + ah.max_routing_entries()); _pkt->header()->initialize(RipPacketHeader::RESPONSE, RipPacketHeader::IPv4_VERSION); } template <> inline bool ResponsePacketAssembler<IPv4>::packet_full() const { const AuthHandlerBase& ah = _sp_state.ah(); return _pos == ah.max_routing_entries(); } template <> inline bool ResponsePacketAssembler<IPv4>::packet_add_route(const Net& net, const Addr& nexthop, uint16_t cost, uint16_t tag) { if (packet_full()) { return false; } _pkt->route_entry(_pos)->initialize(tag, net, nexthop, cost); _pos++; return true; } template <> inline bool ResponsePacketAssembler<IPv4>::packet_finish( list<RipPacket<IPv4>* >& auth_packets) { AuthHandlerBase& ah = _sp_state.ah(); _pkt->set_max_entries(_pos); size_t n_routes = 0; if ((ah.authenticate_outbound(*_pkt, auth_packets, n_routes) != true) || (n_routes == 0)) { XLOG_ERROR("Outbound authentication error: %s\n", ah.error().c_str()); return false; } return true; } // ---------------------------------------------------------------------------- // ResponsePacketAssembler<IPv6> implementation template <> inline ResponsePacketAssembler<IPv6>::ResponsePacketAssembler(Port<IPv6>& port) : _pkt(0), _pos(0), _sp_state(port) { } template <> inline ResponsePacketAssembler<IPv6>::ResponsePacketAssembler(SpState& sp) : _pkt(0), _pos(0), _sp_state(sp) { } template <> inline ResponsePacketAssembler<IPv6>::~ResponsePacketAssembler() { } template <> inline void ResponsePacketAssembler<IPv6>::packet_start(RipPacket<IPv6>* pkt) { _pkt = pkt; _pos = 0; _sp_state.reset_last_nexthop(); _pkt->header()->initialize(RipPacketHeader::RESPONSE, RipPacketHeader::IPv6_VERSION); } template <> inline bool ResponsePacketAssembler<IPv6>::packet_full() const { return (_sp_state.max_entries() - _pos) <= 2; } template <> inline bool ResponsePacketAssembler<IPv6>::packet_add_route(const Net& net, const Addr& nexthop, uint16_t cost, uint16_t tag) { if (packet_full()) { return false; } if (nexthop != _sp_state.last_nexthop()) { _pkt->route_entry(_pos)->initialize_nexthop(nexthop); _pos++; _sp_state.set_last_nexthop(nexthop); } _pkt->route_entry(_pos)->initialize_route(tag, net, cost); _pos++; return true; } template <> inline bool ResponsePacketAssembler<IPv6>::packet_finish( list<RipPacket<IPv6>* >& auth_packets) { _pkt->set_max_entries(_pos); RipPacket<IPv6>* packet = new RipPacket<IPv6>(*_pkt); auth_packets.push_back(packet); return true; } // ---------------------------------------------------------------------------- // RequestTablePacketAssembler<IPv4> implementation template<> inline bool RequestTablePacketAssembler<IPv4>::prepare(RipPacket<IPv4>* pkt, list<RipPacket<IPv4>* >& auth_packets) { pkt->header()->initialize(RipPacketHeader::REQUEST, RipPacketHeader::IPv4_VERSION); AuthHandlerBase& ah = _sp_state.ah(); pkt->set_max_entries(1 + ah.head_entries()); pkt->route_entry(ah.head_entries())->initialize_table_request(); size_t n_routes = 0; if ((ah.authenticate_outbound(*pkt, auth_packets, n_routes) != true) || (n_routes == 0)) { XLOG_ERROR("Outbound authentication error: %s\n", ah.error().c_str()); return false; } return true; } // ---------------------------------------------------------------------------- // RequestTablePacketAssembler<IPv6> implementation template<> inline bool RequestTablePacketAssembler<IPv6>::prepare(RipPacket<IPv6>* pkt, list<RipPacket<IPv6>* >& auth_packets) { pkt->header()->initialize(RipPacketHeader::REQUEST, RipPacketHeader::IPv6_VERSION); pkt->set_max_entries(1); pkt->route_entry(0)->initialize_table_request(); RipPacket<IPv6>* packet = new RipPacket<IPv6>(*pkt); auth_packets.push_back(packet); return true; } #endif // __RIP_PACKET_ASSEMBLY_HH__