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:
24
Dockerfile.rtrdump
Normal file
24
Dockerfile.rtrdump
Normal 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
17
Dockerfile.rtrdump.prod
Normal 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
124
cmd/rtrdump/rtrdump.go
Normal 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
158
lib/client.go
Normal 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
|
||||
}
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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"`
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user