#ifndef L2TP_TUNNEL_H #define L2TP_TUNNEL_H #include #include "l2tpd.h" #include "queue.h" #include "link.h" #include "timer.h" #include "aps_if.h" #include "l2tp_monitor.h" extern int l2tp_hard_mru, l2tp_hard_mtu; extern unsigned l2tp_default_tunnel_csum_payload; class l2tp_peer_t; 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 decode_hidden_avp(l2tp_peer_t *peer, u16 vendor, u16 type, u16 length, u16 *value); int parse_packet(l2tp_peer_t *peer, u16 *data, int size); int is_avp_mandatory(int avp); int avp_length(int avp); char *avp_strdup(int avp); int avp_u16(int avp) { if (!AVPs[avp]) return -1; return ntohs(*AVPs[avp]); } CPppPacket *pkt_from_avp(int avp) { int len = avp_length(avp); if (len <= 0) return NULL; CPppPacket *pkt = new CPppPacket((u8*)AVPs[avp], len); return pkt; } 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(); CPppPacket *pkt; while (NULL != (pkt = (CPppPacket *)Pull())) { delete pkt; } } 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); void dfs_l2tp_tx_queue_t(ctrlfd_t *cfd); void dfs_restore(dfs_object_t *obj); }; class l2tp_tunnel_hello : public CTimer { public: int hello_interval; virtual void send_HELLO(void) { } l2tp_tunnel_hello() { hello_interval = 60 * 100; } 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_States { L2TP_Tunnel_Idle, L2TP_Tunnel_WaitCtlReply, L2TP_Tunnel_WaitCtlConn, L2TP_Tunnel_Established, } tunnel_state; public: l2tp_peer_t *peer; l2tp_session_t *sessions[65536]; l2tp_session_t *assigned_sessions[65536]; int tunnel_id; int peer_tunnel_id; u16 tunnel_Ns, tunnel_Nr; u32 call_serial_number; int need_zlb; int tunnel_aborted; int free_session_id; int nr_free_session_ids; u8 challenge_bytes[16]; l2tp_tunnel_t(l2tp_peer_t *peer, u16 tunnel); l2tp_tunnel_t(l2tp_peer_t *peer, dfs_object_t *obj); ~l2tp_tunnel_t(); void handle_packet(l2tp_packet_t *the_pkt, union sockaddr_union *sau); void handle_control_packet(void); void tunnel_abort(const char *msg); CPppPacket *prepare_pkt(u16 flags, u16 session_id); void add_u16_avp(CPppPacket *pkt, u16 flags, u16 type, u16 data) { if ((flags & 0xff) != 8) *(char *)0 = 0; pkt->Put16(flags); pkt->Put16(0); /* vendor */ pkt->Put16(type); pkt->Put16(data); } void add_u32_avp(CPppPacket *pkt, u16 flags, u16 type, u32 data) { if ((flags & 0xff) != 10) *(char *)0 = 0; pkt->Put16(flags); pkt->Put16(0); /* vendor */ pkt->Put16(type); pkt->Put32(data); } void add_pkt_avp(CPppPacket *pkt, u16 flags, u16 type, CPppPacket *p) { if ((flags & 0xff) != 6) *(char *)0 = 0; flags += p->GetLength(); pkt->Put16(flags); pkt->Put16(0); /* vendor */ pkt->Put16(type); pkt->Put(p); } void add_bytes_avp(CPppPacket *pkt, u16 flags, u16 type, u8 *data, unsigned len) { if ((flags & 0xff) != (6 + len)) *(char *)0 = 0; pkt->Put16(flags); pkt->Put16(0); /* vendor */ pkt->Put16(type); pkt->Put(data, len); } void add_hostname_avp(CPppPacket *pkt); void send_pkt(CPppPacket *pkt, int incr_seq = 1, l2tp_monitor_t *monitor = NULL, const char *monitor_msg = NULL); 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, const char *called_number, const char *calling_number); void send_ICRP(l2tp_session_t *session); void send_ICCN(u16 session_id, u32 txspeed = 57600, u32 framing = 1, CLink *multihop = NULL, l2tp_session_t *multihop_session = NULL); void send_CDN(u16 session_id, u16 peer_session_id, u16 result, l2tp_monitor_t *monitor); 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 handle_OCRQ(void); //void handle_OCRP(void); //void handle_OCCN(void); void clean_up(const char *reason = NULL); void tunnel_up(void); void dump_sessions(ctrlfd_t *cfd, int verbose); int build_challenge_rsp(u8 *where, unsigned char msgtype); int check_challenge_rsp(unsigned char msgtype); void bring_up(void); void open_session(ctrlfd_t *cfd, const char *site = NULL, int wait_for_hangup = 0, char *called_number = NULL); int alloc_session_id(void); int is_established(void) { return L2TP_Tunnel_Established == tunnel_state; } void stop_tunnel(void); const char *get_state(void); int set_csum_payload(unsigned do_csum); bool get_hw_info(struct l2tp_hardwire_info *info) { bool valid = peer->get_hw_info(info); info->hw_peer_tunnel = htons(peer_tunnel_id); return valid; } void update_hw_peer(struct l2tp_hardwire_info *info); private: int tunnel_check_auth(const char *which, int type); enum L2TP_Tunnel_States tag_to_tunnel_state(const char *str); }; class l2tp_session_t : public CLink, CTimer { l2tp_tunnel_t *tunnel; channel_t ch; enum Session_Modes { Session_None, Session_LAC, Session_LNS, } session_mode; enum Session_Modes tag_to_session_mode(const char *str); enum L2TP_Session_States { L2TP_Session_Idle, L2TP_Session_WaitConnect, L2TP_Session_Established, L2TP_Session_WaitTunnel, L2TP_Session_WaitReply, } session_state; enum L2TP_Session_States tag_to_session_state(const char *str); public: u16 session_id; u16 peer_session_id; ctrlfd_t *session_cfd; char *session_site; char *session_called_number; CPppPacket *proxy_auth_name; CPppPacket *proxy_auth_challenge; CPppPacket *proxy_auth_response; int proxy_auth_type; int proxy_auth_id; l2tp_session_t *multihop_session; CLink *multihop_clink; unsigned wait_for_hangup : 1; l2tp_monitor_t *m_monitor; public: void tunnel_up(void); void abort(const char *msg); void init_session(l2tp_tunnel_t *tun, u16 peer_id, u16 id, ctrlfd_t *cfd = NULL, const char *site = NULL, const char *called_number = 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; session_called_number = called_number ? strdup(called_number) : NULL; proxy_auth_name = NULL; proxy_auth_challenge = NULL; proxy_auth_response = NULL; proxy_auth_type = -1; proxy_auth_id = -1; multihop_session = NULL; multihop_clink = NULL; m_monitor = NULL; //fprintf(stderr, "init session(%p) [%d]\n", this, session_id); tunnel->sessions[session_id] = this; if (peer_session_id) tunnel->assigned_sessions[peer_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 = l2tp_hard_mtu; link->m_hard_mru = l2tp_hard_mru; link->m_channel = &ch; sprintf(link->ifOptions.phone, "%s:%04x:%04x", sau_to_str(&tunnel->peer->remote_sau), session_id, peer_session_id); 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, int wait_for_hangup_in, char *called_number) { wait_for_hangup = wait_for_hangup_in; init_session(tun, 0, 0, cfd, site, called_number); 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(l2tp_tunnel_t *tun, CLink *in_link); l2tp_session_t(l2tp_tunnel_t *tun, dfs_object_t *obj); virtual void multihop_unlink(void) { CLink *other = multihop_clink; if (other) { if (other->multihop_link != this) fprintf(stderr, "~l2tp_session_t: BUG - multihop clink\n"); other->multihop_link = NULL; } multihop_clink = NULL; multihop_link = NULL; Hangup(); } ~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"); if (this != tunnel->assigned_sessions[peer_session_id]) fprintf(stderr, "BUG: assigned_sessions[%u] delete: %p vs %p\n", peer_session_id, this, tunnel->assigned_sessions[peer_session_id]); 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 (session_called_number) { free(session_called_number); session_called_number = NULL; } /* FIXME: use disconnect_session() */ 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; other->multihop_link = NULL; other->multihop_clink = NULL; multihop_link = NULL; multihop_clink = NULL; multihop_session = NULL; other->Hangup(); } if (multihop_clink) { CLink *other = multihop_clink; multihop_unlink(); other->Hangup(); } if (session_state != L2TP_Session_Idle) send_CDN(1); if (m_monitor) m_monitor->remove_session(this); if (session_state == L2TP_Session_Established) { fprintf(stderr, "warning: session still established!\n"); num_est_sessions--; } if (ch.device_name) { free((void *)ch.device_name); ch.device_name = NULL; } if (session_id) { if (0 != ch_ioctl(BIOCHANGUP, 0)) perror("~l2tp_session_t: ch_ioctl(BIOCHANGUP)"); } if (proxy_auth_name) delete proxy_auth_name; proxy_auth_name = NULL; if (proxy_auth_challenge) delete proxy_auth_challenge; proxy_auth_challenge = NULL; if (proxy_auth_response) delete proxy_auth_response; proxy_auth_response = NULL; proxy_auth_type = -1; proxy_auth_id = -1; if (this == tunnel->sessions[session_id]) tunnel->sessions[session_id] = NULL; if (this == tunnel->assigned_sessions[peer_session_id]) tunnel->assigned_sessions[peer_session_id] = NULL; tunnel->nr_free_session_ids++; tunnel = NULL; } virtual unsigned DefaultMRU(void) { return 1492; } int is_established(void) { return L2TP_Session_Established == session_state; } void clean_up(const char *reason = NULL); int HardOutput(CPppPacket *pkt, int pri = 0); 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(const char *reason = NULL, int may_send_cdn = 1); void kernel_multihop_up(CLink *multihop); virtual const char *get_multihop_name(void); virtual const char *get_user(void); virtual const char *get_class(void); void send_CDN(u16 result); virtual void GotConfigTunnelGroup(OptionSet_t *os); void monitor_control_pkt(l2tp_packet_t *pkt, const char *msg) { if (m_monitor) m_monitor->monitor_l2tp_control_pkt(pkt, msg); } void update_hw_peer(struct l2tp_hardwire_info *info); }; extern const struct AVPInfo { signed char min_len; /* bytes or -1 if no min, 0 = unknown */ unsigned char name[31]; unsigned log_text:1, log_hex:1; } RecognizedAVPs[MaxAVPs]; extern l2tp_packet_t pkt; #endif