From b2f6dec909e6dcb742c7c04476a8185a3c133483 Mon Sep 17 00:00:00 2001 From: KujouRinka Date: Fri, 29 Mar 2024 20:29:30 +0800 Subject: [PATCH 1/6] feat: add openVPN analyzer --- analyzer/internal/openvpn.go | 44 +++++++ analyzer/tcp/openvpn.go | 234 +++++++++++++++++++++++++++++++++++ analyzer/udp/openvpn.go | 205 ++++++++++++++++++++++++++++++ cmd/root.go | 2 + 4 files changed, 485 insertions(+) create mode 100644 analyzer/internal/openvpn.go create mode 100644 analyzer/tcp/openvpn.go create mode 100644 analyzer/udp/openvpn.go diff --git a/analyzer/internal/openvpn.go b/analyzer/internal/openvpn.go new file mode 100644 index 0000000..ab1a473 --- /dev/null +++ b/analyzer/internal/openvpn.go @@ -0,0 +1,44 @@ +package internal + +// Ref paper: +// https://www.usenix.org/system/files/sec22fall_xue-diwen.pdf + +// OpenVPN Opcodes definitions from: +// https://github.com/OpenVPN/openvpn/blob/master/src/openvpn/ssl_pkt.h +const ( + OpenVpnControlHardResetClientV1 = 1 + OpenVpnControlHardResetServerV1 = 2 + OpenVpnControlSoftResetV1 = 3 + OpenVpnControlV1 = 4 + OpenVpnAckV1 = 5 + OpenVpnDataV1 = 6 + OpenVpnControlHardResetClientV2 = 7 + OpenVpnControlHardResetServerV2 = 8 + OpenVpnDataV2 = 9 + OpenVpnControlHardResetClientV3 = 10 + OpenVpnControlWkcV1 = 11 +) + +const ( + OpenVpnMinPktLen = 6 + OpenVpnTcpPktDefaultLimit = 256 + OpenVpnUdpPktDefaultLimit = 256 +) + +func OpenVpnCheckForValidOpcode(opcode byte) bool { + switch opcode { + case OpenVpnControlHardResetClientV1, + OpenVpnControlHardResetServerV1, + OpenVpnControlSoftResetV1, + OpenVpnControlV1, + OpenVpnAckV1, + OpenVpnDataV1, + OpenVpnControlHardResetClientV2, + OpenVpnControlHardResetServerV2, + OpenVpnDataV2, + OpenVpnControlHardResetClientV3, + OpenVpnControlWkcV1: + return true + } + return false +} diff --git a/analyzer/tcp/openvpn.go b/analyzer/tcp/openvpn.go new file mode 100644 index 0000000..ad9d70f --- /dev/null +++ b/analyzer/tcp/openvpn.go @@ -0,0 +1,234 @@ +package tcp + +import ( + "github.com/apernet/OpenGFW/analyzer" + "github.com/apernet/OpenGFW/analyzer/internal" + "github.com/apernet/OpenGFW/analyzer/utils" +) + +var _ analyzer.TCPAnalyzer = (*OpenVpnAnalyzer)(nil) +var _ analyzer.TCPStream = (*openVpnStream)(nil) + +type OpenVpnAnalyzer struct{} + +func (a *OpenVpnAnalyzer) Name() string { + return "openvpn_tcp" +} + +func (a *OpenVpnAnalyzer) Limit() int { + return 0 +} + +func (a *OpenVpnAnalyzer) NewTCP(info analyzer.TCPInfo, logger analyzer.Logger) analyzer.TCPStream { + return newOpenVpnTCPStream(logger) +} + +type openVpnStream struct { + logger analyzer.Logger + + reqBuf *utils.ByteBuffer + reqUpdated bool + reqLSM *utils.LinearStateMachine + reqDone bool + + respBuf *utils.ByteBuffer + respUpdated bool + respLSM *utils.LinearStateMachine + respDone bool + + rxPktCnt int + txPktCnt int + pktLimit int + + lastOpcode byte +} + +type openVpnTcpPkt struct { + pktLen uint16 + opcode byte // 5 bits + _keyId byte // 3 bits, not used + + // We don't care about the rest of the packet + // payload []byte +} + +func newOpenVpnTCPStream(logger analyzer.Logger) *openVpnStream { + s := &openVpnStream{ + logger: logger, + reqBuf: &utils.ByteBuffer{}, + respBuf: &utils.ByteBuffer{}, + pktLimit: internal.OpenVpnTcpPktDefaultLimit, + } + s.reqLSM = utils.NewLinearStateMachine( + s.parseCtlHardResetClient, + s.parseReq, + ) + s.respLSM = utils.NewLinearStateMachine( + s.parseCtlHardResetServer, + s.parseResp, + ) + return s +} + +func (o *openVpnStream) Feed(rev, start, end bool, skip int, data []byte) (u *analyzer.PropUpdate, d bool) { + if skip != 0 { + return nil, true + } + if len(data) == 0 { + return nil, false + } + var update *analyzer.PropUpdate + var cancelled bool + if rev { + o.respBuf.Append(data) + o.respUpdated = false + cancelled, o.respDone = o.respLSM.Run() + if o.respUpdated { + update = &analyzer.PropUpdate{ + Type: analyzer.PropUpdateMerge, + M: analyzer.PropMap{"rx_pkt_cnt": o.rxPktCnt}, + } + o.respUpdated = false + } + } else { + o.reqBuf.Append(data) + o.reqUpdated = false + cancelled, o.reqDone = o.reqLSM.Run() + if o.reqUpdated { + update = &analyzer.PropUpdate{ + Type: analyzer.PropUpdateMerge, + M: analyzer.PropMap{"tx_pkt_cnt": o.txPktCnt}, + } + o.reqUpdated = false + } + } + + return update, cancelled || (o.reqDone && o.respDone) || o.rxPktCnt+o.txPktCnt > o.pktLimit +} + +func (o *openVpnStream) Close(limited bool) *analyzer.PropUpdate { + o.reqBuf.Reset() + o.respBuf.Reset() + return nil +} + +func (o *openVpnStream) parseCtlHardResetClient() utils.LSMAction { + pkt, action := o.parsePkt(false) + if action != utils.LSMActionNext { + return action + } + + if pkt.opcode != internal.OpenVpnControlHardResetClientV1 && + pkt.opcode != internal.OpenVpnControlHardResetClientV2 && + pkt.opcode != internal.OpenVpnControlHardResetClientV3 { + return utils.LSMActionCancel + } + o.lastOpcode = pkt.opcode + + return utils.LSMActionNext +} + +func (o *openVpnStream) parseCtlHardResetServer() utils.LSMAction { + if o.lastOpcode != internal.OpenVpnControlHardResetClientV1 && + o.lastOpcode != internal.OpenVpnControlHardResetClientV2 && + o.lastOpcode != internal.OpenVpnControlHardResetClientV3 { + return utils.LSMActionCancel + } + + pkt, action := o.parsePkt(true) + if action != utils.LSMActionNext { + return action + } + + if pkt.opcode != internal.OpenVpnControlHardResetServerV1 && + pkt.opcode != internal.OpenVpnControlHardResetServerV2 { + return utils.LSMActionCancel + } + o.lastOpcode = pkt.opcode + + return utils.LSMActionNext +} + +func (o *openVpnStream) parseReq() utils.LSMAction { + pkt, action := o.parsePkt(false) + if action != utils.LSMActionNext { + return action + } + + if pkt.opcode != internal.OpenVpnControlSoftResetV1 && + pkt.opcode != internal.OpenVpnControlV1 && + pkt.opcode != internal.OpenVpnAckV1 && + pkt.opcode != internal.OpenVpnDataV1 && + pkt.opcode != internal.OpenVpnDataV2 && + pkt.opcode != internal.OpenVpnControlWkcV1 { + return utils.LSMActionCancel + } + + o.txPktCnt += 1 + o.reqUpdated = true + + return utils.LSMActionPause +} + +func (o *openVpnStream) parseResp() utils.LSMAction { + pkt, action := o.parsePkt(true) + if action != utils.LSMActionNext { + return action + } + + if pkt.opcode != internal.OpenVpnControlSoftResetV1 && + pkt.opcode != internal.OpenVpnControlV1 && + pkt.opcode != internal.OpenVpnAckV1 && + pkt.opcode != internal.OpenVpnDataV1 && + pkt.opcode != internal.OpenVpnDataV2 && + pkt.opcode != internal.OpenVpnControlWkcV1 { + return utils.LSMActionCancel + } + + o.rxPktCnt += 1 + o.respUpdated = true + + return utils.LSMActionPause +} + +// Parse OpenVpn packet header but not consume buffer. +func (o *openVpnStream) parsePkt(rev bool) (p *openVpnTcpPkt, action utils.LSMAction) { + var buffer *utils.ByteBuffer + if rev { + buffer = o.respBuf + } else { + buffer = o.reqBuf + } + + // Parse packet length + pktLen, ok := buffer.GetUint16(false, false) + if !ok { + return nil, utils.LSMActionPause + } + + if pktLen < internal.OpenVpnMinPktLen { + return nil, utils.LSMActionCancel + } + + pktOp, ok := buffer.Get(3, false) + if !ok { + return nil, utils.LSMActionPause + } + if !internal.OpenVpnCheckForValidOpcode(pktOp[2] >> 3) { + return nil, utils.LSMActionCancel + } + + pkt, ok := buffer.Get(int(pktLen)+2, true) + if !ok { + return nil, utils.LSMActionPause + } + pkt = pkt[2:] + + // Parse packet header + p = &openVpnTcpPkt{} + p.pktLen = pktLen + p.opcode = pkt[0] >> 3 + p._keyId = pkt[0] & 0x07 + + return p, utils.LSMActionNext +} diff --git a/analyzer/udp/openvpn.go b/analyzer/udp/openvpn.go new file mode 100644 index 0000000..0c77a0a --- /dev/null +++ b/analyzer/udp/openvpn.go @@ -0,0 +1,205 @@ +package udp + +import ( + "github.com/apernet/OpenGFW/analyzer" + "github.com/apernet/OpenGFW/analyzer/internal" + "github.com/apernet/OpenGFW/analyzer/utils" +) + +var _ analyzer.UDPAnalyzer = (*OpenVpnAnalyzer)(nil) +var _ analyzer.UDPStream = (*openVpnStream)(nil) + +type OpenVpnAnalyzer struct{} + +func (a *OpenVpnAnalyzer) Name() string { + return "openvpn_udp" +} + +func (a *OpenVpnAnalyzer) Limit() int { + return 0 +} + +func (a *OpenVpnAnalyzer) NewUDP(info analyzer.UDPInfo, logger analyzer.Logger) analyzer.UDPStream { + return newOpenVPNTCPStream(logger) +} + +type openVpnStream struct { + logger analyzer.Logger + // We don't introduce `invalidCount` here to decrease the false positive rate + // invalidCount int + + curPkt []byte + + reqUpdated bool + reqLSM *utils.LinearStateMachine + reqDone bool + + respUpdated bool + respLSM *utils.LinearStateMachine + respDone bool + + rxPktCnt int + txPktCnt int + pktLimit int + + lastOpcode byte +} + +type openVpnUdpPkt struct { + opcode byte // 5 bits + _keyId byte // 3 bits, not used + + // We don't care about the rest of the packet + // payload []byte +} + +func newOpenVPNTCPStream(logger analyzer.Logger) *openVpnStream { + s := &openVpnStream{ + logger: logger, + pktLimit: internal.OpenVpnUdpPktDefaultLimit, + } + s.reqLSM = utils.NewLinearStateMachine( + s.parseCtlHardResetClient, + s.parseReq, + ) + s.respLSM = utils.NewLinearStateMachine( + s.parseCtlHardResetServer, + s.parseResp, + ) + return s +} + +func (o *openVpnStream) Feed(rev bool, data []byte) (u *analyzer.PropUpdate, d bool) { + if len(data) == 0 { + return nil, false + } + var update *analyzer.PropUpdate + var cancelled bool + o.curPkt = data + if rev { + o.respUpdated = false + cancelled, o.respDone = o.respLSM.Run() + if o.respUpdated { + update = &analyzer.PropUpdate{ + Type: analyzer.PropUpdateMerge, + M: analyzer.PropMap{"rx_pkt_cnt": o.rxPktCnt}, + } + o.respUpdated = false + } + } else { + o.reqUpdated = false + cancelled, o.reqDone = o.reqLSM.Run() + if o.reqUpdated { + update = &analyzer.PropUpdate{ + Type: analyzer.PropUpdateMerge, + M: analyzer.PropMap{"tx_pkt_cnt": o.txPktCnt}, + } + o.reqUpdated = false + } + } + + return update, cancelled || (o.reqDone && o.respDone) || o.rxPktCnt+o.txPktCnt > o.pktLimit +} + +func (o *openVpnStream) Close(limited bool) *analyzer.PropUpdate { + return nil +} + +func (o *openVpnStream) parseCtlHardResetClient() utils.LSMAction { + pkt, action := o.parsePkt() + if action != utils.LSMActionNext { + return action + } + + if pkt.opcode != internal.OpenVpnControlHardResetClientV1 && + pkt.opcode != internal.OpenVpnControlHardResetClientV2 && + pkt.opcode != internal.OpenVpnControlHardResetClientV3 { + return utils.LSMActionCancel + } + o.lastOpcode = pkt.opcode + + return utils.LSMActionNext +} + +func (o *openVpnStream) parseCtlHardResetServer() utils.LSMAction { + + if o.lastOpcode != internal.OpenVpnControlHardResetClientV1 && + o.lastOpcode != internal.OpenVpnControlHardResetClientV2 && + o.lastOpcode != internal.OpenVpnControlHardResetClientV3 { + return utils.LSMActionCancel + } + + pkt, action := o.parsePkt() + if action != utils.LSMActionNext { + return action + } + + if pkt.opcode != internal.OpenVpnControlHardResetServerV1 && + pkt.opcode != internal.OpenVpnControlHardResetServerV2 { + return utils.LSMActionCancel + } + o.lastOpcode = pkt.opcode + + return utils.LSMActionNext +} + +func (o *openVpnStream) parseReq() utils.LSMAction { + pkt, action := o.parsePkt() + if action != utils.LSMActionNext { + return action + } + + if pkt.opcode != internal.OpenVpnControlSoftResetV1 && + pkt.opcode != internal.OpenVpnControlV1 && + pkt.opcode != internal.OpenVpnAckV1 && + pkt.opcode != internal.OpenVpnDataV1 && + pkt.opcode != internal.OpenVpnDataV2 && + pkt.opcode != internal.OpenVpnControlWkcV1 { + return utils.LSMActionCancel + } + + o.txPktCnt += 1 + o.reqUpdated = true + + return utils.LSMActionPause +} + +func (o *openVpnStream) parseResp() utils.LSMAction { + pkt, action := o.parsePkt() + if action != utils.LSMActionNext { + return action + } + + if pkt.opcode != internal.OpenVpnControlSoftResetV1 && + pkt.opcode != internal.OpenVpnControlV1 && + pkt.opcode != internal.OpenVpnAckV1 && + pkt.opcode != internal.OpenVpnDataV1 && + pkt.opcode != internal.OpenVpnDataV2 && + pkt.opcode != internal.OpenVpnControlWkcV1 { + return utils.LSMActionCancel + } + + o.rxPktCnt += 1 + o.respUpdated = true + + return utils.LSMActionPause +} + +// Parse OpenVpn packet header but not consume buffer. +func (o *openVpnStream) parsePkt() (p *openVpnUdpPkt, action utils.LSMAction) { + if o.curPkt == nil { + return nil, utils.LSMActionPause + } + + if !internal.OpenVpnCheckForValidOpcode(o.curPkt[0] >> 3) { + return nil, utils.LSMActionCancel + } + + // Parse packet header + p = &openVpnUdpPkt{} + p.opcode = o.curPkt[0] >> 3 + p._keyId = o.curPkt[0] & 0x07 + + o.curPkt = nil + return p, utils.LSMActionNext +} diff --git a/cmd/root.go b/cmd/root.go index 7e54462..dfb5f4c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -87,11 +87,13 @@ var logFormatMap = map[string]zapcore.EncoderConfig{ var analyzers = []analyzer.Analyzer{ &tcp.FETAnalyzer{}, &tcp.HTTPAnalyzer{}, + &tcp.OpenVpnAnalyzer{}, &tcp.SocksAnalyzer{}, &tcp.SSHAnalyzer{}, &tcp.TLSAnalyzer{}, &tcp.TrojanAnalyzer{}, &udp.DNSAnalyzer{}, + &udp.OpenVpnAnalyzer{}, &udp.QUICAnalyzer{}, &udp.WireGuardAnalyzer{}, } From 2232b553b3bafb7492e584a4e4b3ca94716fce4c Mon Sep 17 00:00:00 2001 From: KujouRinka Date: Fri, 29 Mar 2024 21:37:09 +0800 Subject: [PATCH 2/6] chore: fix typo --- analyzer/tcp/openvpn.go | 4 ++-- analyzer/udp/openvpn.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/analyzer/tcp/openvpn.go b/analyzer/tcp/openvpn.go index ad9d70f..ff28ef3 100644 --- a/analyzer/tcp/openvpn.go +++ b/analyzer/tcp/openvpn.go @@ -20,7 +20,7 @@ func (a *OpenVpnAnalyzer) Limit() int { } func (a *OpenVpnAnalyzer) NewTCP(info analyzer.TCPInfo, logger analyzer.Logger) analyzer.TCPStream { - return newOpenVpnTCPStream(logger) + return newOpenVpnTcpStream(logger) } type openVpnStream struct { @@ -52,7 +52,7 @@ type openVpnTcpPkt struct { // payload []byte } -func newOpenVpnTCPStream(logger analyzer.Logger) *openVpnStream { +func newOpenVpnTcpStream(logger analyzer.Logger) *openVpnStream { s := &openVpnStream{ logger: logger, reqBuf: &utils.ByteBuffer{}, diff --git a/analyzer/udp/openvpn.go b/analyzer/udp/openvpn.go index 0c77a0a..ae0cb90 100644 --- a/analyzer/udp/openvpn.go +++ b/analyzer/udp/openvpn.go @@ -20,7 +20,7 @@ func (a *OpenVpnAnalyzer) Limit() int { } func (a *OpenVpnAnalyzer) NewUDP(info analyzer.UDPInfo, logger analyzer.Logger) analyzer.UDPStream { - return newOpenVPNTCPStream(logger) + return newOpenVPNUdpStream(logger) } type openVpnStream struct { @@ -53,7 +53,7 @@ type openVpnUdpPkt struct { // payload []byte } -func newOpenVPNTCPStream(logger analyzer.Logger) *openVpnStream { +func newOpenVPNUdpStream(logger analyzer.Logger) *openVpnStream { s := &openVpnStream{ logger: logger, pktLimit: internal.OpenVpnUdpPktDefaultLimit, From 0e2ee368654dfe7e70157dacea0b37e39f94b38f Mon Sep 17 00:00:00 2001 From: KujouRinka Date: Sat, 30 Mar 2024 12:00:17 +0800 Subject: [PATCH 3/6] refactor: merge openvpn_tcp and openvpn_udp to openvpn --- analyzer/internal/openvpn.go | 44 ---- analyzer/tcp/openvpn.go | 234 --------------------- analyzer/udp/openvpn.go | 381 +++++++++++++++++++++++++---------- cmd/root.go | 1 - 4 files changed, 278 insertions(+), 382 deletions(-) delete mode 100644 analyzer/internal/openvpn.go delete mode 100644 analyzer/tcp/openvpn.go diff --git a/analyzer/internal/openvpn.go b/analyzer/internal/openvpn.go deleted file mode 100644 index ab1a473..0000000 --- a/analyzer/internal/openvpn.go +++ /dev/null @@ -1,44 +0,0 @@ -package internal - -// Ref paper: -// https://www.usenix.org/system/files/sec22fall_xue-diwen.pdf - -// OpenVPN Opcodes definitions from: -// https://github.com/OpenVPN/openvpn/blob/master/src/openvpn/ssl_pkt.h -const ( - OpenVpnControlHardResetClientV1 = 1 - OpenVpnControlHardResetServerV1 = 2 - OpenVpnControlSoftResetV1 = 3 - OpenVpnControlV1 = 4 - OpenVpnAckV1 = 5 - OpenVpnDataV1 = 6 - OpenVpnControlHardResetClientV2 = 7 - OpenVpnControlHardResetServerV2 = 8 - OpenVpnDataV2 = 9 - OpenVpnControlHardResetClientV3 = 10 - OpenVpnControlWkcV1 = 11 -) - -const ( - OpenVpnMinPktLen = 6 - OpenVpnTcpPktDefaultLimit = 256 - OpenVpnUdpPktDefaultLimit = 256 -) - -func OpenVpnCheckForValidOpcode(opcode byte) bool { - switch opcode { - case OpenVpnControlHardResetClientV1, - OpenVpnControlHardResetServerV1, - OpenVpnControlSoftResetV1, - OpenVpnControlV1, - OpenVpnAckV1, - OpenVpnDataV1, - OpenVpnControlHardResetClientV2, - OpenVpnControlHardResetServerV2, - OpenVpnDataV2, - OpenVpnControlHardResetClientV3, - OpenVpnControlWkcV1: - return true - } - return false -} diff --git a/analyzer/tcp/openvpn.go b/analyzer/tcp/openvpn.go deleted file mode 100644 index ff28ef3..0000000 --- a/analyzer/tcp/openvpn.go +++ /dev/null @@ -1,234 +0,0 @@ -package tcp - -import ( - "github.com/apernet/OpenGFW/analyzer" - "github.com/apernet/OpenGFW/analyzer/internal" - "github.com/apernet/OpenGFW/analyzer/utils" -) - -var _ analyzer.TCPAnalyzer = (*OpenVpnAnalyzer)(nil) -var _ analyzer.TCPStream = (*openVpnStream)(nil) - -type OpenVpnAnalyzer struct{} - -func (a *OpenVpnAnalyzer) Name() string { - return "openvpn_tcp" -} - -func (a *OpenVpnAnalyzer) Limit() int { - return 0 -} - -func (a *OpenVpnAnalyzer) NewTCP(info analyzer.TCPInfo, logger analyzer.Logger) analyzer.TCPStream { - return newOpenVpnTcpStream(logger) -} - -type openVpnStream struct { - logger analyzer.Logger - - reqBuf *utils.ByteBuffer - reqUpdated bool - reqLSM *utils.LinearStateMachine - reqDone bool - - respBuf *utils.ByteBuffer - respUpdated bool - respLSM *utils.LinearStateMachine - respDone bool - - rxPktCnt int - txPktCnt int - pktLimit int - - lastOpcode byte -} - -type openVpnTcpPkt struct { - pktLen uint16 - opcode byte // 5 bits - _keyId byte // 3 bits, not used - - // We don't care about the rest of the packet - // payload []byte -} - -func newOpenVpnTcpStream(logger analyzer.Logger) *openVpnStream { - s := &openVpnStream{ - logger: logger, - reqBuf: &utils.ByteBuffer{}, - respBuf: &utils.ByteBuffer{}, - pktLimit: internal.OpenVpnTcpPktDefaultLimit, - } - s.reqLSM = utils.NewLinearStateMachine( - s.parseCtlHardResetClient, - s.parseReq, - ) - s.respLSM = utils.NewLinearStateMachine( - s.parseCtlHardResetServer, - s.parseResp, - ) - return s -} - -func (o *openVpnStream) Feed(rev, start, end bool, skip int, data []byte) (u *analyzer.PropUpdate, d bool) { - if skip != 0 { - return nil, true - } - if len(data) == 0 { - return nil, false - } - var update *analyzer.PropUpdate - var cancelled bool - if rev { - o.respBuf.Append(data) - o.respUpdated = false - cancelled, o.respDone = o.respLSM.Run() - if o.respUpdated { - update = &analyzer.PropUpdate{ - Type: analyzer.PropUpdateMerge, - M: analyzer.PropMap{"rx_pkt_cnt": o.rxPktCnt}, - } - o.respUpdated = false - } - } else { - o.reqBuf.Append(data) - o.reqUpdated = false - cancelled, o.reqDone = o.reqLSM.Run() - if o.reqUpdated { - update = &analyzer.PropUpdate{ - Type: analyzer.PropUpdateMerge, - M: analyzer.PropMap{"tx_pkt_cnt": o.txPktCnt}, - } - o.reqUpdated = false - } - } - - return update, cancelled || (o.reqDone && o.respDone) || o.rxPktCnt+o.txPktCnt > o.pktLimit -} - -func (o *openVpnStream) Close(limited bool) *analyzer.PropUpdate { - o.reqBuf.Reset() - o.respBuf.Reset() - return nil -} - -func (o *openVpnStream) parseCtlHardResetClient() utils.LSMAction { - pkt, action := o.parsePkt(false) - if action != utils.LSMActionNext { - return action - } - - if pkt.opcode != internal.OpenVpnControlHardResetClientV1 && - pkt.opcode != internal.OpenVpnControlHardResetClientV2 && - pkt.opcode != internal.OpenVpnControlHardResetClientV3 { - return utils.LSMActionCancel - } - o.lastOpcode = pkt.opcode - - return utils.LSMActionNext -} - -func (o *openVpnStream) parseCtlHardResetServer() utils.LSMAction { - if o.lastOpcode != internal.OpenVpnControlHardResetClientV1 && - o.lastOpcode != internal.OpenVpnControlHardResetClientV2 && - o.lastOpcode != internal.OpenVpnControlHardResetClientV3 { - return utils.LSMActionCancel - } - - pkt, action := o.parsePkt(true) - if action != utils.LSMActionNext { - return action - } - - if pkt.opcode != internal.OpenVpnControlHardResetServerV1 && - pkt.opcode != internal.OpenVpnControlHardResetServerV2 { - return utils.LSMActionCancel - } - o.lastOpcode = pkt.opcode - - return utils.LSMActionNext -} - -func (o *openVpnStream) parseReq() utils.LSMAction { - pkt, action := o.parsePkt(false) - if action != utils.LSMActionNext { - return action - } - - if pkt.opcode != internal.OpenVpnControlSoftResetV1 && - pkt.opcode != internal.OpenVpnControlV1 && - pkt.opcode != internal.OpenVpnAckV1 && - pkt.opcode != internal.OpenVpnDataV1 && - pkt.opcode != internal.OpenVpnDataV2 && - pkt.opcode != internal.OpenVpnControlWkcV1 { - return utils.LSMActionCancel - } - - o.txPktCnt += 1 - o.reqUpdated = true - - return utils.LSMActionPause -} - -func (o *openVpnStream) parseResp() utils.LSMAction { - pkt, action := o.parsePkt(true) - if action != utils.LSMActionNext { - return action - } - - if pkt.opcode != internal.OpenVpnControlSoftResetV1 && - pkt.opcode != internal.OpenVpnControlV1 && - pkt.opcode != internal.OpenVpnAckV1 && - pkt.opcode != internal.OpenVpnDataV1 && - pkt.opcode != internal.OpenVpnDataV2 && - pkt.opcode != internal.OpenVpnControlWkcV1 { - return utils.LSMActionCancel - } - - o.rxPktCnt += 1 - o.respUpdated = true - - return utils.LSMActionPause -} - -// Parse OpenVpn packet header but not consume buffer. -func (o *openVpnStream) parsePkt(rev bool) (p *openVpnTcpPkt, action utils.LSMAction) { - var buffer *utils.ByteBuffer - if rev { - buffer = o.respBuf - } else { - buffer = o.reqBuf - } - - // Parse packet length - pktLen, ok := buffer.GetUint16(false, false) - if !ok { - return nil, utils.LSMActionPause - } - - if pktLen < internal.OpenVpnMinPktLen { - return nil, utils.LSMActionCancel - } - - pktOp, ok := buffer.Get(3, false) - if !ok { - return nil, utils.LSMActionPause - } - if !internal.OpenVpnCheckForValidOpcode(pktOp[2] >> 3) { - return nil, utils.LSMActionCancel - } - - pkt, ok := buffer.Get(int(pktLen)+2, true) - if !ok { - return nil, utils.LSMActionPause - } - pkt = pkt[2:] - - // Parse packet header - p = &openVpnTcpPkt{} - p.pktLen = pktLen - p.opcode = pkt[0] >> 3 - p._keyId = pkt[0] & 0x07 - - return p, utils.LSMActionNext -} diff --git a/analyzer/udp/openvpn.go b/analyzer/udp/openvpn.go index ae0cb90..1d64ed3 100644 --- a/analyzer/udp/openvpn.go +++ b/analyzer/udp/openvpn.go @@ -2,17 +2,44 @@ package udp import ( "github.com/apernet/OpenGFW/analyzer" - "github.com/apernet/OpenGFW/analyzer/internal" "github.com/apernet/OpenGFW/analyzer/utils" ) var _ analyzer.UDPAnalyzer = (*OpenVpnAnalyzer)(nil) -var _ analyzer.UDPStream = (*openVpnStream)(nil) +var _ analyzer.TCPAnalyzer = (*OpenVpnAnalyzer)(nil) + +var _ analyzer.UDPStream = (*openVpnUdpStream)(nil) +var _ analyzer.TCPStream = (*openVpnTcpStream)(nil) + +// Ref paper: +// https://www.usenix.org/system/files/sec22fall_xue-diwen.pdf + +// OpenVPN Opcodes definitions from: +// https://github.com/OpenVPN/openvpn/blob/master/src/openvpn/ssl_pkt.h +const ( + OpenVpnControlHardResetClientV1 = 1 + OpenVpnControlHardResetServerV1 = 2 + OpenVpnControlSoftResetV1 = 3 + OpenVpnControlV1 = 4 + OpenVpnAckV1 = 5 + OpenVpnDataV1 = 6 + OpenVpnControlHardResetClientV2 = 7 + OpenVpnControlHardResetServerV2 = 8 + OpenVpnDataV2 = 9 + OpenVpnControlHardResetClientV3 = 10 + OpenVpnControlWkcV1 = 11 +) + +const ( + OpenVpnMinPktLen = 6 + OpenVpnTcpPktDefaultLimit = 256 + OpenVpnUdpPktDefaultLimit = 256 +) type OpenVpnAnalyzer struct{} func (a *OpenVpnAnalyzer) Name() string { - return "openvpn_udp" + return "openvpn" } func (a *OpenVpnAnalyzer) Limit() int { @@ -20,15 +47,24 @@ func (a *OpenVpnAnalyzer) Limit() int { } func (a *OpenVpnAnalyzer) NewUDP(info analyzer.UDPInfo, logger analyzer.Logger) analyzer.UDPStream { - return newOpenVPNUdpStream(logger) + return newOpenVpnUdpStream(logger) +} + +func (a *OpenVpnAnalyzer) NewTCP(info analyzer.TCPInfo, logger analyzer.Logger) analyzer.TCPStream { + return newOpenVpnTcpStream(logger) +} + +type openVpnPkt struct { + pktLen uint16 // 16 bits, TCP proto only + opcode byte // 5 bits + _keyId byte // 3 bits, not used + + // We don't care about the rest of the packet + // payload []byte } type openVpnStream struct { logger analyzer.Logger - // We don't introduce `invalidCount` here to decrease the false positive rate - // invalidCount int - - curPkt []byte reqUpdated bool reqLSM *utils.LinearStateMachine @@ -42,22 +78,107 @@ type openVpnStream struct { txPktCnt int pktLimit int + reqPktParse func() (*openVpnPkt, utils.LSMAction) + respPktParse func() (*openVpnPkt, utils.LSMAction) + lastOpcode byte } -type openVpnUdpPkt struct { - opcode byte // 5 bits - _keyId byte // 3 bits, not used +func (o *openVpnStream) parseCtlHardResetClient() utils.LSMAction { + pkt, action := o.reqPktParse() + if action != utils.LSMActionNext { + return action + } - // We don't care about the rest of the packet - // payload []byte + if pkt.opcode != OpenVpnControlHardResetClientV1 && + pkt.opcode != OpenVpnControlHardResetClientV2 && + pkt.opcode != OpenVpnControlHardResetClientV3 { + return utils.LSMActionCancel + } + o.lastOpcode = pkt.opcode + + return utils.LSMActionNext } -func newOpenVPNUdpStream(logger analyzer.Logger) *openVpnStream { - s := &openVpnStream{ - logger: logger, - pktLimit: internal.OpenVpnUdpPktDefaultLimit, +func (o *openVpnStream) parseCtlHardResetServer() utils.LSMAction { + if o.lastOpcode != OpenVpnControlHardResetClientV1 && + o.lastOpcode != OpenVpnControlHardResetClientV2 && + o.lastOpcode != OpenVpnControlHardResetClientV3 { + return utils.LSMActionCancel } + + pkt, action := o.respPktParse() + if action != utils.LSMActionNext { + return action + } + + if pkt.opcode != OpenVpnControlHardResetServerV1 && + pkt.opcode != OpenVpnControlHardResetServerV2 { + return utils.LSMActionCancel + } + o.lastOpcode = pkt.opcode + + return utils.LSMActionNext +} + +func (o *openVpnStream) parseReq() utils.LSMAction { + pkt, action := o.reqPktParse() + if action != utils.LSMActionNext { + return action + } + + if pkt.opcode != OpenVpnControlSoftResetV1 && + pkt.opcode != OpenVpnControlV1 && + pkt.opcode != OpenVpnAckV1 && + pkt.opcode != OpenVpnDataV1 && + pkt.opcode != OpenVpnDataV2 && + pkt.opcode != OpenVpnControlWkcV1 { + return utils.LSMActionCancel + } + + o.txPktCnt += 1 + o.reqUpdated = true + + return utils.LSMActionPause +} + +func (o *openVpnStream) parseResp() utils.LSMAction { + pkt, action := o.respPktParse() + if action != utils.LSMActionNext { + return action + } + + if pkt.opcode != OpenVpnControlSoftResetV1 && + pkt.opcode != OpenVpnControlV1 && + pkt.opcode != OpenVpnAckV1 && + pkt.opcode != OpenVpnDataV1 && + pkt.opcode != OpenVpnDataV2 && + pkt.opcode != OpenVpnControlWkcV1 { + return utils.LSMActionCancel + } + + o.rxPktCnt += 1 + o.respUpdated = true + + return utils.LSMActionPause +} + +type openVpnUdpStream struct { + openVpnStream + curPkt []byte + // We don't introduce `invalidCount` here to decrease the false positive rate + // invalidCount int +} + +func newOpenVpnUdpStream(logger analyzer.Logger) *openVpnUdpStream { + s := &openVpnUdpStream{ + openVpnStream: openVpnStream{ + logger: logger, + pktLimit: OpenVpnUdpPktDefaultLimit, + }, + } + s.respPktParse = s.parsePkt + s.reqPktParse = s.parsePkt s.reqLSM = utils.NewLinearStateMachine( s.parseCtlHardResetClient, s.parseReq, @@ -69,7 +190,7 @@ func newOpenVPNUdpStream(logger analyzer.Logger) *openVpnStream { return s } -func (o *openVpnStream) Feed(rev bool, data []byte) (u *analyzer.PropUpdate, d bool) { +func (o *openVpnUdpStream) Feed(rev bool, data []byte) (u *analyzer.PropUpdate, d bool) { if len(data) == 0 { return nil, false } @@ -101,105 +222,159 @@ func (o *openVpnStream) Feed(rev bool, data []byte) (u *analyzer.PropUpdate, d b return update, cancelled || (o.reqDone && o.respDone) || o.rxPktCnt+o.txPktCnt > o.pktLimit } -func (o *openVpnStream) Close(limited bool) *analyzer.PropUpdate { +func (o *openVpnUdpStream) Close(limited bool) *analyzer.PropUpdate { return nil } -func (o *openVpnStream) parseCtlHardResetClient() utils.LSMAction { - pkt, action := o.parsePkt() - if action != utils.LSMActionNext { - return action - } - - if pkt.opcode != internal.OpenVpnControlHardResetClientV1 && - pkt.opcode != internal.OpenVpnControlHardResetClientV2 && - pkt.opcode != internal.OpenVpnControlHardResetClientV3 { - return utils.LSMActionCancel - } - o.lastOpcode = pkt.opcode - - return utils.LSMActionNext -} - -func (o *openVpnStream) parseCtlHardResetServer() utils.LSMAction { - - if o.lastOpcode != internal.OpenVpnControlHardResetClientV1 && - o.lastOpcode != internal.OpenVpnControlHardResetClientV2 && - o.lastOpcode != internal.OpenVpnControlHardResetClientV3 { - return utils.LSMActionCancel - } - - pkt, action := o.parsePkt() - if action != utils.LSMActionNext { - return action - } - - if pkt.opcode != internal.OpenVpnControlHardResetServerV1 && - pkt.opcode != internal.OpenVpnControlHardResetServerV2 { - return utils.LSMActionCancel - } - o.lastOpcode = pkt.opcode - - return utils.LSMActionNext -} - -func (o *openVpnStream) parseReq() utils.LSMAction { - pkt, action := o.parsePkt() - if action != utils.LSMActionNext { - return action - } - - if pkt.opcode != internal.OpenVpnControlSoftResetV1 && - pkt.opcode != internal.OpenVpnControlV1 && - pkt.opcode != internal.OpenVpnAckV1 && - pkt.opcode != internal.OpenVpnDataV1 && - pkt.opcode != internal.OpenVpnDataV2 && - pkt.opcode != internal.OpenVpnControlWkcV1 { - return utils.LSMActionCancel - } - - o.txPktCnt += 1 - o.reqUpdated = true - - return utils.LSMActionPause -} - -func (o *openVpnStream) parseResp() utils.LSMAction { - pkt, action := o.parsePkt() - if action != utils.LSMActionNext { - return action - } - - if pkt.opcode != internal.OpenVpnControlSoftResetV1 && - pkt.opcode != internal.OpenVpnControlV1 && - pkt.opcode != internal.OpenVpnAckV1 && - pkt.opcode != internal.OpenVpnDataV1 && - pkt.opcode != internal.OpenVpnDataV2 && - pkt.opcode != internal.OpenVpnControlWkcV1 { - return utils.LSMActionCancel - } - - o.rxPktCnt += 1 - o.respUpdated = true - - return utils.LSMActionPause -} - -// Parse OpenVpn packet header but not consume buffer. -func (o *openVpnStream) parsePkt() (p *openVpnUdpPkt, action utils.LSMAction) { +// Parse OpenVpn UDP packet. +func (o *openVpnUdpStream) parsePkt() (p *openVpnPkt, action utils.LSMAction) { if o.curPkt == nil { return nil, utils.LSMActionPause } - if !internal.OpenVpnCheckForValidOpcode(o.curPkt[0] >> 3) { + if !OpenVpnCheckForValidOpcode(o.curPkt[0] >> 3) { return nil, utils.LSMActionCancel } // Parse packet header - p = &openVpnUdpPkt{} + p = &openVpnPkt{} p.opcode = o.curPkt[0] >> 3 p._keyId = o.curPkt[0] & 0x07 o.curPkt = nil return p, utils.LSMActionNext } + +type openVpnTcpStream struct { + openVpnStream + reqBuf *utils.ByteBuffer + respBuf *utils.ByteBuffer +} + +func newOpenVpnTcpStream(logger analyzer.Logger) *openVpnTcpStream { + s := &openVpnTcpStream{ + openVpnStream: openVpnStream{ + logger: logger, + pktLimit: OpenVpnTcpPktDefaultLimit, + }, + reqBuf: &utils.ByteBuffer{}, + respBuf: &utils.ByteBuffer{}, + } + s.respPktParse = func() (*openVpnPkt, utils.LSMAction) { + return s.parsePkt(true) + } + s.reqPktParse = func() (*openVpnPkt, utils.LSMAction) { + return s.parsePkt(false) + } + s.reqLSM = utils.NewLinearStateMachine( + s.parseCtlHardResetClient, + s.parseReq, + ) + s.respLSM = utils.NewLinearStateMachine( + s.parseCtlHardResetServer, + s.parseResp, + ) + return s +} + +func (o *openVpnTcpStream) Feed(rev, start, end bool, skip int, data []byte) (u *analyzer.PropUpdate, d bool) { + if skip != 0 { + return nil, true + } + if len(data) == 0 { + return nil, false + } + var update *analyzer.PropUpdate + var cancelled bool + if rev { + o.respBuf.Append(data) + o.respUpdated = false + cancelled, o.respDone = o.respLSM.Run() + if o.respUpdated { + update = &analyzer.PropUpdate{ + Type: analyzer.PropUpdateMerge, + M: analyzer.PropMap{"rx_pkt_cnt": o.rxPktCnt}, + } + o.respUpdated = false + } + } else { + o.reqBuf.Append(data) + o.reqUpdated = false + cancelled, o.reqDone = o.reqLSM.Run() + if o.reqUpdated { + update = &analyzer.PropUpdate{ + Type: analyzer.PropUpdateMerge, + M: analyzer.PropMap{"tx_pkt_cnt": o.txPktCnt}, + } + o.reqUpdated = false + } + } + + return update, cancelled || (o.reqDone && o.respDone) || o.rxPktCnt+o.txPktCnt > o.pktLimit +} + +func (o *openVpnTcpStream) Close(limited bool) *analyzer.PropUpdate { + o.reqBuf.Reset() + o.respBuf.Reset() + return nil +} + +// Parse OpenVpn TCP packet. +func (o *openVpnTcpStream) parsePkt(rev bool) (p *openVpnPkt, action utils.LSMAction) { + var buffer *utils.ByteBuffer + if rev { + buffer = o.respBuf + } else { + buffer = o.reqBuf + } + + // Parse packet length + pktLen, ok := buffer.GetUint16(false, false) + if !ok { + return nil, utils.LSMActionPause + } + + if pktLen < OpenVpnMinPktLen { + return nil, utils.LSMActionCancel + } + + pktOp, ok := buffer.Get(3, false) + if !ok { + return nil, utils.LSMActionPause + } + if !OpenVpnCheckForValidOpcode(pktOp[2] >> 3) { + return nil, utils.LSMActionCancel + } + + pkt, ok := buffer.Get(int(pktLen)+2, true) + if !ok { + return nil, utils.LSMActionPause + } + pkt = pkt[2:] + + // Parse packet header + p = &openVpnPkt{} + p.pktLen = pktLen + p.opcode = pkt[0] >> 3 + p._keyId = pkt[0] & 0x07 + + return p, utils.LSMActionNext +} + +func OpenVpnCheckForValidOpcode(opcode byte) bool { + switch opcode { + case OpenVpnControlHardResetClientV1, + OpenVpnControlHardResetServerV1, + OpenVpnControlSoftResetV1, + OpenVpnControlV1, + OpenVpnAckV1, + OpenVpnDataV1, + OpenVpnControlHardResetClientV2, + OpenVpnControlHardResetServerV2, + OpenVpnDataV2, + OpenVpnControlHardResetClientV3, + OpenVpnControlWkcV1: + return true + } + return false +} diff --git a/cmd/root.go b/cmd/root.go index dfb5f4c..7ba319c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -87,7 +87,6 @@ var logFormatMap = map[string]zapcore.EncoderConfig{ var analyzers = []analyzer.Analyzer{ &tcp.FETAnalyzer{}, &tcp.HTTPAnalyzer{}, - &tcp.OpenVpnAnalyzer{}, &tcp.SocksAnalyzer{}, &tcp.SSHAnalyzer{}, &tcp.TLSAnalyzer{}, From 98264d9e275f9ab07a76f85a75335ce3dea8656b Mon Sep 17 00:00:00 2001 From: Toby Date: Sat, 30 Mar 2024 13:23:46 -0700 Subject: [PATCH 4/6] chore: format --- analyzer/udp/openvpn.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/analyzer/udp/openvpn.go b/analyzer/udp/openvpn.go index 1d64ed3..00ac34c 100644 --- a/analyzer/udp/openvpn.go +++ b/analyzer/udp/openvpn.go @@ -5,11 +5,15 @@ import ( "github.com/apernet/OpenGFW/analyzer/utils" ) -var _ analyzer.UDPAnalyzer = (*OpenVpnAnalyzer)(nil) -var _ analyzer.TCPAnalyzer = (*OpenVpnAnalyzer)(nil) +var ( + _ analyzer.UDPAnalyzer = (*OpenVpnAnalyzer)(nil) + _ analyzer.TCPAnalyzer = (*OpenVpnAnalyzer)(nil) +) -var _ analyzer.UDPStream = (*openVpnUdpStream)(nil) -var _ analyzer.TCPStream = (*openVpnTcpStream)(nil) +var ( + _ analyzer.UDPStream = (*openVpnUdpStream)(nil) + _ analyzer.TCPStream = (*openVpnTcpStream)(nil) +) // Ref paper: // https://www.usenix.org/system/files/sec22fall_xue-diwen.pdf From ecd60d0ff1319d505b3a04bd9d31e3aa054a958a Mon Sep 17 00:00:00 2001 From: Toby Date: Sat, 30 Mar 2024 13:29:19 -0700 Subject: [PATCH 5/6] chore: improve case spelling --- analyzer/udp/openvpn.go | 184 ++++++++++++++++++++-------------------- cmd/root.go | 2 +- 2 files changed, 93 insertions(+), 93 deletions(-) diff --git a/analyzer/udp/openvpn.go b/analyzer/udp/openvpn.go index 00ac34c..1bb2e8e 100644 --- a/analyzer/udp/openvpn.go +++ b/analyzer/udp/openvpn.go @@ -6,13 +6,13 @@ import ( ) var ( - _ analyzer.UDPAnalyzer = (*OpenVpnAnalyzer)(nil) - _ analyzer.TCPAnalyzer = (*OpenVpnAnalyzer)(nil) + _ analyzer.UDPAnalyzer = (*OpenVPNAnalyzer)(nil) + _ analyzer.TCPAnalyzer = (*OpenVPNAnalyzer)(nil) ) var ( - _ analyzer.UDPStream = (*openVpnUdpStream)(nil) - _ analyzer.TCPStream = (*openVpnTcpStream)(nil) + _ analyzer.UDPStream = (*openvpnUDPStream)(nil) + _ analyzer.TCPStream = (*openvpnTCPStream)(nil) ) // Ref paper: @@ -21,44 +21,44 @@ var ( // OpenVPN Opcodes definitions from: // https://github.com/OpenVPN/openvpn/blob/master/src/openvpn/ssl_pkt.h const ( - OpenVpnControlHardResetClientV1 = 1 - OpenVpnControlHardResetServerV1 = 2 - OpenVpnControlSoftResetV1 = 3 - OpenVpnControlV1 = 4 - OpenVpnAckV1 = 5 - OpenVpnDataV1 = 6 - OpenVpnControlHardResetClientV2 = 7 - OpenVpnControlHardResetServerV2 = 8 - OpenVpnDataV2 = 9 - OpenVpnControlHardResetClientV3 = 10 - OpenVpnControlWkcV1 = 11 + OpenVPNControlHardResetClientV1 = 1 + OpenVPNControlHardResetServerV1 = 2 + OpenVPNControlSoftResetV1 = 3 + OpenVPNControlV1 = 4 + OpenVPNAckV1 = 5 + OpenVPNDataV1 = 6 + OpenVPNControlHardResetClientV2 = 7 + OpenVPNControlHardResetServerV2 = 8 + OpenVPNDataV2 = 9 + OpenVPNControlHardResetClientV3 = 10 + OpenVPNControlWkcV1 = 11 ) const ( - OpenVpnMinPktLen = 6 - OpenVpnTcpPktDefaultLimit = 256 - OpenVpnUdpPktDefaultLimit = 256 + OpenVPNMinPktLen = 6 + OpenVPNTCPPktDefaultLimit = 256 + OpenVPNUDPPktDefaultLimit = 256 ) -type OpenVpnAnalyzer struct{} +type OpenVPNAnalyzer struct{} -func (a *OpenVpnAnalyzer) Name() string { +func (a *OpenVPNAnalyzer) Name() string { return "openvpn" } -func (a *OpenVpnAnalyzer) Limit() int { +func (a *OpenVPNAnalyzer) Limit() int { return 0 } -func (a *OpenVpnAnalyzer) NewUDP(info analyzer.UDPInfo, logger analyzer.Logger) analyzer.UDPStream { - return newOpenVpnUdpStream(logger) +func (a *OpenVPNAnalyzer) NewUDP(info analyzer.UDPInfo, logger analyzer.Logger) analyzer.UDPStream { + return newOpenVPNUDPStream(logger) } -func (a *OpenVpnAnalyzer) NewTCP(info analyzer.TCPInfo, logger analyzer.Logger) analyzer.TCPStream { - return newOpenVpnTcpStream(logger) +func (a *OpenVPNAnalyzer) NewTCP(info analyzer.TCPInfo, logger analyzer.Logger) analyzer.TCPStream { + return newOpenVPNTCPStream(logger) } -type openVpnPkt struct { +type openvpnPkt struct { pktLen uint16 // 16 bits, TCP proto only opcode byte // 5 bits _keyId byte // 3 bits, not used @@ -67,7 +67,7 @@ type openVpnPkt struct { // payload []byte } -type openVpnStream struct { +type openvpnStream struct { logger analyzer.Logger reqUpdated bool @@ -82,21 +82,21 @@ type openVpnStream struct { txPktCnt int pktLimit int - reqPktParse func() (*openVpnPkt, utils.LSMAction) - respPktParse func() (*openVpnPkt, utils.LSMAction) + reqPktParse func() (*openvpnPkt, utils.LSMAction) + respPktParse func() (*openvpnPkt, utils.LSMAction) lastOpcode byte } -func (o *openVpnStream) parseCtlHardResetClient() utils.LSMAction { +func (o *openvpnStream) parseCtlHardResetClient() utils.LSMAction { pkt, action := o.reqPktParse() if action != utils.LSMActionNext { return action } - if pkt.opcode != OpenVpnControlHardResetClientV1 && - pkt.opcode != OpenVpnControlHardResetClientV2 && - pkt.opcode != OpenVpnControlHardResetClientV3 { + if pkt.opcode != OpenVPNControlHardResetClientV1 && + pkt.opcode != OpenVPNControlHardResetClientV2 && + pkt.opcode != OpenVPNControlHardResetClientV3 { return utils.LSMActionCancel } o.lastOpcode = pkt.opcode @@ -104,10 +104,10 @@ func (o *openVpnStream) parseCtlHardResetClient() utils.LSMAction { return utils.LSMActionNext } -func (o *openVpnStream) parseCtlHardResetServer() utils.LSMAction { - if o.lastOpcode != OpenVpnControlHardResetClientV1 && - o.lastOpcode != OpenVpnControlHardResetClientV2 && - o.lastOpcode != OpenVpnControlHardResetClientV3 { +func (o *openvpnStream) parseCtlHardResetServer() utils.LSMAction { + if o.lastOpcode != OpenVPNControlHardResetClientV1 && + o.lastOpcode != OpenVPNControlHardResetClientV2 && + o.lastOpcode != OpenVPNControlHardResetClientV3 { return utils.LSMActionCancel } @@ -116,8 +116,8 @@ func (o *openVpnStream) parseCtlHardResetServer() utils.LSMAction { return action } - if pkt.opcode != OpenVpnControlHardResetServerV1 && - pkt.opcode != OpenVpnControlHardResetServerV2 { + if pkt.opcode != OpenVPNControlHardResetServerV1 && + pkt.opcode != OpenVPNControlHardResetServerV2 { return utils.LSMActionCancel } o.lastOpcode = pkt.opcode @@ -125,18 +125,18 @@ func (o *openVpnStream) parseCtlHardResetServer() utils.LSMAction { return utils.LSMActionNext } -func (o *openVpnStream) parseReq() utils.LSMAction { +func (o *openvpnStream) parseReq() utils.LSMAction { pkt, action := o.reqPktParse() if action != utils.LSMActionNext { return action } - if pkt.opcode != OpenVpnControlSoftResetV1 && - pkt.opcode != OpenVpnControlV1 && - pkt.opcode != OpenVpnAckV1 && - pkt.opcode != OpenVpnDataV1 && - pkt.opcode != OpenVpnDataV2 && - pkt.opcode != OpenVpnControlWkcV1 { + if pkt.opcode != OpenVPNControlSoftResetV1 && + pkt.opcode != OpenVPNControlV1 && + pkt.opcode != OpenVPNAckV1 && + pkt.opcode != OpenVPNDataV1 && + pkt.opcode != OpenVPNDataV2 && + pkt.opcode != OpenVPNControlWkcV1 { return utils.LSMActionCancel } @@ -146,18 +146,18 @@ func (o *openVpnStream) parseReq() utils.LSMAction { return utils.LSMActionPause } -func (o *openVpnStream) parseResp() utils.LSMAction { +func (o *openvpnStream) parseResp() utils.LSMAction { pkt, action := o.respPktParse() if action != utils.LSMActionNext { return action } - if pkt.opcode != OpenVpnControlSoftResetV1 && - pkt.opcode != OpenVpnControlV1 && - pkt.opcode != OpenVpnAckV1 && - pkt.opcode != OpenVpnDataV1 && - pkt.opcode != OpenVpnDataV2 && - pkt.opcode != OpenVpnControlWkcV1 { + if pkt.opcode != OpenVPNControlSoftResetV1 && + pkt.opcode != OpenVPNControlV1 && + pkt.opcode != OpenVPNAckV1 && + pkt.opcode != OpenVPNDataV1 && + pkt.opcode != OpenVPNDataV2 && + pkt.opcode != OpenVPNControlWkcV1 { return utils.LSMActionCancel } @@ -167,18 +167,18 @@ func (o *openVpnStream) parseResp() utils.LSMAction { return utils.LSMActionPause } -type openVpnUdpStream struct { - openVpnStream +type openvpnUDPStream struct { + openvpnStream curPkt []byte // We don't introduce `invalidCount` here to decrease the false positive rate // invalidCount int } -func newOpenVpnUdpStream(logger analyzer.Logger) *openVpnUdpStream { - s := &openVpnUdpStream{ - openVpnStream: openVpnStream{ +func newOpenVPNUDPStream(logger analyzer.Logger) *openvpnUDPStream { + s := &openvpnUDPStream{ + openvpnStream: openvpnStream{ logger: logger, - pktLimit: OpenVpnUdpPktDefaultLimit, + pktLimit: OpenVPNUDPPktDefaultLimit, }, } s.respPktParse = s.parsePkt @@ -194,7 +194,7 @@ func newOpenVpnUdpStream(logger analyzer.Logger) *openVpnUdpStream { return s } -func (o *openVpnUdpStream) Feed(rev bool, data []byte) (u *analyzer.PropUpdate, d bool) { +func (o *openvpnUDPStream) Feed(rev bool, data []byte) (u *analyzer.PropUpdate, d bool) { if len(data) == 0 { return nil, false } @@ -226,22 +226,22 @@ func (o *openVpnUdpStream) Feed(rev bool, data []byte) (u *analyzer.PropUpdate, return update, cancelled || (o.reqDone && o.respDone) || o.rxPktCnt+o.txPktCnt > o.pktLimit } -func (o *openVpnUdpStream) Close(limited bool) *analyzer.PropUpdate { +func (o *openvpnUDPStream) Close(limited bool) *analyzer.PropUpdate { return nil } -// Parse OpenVpn UDP packet. -func (o *openVpnUdpStream) parsePkt() (p *openVpnPkt, action utils.LSMAction) { +// Parse OpenVPN UDP packet. +func (o *openvpnUDPStream) parsePkt() (p *openvpnPkt, action utils.LSMAction) { if o.curPkt == nil { return nil, utils.LSMActionPause } - if !OpenVpnCheckForValidOpcode(o.curPkt[0] >> 3) { + if !OpenVPNCheckForValidOpcode(o.curPkt[0] >> 3) { return nil, utils.LSMActionCancel } // Parse packet header - p = &openVpnPkt{} + p = &openvpnPkt{} p.opcode = o.curPkt[0] >> 3 p._keyId = o.curPkt[0] & 0x07 @@ -249,25 +249,25 @@ func (o *openVpnUdpStream) parsePkt() (p *openVpnPkt, action utils.LSMAction) { return p, utils.LSMActionNext } -type openVpnTcpStream struct { - openVpnStream +type openvpnTCPStream struct { + openvpnStream reqBuf *utils.ByteBuffer respBuf *utils.ByteBuffer } -func newOpenVpnTcpStream(logger analyzer.Logger) *openVpnTcpStream { - s := &openVpnTcpStream{ - openVpnStream: openVpnStream{ +func newOpenVPNTCPStream(logger analyzer.Logger) *openvpnTCPStream { + s := &openvpnTCPStream{ + openvpnStream: openvpnStream{ logger: logger, - pktLimit: OpenVpnTcpPktDefaultLimit, + pktLimit: OpenVPNTCPPktDefaultLimit, }, reqBuf: &utils.ByteBuffer{}, respBuf: &utils.ByteBuffer{}, } - s.respPktParse = func() (*openVpnPkt, utils.LSMAction) { + s.respPktParse = func() (*openvpnPkt, utils.LSMAction) { return s.parsePkt(true) } - s.reqPktParse = func() (*openVpnPkt, utils.LSMAction) { + s.reqPktParse = func() (*openvpnPkt, utils.LSMAction) { return s.parsePkt(false) } s.reqLSM = utils.NewLinearStateMachine( @@ -281,7 +281,7 @@ func newOpenVpnTcpStream(logger analyzer.Logger) *openVpnTcpStream { return s } -func (o *openVpnTcpStream) Feed(rev, start, end bool, skip int, data []byte) (u *analyzer.PropUpdate, d bool) { +func (o *openvpnTCPStream) Feed(rev, start, end bool, skip int, data []byte) (u *analyzer.PropUpdate, d bool) { if skip != 0 { return nil, true } @@ -317,14 +317,14 @@ func (o *openVpnTcpStream) Feed(rev, start, end bool, skip int, data []byte) (u return update, cancelled || (o.reqDone && o.respDone) || o.rxPktCnt+o.txPktCnt > o.pktLimit } -func (o *openVpnTcpStream) Close(limited bool) *analyzer.PropUpdate { +func (o *openvpnTCPStream) Close(limited bool) *analyzer.PropUpdate { o.reqBuf.Reset() o.respBuf.Reset() return nil } -// Parse OpenVpn TCP packet. -func (o *openVpnTcpStream) parsePkt(rev bool) (p *openVpnPkt, action utils.LSMAction) { +// Parse OpenVPN TCP packet. +func (o *openvpnTCPStream) parsePkt(rev bool) (p *openvpnPkt, action utils.LSMAction) { var buffer *utils.ByteBuffer if rev { buffer = o.respBuf @@ -338,7 +338,7 @@ func (o *openVpnTcpStream) parsePkt(rev bool) (p *openVpnPkt, action utils.LSMAc return nil, utils.LSMActionPause } - if pktLen < OpenVpnMinPktLen { + if pktLen < OpenVPNMinPktLen { return nil, utils.LSMActionCancel } @@ -346,7 +346,7 @@ func (o *openVpnTcpStream) parsePkt(rev bool) (p *openVpnPkt, action utils.LSMAc if !ok { return nil, utils.LSMActionPause } - if !OpenVpnCheckForValidOpcode(pktOp[2] >> 3) { + if !OpenVPNCheckForValidOpcode(pktOp[2] >> 3) { return nil, utils.LSMActionCancel } @@ -357,7 +357,7 @@ func (o *openVpnTcpStream) parsePkt(rev bool) (p *openVpnPkt, action utils.LSMAc pkt = pkt[2:] // Parse packet header - p = &openVpnPkt{} + p = &openvpnPkt{} p.pktLen = pktLen p.opcode = pkt[0] >> 3 p._keyId = pkt[0] & 0x07 @@ -365,19 +365,19 @@ func (o *openVpnTcpStream) parsePkt(rev bool) (p *openVpnPkt, action utils.LSMAc return p, utils.LSMActionNext } -func OpenVpnCheckForValidOpcode(opcode byte) bool { +func OpenVPNCheckForValidOpcode(opcode byte) bool { switch opcode { - case OpenVpnControlHardResetClientV1, - OpenVpnControlHardResetServerV1, - OpenVpnControlSoftResetV1, - OpenVpnControlV1, - OpenVpnAckV1, - OpenVpnDataV1, - OpenVpnControlHardResetClientV2, - OpenVpnControlHardResetServerV2, - OpenVpnDataV2, - OpenVpnControlHardResetClientV3, - OpenVpnControlWkcV1: + case OpenVPNControlHardResetClientV1, + OpenVPNControlHardResetServerV1, + OpenVPNControlSoftResetV1, + OpenVPNControlV1, + OpenVPNAckV1, + OpenVPNDataV1, + OpenVPNControlHardResetClientV2, + OpenVPNControlHardResetServerV2, + OpenVPNDataV2, + OpenVPNControlHardResetClientV3, + OpenVPNControlWkcV1: return true } return false diff --git a/cmd/root.go b/cmd/root.go index 7ba319c..4ccfe3a 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -92,7 +92,7 @@ var analyzers = []analyzer.Analyzer{ &tcp.TLSAnalyzer{}, &tcp.TrojanAnalyzer{}, &udp.DNSAnalyzer{}, - &udp.OpenVpnAnalyzer{}, + &udp.OpenVPNAnalyzer{}, &udp.QUICAnalyzer{}, &udp.WireGuardAnalyzer{}, } From e5357690869bfbeb0b6feb824e0dbe19aaee2144 Mon Sep 17 00:00:00 2001 From: Toby Date: Sat, 30 Mar 2024 14:30:20 -0700 Subject: [PATCH 6/6] fix: make sure rx_pkt_cnt & tx_pkt_cnt both always exist --- analyzer/udp/openvpn.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/analyzer/udp/openvpn.go b/analyzer/udp/openvpn.go index 1bb2e8e..3853726 100644 --- a/analyzer/udp/openvpn.go +++ b/analyzer/udp/openvpn.go @@ -206,8 +206,8 @@ func (o *openvpnUDPStream) Feed(rev bool, data []byte) (u *analyzer.PropUpdate, cancelled, o.respDone = o.respLSM.Run() if o.respUpdated { update = &analyzer.PropUpdate{ - Type: analyzer.PropUpdateMerge, - M: analyzer.PropMap{"rx_pkt_cnt": o.rxPktCnt}, + Type: analyzer.PropUpdateReplace, + M: analyzer.PropMap{"rx_pkt_cnt": o.rxPktCnt, "tx_pkt_cnt": o.txPktCnt}, } o.respUpdated = false } @@ -216,8 +216,8 @@ func (o *openvpnUDPStream) Feed(rev bool, data []byte) (u *analyzer.PropUpdate, cancelled, o.reqDone = o.reqLSM.Run() if o.reqUpdated { update = &analyzer.PropUpdate{ - Type: analyzer.PropUpdateMerge, - M: analyzer.PropMap{"tx_pkt_cnt": o.txPktCnt}, + Type: analyzer.PropUpdateReplace, + M: analyzer.PropMap{"rx_pkt_cnt": o.rxPktCnt, "tx_pkt_cnt": o.txPktCnt}, } o.reqUpdated = false } @@ -296,8 +296,8 @@ func (o *openvpnTCPStream) Feed(rev, start, end bool, skip int, data []byte) (u cancelled, o.respDone = o.respLSM.Run() if o.respUpdated { update = &analyzer.PropUpdate{ - Type: analyzer.PropUpdateMerge, - M: analyzer.PropMap{"rx_pkt_cnt": o.rxPktCnt}, + Type: analyzer.PropUpdateReplace, + M: analyzer.PropMap{"rx_pkt_cnt": o.rxPktCnt, "tx_pkt_cnt": o.txPktCnt}, } o.respUpdated = false } @@ -308,7 +308,7 @@ func (o *openvpnTCPStream) Feed(rev, start, end bool, skip int, data []byte) (u if o.reqUpdated { update = &analyzer.PropUpdate{ Type: analyzer.PropUpdateMerge, - M: analyzer.PropMap{"tx_pkt_cnt": o.txPktCnt}, + M: analyzer.PropMap{"rx_pkt_cnt": o.rxPktCnt, "tx_pkt_cnt": o.txPktCnt}, } o.reqUpdated = false }