├── AUTHORS ├── .gitignore ├── .travis.yml ├── doc.go ├── client.go ├── cmd └── main.go ├── LICENSE ├── README.md ├── msg_test.go ├── types.go ├── error.go ├── param.go ├── attr.go └── msg.go /AUTHORS: -------------------------------------------------------------------------------- 1 | Miek Gieben 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | cmd/cmd 2 | tags 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.3 5 | - 1.4 6 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package bgp implements the BGP-4 protocol as described in RFC 4271 and subsequent RFCs. 3 | It is able to parse all BGP messages and deals with 32 bit ASNs (support for 16 bit ASNs 4 | is not implemented). 5 | */ 6 | package bgp 7 | -------------------------------------------------------------------------------- /client.go: -------------------------------------------------------------------------------- 1 | package bgp 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | ) 7 | 8 | // Do sends a bgp message to the connection conn and waits for a reply. 9 | // The reply message is returned or an error, if one is encountered. 10 | func Do(conn net.Conn, m Msg) (Msg, error) { 11 | buf := bytes(m) 12 | n, err := conn.Write(buf) 13 | if err != nil { 14 | return nil, err 15 | } 16 | 17 | buf1 := make([]byte, MaxSize) 18 | n, err = conn.Read(buf1) 19 | if err != nil { 20 | return nil, err 21 | } 22 | buf1 = buf1[:n] 23 | 24 | fmt.Printf("%v\n", buf1) 25 | m1, n, err := setBytes(buf1) 26 | if err != nil { 27 | return nil, err 28 | } 29 | return m1, nil 30 | } 31 | -------------------------------------------------------------------------------- /cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net" 6 | 7 | "github.com/miekg/bgp" 8 | ) 9 | 10 | // Connect to a BGP server and send an Open message with a parameters 11 | // advertizing that we can do 32 bit ASN. 12 | 13 | func main() { 14 | conn, err := net.Dial("tcp", "localhost:179") 15 | if err != nil { 16 | log.Fatalf("%s", err) 17 | } 18 | 19 | // Create Open message... 20 | open := &bgp.Open{HoldTime: 80, BGPIdentifier: net.ParseIP("127.0.0.1").To4()} 21 | // ... with some capabilities. 22 | open.Parameters = make([]bgp.Parameter, 1) 23 | 24 | c := &bgp.Capability{} 25 | c.Append(bgp.CAP_AS4, 80000) 26 | 27 | open.Parameters[0].Append(bgp.CAP, c) 28 | 29 | log.Printf("%+v\n", open) 30 | 31 | resp, err := bgp.Do(conn, open) 32 | if err != nil { 33 | log.Fatalf("%s", err) 34 | } 35 | 36 | log.Printf("%+v\n", resp) 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 The BGP Authors 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/miekg/bgp.svg?branch=master)](https://travis-ci.org/miekg/bgp) 2 | 3 | # BGP 4 | 5 | BGP is a BGP-4 implementation in Go. 6 | 7 | ## RFCs 8 | 9 | * BGP Communities: 10 | * Capabilities Advertisement with BGP-4: 11 | * BGP-4: 12 | * BGP Extended Communities: 13 | * BGP 32 bit AS numbers: 14 | * BGP 32 bit AS numbers: 15 | 16 | 17 | ## Notes 18 | 19 | Parameters in the open message seems not to be used. Can hide it 20 | in its entirety and just focus on capabilities. i.e make Append() 21 | work on the open message itself. 22 | 23 | Also the data in now a []TLV, maybe it would be nicer to have a 24 | map map[int]TLV indexed on the Code so we can implement a .Clear(code int) 25 | as well to clear a message. 26 | 27 | ## TODO 28 | 29 | * multiple message after each other, i.e. Open and Notifcation. 30 | * fix all error uses 31 | * create server infra ala net/http, godns 32 | * Fix Path Attributes, these are like dns RR's, define an interface 33 | and use reflection to pack/unpack. 34 | * Unpack doesn't do header, Pack does do header 35 | Makes more sense if they *all* do or neither. 36 | * Tests! 37 | -------------------------------------------------------------------------------- /msg_test.go: -------------------------------------------------------------------------------- 1 | package bgp 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | ) 7 | 8 | type testCase struct { 9 | in []byte // buffer to parse 10 | n int // number of bytes we should have parsed 11 | msg Msg // resulting message 12 | } 13 | 14 | var tests = []testCase{ 15 | { 16 | []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 45, 1, 4, 253, 232, 0, 240, 176, 58, 119, 54, 16, 2, 14, 1, 4, 0, 1, 0, 1, 2, 0, 65, 4, 0, 0, 253, 232}, 17 | 45, 18 | &Open{ 19 | Version: 4, 20 | AS: 65000, 21 | HoldTime: 240, 22 | BGPIdentifier: net.ParseIP("b03a:7736::ffff:0:0"), 23 | //Parameters:[{Type:2 data:[0x1842e0b0]}] header:0x1842e080} 24 | 25 | }, 26 | }, 27 | } 28 | 29 | func msgCompare(t *testing.T, te Msg, a Msg) { 30 | switch typ := a.(type) { 31 | case *Open: 32 | te := te.(*Open) 33 | a := a.(*Open) 34 | if te.Version != a.Version { 35 | t.Fatalf("open version mismatch: expected %d, got %d", te.Version, a.Version) 36 | } 37 | if te.AS != a.AS { 38 | t.Fatalf("open as mismatch: expected %d, got %d", te.AS, a.AS) 39 | } 40 | if te.HoldTime != a.HoldTime { 41 | t.Fatalf("open holdtime mismatch: expected %d, got %d", te.HoldTime, a.HoldTime) 42 | } 43 | t.Logf("%s\n", a.BGPIdentifier) 44 | t.Logf("%s\n", te.BGPIdentifier) 45 | default: 46 | t.Fatalf("unknown message type %T", typ) 47 | } 48 | } 49 | 50 | func TestMsgsetBytes(t *testing.T) { 51 | for _, te := range tests { 52 | m, n, e := setBytes(te.in) 53 | if e != nil { 54 | t.Fatalf("setBytes() failed: %s", e) 55 | } 56 | if n != te.n { 57 | t.Fatalf("parsed octets: expected %d, got %d", te.n, n) 58 | } 59 | msgCompare(t, te.msg, m) // will Fatalf for us. 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /types.go: -------------------------------------------------------------------------------- 1 | package bgp 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | const ( 8 | _ = iota 9 | 10 | // The different types of messages. 11 | open 12 | update 13 | notification 14 | keepalive 15 | routerefresh // See RFC 2918 16 | 17 | headerLen = 19 18 | 19 | MaxSize = 4096 // Maximum size of a BGP message. 20 | Version = 4 // Current defined version of BGP. 21 | ) 22 | 23 | // TLV is a Type-Length-Value that is used in all on-the-wire messages. 24 | type TLV interface { 25 | // Bytes return the bytes of the value in wire format. 26 | Bytes() []byte 27 | // SetBytes sets the value of the TLV, the bytes must be in network order. 28 | // It returns a new offset in the bytes slice. 29 | SetBytes([]byte) (int, error) 30 | 31 | // maybe add Append here as well. Append(t int, v ...interface{}) error 32 | } 33 | 34 | // Message is a BGP message. 35 | type Msg interface { 36 | bytes() []byte 37 | setBytes([]byte) (int, error) 38 | } 39 | 40 | // Open holds the information used in the OPEN message format. RFC 4271, Section 4.2. 41 | type Open struct { 42 | // Version MUST be 4. If it is zero, 4 will be used when converting to wire format. 43 | Version uint8 44 | // AS should be AS_TRANS when using 32 bit ASN. If this is 0, AS_TRANS will be 45 | // substitued when converting to wire format. 46 | AS uint16 47 | HoldTime uint16 48 | // BGPIdentifier must always be 4 bytes. It will be truncated to 4 bytes 49 | // when the Open messgae is converted to wire format. 50 | BGPIdentifier net.IP 51 | Parameters []Parameter 52 | 53 | *header 54 | } 55 | 56 | // Update holds the information used in the UPDATE message format. RFC 4271, section 4.3 57 | type Update struct { 58 | WithdrawnRoutes []Prefix 59 | Attributes []Attribute 60 | ReachabilityInfo []Prefix 61 | 62 | *header 63 | } 64 | 65 | // Keepalive holds only the header and is used for keep alive pings. 66 | type Keepalive struct { 67 | *header 68 | } 69 | 70 | // Notification holds an error. The TCP connection is closed after sending it. 71 | type Notification struct { 72 | ErrorCode uint8 73 | ErrorSubcode uint8 74 | Data []byte 75 | *header 76 | } 77 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | package bgp 2 | 3 | import "strconv" 4 | 5 | // Error is an error the BGP protocol can return. 6 | type Error struct { 7 | Code int // Code as defined in RFC 4271. 8 | Subcode int // Subcode as defined in RFC 4271. 9 | Err string // Non mandatory extra text added by this package. 10 | } 11 | 12 | // NewError returns a pointer to an Error. 13 | func NewError(code, subcode int, extra string) *Error { 14 | return &Error{code, subcode, extra} 15 | } 16 | 17 | func (e *Error) Error() string { 18 | s := "bgp: " 19 | if v, ok := errorCodes[e.Code]; ok { 20 | s += v 21 | } else { 22 | s += strconv.Itoa(e.Code) 23 | } 24 | s += ": " 25 | 26 | v := strconv.Itoa(e.Subcode) 27 | switch e.Code { 28 | case 1: 29 | if _, ok := errorSubcodesHeader[e.Subcode]; ok { 30 | v = errorSubcodesHeader[e.Subcode] 31 | } 32 | case 2: 33 | if _, ok := errorSubcodesOpen[e.Subcode]; ok { 34 | v = errorSubcodesOpen[e.Subcode] 35 | } 36 | case 3: 37 | if _, ok := errorSubcodesUpdate[e.Subcode]; ok { 38 | v = errorSubcodesUpdate[e.Subcode] 39 | } 40 | } 41 | s += v 42 | if e.Err != "" { 43 | s += ": " + e.Err 44 | } 45 | return s 46 | } 47 | 48 | var errBuf = &Error{Err: "buffer size too small"} 49 | 50 | var errorCodes = map[int]string{ 51 | 1: "message header error", 52 | 2: "OPEN message error", 53 | 3: "UPDATE message error", 54 | 4: "hold timer expired", 55 | 5: "finite state machine error", 56 | 6: "cease", 57 | } 58 | 59 | var errorSubcodesHeader = map[int]string{ 60 | 1: "connection not synchronized", 61 | 2: "bad message length", 62 | 3: "bad message type", 63 | } 64 | 65 | var errorSubcodesOpen = map[int]string{ 66 | 1: "unsupported version number", 67 | 2: "bad peer AS", 68 | 3: "bad BGP identifier", 69 | 4: "unsupported optional parameter", 70 | // 5 deprecated 71 | 6: "unacceptable hold time", 72 | 7: "unsupported capability", 73 | } 74 | 75 | var errorSubcodesUpdate = map[int]string{ 76 | 1: "malformed attribute list", 77 | 2: "unrecognized well-known attribute", 78 | 3: "missing well-known attribute", 79 | 4: "attribute flags error", 80 | 5: "attribute length error", 81 | 6: "invalid ORIGIN attribute", 82 | // 7 deprecated 83 | 8: "invalid NEXT_HOP attribute", 84 | 9: "optional attribute error", 85 | 10: "invalid network field", 86 | 11: "malformed AS_PATH", 87 | } 88 | -------------------------------------------------------------------------------- /param.go: -------------------------------------------------------------------------------- 1 | package bgp 2 | 3 | import "encoding/binary" 4 | 5 | // Parameter is used in the Open message to negotiate options. 6 | type Parameter struct { 7 | Type uint8 8 | data []TLV 9 | } 10 | 11 | func (p *Parameter) Append(t int, v TLV) { 12 | p.Type = uint8(t) 13 | p.data = append(p.data, v) 14 | } 15 | 16 | func (p *Parameter) Bytes() []byte { 17 | buf := []byte{} 18 | for _, d := range p.data { 19 | buf = append(buf, d.Bytes()...) 20 | } 21 | return append([]byte{p.Type, byte(len(buf))}, buf...) 22 | } 23 | 24 | func (p *Parameter) SetBytes(buf []byte) (int, error) { 25 | if len(buf) < 3 { 26 | return 0, errBuf 27 | } 28 | p.Type = buf[0] 29 | length := int(buf[1]) 30 | if len(buf) < length { 31 | return 0, errBuf 32 | } 33 | switch p.Type { 34 | case CAP: 35 | c := &Capability{} 36 | 37 | i := 2 38 | for i < length { 39 | n, e := c.SetBytes(buf[i:]) 40 | if e != nil { 41 | return i + n, e 42 | } 43 | i += n 44 | } 45 | p.Append(CAP, c) 46 | default: 47 | println("bgp: unknown type", p.Type) 48 | } 49 | return length + 2, nil // Add 2 for the 2 byte header 50 | } 51 | 52 | const ( 53 | CAP = 2 54 | ) 55 | 56 | // The different capabilities 57 | const ( 58 | _ = iota 59 | CAP_MULTI_PROTOCOL 60 | CAP_ROUTE_REFRESH 61 | CAP_ROUTE_FILTERING 62 | CAP_MULTIPLE_ROUTES 63 | CAP_EXTENDED_NEXTHOP 64 | 65 | CAP_GRACEFUL_RESTART = 64 66 | CAP_AS4 = 65 67 | ) 68 | 69 | type typeData struct { 70 | t int 71 | d []byte 72 | } 73 | 74 | type Capability struct { 75 | data []typeData 76 | } 77 | 78 | func (c *Capability) Append(t int, v ...interface{}) error { 79 | switch t { 80 | case CAP_MULTI_PROTOCOL: 81 | if len(v) != 2 { 82 | return nil 83 | } 84 | d := make([]byte, 4) 85 | binary.BigEndian.PutUint16(d, uint16(v[0].(int))) 86 | d[3] = uint8(v[1].(int)) 87 | c.data = append(c.data, typeData{CAP_MULTI_PROTOCOL, d}) 88 | case CAP_ROUTE_REFRESH: 89 | c.data = append(c.data, typeData{CAP_ROUTE_REFRESH, nil}) 90 | case CAP_AS4: 91 | d := make([]byte, 4) 92 | binary.BigEndian.PutUint32(d, uint32(v[0].(int))) 93 | c.data = append(c.data, typeData{CAP_AS4, d}) 94 | default: 95 | // unknown capability 96 | } 97 | return nil 98 | } 99 | 100 | func (c *Capability) Bytes() []byte { 101 | buf := make([]byte, 0) 102 | for _, d := range c.data { 103 | switch d.t { 104 | case CAP_MULTI_PROTOCOL: 105 | buf = append(buf, CAP_MULTI_PROTOCOL) 106 | buf = append(buf, 4) // length 107 | buf = append(buf, d.d...) 108 | case CAP_ROUTE_REFRESH: 109 | buf = append(buf, CAP_ROUTE_REFRESH) 110 | buf = append(buf, 0) // length 111 | // no data 112 | case CAP_AS4: 113 | buf = append(buf, CAP_AS4) 114 | buf = append(buf, 4) // length 115 | buf = append(buf, d.d...) 116 | } 117 | } 118 | return buf 119 | } 120 | 121 | func (c *Capability) SetBytes(buf []byte) (int, error) { 122 | i := 0 123 | for i < len(buf) { 124 | switch buf[i] { 125 | // i+1 will overflow: TODO 126 | case CAP_MULTI_PROTOCOL: 127 | if buf[i+1] != 4 { 128 | println("bgp: CAP_MULTI_PROTOCOL not 4 bytes", int(buf[i+1])) 129 | return i, errBuf 130 | } 131 | afi := int(binary.BigEndian.Uint16(buf[i+2:])) 132 | safi := int((buf[i+5])) 133 | c.Append(CAP_MULTI_PROTOCOL, afi, safi) 134 | i += 6 135 | case CAP_ROUTE_REFRESH: 136 | if buf[i+1] != 0 { 137 | println("bgp: CAP_ROUTE_REFRESH not 0 bytes", int(buf[i+1])) 138 | return i, errBuf 139 | } 140 | c.Append(CAP_ROUTE_REFRESH, nil) 141 | i += 2 142 | case CAP_AS4: 143 | if len(buf[i:]) < 6 { 144 | return i, errBuf 145 | } 146 | if buf[i+1] != 4 { 147 | println("bgp: CAP_AS4 not 4 bytes") 148 | return i, errBuf 149 | } 150 | // We going from binary->uint32->binary, we might not be the best way 151 | v := binary.BigEndian.Uint32(buf[i+2 : i+6]) 152 | c.Append(CAP_AS4, int(v)) 153 | i += 7 154 | default: 155 | println("bgp: unknown capability", buf[i]) 156 | i++ 157 | } 158 | } 159 | return i, nil 160 | } 161 | -------------------------------------------------------------------------------- /attr.go: -------------------------------------------------------------------------------- 1 | package bgp 2 | 3 | // Path attributes as used in the Update message. 4 | 5 | import ( 6 | "encoding/binary" 7 | "net" 8 | ) 9 | 10 | // Define the types used for well-known path attributes in an Update message. 11 | const ( 12 | _ = iota 13 | origin 14 | path 15 | next_hop 16 | multi_exit_disc 17 | local_pref 18 | atomic_aggregate 19 | aggregator 20 | communities 21 | ) 22 | 23 | // Values used in the well-known path attributes. 24 | const ( 25 | // ORIGIN 26 | IGP = 0 27 | EGP = 1 28 | INCOMPLETE = 2 29 | 30 | // PATH 31 | AS_SET = 1 32 | AS_SEQUENCE = 2 33 | 34 | // COMMUNITIES 35 | NO_EXPORT = uint32(0xFFFFFF01) 36 | NO_ADVERTISE = uint32(0xFFFFFF02) 37 | NO_EXPORT_SUBCONFED = uint32(0xFFFFFF03) 38 | ) 39 | 40 | const AS_TRANS = 23456 41 | 42 | // Path attribute header flags. 43 | const ( 44 | FlagOptional = 1 << 8 45 | FlagTransitive = 1 << 7 46 | FlagPartial = 1 << 6 47 | FlagLength = 1 << 5 48 | ) 49 | 50 | // Attribute is a path attribute as used in the Update message. 51 | type Attribute struct { 52 | Flags uint8 53 | Code uint8 54 | Length uint16 55 | data []TLV 56 | 57 | // maybe put the data in a map based on Code. So Cel 58 | } 59 | 60 | func (p *Attribute) Append(t int, v TLV) error { 61 | p.Code = uint8(t) 62 | p.data = append(p.data, v) 63 | return nil 64 | } 65 | 66 | func (p *Attribute) Bytes() []byte { 67 | buf := []byte{} 68 | for _, d := range p.data { 69 | buf = append(buf, d.Bytes()...) 70 | } 71 | header := make([]byte, 4) 72 | header[0] = p.Flags 73 | header[1] = p.Code 74 | if len(buf) > 65536-1 { 75 | header[0] |= FlagLength 76 | binary.BigEndian.PutUint16(header[2:], uint16(p.Length)) 77 | } else { 78 | header[2] = uint8(len(buf)) 79 | header = header[:3] 80 | } 81 | return append(header, buf...) 82 | } 83 | 84 | func (p *Attribute) SetBytes(buf []byte) (int, error) { 85 | // TODO(Miek): does not work. 86 | if len(buf) < 3 { 87 | return 0, errBuf 88 | } 89 | p.Flags = buf[0] 90 | p.Code = buf[1] 91 | if p.Flags&FlagLength == FlagLength { 92 | if len(buf) < 4 { 93 | return 2, errBuf 94 | } 95 | p.Length = binary.BigEndian.Uint16(buf[2:]) 96 | return 4, nil 97 | } 98 | p.Length = uint16(buf[2]) 99 | return 3, nil 100 | } 101 | 102 | // Origin implements the ORIGIN path attribute. 103 | type Origin uint8 104 | 105 | func (p *Origin) Bytes() []byte { return []byte{uint8(*p)} } 106 | func (p *Origin) SetBytes(buf []byte) (int, error) { 107 | if len(buf) < 1 { 108 | return 0, errBuf 109 | } 110 | *p = Origin(buf[0]) 111 | return 1, nil 112 | } 113 | 114 | // Community implements RFC 1997 COMMUNITIES path attribute. 115 | type Community []uint32 116 | 117 | func (p *Community) Bytes() []byte { 118 | buf := make([]byte, 4*len(*p)) 119 | for i, v := range *p { 120 | binary.BigEndian.PutUint32(buf[i*4:], v) 121 | } 122 | return buf 123 | } 124 | 125 | func (p *Community) SetBytes(buf []byte) (int, error) { 126 | offset := 0 127 | for offset < len(buf)-4 { 128 | *p = append(*p, binary.BigEndian.Uint32(buf[offset:])) 129 | offset += 4 130 | } 131 | return offset, nil 132 | } 133 | 134 | // AsPath implements the AS_PATH path attribute. 135 | type Path []AsPath 136 | 137 | // Hide AsPath, use append to add to Path. 138 | type AsPath struct { 139 | Type uint8 // Either AS_SET of AS_SEQUENCE. 140 | AS []uint32 // The AS numbers as 32 bit entities. 141 | } 142 | 143 | func (p *Path) Bytes() []byte { 144 | var buf []byte 145 | for _, a := range *p { 146 | b := make([]byte, 2+4*len(a.AS)) 147 | b[0] = a.Type 148 | b[1] = byte(len(a.AS)) 149 | offset := 2 150 | for _, as := range a.AS { 151 | binary.BigEndian.PutUint32(b[offset:], as) 152 | offset += 4 153 | } 154 | buf = append(buf, b...) 155 | } 156 | return buf 157 | } 158 | 159 | func (p *Path) SetBytes(buf []byte) (int, error) { 160 | return 0, nil 161 | } 162 | 163 | type NextHop net.IP 164 | 165 | func (p *NextHop) Bytes() []byte { 166 | return nil 167 | } 168 | 169 | func (p *NextHop) SetBytes(buf []byte) (int, error) { 170 | return 0, nil 171 | } 172 | -------------------------------------------------------------------------------- /msg.go: -------------------------------------------------------------------------------- 1 | package bgp 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "net" 7 | ) 8 | 9 | type header struct { 10 | Length uint16 11 | Type uint8 12 | } 13 | 14 | func (h *header) bytes() []byte { 15 | buf := make([]byte, 19) 16 | buf[0], buf[1], buf[2], buf[3] = 0xff, 0xff, 0xff, 0xff 17 | buf[4], buf[5], buf[6], buf[7] = 0xff, 0xff, 0xff, 0xff 18 | buf[8], buf[9], buf[10], buf[11] = 0xff, 0xff, 0xff, 0xff 19 | buf[12], buf[13], buf[14], buf[15] = 0xff, 0xff, 0xff, 0xff 20 | 21 | binary.BigEndian.PutUint16(buf[16:], h.Length) 22 | buf[18] = h.Type 23 | 24 | return buf 25 | } 26 | 27 | func (h *header) setBytes(buf []byte) (int, error) { 28 | if len(buf) < headerLen { 29 | return 0, NewError(1, 2, fmt.Sprintf("unpack: buffer size too small: %d < %d", len(buf), headerLen)) 30 | } 31 | // Just skip the marker. 32 | h.Length = binary.BigEndian.Uint16(buf[16:]) 33 | h.Type = buf[18] 34 | return 19, nil 35 | } 36 | 37 | // Prefix is used as the (Length, Prefix) tuple in Update messages. 38 | type Prefix net.IPNet 39 | 40 | func (p *Prefix) size() int { _, bits := p.Mask.Size(); return bits } 41 | func (p *Prefix) bytes() []byte { return append([]byte{byte(p.size())}, p.IP...) } 42 | 43 | func (p *Prefix) setBytes(buf []byte) (int, error) { 44 | p.Mask = net.CIDRMask(int(buf[0]), int(buf[0])) 45 | 46 | for i := 0; i < int(p.size()/8); i++ { 47 | p.IP[i] = buf[1+i] 48 | } 49 | // now zero the last byte, otherwise there could be random crap in there. 50 | if mod := p.size() % 8; mod != 0 { 51 | // need to double check all this (and test!) 52 | buf[2+mod] &= ^(0xFF >> uint(mod)) 53 | } 54 | return 1 + int(p.size()/8), nil 55 | } 56 | 57 | func (m *Open) bytes() []byte { 58 | buf := make([]byte, 10) 59 | 60 | buf[0] = m.Version 61 | if m.Version == 0 { 62 | buf[0] = Version 63 | } 64 | binary.BigEndian.PutUint16(buf[1:], m.AS) 65 | if m.AS == 0 { 66 | binary.BigEndian.PutUint16(buf[1:], AS_TRANS) 67 | } 68 | binary.BigEndian.PutUint16(buf[3:], m.HoldTime) 69 | buf[5], buf[6], buf[7], buf[8] = 70 | m.BGPIdentifier[0], m.BGPIdentifier[1], m.BGPIdentifier[2], m.BGPIdentifier[3] 71 | 72 | pbuf := make([]byte, 0) 73 | for _, p := range m.Parameters { 74 | pbuf = append(pbuf, p.Bytes()...) 75 | } 76 | buf[9] = uint8(len(pbuf)) // Length of the parameters. 77 | buf = append(buf, pbuf...) 78 | 79 | m.header = &header{} 80 | m.Length = headerLen + uint16(10+len(pbuf)) 81 | m.Type = open 82 | 83 | header := m.header.bytes() 84 | return append(header, buf...) 85 | } 86 | 87 | func (m *Open) setBytes(buf []byte) (int, error) { 88 | m.header = &header{} 89 | offset, err := m.header.setBytes(buf) 90 | if err != nil { 91 | return offset, err 92 | } 93 | 94 | if len(buf) < int(m.Length) { 95 | return 0, NewError(2, 0, fmt.Sprintf("buffer size too small: %d < %d", len(buf), m.Length)) 96 | } 97 | 98 | buf = buf[offset:] 99 | m.Version = buf[0] 100 | m.AS = binary.BigEndian.Uint16(buf[1:]) 101 | m.HoldTime = binary.BigEndian.Uint16(buf[3:]) 102 | m.BGPIdentifier = net.IPv4(0, 0, 0, 0) 103 | m.BGPIdentifier[0], m.BGPIdentifier[1], m.BGPIdentifier[2], m.BGPIdentifier[3] = 104 | buf[5], buf[6], buf[7], buf[8] 105 | 106 | pLength := int(buf[9]) 107 | // offset = 10 108 | if len(buf) < 10+pLength { 109 | return offset, NewError(2, 0, fmt.Sprintf("buffer size too small: %d < %d", len(buf), offset+pLength)) 110 | } 111 | 112 | i := 0 113 | for i < pLength { 114 | p := Parameter{} 115 | n, e := p.SetBytes(buf[10+i:]) 116 | if e != nil { 117 | return 10 + i, e 118 | } 119 | i += n 120 | m.Parameters = append(m.Parameters, p) 121 | } 122 | return offset + 10 + i, nil 123 | } 124 | 125 | func (m *Keepalive) bytes() []byte { 126 | m.Length = headerLen 127 | m.Type = keepalive 128 | 129 | header := m.header.bytes() 130 | return header 131 | } 132 | 133 | func (m *Keepalive) setBytes(buf []byte) (int, error) { 134 | offset := 0 135 | 136 | m.header = &header{} 137 | offset, err := m.header.setBytes(buf) 138 | if err != nil { 139 | return offset, err 140 | } 141 | return offset, nil 142 | } 143 | 144 | /* 145 | func (m *Notification) bytes() []byte { 146 | m.Length = uint16(m.Len()) 147 | m.Type = NOTIFICATION 148 | header := m.header.Bytes() 149 | 150 | buf := make([]byte, m.Len()-len(header)) 151 | 152 | offset := 0 153 | 154 | buf[offset] = m.ErrorCode 155 | offset++ 156 | 157 | buf[offset] = m.ErrorSubcode 158 | offset++ 159 | 160 | copy(buf[offset:], m.Data) 161 | return append(header, buf...) 162 | } 163 | 164 | func (m *Notification) setBytes(buf []byte) (int, error) { 165 | m.header = &header 166 | offset, err := m.header.SetBytes(buf) 167 | if err != nil { 168 | return offset, err 169 | } 170 | 171 | if len(buf) < int(m.Length) { 172 | return 0, NewError(0, 0, fmt.Sprintf("buffer size too small: %d < %d", len(buf), m.Length)) 173 | } 174 | 175 | offset = 0 176 | m.ErrorCode = buf[offset] 177 | offset++ 178 | 179 | m.ErrorSubcode = buf[offset] 180 | offset++ 181 | 182 | // TODO(miek): copy data until end of message 183 | 184 | return offset, nil 185 | } 186 | 187 | func (m *Update) setBytes(buf []byte) (int, error) { 188 | m.Length = uint16(m.Len()) 189 | m.Type = UPDATE 190 | 191 | offset := 0 192 | n, err := m.header.SetBytes(buf[offset:]) 193 | if err != nil { 194 | return offset, err 195 | } 196 | offset += n 197 | 198 | // withdrawnRoutesLength 199 | wlengthOffset := offset 200 | offset += 2 201 | 202 | l := 0 203 | for _, w := range m.WithdrawnRoutes { 204 | n, err := w.pack(buf[offset:]) 205 | if err != nil { 206 | return offset, err 207 | } 208 | l += w.len() 209 | offset += n 210 | } 211 | binary.BigEndian.PutUint16(buf[wlengthOffset:], uint16(l)) 212 | 213 | plengthOffset := offset 214 | for _, p := range m.PathAttrs { 215 | n, err := p.Pack(buf[offset:]) 216 | if err != nil { 217 | return offset, err 218 | } 219 | l += p.Len() 220 | offset += n 221 | } 222 | binary.BigEndian.PutUint16(buf[plengthOffset:], uint16(l)) 223 | 224 | for _, r := range m.ReachabilityInfo { 225 | n, err := r.pack(buf[offset:]) 226 | if err != nil { 227 | return offset, err 228 | } 229 | l += r.len() 230 | offset += n 231 | } 232 | 233 | return offset, nil 234 | } 235 | 236 | // Unpack converts wire format in buf to an UPDATE message. 237 | // Unpack returns the amount of bytes parsed or an error. 238 | func (m *Update) Unpack(buf []byte) (int, error) { 239 | offset := 0 240 | 241 | m.header = &header{} 242 | offset, err := m.header.unpack(buf) 243 | if err != nil { 244 | return offset, err 245 | } 246 | 247 | if len(buf) < int(m.Length) { 248 | return 0, NewError(3, 0, fmt.Sprintf("buffer size too small: %d < %d", len(buf), m.Length)) 249 | } 250 | 251 | offset = 0 252 | 253 | wLength := int(binary.BigEndian.Uint16(buf[offset:])) 254 | offset += 2 255 | if len(buf) < offset+wLength { 256 | return offset, NewError(3, 0, fmt.Sprintf("buffer size too small: %d < %d", len(buf), offset+wLength)) 257 | } 258 | 259 | i := 0 260 | for i < wLength { 261 | p := Prefix{} 262 | n, e := p.unpack(buf[i+offset:]) 263 | if e != nil { 264 | return offset, e 265 | } 266 | i += n 267 | offset += n 268 | m.WithdrawnRoutes = append(m.WithdrawnRoutes, p) 269 | } 270 | 271 | pLength := int(binary.BigEndian.Uint16(buf[offset:])) 272 | offset += 2 273 | if len(buf) < offset+pLength { 274 | return offset, NewError(3, 0, fmt.Sprintf("buffer size too small: %d < %d", len(buf), offset+pLength)) 275 | } 276 | 277 | i = 0 278 | var ( 279 | p TLV 280 | e error 281 | n int 282 | ) 283 | for i < pLength { 284 | switch buf[i+offset+1] { //second byte has the type TODO(miek): should check if the access is valid 285 | case ORIGIN: 286 | p = new(Origin) 287 | n, e = p.Unpack(buf[i+offset:]) 288 | if e != nil { 289 | return offset, e 290 | } 291 | case AS_PATH: 292 | p = new(AsPath) 293 | n, e = p.Unpack(buf[i+offset:]) 294 | if e != nil { 295 | return offset, e 296 | } 297 | case COMMUNITIES: 298 | p = new(Community) 299 | n, e = p.Unpack(buf[i+offset:]) 300 | if e != nil { 301 | return offset, e 302 | } 303 | default: 304 | // unknown 305 | } 306 | i += n 307 | offset += n 308 | m.PathAttrs = append(m.PathAttrs, p) 309 | } 310 | 311 | rLength := int(m.Length) - offset 312 | if len(buf) < offset+rLength { 313 | return offset, NewError(3, 0, fmt.Sprintf("buffer size too small: %d < %d", len(buf), offset+rLength)) 314 | } 315 | 316 | i = 0 317 | for i < rLength { 318 | r := Prefix{} 319 | n, e := r.unpack(buf[i+offset:]) 320 | if e != nil { 321 | return offset, e 322 | } 323 | i += n 324 | offset += n 325 | m.ReachabilityInfo = append(m.ReachabilityInfo, r) 326 | } 327 | return offset, nil 328 | } 329 | */ 330 | 331 | // setBytes converts the wire format in buf to a BGP message. The first parsed 332 | // message is returned together with the new offset in buf. If the parsing 333 | // fails an error is returned. 334 | func setBytes(buf []byte) (m Msg, n int, e error) { 335 | if len(buf) < headerLen { 336 | return nil, 0, NewError(1, 2, fmt.Sprintf("pack: buffer size too small: %d < %d", len(buf), headerLen)) 337 | } 338 | // Byte 18 has the type. 339 | switch buf[18] { 340 | case open: 341 | m = &Open{} 342 | n, e = m.(*Open).setBytes(buf) 343 | // case update: 344 | // m = &Update{} 345 | // n, e = m.(*Update).SetBytes(buf) 346 | case notification: 347 | m = &Notification{} 348 | n, e = m.(*Notification).setBytes(buf) 349 | case keepalive: 350 | m = &Keepalive{} 351 | n, e = m.(*Keepalive).setBytes(buf) 352 | default: 353 | return nil, 0, NewError(1, 3, fmt.Sprintf("bad type: %d", buf[18])) 354 | } 355 | if e != nil { 356 | return nil, n, e 357 | } 358 | return m, n, nil 359 | } 360 | 361 | // Bytes convert Message into wireformat and returns it as a byte slice. 362 | func bytes(m Msg) []byte { 363 | switch x := m.(type) { 364 | case *Open: 365 | return x.bytes() 366 | // case *Update: 367 | // return x.bytes() 368 | case *Notification: 369 | return x.bytes() 370 | case *Keepalive: 371 | return x.bytes() 372 | } 373 | return nil 374 | } 375 | --------------------------------------------------------------------------------