Draw the actual rest of the fucking owl.
Change-Id: Ia04fb49ebbe3a5afccc57e62f6335e35b45192fe
diff --git a/bgpwtf/cccampix/birdie/BUILD.bazel b/bgpwtf/cccampix/birdie/BUILD.bazel
new file mode 100644
index 0000000..67fe480
--- /dev/null
+++ b/bgpwtf/cccampix/birdie/BUILD.bazel
@@ -0,0 +1,21 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
+
+go_library(
+ name = "go_default_library",
+ srcs = ["birdie.go"],
+ importpath = "code.hackerspace.pl/hscloud/bgpwtf/cccampix/birdie",
+ visibility = ["//visibility:private"],
+ deps = [
+ "//bgpwtf/cccampix/proto:go_default_library",
+ "@com_github_golang_glog//:go_default_library",
+ "@org_golang_google_grpc//:go_default_library",
+ "@org_golang_google_grpc//credentials:go_default_library",
+ ],
+)
+
+go_binary(
+ name = "birdie",
+ embed = [":go_default_library"],
+ static = "on",
+ visibility = ["//visibility:public"],
+)
diff --git a/bgpwtf/cccampix/birdie/birdie.go b/bgpwtf/cccampix/birdie/birdie.go
new file mode 100644
index 0000000..f455844
--- /dev/null
+++ b/bgpwtf/cccampix/birdie/birdie.go
@@ -0,0 +1,197 @@
+package main
+
+import (
+ "context"
+ "crypto/x509"
+ "flag"
+ "fmt"
+ "sort"
+ "strings"
+
+ pb "code.hackerspace.pl/hscloud/bgpwtf/cccampix/proto"
+ "github.com/golang/glog"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/credentials"
+)
+
+var (
+ flagVerifier string
+ flagIdentity string
+ flagRouterID string
+ flagV6 bool
+ flagLocalIP string
+)
+
+func init() {
+ flag.Set("logtostderr", "true")
+}
+
+func main() {
+ flag.StringVar(&flagVerifier, "verifier", "", "Verifier endpoint")
+ flag.StringVar(&flagIdentity, "identity", "", "Router identity")
+ flag.StringVar(&flagRouterID, "router_id", "", "Router ID")
+ flag.StringVar(&flagLocalIP, "local_ip", "", "Local IP")
+ flag.BoolVar(&flagV6, "v6", false, "V6")
+
+ flag.Parse()
+
+ if flagVerifier == "" {
+ glog.Exit("verifier must be set")
+ }
+ if flagIdentity == "" {
+ glog.Exit("identity must be set")
+ }
+ if flagRouterID == "" {
+ glog.Exit("router_id must be set")
+ }
+
+ cpool, _ := x509.SystemCertPool()
+ creds := credentials.NewClientTLSFromCert(cpool, "")
+ conn, err := grpc.Dial(flagVerifier, grpc.WithTransportCredentials(creds))
+ if err != nil {
+ glog.Exitf("Dial: %v", err)
+ }
+
+ ctx := context.Background()
+ verifier := pb.NewVerifierClient(conn)
+
+ req := &pb.RouterHeartbeatRequest{
+ Name: flagIdentity,
+ }
+
+ res, err := verifier.RouterHeartbeat(ctx, req)
+ if err != nil {
+ glog.Exitf("RouterHeartbeat: %v", err)
+ }
+
+ var config string
+ if flagV6 {
+ config = `
+log syslog all;
+
+router id %s;
+debug protocols { states, interfaces, events };
+
+timeformat base iso long;
+timeformat log iso long;
+timeformat protocol iso long;
+timeformat route iso long;
+
+protocol device {}
+
+function net_martians() {
+ return net ~ [ fc00::/7+, fec0::/10+, ::/128-, ::/0{0,15}, ::/0{49,128} ];
+}
+
+function generic_in() {
+ if net_martians() then return false;
+ if bgp_path.len > 64 then return false;
+ if net.len > 64 then return false;
+ if net.len < 12 then return false;
+ return true;
+}
+`
+ config = fmt.Sprintf(config, flagRouterID)
+ } else {
+ config = `
+log syslog all;
+
+router id %s;
+debug protocols { states, interfaces, events };
+
+timeformat base iso long;
+timeformat log iso long;
+timeformat protocol iso long;
+timeformat route iso long;
+
+protocol device {}
+
+function net_martians() {
+ return net ~ [ 169.254.0.0/16+, 172.16.0.0/12+, 192.168.0.0/16+, 10.0.0.0/8+,
+ 127.0.0.0/8+, 224.0.0.0/4+, 240.0.0.0/4+, 0.0.0.0/32-, 0.0.0.0/0{25,32}, 0.0.0.0/0{0,7} ];
+}
+
+function generic_in() {
+ if net_martians() then return false;
+ if bgp_path.len > 64 then return false;
+ if net.len > 24 then return false;
+ if net.len < 8 then return false;
+ return true;
+}
+`
+ config = fmt.Sprintf(config, flagRouterID)
+ }
+
+ sort.Slice(res.AsConfigs, func(i, j int) bool {
+ return res.AsConfigs[i].Asn < res.AsConfigs[j].Asn
+ })
+
+ for _, asc := range res.AsConfigs {
+ sort.Slice(asc.Routers, func(i, j int) bool {
+ return asc.Routers[i].Password < asc.Routers[j].Password
+ })
+ for i, router := range asc.Routers {
+ addr := ""
+ if flagV6 {
+ addr = router.Ipv6
+ } else {
+ addr = router.Ipv4
+ }
+ if addr == "" {
+ continue
+ }
+ peerid := fmt.Sprintf("PEER_%d_%d", asc.Asn, i)
+ prefixes := []string{}
+ for _, prefix := range asc.Prefixes {
+ if flagV6 && !strings.Contains(prefix.Prefix, ":") {
+ continue
+ }
+ if !flagV6 && !strings.Contains(prefix.Prefix, ".") {
+ continue
+ }
+ parts := strings.Split(prefix.Prefix, "/")
+ addr := parts[0]
+ bits := parts[1]
+ filter := fmt.Sprintf("%s/%s{%s,%d}", addr, bits, bits, prefix.MaxLength)
+ if fmt.Sprintf("%d", prefix.MaxLength) == bits {
+ filter = fmt.Sprintf("%s/%s", addr, bits)
+ }
+ prefixes = append(prefixes, filter)
+ }
+ if len(prefixes) == 0 {
+ continue
+ }
+ allowed := strings.Join(prefixes, ",")
+
+ part := `
+filter %s_in {
+ if !generic_in() then reject;
+ if net ~ [ %s ] then accept;
+ reject;
+}
+`
+ part = fmt.Sprintf(part, peerid, allowed)
+ config += part
+
+ part = `
+filter %s_out {
+ accept;
+}
+`
+ part = fmt.Sprintf(part, peerid)
+ config += part
+
+ part = `
+protocol bgp %s {
+ local %s as 208521;
+ neighbor %s as %d;
+ import filter %s_in;
+}
+`
+ part = fmt.Sprintf(part, peerid, flagLocalIP, addr, asc.Asn, peerid)
+ config += part
+ }
+ }
+
+ fmt.Println(config)
+}