refactor: merge openvpn_tcp and openvpn_udp to openvpn

This commit is contained in:
KujouRinka 2024-03-30 12:00:17 +08:00
parent 2232b553b3
commit 0e2ee36865
4 changed files with 278 additions and 382 deletions

View File

@ -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
}

View File

@ -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
}

View File

@ -2,17 +2,44 @@ package udp
import ( import (
"github.com/apernet/OpenGFW/analyzer" "github.com/apernet/OpenGFW/analyzer"
"github.com/apernet/OpenGFW/analyzer/internal"
"github.com/apernet/OpenGFW/analyzer/utils" "github.com/apernet/OpenGFW/analyzer/utils"
) )
var _ analyzer.UDPAnalyzer = (*OpenVpnAnalyzer)(nil) 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{} type OpenVpnAnalyzer struct{}
func (a *OpenVpnAnalyzer) Name() string { func (a *OpenVpnAnalyzer) Name() string {
return "openvpn_udp" return "openvpn"
} }
func (a *OpenVpnAnalyzer) Limit() int { 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 { 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 { type openVpnStream struct {
logger analyzer.Logger logger analyzer.Logger
// We don't introduce `invalidCount` here to decrease the false positive rate
// invalidCount int
curPkt []byte
reqUpdated bool reqUpdated bool
reqLSM *utils.LinearStateMachine reqLSM *utils.LinearStateMachine
@ -42,22 +78,107 @@ type openVpnStream struct {
txPktCnt int txPktCnt int
pktLimit int pktLimit int
reqPktParse func() (*openVpnPkt, utils.LSMAction)
respPktParse func() (*openVpnPkt, utils.LSMAction)
lastOpcode byte lastOpcode byte
} }
type openVpnUdpPkt struct { func (o *openVpnStream) parseCtlHardResetClient() utils.LSMAction {
opcode byte // 5 bits pkt, action := o.reqPktParse()
_keyId byte // 3 bits, not used if action != utils.LSMActionNext {
return action
// We don't care about the rest of the packet
// payload []byte
} }
func newOpenVPNUdpStream(logger analyzer.Logger) *openVpnStream { if pkt.opcode != OpenVpnControlHardResetClientV1 &&
s := &openVpnStream{ pkt.opcode != OpenVpnControlHardResetClientV2 &&
pkt.opcode != OpenVpnControlHardResetClientV3 {
return utils.LSMActionCancel
}
o.lastOpcode = pkt.opcode
return utils.LSMActionNext
}
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, logger: logger,
pktLimit: internal.OpenVpnUdpPktDefaultLimit, pktLimit: OpenVpnUdpPktDefaultLimit,
},
} }
s.respPktParse = s.parsePkt
s.reqPktParse = s.parsePkt
s.reqLSM = utils.NewLinearStateMachine( s.reqLSM = utils.NewLinearStateMachine(
s.parseCtlHardResetClient, s.parseCtlHardResetClient,
s.parseReq, s.parseReq,
@ -69,7 +190,7 @@ func newOpenVPNUdpStream(logger analyzer.Logger) *openVpnStream {
return s 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 { if len(data) == 0 {
return nil, false 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 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 return nil
} }
func (o *openVpnStream) parseCtlHardResetClient() utils.LSMAction { // Parse OpenVpn UDP packet.
pkt, action := o.parsePkt() func (o *openVpnUdpStream) parsePkt() (p *openVpnPkt, action utils.LSMAction) {
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 { if o.curPkt == nil {
return nil, utils.LSMActionPause return nil, utils.LSMActionPause
} }
if !internal.OpenVpnCheckForValidOpcode(o.curPkt[0] >> 3) { if !OpenVpnCheckForValidOpcode(o.curPkt[0] >> 3) {
return nil, utils.LSMActionCancel return nil, utils.LSMActionCancel
} }
// Parse packet header // Parse packet header
p = &openVpnUdpPkt{} p = &openVpnPkt{}
p.opcode = o.curPkt[0] >> 3 p.opcode = o.curPkt[0] >> 3
p._keyId = o.curPkt[0] & 0x07 p._keyId = o.curPkt[0] & 0x07
o.curPkt = nil o.curPkt = nil
return p, utils.LSMActionNext 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
}

View File

@ -87,7 +87,6 @@ var logFormatMap = map[string]zapcore.EncoderConfig{
var analyzers = []analyzer.Analyzer{ var analyzers = []analyzer.Analyzer{
&tcp.FETAnalyzer{}, &tcp.FETAnalyzer{},
&tcp.HTTPAnalyzer{}, &tcp.HTTPAnalyzer{},
&tcp.OpenVpnAnalyzer{},
&tcp.SocksAnalyzer{}, &tcp.SocksAnalyzer{},
&tcp.SSHAnalyzer{}, &tcp.SSHAnalyzer{},
&tcp.TLSAnalyzer{}, &tcp.TLSAnalyzer{},