├── .gitignore ├── LICENSE ├── README.md ├── auth.go ├── control.go ├── control_test.go └── session.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jonathan Thurman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-bfd 2 | 3 | Bidirectional Forwarding Detection library for Go 4 | 5 | ## Description 6 | 7 | Implementation of Bidirectional Forwarding Detection (BFD) 8 | 9 | * Based on: 10 | * Bidirectional Forwarding Detection [RFC5880](http://tools.ietf.org/html/rfc5880) 11 | * BFD for IPv4 and IPv6 (Single Hop) [RFC5881](http://tools.ietf.org/html/rfc5881) 12 | 13 | BFD Control packets MUST be transmitted in UDP packets with 14 | destination port 3784, within an IPv4 or IPv6 packet. The source 15 | port MUST be in the range 49152 through 65535 16 | -------------------------------------------------------------------------------- /auth.go: -------------------------------------------------------------------------------- 1 | package bfd 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | ) 8 | 9 | /* 10 | * 0 1 2 3 11 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 12 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 13 | * | Auth Type | Auth Len | Authentication Data... | 14 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 15 | * 16 | * 0 1 2 3 17 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 18 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 19 | * | Auth Type | Auth Len | Auth Key ID | Password... | 20 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 21 | * | ... | 22 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 23 | * 24 | * 0 1 2 3 25 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 26 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 27 | * | Auth Type | Auth Len | Auth Key ID | Reserved | 28 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 29 | * | Sequence Number | 30 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 31 | * | Auth Key/Digest... | 32 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 33 | * | ... | 34 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 35 | */ 36 | type BfdAuthHeader struct { 37 | Type AuthenticationType 38 | AuthKeyID uint8 39 | SequenceNumber uint32 40 | AuthData []byte 41 | } 42 | 43 | type AuthenticationType uint8 44 | 45 | const ( 46 | BFD_AUTH_TYPE_RESERVED AuthenticationType = 0 // Reserved 47 | BFD_AUTH_TYPE_SIMPLE AuthenticationType = 1 // Simple Password 48 | BFD_AUTH_TYPE_KEYED_MD5 AuthenticationType = 2 // Keyed MD5 49 | BFD_AUTH_TYPE_METICULOUS_MD5 AuthenticationType = 3 // Meticulous Keyed MD5 50 | BFD_AUTH_TYPE_KEYED_SHA1 AuthenticationType = 4 // Keyed SHA1 51 | BFD_AUTH_TYPE_METICULOUS_SHA1 AuthenticationType = 5 // Meticulous Keyed SHA1 52 | ) 53 | 54 | /* 55 | * Decode the Auth header section 56 | */ 57 | func decodeBfdAuthHeader(data []byte) (*BfdAuthHeader, error) { 58 | var err error 59 | h := &BfdAuthHeader{} 60 | 61 | h.Type = AuthenticationType(data[0]) 62 | length := uint8(data[1]) 63 | 64 | if length > 0 { 65 | h.AuthKeyID = uint8(data[2]) 66 | 67 | switch h.Type { 68 | case BFD_AUTH_TYPE_SIMPLE: 69 | h.AuthData = data[3:] 70 | break 71 | case BFD_AUTH_TYPE_KEYED_MD5, BFD_AUTH_TYPE_METICULOUS_MD5: 72 | h.SequenceNumber = binary.BigEndian.Uint32(data[4:8]) 73 | h.AuthData = data[8:] 74 | if len(h.AuthData) != 16 { 75 | err = errors.New("Invalid MD5 Auth Key/Digest length!") 76 | } 77 | case BFD_AUTH_TYPE_KEYED_SHA1, BFD_AUTH_TYPE_METICULOUS_SHA1: 78 | h.SequenceNumber = binary.BigEndian.Uint32(data[4:8]) 79 | h.AuthData = data[8:] 80 | if len(h.AuthData) != 20 { 81 | err = errors.New("Invalid SHA1 Auth Key/Hash length!") 82 | } 83 | default: 84 | err = errors.New("Unsupported Authentication type!") 85 | } 86 | } 87 | 88 | if err != nil { 89 | return nil, err 90 | } 91 | 92 | return h, nil 93 | } 94 | 95 | /* 96 | * Marshal the Auth header section 97 | */ 98 | func (h *BfdAuthHeader) Marshal() []byte { 99 | buf := bytes.NewBuffer([]uint8{}) 100 | var length uint8 101 | 102 | if h.Type != BFD_AUTH_TYPE_SIMPLE { 103 | length = uint8(len(h.AuthData) + 8) 104 | } else { 105 | length = uint8(len(h.AuthData) + 3) 106 | } 107 | 108 | binary.Write(buf, binary.BigEndian, h.Type) 109 | binary.Write(buf, binary.BigEndian, length) 110 | binary.Write(buf, binary.BigEndian, h.AuthKeyID) 111 | 112 | if h.Type != BFD_AUTH_TYPE_SIMPLE { 113 | binary.Write(buf, binary.BigEndian, uint8(0)) 114 | binary.Write(buf, binary.BigEndian, h.SequenceNumber) 115 | } 116 | 117 | binary.Write(buf, binary.BigEndian, h.AuthData) 118 | 119 | return buf.Bytes() 120 | } 121 | -------------------------------------------------------------------------------- /control.go: -------------------------------------------------------------------------------- 1 | package bfd 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "fmt" 8 | "time" 9 | ) 10 | 11 | type BfdState uint8 12 | 13 | const ( 14 | STATE_ADMIN_DOWN BfdState = 0 // AdminDown 15 | STATE_DOWN BfdState = 1 // Down 16 | STATE_INIT BfdState = 2 // Init 17 | STATE_UP BfdState = 3 // Up 18 | ) 19 | 20 | type BfdDiagnostic uint8 21 | 22 | const ( 23 | DIAG_NONE BfdDiagnostic = 0 // No Diagnostic 24 | DIAG_TIME_EXPIRED BfdDiagnostic = 1 // Control Detection Time Expired 25 | DIAG_ECHO_FAILED BfdDiagnostic = 2 // Echo Function Failed 26 | DIAG_NEIGHBOR_SIGNAL_DOWN BfdDiagnostic = 3 // Neighbor Signaled Session Down 27 | DIAG_FORWARD_PLANE_RESET BfdDiagnostic = 4 // Forwarding Plane Reset 28 | DIAG_PATH_DOWN BfdDiagnostic = 5 // Path Down 29 | DIAG_CONCAT_PATH_DOWN BfdDiagnostic = 6 // Concatenated Path Down 30 | DIAG_ADMIN_DOWN BfdDiagnostic = 7 // Administratively Down 31 | DIAG_REV_CONCAT_PATH_DOWN BfdDiagnostic = 8 // Reverse Concatenated Path Down 32 | ) 33 | 34 | /* 35 | * 0 1 2 3 36 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 37 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 38 | * |Vers | Diag |Sta|P|F|C|A|D|M| Detect Mult | Length | 39 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 40 | * | My Discriminator | 41 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 42 | * | Your Discriminator | 43 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 44 | * | Desired Min TX Interval | 45 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 46 | * | Required Min RX Interval | 47 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 48 | * | Required Min Echo RX Interval | 49 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 50 | * An optional Authentication Section MAY be present 51 | */ 52 | 53 | type BfdControlPacket struct { 54 | Version uint8 55 | Diagnostic BfdDiagnostic 56 | State BfdState 57 | Poll bool 58 | Final bool 59 | ControlPlaneIndependent bool 60 | AuthPresent bool 61 | Demand bool 62 | Multipoint bool // Must always be zero 63 | DetectMult uint8 64 | MyDiscriminator uint32 65 | YourDiscriminator uint32 66 | DesiredMinTxInterval time.Duration 67 | RequiredMinRxInterval time.Duration 68 | RequiredMinEchoRxInterval time.Duration 69 | AuthHeader *BfdAuthHeader 70 | } 71 | 72 | var BfdControlPacketDefaults = BfdControlPacket{ 73 | Version: 1, 74 | Diagnostic: DIAG_NONE, 75 | State: STATE_DOWN, 76 | Poll: false, 77 | Final: false, 78 | ControlPlaneIndependent: false, 79 | AuthPresent: false, 80 | Demand: false, 81 | Multipoint: false, 82 | DetectMult: 3, 83 | MyDiscriminator: 0, 84 | YourDiscriminator: 0, 85 | DesiredMinTxInterval: 1000000, 86 | RequiredMinRxInterval: 1000000, 87 | RequiredMinEchoRxInterval: 0, 88 | AuthHeader: nil, 89 | } 90 | 91 | /* 92 | * Decode the control packet 93 | */ 94 | func decodeBfdPacket(data []byte) (*BfdControlPacket, error) { 95 | var err error 96 | packet := &BfdControlPacket{} 97 | 98 | packet.Version = uint8((data[0] & 0xE0) >> 5) 99 | packet.Diagnostic = BfdDiagnostic(data[0] & 0x1F) 100 | 101 | packet.State = BfdState((data[1] & 0xD0) >> 6) 102 | 103 | // bit flags 104 | packet.Poll = (data[1]&0x20 != 0) 105 | packet.Final = (data[1]&0x10 != 0) 106 | packet.ControlPlaneIndependent = (data[1]&0x08 != 0) 107 | packet.AuthPresent = (data[1]&0x04 != 0) 108 | packet.Demand = (data[1]&0x02 != 0) 109 | packet.Multipoint = (data[1]&0x01 != 0) 110 | packet.DetectMult = uint8(data[2]) 111 | 112 | length := uint8(data[3]) // No need to store this 113 | if uint8(len(data)) != length { 114 | err = errors.New("Packet length mis-match!") 115 | return nil, err 116 | } 117 | 118 | packet.MyDiscriminator = binary.BigEndian.Uint32(data[4:8]) 119 | packet.YourDiscriminator = binary.BigEndian.Uint32(data[8:12]) 120 | packet.DesiredMinTxInterval = time.Duration(binary.BigEndian.Uint32(data[12:16])) 121 | packet.RequiredMinRxInterval = time.Duration(binary.BigEndian.Uint32(data[16:20])) 122 | packet.RequiredMinEchoRxInterval = time.Duration(binary.BigEndian.Uint32(data[20:24])) 123 | 124 | if packet.AuthPresent { 125 | if len(data) > 24 { 126 | packet.AuthHeader, err = decodeBfdAuthHeader(data[24:]) 127 | } else { 128 | err = errors.New("Header flag set, but packet too short!") 129 | } 130 | } 131 | 132 | return packet, err 133 | } 134 | 135 | func (p *BfdControlPacket) Marshal() []byte { 136 | var auth []byte 137 | buf := bytes.NewBuffer([]uint8{}) 138 | flags := uint8(0) 139 | length := uint8(24) 140 | 141 | binary.Write(buf, binary.BigEndian, (p.Version<<5 | (uint8(p.Diagnostic) & 0x1f))) 142 | 143 | if p.Poll { 144 | flags |= 0x20 145 | } 146 | if p.Final { 147 | flags |= 0x10 148 | } 149 | if p.ControlPlaneIndependent { 150 | flags |= 0x08 151 | } 152 | if p.AuthPresent && (p.AuthHeader != nil) { 153 | flags |= 0x04 154 | auth = p.AuthHeader.Marshal() 155 | length += uint8(len(auth)) 156 | } 157 | if p.Demand { 158 | flags |= 0x02 159 | } 160 | if p.Multipoint { 161 | flags |= 0x01 162 | } 163 | 164 | binary.Write(buf, binary.BigEndian, (uint8(p.State)<<6 | flags)) 165 | binary.Write(buf, binary.BigEndian, p.DetectMult) 166 | binary.Write(buf, binary.BigEndian, length) 167 | 168 | binary.Write(buf, binary.BigEndian, p.MyDiscriminator) 169 | binary.Write(buf, binary.BigEndian, p.YourDiscriminator) 170 | binary.Write(buf, binary.BigEndian, uint32(p.DesiredMinTxInterval)) 171 | binary.Write(buf, binary.BigEndian, uint32(p.RequiredMinRxInterval)) 172 | binary.Write(buf, binary.BigEndian, uint32(p.RequiredMinEchoRxInterval)) 173 | 174 | if len(auth) > 0 { 175 | binary.Write(buf, binary.BigEndian, auth) 176 | } 177 | 178 | return buf.Bytes() 179 | } 180 | 181 | func (p *BfdControlPacket) String() string { 182 | return fmt.Sprintf("[Ver: %d]", p.Version) 183 | } 184 | -------------------------------------------------------------------------------- /control_test.go: -------------------------------------------------------------------------------- 1 | package bfd 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "reflect" 7 | "testing" 8 | ) 9 | 10 | type bfdControlPacketTestSet struct { 11 | Name string 12 | Data []byte 13 | Packet BfdControlPacket 14 | } 15 | 16 | var tests = []bfdControlPacketTestSet{ 17 | { 18 | Name: "Default", 19 | Data: []byte{ 20 | 0x20, // Version 1, No Diagnostics 21 | 0x40, // Session State: DOWN, Message Flags: None 22 | 0x03, // Detect Time Multiplier (3) 23 | 0x18, // Message Length (24) 24 | 0x00, 0x00, 0x00, 0x00, // My Discriminator (0) 25 | 0x00, 0x00, 0x00, 0x00, // Your Discriminator (0) 26 | 0x00, 0x0f, 0x42, 0x40, // Desired Min TX interval (1000000) 27 | 0x00, 0x0f, 0x42, 0x40, // Required Min RX interval (1000000) 28 | 0x00, 0x00, 0x00, 0x00, // Required Min Echo interval (0) 29 | }, 30 | Packet: BfdControlPacketDefaults, 31 | }, 32 | { 33 | Name: "Discriminator", 34 | Data: []byte{ 35 | 0x20, 0x40, 0x03, 0x18, 0x00, 0x00, 0x00, 0x01, // Mine: 1 36 | 0x00, 0x00, 0x00, 0x19, 0x00, 0x0f, 0x42, 0x40, // Yours: 25 37 | 0x00, 0x0f, 0x42, 0x40, 0x00, 0x00, 0x00, 0x00, 38 | }, 39 | Packet: BfdControlPacket{ 40 | Version: 1, Diagnostic: DIAG_NONE, State: STATE_DOWN, 41 | Poll: false, Final: false, ControlPlaneIndependent: false, AuthPresent: false, Demand: false, Multipoint: false, 42 | DetectMult: 3, MyDiscriminator: 1, YourDiscriminator: 25, 43 | DesiredMinTxInterval: 1000000, RequiredMinRxInterval: 1000000, RequiredMinEchoRxInterval: 0, 44 | AuthHeader: nil, 45 | }, 46 | }, 47 | { 48 | Name: "State: Admin Down", 49 | Data: []byte{ 50 | 0x20, 0x00, 0x03, 0x18, 0x00, 0x00, 0x00, 0x01, // State: Admin Down 51 | 0x00, 0x00, 0x00, 0x19, 0x00, 0x0f, 0x42, 0x40, 52 | 0x00, 0x0f, 0x42, 0x40, 0x00, 0x00, 0x00, 0x00, 53 | }, 54 | Packet: BfdControlPacket{ 55 | Version: 1, Diagnostic: DIAG_NONE, State: STATE_ADMIN_DOWN, 56 | Poll: false, Final: false, ControlPlaneIndependent: false, AuthPresent: false, Demand: false, Multipoint: false, 57 | DetectMult: 3, MyDiscriminator: 1, YourDiscriminator: 25, 58 | DesiredMinTxInterval: 1000000, RequiredMinRxInterval: 1000000, RequiredMinEchoRxInterval: 0, 59 | AuthHeader: nil, 60 | }, 61 | }, 62 | { 63 | Name: "State: Init", 64 | Data: []byte{ 65 | 0x20, 0x80, 0x03, 0x18, 0x00, 0x00, 0x00, 0x01, // State: Init 66 | 0x00, 0x00, 0x00, 0x19, 0x00, 0x0f, 0x42, 0x40, 67 | 0x00, 0x0f, 0x42, 0x40, 0x00, 0x00, 0x00, 0x00, 68 | }, 69 | Packet: BfdControlPacket{ 70 | Version: 1, Diagnostic: DIAG_NONE, State: STATE_INIT, 71 | Poll: false, Final: false, ControlPlaneIndependent: false, AuthPresent: false, Demand: false, Multipoint: false, 72 | DetectMult: 3, MyDiscriminator: 1, YourDiscriminator: 25, 73 | DesiredMinTxInterval: 1000000, RequiredMinRxInterval: 1000000, RequiredMinEchoRxInterval: 0, 74 | AuthHeader: nil, 75 | }, 76 | }, 77 | { 78 | Name: "State: UP", 79 | Data: []byte{ 80 | 0x20, 0xc0, 0x03, 0x18, 0x00, 0x00, 0x00, 0x01, // State: UP 81 | 0x00, 0x00, 0x00, 0x19, 0x00, 0x0f, 0x42, 0x40, 82 | 0x00, 0x0f, 0x42, 0x40, 0x00, 0x00, 0x00, 0x00, 83 | }, 84 | Packet: BfdControlPacket{ 85 | Version: 1, Diagnostic: DIAG_NONE, State: STATE_UP, 86 | Poll: false, Final: false, ControlPlaneIndependent: false, AuthPresent: false, Demand: false, Multipoint: false, 87 | DetectMult: 3, MyDiscriminator: 1, YourDiscriminator: 25, 88 | DesiredMinTxInterval: 1000000, RequiredMinRxInterval: 1000000, RequiredMinEchoRxInterval: 0, 89 | AuthHeader: nil, 90 | }, 91 | }, 92 | { 93 | Name: "Flag: Poll", 94 | Data: []byte{0x20, 0xe0, 0x03, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x0f, 0x42, 0x40, 0x00, 0x0f, 0x42, 0x40, 0x00, 0x00, 0x00, 0x00}, 95 | Packet: BfdControlPacket{ 96 | Version: 1, Diagnostic: DIAG_NONE, State: STATE_UP, 97 | Poll: true, Final: false, ControlPlaneIndependent: false, AuthPresent: false, Demand: false, Multipoint: false, 98 | DetectMult: 3, MyDiscriminator: 1, YourDiscriminator: 25, 99 | DesiredMinTxInterval: 1000000, RequiredMinRxInterval: 1000000, RequiredMinEchoRxInterval: 0, 100 | AuthHeader: nil, 101 | }, 102 | }, 103 | { 104 | Name: "Flag: Final", 105 | Data: []byte{0x20, 0xd0, 0x03, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x0f, 0x42, 0x40, 0x00, 0x0f, 0x42, 0x40, 0x00, 0x00, 0x00, 0x00}, 106 | Packet: BfdControlPacket{ 107 | Version: 1, Diagnostic: DIAG_NONE, State: STATE_UP, 108 | Poll: false, Final: true, ControlPlaneIndependent: false, AuthPresent: false, Demand: false, Multipoint: false, 109 | DetectMult: 3, MyDiscriminator: 1, YourDiscriminator: 25, 110 | DesiredMinTxInterval: 1000000, RequiredMinRxInterval: 1000000, RequiredMinEchoRxInterval: 0, 111 | AuthHeader: nil, 112 | }, 113 | }, 114 | { 115 | Name: "Flag: Control Plane Independent", 116 | Data: []byte{0x20, 0xc8, 0x03, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x0f, 0x42, 0x40, 0x00, 0x0f, 0x42, 0x40, 0x00, 0x00, 0x00, 0x00}, 117 | Packet: BfdControlPacket{ 118 | Version: 1, Diagnostic: DIAG_NONE, State: STATE_UP, 119 | Poll: false, Final: false, ControlPlaneIndependent: true, AuthPresent: false, Demand: false, Multipoint: false, 120 | DetectMult: 3, MyDiscriminator: 1, YourDiscriminator: 25, 121 | DesiredMinTxInterval: 1000000, RequiredMinRxInterval: 1000000, RequiredMinEchoRxInterval: 0, 122 | AuthHeader: nil, 123 | }, 124 | }, 125 | { 126 | Name: "Flag: Demand", 127 | Data: []byte{0x20, 0xc2, 0x03, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x0f, 0x42, 0x40, 0x00, 0x0f, 0x42, 0x40, 0x00, 0x00, 0x00, 0x00}, 128 | Packet: BfdControlPacket{ 129 | Version: 1, Diagnostic: DIAG_NONE, State: STATE_UP, 130 | Poll: false, Final: false, ControlPlaneIndependent: false, AuthPresent: false, Demand: true, Multipoint: false, 131 | DetectMult: 3, MyDiscriminator: 1, YourDiscriminator: 25, 132 | DesiredMinTxInterval: 1000000, RequiredMinRxInterval: 1000000, RequiredMinEchoRxInterval: 0, 133 | AuthHeader: nil, 134 | }, 135 | }, 136 | { 137 | Name: "Flag: Multipoint", 138 | Data: []byte{0x20, 0xc1, 0x03, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x0f, 0x42, 0x40, 0x00, 0x0f, 0x42, 0x40, 0x00, 0x00, 0x00, 0x00}, 139 | Packet: BfdControlPacket{ 140 | Version: 1, Diagnostic: DIAG_NONE, State: STATE_UP, 141 | Poll: false, Final: false, ControlPlaneIndependent: false, AuthPresent: false, Demand: false, Multipoint: true, 142 | DetectMult: 3, MyDiscriminator: 1, YourDiscriminator: 25, 143 | DesiredMinTxInterval: 1000000, RequiredMinRxInterval: 1000000, RequiredMinEchoRxInterval: 0, 144 | AuthHeader: nil, 145 | }, 146 | }, 147 | { 148 | Name: "Detection: Multiplier", 149 | Data: []byte{0x20, 0xc0, 0x0a, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x0f, 0x42, 0x40, 0x00, 0x0f, 0x42, 0x40, 0x00, 0x00, 0x00, 0x00}, 150 | Packet: BfdControlPacket{ 151 | Version: 1, Diagnostic: DIAG_NONE, State: STATE_UP, 152 | Poll: false, Final: false, ControlPlaneIndependent: false, AuthPresent: false, Demand: false, Multipoint: false, 153 | DetectMult: 10, MyDiscriminator: 1, YourDiscriminator: 25, 154 | DesiredMinTxInterval: 1000000, RequiredMinRxInterval: 1000000, RequiredMinEchoRxInterval: 0, 155 | AuthHeader: nil, 156 | }, 157 | }, 158 | { 159 | Name: "Detection: DesiredMinTxInterval", 160 | Data: []byte{0x20, 0xc0, 0x0a, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x2d, 0xc6, 0xc0, 0x00, 0x0f, 0x42, 0x40, 0x00, 0x00, 0x00, 0x00}, 161 | Packet: BfdControlPacket{ 162 | Version: 1, Diagnostic: DIAG_NONE, State: STATE_UP, 163 | Poll: false, Final: false, ControlPlaneIndependent: false, AuthPresent: false, Demand: false, Multipoint: false, 164 | DetectMult: 10, MyDiscriminator: 1, YourDiscriminator: 25, 165 | DesiredMinTxInterval: 3000000, RequiredMinRxInterval: 1000000, RequiredMinEchoRxInterval: 0, 166 | AuthHeader: nil, 167 | }, 168 | }, 169 | { 170 | Name: "Detection: RequiredMinRxInterval", 171 | Data: []byte{0x20, 0xc0, 0x0a, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x0f, 0x42, 0x40, 0x00, 0x2d, 0xc6, 0xc0, 0x00, 0x00, 0x00, 0x00}, 172 | Packet: BfdControlPacket{ 173 | Version: 1, Diagnostic: DIAG_NONE, State: STATE_UP, 174 | Poll: false, Final: false, ControlPlaneIndependent: false, AuthPresent: false, Demand: false, Multipoint: false, 175 | DetectMult: 10, MyDiscriminator: 1, YourDiscriminator: 25, 176 | DesiredMinTxInterval: 1000000, RequiredMinRxInterval: 3000000, RequiredMinEchoRxInterval: 0, 177 | AuthHeader: nil, 178 | }, 179 | }, 180 | { 181 | Name: "Detection: RequiredMinEchoRxInterval", 182 | Data: []byte{0x20, 0xc0, 0x0a, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x0f, 0x42, 0x40, 0x00, 0x0f, 0x42, 0x40, 0x00, 0x0f, 0x42, 0x40}, 183 | Packet: BfdControlPacket{ 184 | Version: 1, Diagnostic: DIAG_NONE, State: STATE_UP, 185 | Poll: false, Final: false, ControlPlaneIndependent: false, AuthPresent: false, Demand: false, Multipoint: false, 186 | DetectMult: 10, MyDiscriminator: 1, YourDiscriminator: 25, 187 | DesiredMinTxInterval: 1000000, RequiredMinRxInterval: 1000000, RequiredMinEchoRxInterval: 1000000, 188 | AuthHeader: nil, 189 | }, 190 | }, 191 | { 192 | Name: "Authentication: SIMPLE", 193 | Data: []byte{ 194 | 0x20, 0xc4, 0x03, 0x23, 0x00, 0x00, 0x00, 0x01, // State: UP, Auth Present 195 | 0x00, 0x00, 0x00, 0x19, 0x00, 0x0f, 0x42, 0x40, 196 | 0x00, 0x0f, 0x42, 0x40, 0x00, 0x00, 0x00, 0x00, 197 | 0x01, 0x0b, 0x01, 198 | 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, // password 199 | }, 200 | Packet: BfdControlPacket{ 201 | Version: 1, Diagnostic: DIAG_NONE, State: STATE_UP, 202 | Poll: false, Final: false, ControlPlaneIndependent: false, AuthPresent: true, Demand: false, Multipoint: false, 203 | DetectMult: 3, MyDiscriminator: 1, YourDiscriminator: 25, 204 | DesiredMinTxInterval: 1000000, RequiredMinRxInterval: 1000000, RequiredMinEchoRxInterval: 0, 205 | AuthHeader: &BfdAuthHeader{ 206 | Type: BFD_AUTH_TYPE_SIMPLE, 207 | AuthKeyID: 1, 208 | AuthData: []byte{0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64}, 209 | }, 210 | }, 211 | }, 212 | { 213 | Name: "Authentication: Keyed MD5", 214 | Data: []byte{ 215 | 0x20, 0xc4, 0x03, 0x30, 0x00, 0x00, 0x00, 0x01, // State: UP, Auth Present 216 | 0x00, 0x00, 0x00, 0x19, 0x00, 0x0f, 0x42, 0x40, 217 | 0x00, 0x0f, 0x42, 0x40, 0x00, 0x00, 0x00, 0x00, 218 | 0x02, 0x18, 0x01, 0x00, 219 | 0x00, 0x00, 0x00, 0x01, 220 | 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, // abcdefghijklmnop 221 | }, 222 | Packet: BfdControlPacket{ 223 | Version: 1, Diagnostic: DIAG_NONE, State: STATE_UP, 224 | Poll: false, Final: false, ControlPlaneIndependent: false, AuthPresent: true, Demand: false, Multipoint: false, 225 | DetectMult: 3, MyDiscriminator: 1, YourDiscriminator: 25, 226 | DesiredMinTxInterval: 1000000, RequiredMinRxInterval: 1000000, RequiredMinEchoRxInterval: 0, 227 | AuthHeader: &BfdAuthHeader{ 228 | Type: BFD_AUTH_TYPE_KEYED_MD5, 229 | AuthKeyID: 1, 230 | SequenceNumber: 1, 231 | AuthData: []byte{0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70}, 232 | }, 233 | }, 234 | }, 235 | { 236 | Name: "Authentication: Keyed SHA1", 237 | Data: []byte{ 238 | 0x20, 0xc4, 0x03, 0x34, 0x00, 0x00, 0x00, 0x01, // State: UP, Auth Present 239 | 0x00, 0x00, 0x00, 0x19, 0x00, 0x0f, 0x42, 0x40, 240 | 0x00, 0x0f, 0x42, 0x40, 0x00, 0x00, 0x00, 0x00, 241 | 0x04, 0x1c, 0x01, 0x00, 242 | 0x00, 0x00, 0x00, 0x01, 243 | 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, // abcdefghijklmnopqrst 244 | }, 245 | Packet: BfdControlPacket{ 246 | Version: 1, Diagnostic: DIAG_NONE, State: STATE_UP, 247 | Poll: false, Final: false, ControlPlaneIndependent: false, AuthPresent: true, Demand: false, Multipoint: false, 248 | DetectMult: 3, MyDiscriminator: 1, YourDiscriminator: 25, 249 | DesiredMinTxInterval: 1000000, RequiredMinRxInterval: 1000000, RequiredMinEchoRxInterval: 0, 250 | AuthHeader: &BfdAuthHeader{ 251 | Type: BFD_AUTH_TYPE_KEYED_SHA1, 252 | AuthKeyID: 1, 253 | SequenceNumber: 1, 254 | AuthData: []byte{0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74}, 255 | }, 256 | }, 257 | }, 258 | } 259 | 260 | /* 261 | * Loop through the data sets to test decoding 262 | */ 263 | func TestDecodeBfdControlPacket(t *testing.T) { 264 | var got *BfdControlPacket 265 | var err error 266 | 267 | for _, e := range tests { 268 | got, err = decodeBfdPacket(e.Data) 269 | if err != nil { 270 | fmt.Println("Error decoding BFD Control Packet:", err) 271 | } 272 | if !reflect.DeepEqual(&e.Packet, got) { 273 | t.Errorf("BFD mismatch for test '%s', \nexpected:\n%#v\n\ngot:\n%#v\n\n", e.Name, e.Packet, got) 274 | if e.Packet.AuthHeader != nil { 275 | t.Errorf("Auth expected:\n%#v\n\ngot:\n%#v\n\n", e.Packet.AuthHeader, got.AuthHeader) 276 | } 277 | } 278 | } 279 | } 280 | 281 | /* 282 | * Loop through the data sets to test marshalling 283 | */ 284 | func TestMarshalBfdControlPacket(t *testing.T) { 285 | var got []byte 286 | 287 | for _, e := range tests { 288 | got = e.Packet.Marshal() 289 | if got == nil { 290 | fmt.Println("Error Marshalling BFD Control Packet:", e.Name) 291 | } 292 | if !bytes.Equal(e.Data, got) { 293 | t.Errorf("BFD mismatch for test '%s', \nexpected:\n%#v\n\ngot:\n%#v\n\n", e.Name, e.Data, got) 294 | } 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /session.go: -------------------------------------------------------------------------------- 1 | package bfd 2 | 3 | type BfdStatus struct { 4 | SessionState int 5 | RemoteSessionState int 6 | LocalDiscr uint32 7 | RemoteDiscr uint32 8 | LocalDiat int 9 | DesiredMinTxInterval int 10 | RequiredMinRxInterval int 11 | RemoteMinRxInterval int 12 | DemandMode bool 13 | RemoteDemandMode bool 14 | DetectMult uint8 15 | AuthType AuthenticationType 16 | RcvAuthSeq uint32 17 | XmitAuthSeq uint32 18 | AuthSeqKnown bool 19 | } 20 | 21 | /* State Machine 22 | +--+ 23 | | | UP, ADMIN DOWN, TIMER 24 | | V 25 | DOWN +------+ INIT 26 | +------------| |------------+ 27 | | | DOWN | | 28 | | +-------->| |<--------+ | 29 | | | +------+ | | 30 | | | | | 31 | | | ADMIN DOWN,| | 32 | | |ADMIN DOWN, DOWN,| | 33 | | |TIMER TIMER| | 34 | V | | V 35 | +------+ +------+ 36 | +----| | | |----+ 37 | DOWN| | INIT |--------------------->| UP | |INIT, UP 38 | +--->| | INIT, UP | |<---+ 39 | +------+ +------+ 40 | */ 41 | --------------------------------------------------------------------------------