You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

141 lines
3.2 KiB

  1. package p2p
  2. import (
  3. "math/rand"
  4. "net"
  5. "sync"
  6. "time"
  7. cfg "github.com/tendermint/go-config"
  8. )
  9. //--------------------------------------------------------
  10. // delay reads/writes
  11. // randomly drop reads/writes
  12. // randomly drop connections
  13. const (
  14. FuzzModeDrop = "drop"
  15. FuzzModeDelay = "delay"
  16. )
  17. func FuzzConn(config cfg.Config, conn net.Conn) net.Conn {
  18. return &FuzzedConnection{
  19. conn: conn,
  20. start: time.After(time.Second * 10), // so we have time to do peer handshakes and get set up
  21. params: config,
  22. }
  23. }
  24. type FuzzedConnection struct {
  25. conn net.Conn
  26. mtx sync.Mutex
  27. fuzz bool // we don't start fuzzing right away
  28. start <-chan time.Time
  29. // fuzz params
  30. params cfg.Config
  31. }
  32. func (fc *FuzzedConnection) randomDuration() time.Duration {
  33. return time.Millisecond * time.Duration(rand.Int()%fc.MaxDelayMilliseconds())
  34. }
  35. func (fc *FuzzedConnection) Active() bool {
  36. return fc.params.GetBool(configFuzzActive)
  37. }
  38. func (fc *FuzzedConnection) Mode() string {
  39. return fc.params.GetString(configFuzzMode)
  40. }
  41. func (fc *FuzzedConnection) ProbDropRW() float64 {
  42. return fc.params.GetFloat64(configFuzzProbDropRW)
  43. }
  44. func (fc *FuzzedConnection) ProbDropConn() float64 {
  45. return fc.params.GetFloat64(configFuzzProbDropConn)
  46. }
  47. func (fc *FuzzedConnection) ProbSleep() float64 {
  48. return fc.params.GetFloat64(configFuzzProbSleep)
  49. }
  50. func (fc *FuzzedConnection) MaxDelayMilliseconds() int {
  51. return fc.params.GetInt(configFuzzMaxDelayMilliseconds)
  52. }
  53. // implements the fuzz (delay, kill conn)
  54. // and returns whether or not the read/write should be ignored
  55. func (fc *FuzzedConnection) Fuzz() bool {
  56. if !fc.shouldFuzz() {
  57. return false
  58. }
  59. switch fc.Mode() {
  60. case FuzzModeDrop:
  61. // randomly drop the r/w, drop the conn, or sleep
  62. r := rand.Float64()
  63. if r <= fc.ProbDropRW() {
  64. return true
  65. } else if r < fc.ProbDropRW()+fc.ProbDropConn() {
  66. // XXX: can't this fail because machine precision?
  67. // XXX: do we need an error?
  68. fc.Close()
  69. return true
  70. } else if r < fc.ProbDropRW()+fc.ProbDropConn()+fc.ProbSleep() {
  71. time.Sleep(fc.randomDuration())
  72. }
  73. case FuzzModeDelay:
  74. // sleep a bit
  75. time.Sleep(fc.randomDuration())
  76. }
  77. return false
  78. }
  79. // we don't fuzz until start chan fires
  80. func (fc *FuzzedConnection) shouldFuzz() bool {
  81. if !fc.Active() {
  82. return false
  83. }
  84. fc.mtx.Lock()
  85. defer fc.mtx.Unlock()
  86. if fc.fuzz {
  87. return true
  88. }
  89. select {
  90. case <-fc.start:
  91. fc.fuzz = true
  92. default:
  93. }
  94. return false
  95. }
  96. func (fc *FuzzedConnection) Read(data []byte) (n int, err error) {
  97. if fc.Fuzz() {
  98. return 0, nil
  99. }
  100. return fc.conn.Read(data)
  101. }
  102. func (fc *FuzzedConnection) Write(data []byte) (n int, err error) {
  103. if fc.Fuzz() {
  104. return 0, nil
  105. }
  106. return fc.conn.Write(data)
  107. }
  108. // Implements net.Conn
  109. func (fc *FuzzedConnection) Close() error { return fc.conn.Close() }
  110. func (fc *FuzzedConnection) LocalAddr() net.Addr { return fc.conn.LocalAddr() }
  111. func (fc *FuzzedConnection) RemoteAddr() net.Addr { return fc.conn.RemoteAddr() }
  112. func (fc *FuzzedConnection) SetDeadline(t time.Time) error { return fc.conn.SetDeadline(t) }
  113. func (fc *FuzzedConnection) SetReadDeadline(t time.Time) error {
  114. return fc.conn.SetReadDeadline(t)
  115. }
  116. func (fc *FuzzedConnection) SetWriteDeadline(t time.Time) error {
  117. return fc.conn.SetWriteDeadline(t)
  118. }