#ifndef L2TP_TUNNEL_H #define L2TP_TUNNEL_H #include #include "l2tpd.h" #include "queue.h" #include "link.h" #include "timer.h" class l2tp_packet_t { public: u16 *raw_packet; int raw_length; u16 *DataPtr; u16 Flags; u16 Length; u16 Tunnel; u16 Session; int Ns; int Nr; int nr_avps; #define MaxAVPs 40 u16 *AVPs[MaxAVPs]; void reset(void) { raw_packet = NULL; nr_avps = 0; Ns = -1; Nr = -1; memset(AVPs, 0, sizeof(AVPs)); } int decode_avp(u16 vendor, u16 type, u16 length, u16 *value); int parse_packet(u16 *data, int size); int is_avp_mandatory(int avp); int avp_length(int avp); l2tp_packet_t(void) { this->reset(); } }; class l2tp_tunnel_t; class l2tp_session_t; class l2tp_tx_queue_t : CQueue, CTimer { private: unsigned long retransmit_interval; int nr_retransmits; public: l2tp_tx_queue_t() { retransmit_interval = 10; // 1/10s nr_retransmits = 0; } ~l2tp_tx_queue_t() { Stop(); } virtual void tx_retransmit(CPppPacket *pkt) { } virtual void tx_timed_out(void) { } void TimerExpired(void); void tx_append(CPppPacket *pkt) { if (IsEmpty()) { Start(10); nr_retransmits = 0; } Append(pkt); } void discard_acked_packets(u16 seq); }; class l2tp_tunnel_hello : public CTimer { public: int hello_interval; virtual void send_HELLO(void) { } void TimerExpired(void); // used for Hello requests void start_hello_timer(void); void stop_hello_timer(void) { Stop(); } }; class l2tp_tunnel_t : public l2tp_tx_queue_t, l2tp_tunnel_hello { enum { L2TP_Tunnel_Idle, L2TP_Tunnel_WaitCtlReply, L2TP_Tunnel_WaitCtlConn, L2TP_Tunnel_Established, } tunnel_state; public: l2tp_peer_t *peer; l2tp_session_t *sessions[65536]; int tunnel_id; int peer_tunnel_id; u16 tunnel_Ns, tunnel_Nr; u32 call_serial_number; int need_zlb; int free_session_id; l2tp_tunnel_t(l2tp_peer_t *peer, u16 tunnel); ~l2tp_tunnel_t(); void handle_packet(char *buf, unsigned len, struct sockaddr_in *sin); void handle_control_packet(void); void tunnel_abort(const char *msg); void send_pkt(u16 *data, int len, int incr_seq = 1); void tx_retransmit(CPppPacket *pkt); void tx_timed_out(void); int is_idle(void) { return L2TP_Tunnel_Idle == tunnel_state; } void send_ZLB(void); void send_SCCRQ(void); void send_SCCRP(void); void send_SCCCN(void); void send_StopCCN(void); void send_HELLO(void); void send_ICRQ(u16 session_id); void send_ICRP(u16 peer_session_id, u16 session_id); void send_ICCN(u16 session_id, u32 txspeed = 57600, u32 framing = 1); void send_CDN(u16 session_id, u16 result); void handle_ICRQ(void); void handle_ICRP(void); void handle_ICCN(void); void handle_CDN(void); void handle_SCCRQ(void); void handle_SCCRP(void); void handle_SCCCN(void); void handle_StopCCN(void); void handle_HELLO(void); void clean_up(void); void tunnel_up(void); void dump_sessions(ctrlfd_t *cfd, int verbose); int build_challenge_rsp(u16 *where, unsigned char msgtype); void bring_up(void); void open_session(ctrlfd_t *cfd, const char *site = NULL); int alloc_session_id(void); int is_established(void) { return L2TP_Tunnel_Established == tunnel_state; } }; class l2tp_session_t : CLink, CTimer { l2tp_tunnel_t *tunnel; channel_t ch; enum { Session_None, Session_LAC, Session_LNS, } session_mode; public: u16 session_id; u16 peer_session_id; enum { L2TP_Session_Idle, L2TP_Session_WaitConnect, L2TP_Session_Established, L2TP_Session_WaitTunnel, L2TP_Session_WaitReply, } session_state; ctrlfd_t *session_cfd; char *session_site; l2tp_session_t *multihop_session; public: void tunnel_up(void); void init_session(l2tp_tunnel_t *tun, u16 peer_id, u16 id, ctrlfd_t *cfd = NULL, const char *site = NULL) { char tmp[64]; num_sessions++; session_mode = Session_None; tunnel = tun; peer_session_id = peer_id; if (!id) id = tun->alloc_session_id(); session_id = id; session_state = L2TP_Session_Idle; session_cfd = cfd; session_site = site ? strdup(site) : NULL; multihop_session = NULL; //fprintf(stderr, "init session(%p) [%d]\n", this, session_id); tunnel->sessions[session_id] = this; memset(&ch, 0, sizeof(ch)); sprintf(tmp, "l2tp%d.%d", tunnel->tunnel_id, session_id); ch.device_name = strdup(tmp); ch.dev_class = "l2tp"; CLink *link = this; link->m_hard_mtu = link->m_hard_mru = 1442; link->m_channel = &ch; ch.link = this; if (cfd) cfd->unlinkme(&session_cfd); } l2tp_session_t(l2tp_tunnel_t *tun, u16 peer_id, u16 id) { init_session(tun, peer_id, id); Open(); } l2tp_session_t(l2tp_tunnel_t *tun, ctrlfd_t *cfd, const char *site) { init_session(tun, 0, 0, cfd, site); if (cfd) cfd->printf("session@%p id: %d\n", this, session_id); session_state = L2TP_Session_WaitTunnel; if (tun->is_established()) tunnel_up(); else tun->bring_up(); } l2tp_session_t(l2tp_tunnel_t *tun, l2tp_session_t *in_session); ~l2tp_session_t() { num_sessions--; //fprintf(stderr, "delete session@%p [%d]\n", this, session_id); m_channel->state = CS_DISCONNECTING; Down(); //Close(0, "l2tp session destroyed"); if (this != tunnel->sessions[session_id]) fprintf(stderr, "BUG: session delete\n"); tunnel->sessions[session_id] = NULL; tunnel = NULL; if (session_cfd) { session_cfd->printf("l2tp session destroyed"); session_cfd->done(1); session_cfd = NULL; } if (session_site) { free(session_site); session_site = NULL; } if (multihop_session) { l2tp_session_t *other = multihop_session; if (other->multihop_session != this) fprintf(stderr, "~l2tp_session_t: BUG - multihop\n"); other->multihop_session = NULL; multihop_session = NULL; delete other; } if (session_state == L2TP_Session_Established) num_est_sessions--; if (ch.device_name) { free(ch.device_name); ch.device_name = NULL; } } void clean_up(void); int HardOutput(CPppPacket *pkt); int ch_ioctl(unsigned int cmd, unsigned long arg); void HardHangup(void); int HardConnect(const char *number, u32 callType); void GetConfig(void (*cbf)(void *, OptionSet_t *), void *obj, OptionSet_t *options); void handle_ICRQ(l2tp_packet_t *pkt); void handle_ICRP(l2tp_packet_t *pkt); void handle_ICCN(l2tp_packet_t *pkt); void handle_CDN(l2tp_packet_t *pkt); void handle_packet(l2tp_packet_t *pkt); void dump_session(ctrlfd_t *cfd); void disconnect_session(void); }; #endif