1
0
mirror of https://github.com/cloudflare/gortr.git synced 2024-05-19 06:50:10 +00:00

RTRdump: dump the content of a RTR server in JSON format

This commit is contained in:
Louis Poinsignon
2018-11-05 13:22:41 -08:00
parent 6835dfe225
commit 8f6f16dbd0
9 changed files with 346 additions and 6 deletions

24
Dockerfile.rtrdump Normal file
View File

@ -0,0 +1,24 @@
ARG src_dir="/go/src/github.com/cloudflare/gortr"
FROM golang:alpine as builder
ARG src_dir
RUN apk --update --no-cache add git && \
mkdir -p ${src_dir}
WORKDIR ${src_dir}
COPY . .
RUN go get -u github.com/golang/dep/cmd/dep && \
dep ensure && \
go build cmd/rtrdump/rtrdump.go
FROM alpine:latest
ARG src_dir
RUN apk --update --no-cache add ca-certificates && \
adduser -S -D -H -h / rtr
USER rtr
COPY --from=builder ${src_dir}/rtrdump /
ENTRYPOINT ["./rtrdump"]

17
Dockerfile.rtrdump.prod Normal file
View File

@ -0,0 +1,17 @@
ARG src_uri=github.com/cloudflare/gortr/cmd/rtrdump
FROM golang:alpine as builder
ARG src_uri
RUN apk --update --no-cache add git && \
go get -u $src_uri
FROM alpine:latest
ARG src_uri
RUN apk --update --no-cache add ca-certificates && \
adduser -S -D -H -h / rtr
USER rtr
COPY --from=builder /go/bin/rtrdump /
ENTRYPOINT ["./rtrdump"]

124
cmd/rtrdump/rtrdump.go Normal file
View File

@ -0,0 +1,124 @@
package main
import (
"flag"
"fmt"
rtr "github.com/cloudflare/gortr/lib"
"github.com/cloudflare/gortr/prefixfile"
log "github.com/sirupsen/logrus"
"os"
"runtime"
"encoding/json"
"time"
"io"
"crypto/tls"
)
const AppVersion = "RTRdump 0.9.4"
var (
Connect = flag.String("connect", "127.0.0.1:8282", "Connection address")
OutFile = flag.String("file", "output.json", "Output file")
UseTLS = flag.Bool("tls.enable", false, "Use TLS")
ValidateCert = flag.Bool("tls.validate", true, "Validate TLS")
RefreshInterval = flag.Int("refresh", 600, "Refresh interval in seconds")
LogLevel = flag.String("loglevel", "info", "Log level")
Version = flag.Bool("version", false, "Print version")
)
type Client struct {
Data prefixfile.ROAList
}
func (c *Client) HandlePDU(cs *rtr.ClientSession, pdu rtr.PDU) {
log.Debugf("Received: %v", pdu)
switch pdu := pdu.(type) {
case *rtr.PDUIPv4Prefix:
rj := prefixfile.ROAJson{
Prefix: pdu.Prefix.String(),
ASN: fmt.Sprintf("AS%v", pdu.ASN),
Length: pdu.MaxLen,
}
c.Data.Data = append(c.Data.Data, rj)
c.Data.Metadata.Counts++
case *rtr.PDUIPv6Prefix:
rj := prefixfile.ROAJson{
Prefix: pdu.Prefix.String(),
ASN: fmt.Sprintf("AS%v", pdu.ASN),
Length: pdu.MaxLen,
}
c.Data.Data = append(c.Data.Data, rj)
c.Data.Metadata.Counts++
case *rtr.PDUEndOfData:
t := time.Now().UTC().UnixNano()/1000000000
c.Data.Metadata.Generated = int(t)
c.Data.Metadata.Valid = int(pdu.SerialNumber)
cs.Disconnect()
}
}
func (c *Client) Connected(cs *rtr.ClientSession) {
cs.SendResetQuery()
}
func (c *Client) Disconnected(cs *rtr.ClientSession) {
}
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
flag.Parse()
if *Version {
fmt.Println(AppVersion)
os.Exit(0)
}
lvl, _ := log.ParseLevel(*LogLevel)
log.SetLevel(lvl)
cc := rtr.ClientConfiguration{
ProtocolVersion: rtr.PROTOCOL_VERSION_0,
Log: log.StandardLogger(),
}
client := &Client{
Data: prefixfile.ROAList{
Metadata: prefixfile.MetaData{
},
Data: make([]prefixfile.ROAJson, 0),
},
}
clientSession := rtr.NewClientSession(cc, client)
config := &tls.Config{
InsecureSkipVerify: !*ValidateCert,
}
err := clientSession.Start(*Connect, *UseTLS, config)
if err != nil {
log.Fatal(err)
}
var f io.Writer
if *OutFile != "" {
ff, err := os.Create(*OutFile)
defer ff.Close()
if err != nil {
log.Fatal(err)
}
f = ff
} else {
f = os.Stdout
}
enc := json.NewEncoder(f)
err = enc.Encode(client.Data)
if err != nil {
log.Fatal(err)
}
}

158
lib/client.go Normal file
View File

