Skip to content

Commit a7217c0

Browse files
committed
a bit of refactor and cleanup, bump deps, tests
1 parent 70b3a39 commit a7217c0

File tree

16 files changed

+664
-368
lines changed

16 files changed

+664
-368
lines changed

.github/workflows

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: dnstap-bgp
2+
3+
on: [push]
4+
5+
jobs:
6+
build:
7+
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: actions/checkout@v3
11+
12+
- name: Set up Go
13+
uses: actions/setup-go@v4
14+
with:
15+
go-version: 1.20
16+
17+
- name: Build
18+
run: go build -v ./...
19+
20+
- name: Test
21+
run: go test -v ./...

.travis.yml

Lines changed: 0 additions & 11 deletions
This file was deleted.

Makefile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ DESCRIPTION := DNSTap to BGP exporter
44
LICENSE := MPLv2
55

66
GO ?= go
7-
DEP ?= dep
87
VERSION := $(shell cat VERSION)
98
OUT := .out
109
PACKAGE := github.com/blind-oracle/$(NAME)

README.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
[![Go Report Card](https://goreportcard.com/badge/github.com/blind-oracle/dnstap-bgp)](https://goreportcard.com/report/github.com/blind-oracle/dnstap-bgp)
22
[![Coverage Status](https://coveralls.io/repos/github/blind-oracle/dnstap-bgp/badge.svg?branch=master)](https://coveralls.io/github/blind-oracle/dnstap-bgp?branch=master)
3-
[![Build Status](https://travis-ci.org/blind-oracle/dnstap-bgp.svg?branch=master)](https://travis-ci.org/blind-oracle/dnstap-bgp)
43

54
# dnstap-bgp
65

@@ -16,13 +15,13 @@ This daemon was created to solve the problem of manipulating traffic based on do
1615
## Features
1716
* Load a list of domains to intercept: the prefix tree is used to match subdomains
1817
* Hot-reload of the domain list by a HUP signal
19-
* Full support for IPv6 (I hope): in DNS (AAAA RRs), in BGP and in syncer
18+
* Support for IPv6 - in DNS (AAAA RRs), in BGP and in syncer
2019
* Support for CNAMEs - they are resolved and stored as separate ip -> domain entries
2120
* Export routes to any number of BGP peers
2221
* Configurable timeout to purge entries from the cache
2322
* Persist the cache on disk (in a Bolt database)
2423
* Sync the obtained IPs with other instances of **dnstap-bgp**
25-
* Can switch itself to a pre-created network namespace before initializing network. This can be useful if you want to peer with a BGP server running on the same host (e.g. **bird** does not support peering with any of the local interfaces). This requires running as *root*.
24+
* Can be switched to a dedicated namespace using `ip netns` - see `deploy/*` init scripts for systemd. Useful when running with BGP router on the same host - ususally it can't peer with its own IPs (at least `bird`)
2625

2726
## Synchronization
2827
**dnstap-bgp** can optionally push the obtained IPs to other **dnstap-bgp** instances. It also periodically syncs its cache with peers to keep it up-to-date in case of network outages. The interaction is done using simple HTTP queries and JSON.
@@ -39,7 +38,7 @@ This daemon was created to solve the problem of manipulating traffic based on do
3938
Get *deb* or *rpm* packages from the releases page.
4039

4140
### From source
42-
You'll need Go environment set up and *dep* installed, then just run `make`
41+
You'll need Go environment set up, then just run `make`
4342

4443
### Building packages
4544
To build a package you'll need *fpm* tool installed, then just run `make rpm` or `make deb`

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.0.8
1+
1.1.0

bgp.go

Lines changed: 19 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,23 @@ package main
33
import (
44
"context"
55
"fmt"
6-
"log"
76
"net"
87
"strconv"
98
"strings"
109
"time"
1110

1211
"github.com/golang/protobuf/ptypes"
1312
"github.com/golang/protobuf/ptypes/any"
14-
api "github.com/osrg/gobgp/api"
15-
gobgp "github.com/osrg/gobgp/pkg/server"
13+
api "github.com/osrg/gobgp/v3/api"
14+
gobgp "github.com/osrg/gobgp/v3/pkg/server"
1615
)
1716

1817
type bgpCfg struct {
19-
AS uint32
20-
RouterID string
21-
NextHop string
18+
AS uint32
19+
RouterID string
20+
NextHop string
2221
NextHopIPv6 string
23-
SourceIP string
24-
SourceIF string
22+
SourceIP string
2523

2624
Peers []string
2725
IPv6 bool
@@ -34,15 +32,11 @@ type bgpServer struct {
3432

3533
func newBgp(c *bgpCfg) (b *bgpServer, err error) {
3634
if c.AS == 0 {
37-
return nil, fmt.Errorf("You need to provide AS")
38-
}
39-
40-
if c.SourceIP != "" && c.SourceIF != "" {
41-
return nil, fmt.Errorf("SourceIP and SourceIF are mutually exclusive")
35+
return nil, fmt.Errorf("you need to provide AS")
4236
}
4337

4438
if len(c.Peers) == 0 {
45-
return nil, fmt.Errorf("You need to provide at least one peer")
39+
return nil, fmt.Errorf("you need to provide at least one peer")
4640
}
4741

4842
b = &bgpServer{
@@ -53,18 +47,14 @@ func newBgp(c *bgpCfg) (b *bgpServer, err error) {
5347

5448
if err = b.s.StartBgp(context.Background(), &api.StartBgpRequest{
5549
Global: &api.Global{
56-
As: c.AS,
50+
Asn: c.AS,
5751
RouterId: c.RouterID,
5852
ListenPort: -1,
5953
},
6054
}); err != nil {
6155
return
6256
}
6357

64-
if err = b.s.MonitorPeer(context.Background(), &api.MonitorPeerRequest{}, func(p *api.Peer) { log.Println(p) }); err != nil {
65-
return
66-
}
67-
6858
for _, p := range c.Peers {
6959
if err = b.addPeer(p); err != nil {
7060
return
@@ -81,14 +71,14 @@ func (b *bgpServer) addPeer(addr string) (err error) {
8171
addr = t[0]
8272

8373
if port, err = strconv.Atoi(t[1]); err != nil {
84-
return fmt.Errorf("Unable to parse port '%s' as int: %s", t[1], err)
74+
return fmt.Errorf("unable to parse port '%s' as int: %s", t[1], err)
8575
}
8676
}
8777

8878
p := &api.Peer{
8979
Conf: &api.PeerConf{
9080
NeighborAddress: addr,
91-
PeerAs: b.c.AS,
81+
PeerAsn: b.c.AS,
9282
},
9383

9484
AfiSafis: []*api.AfiSafi{
@@ -129,10 +119,6 @@ func (b *bgpServer) addPeer(addr string) (err error) {
129119
p.Transport.LocalAddress = b.c.SourceIP
130120
}
131121

132-
if b.c.SourceIF != "" {
133-
p.Transport.BindInterface = b.c.SourceIF
134-
}
135-
136122
return b.s.AddPeer(context.Background(), &api.AddPeerRequest{
137123
Peer: p,
138124
})
@@ -159,22 +145,21 @@ func (b *bgpServer) getPath(ip net.IP) *api.Path {
159145
})
160146

161147
if ip.To4() == nil {
162-
163148
v6Family := &api.Family{
164149
Afi: api.Family_AFI_IP6,
165150
Safi: api.Family_SAFI_UNICAST,
166151
}
167152

168153
if b.c.NextHopIPv6 != "" {
169-
nh = b.c.NextHopIPv6
154+
nh = b.c.NextHopIPv6
170155
} else {
171-
nh = "fd00::1"
156+
nh = "fd00::1"
172157
}
173158

174159
v6Attrs, _ := ptypes.MarshalAny(&api.MpReachNLRIAttribute{
175-
Family: v6Family,
176-
NextHops: []string{nh},
177-
Nlris: []*any.Any{nlri},
160+
Family: v6Family,
161+
NextHops: []string{nh},
162+
Nlris: []*any.Any{nlri},
178163
})
179164

180165
return &api.Path{
@@ -197,9 +182,9 @@ func (b *bgpServer) getPath(ip net.IP) *api.Path {
197182
})
198183

199184
return &api.Path{
200-
Family: &api.Family{
201-
Afi: api.Family_AFI_IP,
202-
Safi: api.Family_SAFI_UNICAST,
185+
Family: &api.Family{
186+
Afi: api.Family_AFI_IP,
187+
Safi: api.Family_SAFI_UNICAST,
203188
},
204189
Nlri: nlri,
205190
Pattrs: []*any.Any{a1, a2},

cache.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ func (c *cache) add(e *cacheEntry) {
5757
c.Lock()
5858
c.m[string(e.IP)] = e
5959
c.Unlock()
60-
return
6160
}
6261

6362
func (c *cache) getAll() (es []*cacheEntry) {

db.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"encoding/gob"
66
"net"
77

8-
bolt "github.com/etcd-io/bbolt"
8+
bolt "go.etcd.io/bbolt"
99
)
1010

1111
type db struct {

dnstap.go

Lines changed: 43 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import (
88
"strconv"
99

1010
dnstap "github.com/dnstap/golang-dnstap"
11-
"github.com/golang/protobuf/proto"
1211
"github.com/miekg/dns"
12+
"google.golang.org/protobuf/proto"
1313
)
1414

1515
type dnstapCfg struct {
@@ -18,6 +18,11 @@ type dnstapCfg struct {
1818
IPv6 bool
1919
}
2020

21+
type dnsEntry struct {
22+
ip net.IP
23+
fqdn string
24+
}
25+
2126
type fCb func(net.IP, string)
2227
type fCbErr func(error)
2328

@@ -27,72 +32,62 @@ type dnstapServer struct {
2732
cbErr fCbErr
2833
fstrmServer *dnstap.FrameStreamSockInput
2934
l net.Listener
30-
unixSocket bool
3135
ch chan []byte
3236
}
3337

34-
func stripDot(d string) string {
35-
return d[:len(d)-1]
36-
}
37-
38-
func (ds *dnstapServer) handleDNSMsg(m *dns.Msg) {
39-
var (
40-
domain string
41-
cnameTgt string
42-
origCname string
43-
multiCnames bool
44-
)
45-
multiCnames = false
38+
/*
39+
ch-odc.samsungapps.com. 300 IN CNAME ch-odc.gw.samsungapps.com.
40+
ch-odc.gw.samsungapps.com. 10 IN CNAME fe-pew1-ext-s3store-elb-1085125128.eu-west-1.elb.amazonaws.com.
41+
fe-pew1-ext-s3store-elb-1085125128.eu-west-1.elb.amazonaws.com. 60 IN A 34.251.108.185
42+
*/
4643

47-
loop:
44+
func parseDNSReply(m *dns.Msg, ipv6 bool) []*dnsEntry {
45+
var domain string
46+
result := []*dnsEntry{}
4847

4948
for _, rr := range m.Answer {
5049
hdr := rr.Header()
5150

52-
var ip net.IP
53-
switch rr.(type) {
51+
switch rv := rr.(type) {
5452
case *dns.CNAME:
55-
cnameTgt = rr.(*dns.CNAME).Target
56-
domain = hdr.Name
57-
if origCname == "" {
58-
origCname = domain
59-
} else {
60-
multiCnames = true
53+
if domain == "" {
54+
domain = hdr.Name
6155
}
62-
continue loop
6356

64-
case *dns.A, *dns.AAAA:
65-
if cnameTgt == "" {
57+
case *dns.A:
58+
if domain == "" {
6659
domain = hdr.Name
67-
} else if cnameTgt != hdr.Name {
68-
continue loop
6960
}
7061

71-
switch r := rr.(type) {
72-
case *dns.A:
73-
ip = r.A
74-
case *dns.AAAA:
75-
if !ds.cfg.IPv6 {
76-
continue loop
77-
}
78-
ip = r.AAAA
62+
result = append(result, &dnsEntry{rv.A, domain})
63+
64+
case *dns.AAAA:
65+
if !ipv6 {
66+
break
7967
}
8068

81-
default:
82-
continue loop
83-
}
84-
if multiCnames == true {
85-
domain = origCname
69+
if domain == "" {
70+
domain = hdr.Name
71+
}
72+
73+
result = append(result, &dnsEntry{rv.AAAA, domain})
8674
}
87-
ds.cb(ip, stripDot(domain))
75+
}
76+
77+
return result
78+
}
79+
80+
func (ds *dnstapServer) handleDNSMsg(m *dns.Msg) {
81+
for _, d := range parseDNSReply(m, ds.cfg.IPv6) {
82+
ds.cb(d.ip, d.fqdn[:len(d.fqdn)-1])
8883
}
8984
}
9085

9186
func (ds *dnstapServer) ProcessProtobuf() {
9287
for frame := range ds.ch {
9388
tap := &dnstap.Dnstap{}
9489
if err := proto.Unmarshal(frame, tap); err != nil {
95-
ds.cbErr(fmt.Errorf("Unmarshal failed: %s", err))
90+
ds.cbErr(fmt.Errorf("unmarshal failed: %w", err))
9691
continue
9792
}
9893

@@ -103,7 +98,7 @@ func (ds *dnstapServer) ProcessProtobuf() {
10398

10499
dnsMsg := new(dns.Msg)
105100
if err := dnsMsg.Unpack(msg.ResponseMessage); err != nil {
106-
ds.cbErr(fmt.Errorf("Unpack failed: %s", err))
101+
ds.cbErr(fmt.Errorf("unpack failed: %w", err))
107102
continue
108103
}
109104

@@ -120,25 +115,25 @@ func newDnstapServer(c *dnstapCfg, cb fCb, cbErr fCbErr) (ds *dnstapServer, err
120115
}
121116

122117
if c.Listen == "" {
123-
return nil, fmt.Errorf("You need to specify DNSTap listening poing")
118+
return nil, fmt.Errorf("you need to specify DNSTap listening poing")
124119
}
125120

126121
if addr, err := net.ResolveTCPAddr("tcp", c.Listen); err == nil {
127122
if ds.l, err = net.ListenTCP("tcp", addr); err != nil {
128-
return nil, fmt.Errorf("Unable to listen on '%s': %s", c.Listen, err)
123+
return nil, fmt.Errorf("unable to listen on '%s': %w", c.Listen, err)
129124
}
130125

131126
ds.fstrmServer = dnstap.NewFrameStreamSockInput(ds.l)
132127
} else {
133128
ds.fstrmServer, err = dnstap.NewFrameStreamSockInputFromPath(c.Listen)
134129
if err != nil {
135-
return nil, fmt.Errorf("Unable to listen on '%s': %s", c.Listen, err)
130+
return nil, fmt.Errorf("unable to listen on '%s': %w", c.Listen, err)
136131
}
137132

138133
if c.Perm != "" {
139134
octal, err := strconv.ParseInt(c.Perm, 8, 32)
140135
if err != nil {
141-
return nil, fmt.Errorf("Unable to parse '%s' as octal: %s", c.Perm, err)
136+
return nil, fmt.Errorf("unable to parse '%s' as octal: %w", c.Perm, err)
142137
}
143138

144139
os.Chmod(c.Listen, os.FileMode(octal))

0 commit comments

Comments
 (0)