Draw the actual rest of the fucking owl.

Change-Id: Ia04fb49ebbe3a5afccc57e62f6335e35b45192fe
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)
+}