@ -0,0 +1,158 @@
package rtrlib
import (
"net"
"time"
"crypto/tls"
)
type RTRClientSessionEventHandler interface {
//RequestCache(*ClientSession)
HandlePDU(*ClientSession, PDU)
}
type Logger interface{
Debugf(string, ...interface{})
Printf(string, ...interface{})
Errorf(string, ...interface{})
Infof(string, ...interface{})
}
type ClientSession struct {
version uint8
connected bool
curserial uint32
transmits chan PDU
quit chan bool
tcpconn net.Conn
handler RTRClientSessionEventHandler
log Logger
}
type ClientConfiguration struct {
ProtocolVersion uint8
EnforceVersion bool
RefreshInterval uint32
RetryInterval uint32
ExpireInterval uint32
Log Logger
}
func NewClientSession(configuration ClientConfiguration, handler RTRClientSessionEventHandler) *ClientSession {
return &ClientSession{
transmits: make(chan PDU, 256),
quit: make(chan bool),
log: configuration.Log,
handler: handler,
}
}
func (c *ClientSession) SendResetQuery() {
pdu := &PDUResetQuery{}
c.SendPDU(pdu)
}
func (c *ClientSession) SendSerialQuery(serial uint32) {
pdu := &PDUSerialQuery{
// to fill
}
c.SendPDU(pdu)
}
func (c *ClientSession) SendPDU(pdu PDU) {
pdu.SetVersion(c.version)
c.SendRawPDU(pdu)
}
func (c *ClientSession) SendRawPDU(pdu PDU) {
c.transmits <- pdu
}
func (c *ClientSession) sendLoop() {
for c.connected {
select {
case pdu := <-c.transmits:
c.tcpconn.Write(pdu.Bytes())
case <-c.quit:
break
}
}
}
func (c *ClientSession) refreshLoop() {
for c.connected {
select {
case <-time.After(20*time.Second):
// send refresh
}
}
}
func (c *ClientSession) Disconnect() {
c.connected = false
//log.Debugf("Disconnecting client %v", c.String())
//if c.handler != nil {
// c.handler.ClientDisconnected(c)
//}
select {
case c.quit <- true:
default:
}
c.tcpconn.Close()
}
func (c *ClientSession) StartWithConn(tcpconn net.Conn) error {
c.tcpconn = tcpconn
c.connected = true
//if c.handler != nil {
// c.handler.ClientConnected(c)
//}
go c.sendLoop()
c.SendResetQuery()
for c.connected {
dec, err := Decode(c.tcpconn)
if err != nil || dec == nil {
if c.log != nil {
c.log.Errorf("Error %v", err)
}
c.Disconnect()
return err
}
if c.handler != nil {
c.handler.HandlePDU(c, dec)
}
}
return nil
}
func (c *ClientSession) Start(addr string, useTls bool, config *tls.Config) error {
addrTCP, err := net.ResolveTCPAddr("tcp", addr)
if err != nil {
return err
}
if useTls {
tcpconn, err := tls.Dial("tcp", addr, config)
if err != nil {
return err
}
return c.StartWithConn(tcpconn)
} else {
tcpconn, err := net.DialTCP("tcp", nil, addrTCP)
if err != nil {
return err
}
return c.StartWithConn(tcpconn)
}
return nil
}

View File

@ -631,6 +631,7 @@ func (c *Client) Start() {
buf := make([]byte, 8000)
for c.connected {
// Remove this?
length, err := c.tcpconn.Read(buf)
if err != nil || length == 0 {
log.Debugf("Error %v", err)

View File

@ -598,14 +598,30 @@ func Decode(rdr io.Reader) (PDU, error) {
Prefix: ipnet,
}, nil
case PDU_ID_END_OF_DATA:
if len(toread) != 4 {
return nil, errors.New(fmt.Sprintf("Wrong length for End of Data PDU: %v != 4", len(toread)))
if len(toread) != 4 && len(toread) != 16 {
return nil, errors.New(fmt.Sprintf("Wrong length for End of Data PDU: %v != 4 or != 16", len(toread)))
}
serial := binary.BigEndian.Uint32(toread)
var serial uint32
var refreshInterval uint32
var retryInterval uint32
var expireInterval uint32
if len(toread) == 4 {
serial = binary.BigEndian.Uint32(toread)
} else if len(toread) == 16 {
serial = binary.BigEndian.Uint32(toread[0:4])
refreshInterval = binary.BigEndian.Uint32(toread[4:8])
retryInterval = binary.BigEndian.Uint32(toread[8:12])
expireInterval = binary.BigEndian.Uint32(toread[12:16])
}
return &PDUEndOfData{
Version: pver,
SessionId: sessionId,
SerialNumber: serial,
RefreshInterval: refreshInterval,
RetryInterval: retryInterval,
ExpireInterval: expireInterval,
}, nil
case PDU_ID_CACHE_RESET:
if len(toread) != 0 {

View File

@ -80,19 +80,19 @@ type ROAJson struct {
Prefix string `json:"prefix"`
Length uint8 `json:"maxLength"`
ASN string `json:"asn"`
TA string `json:"ta"`
TA string `json:"ta,omitempty"`
}
type MetaData struct {
Counts int `json:"counts"`
Generated int `json:"generated"`
Valid int `json:"valid"`
Valid int `json:"valid,omitempty"`
Signature string `json:"signature,omitempty"`
SignatureDate string `json:"signatureDate,omitempty"`
}
type ROAList struct {
Metadata MetaData `json:"metadata"`
Metadata MetaData `json:"metadata,omitempty"`
Data []ROAJson `json:"roas"`
}