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 flagUpstream bool ) 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.BoolVar(&flagUpstream, "upstream", false, "Is an upstream router") 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; define ourASN = %d; define filteredNumber = 65666; define customerNumber = 65667; define martian = 1; define prefixTooLong64 = 2; define prefixTooLong24 = 3; define pathBogon = 4; define pathTooLong = 5; define pathFirstNotPeer = 6; define prefixFiltered = 9; 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 { bgp_large_community.add((ourASN, filteredNumber, martian)); return false; } if bgp_path.len > 64 then { bgp_large_community.add((ourASN, filteredNumber, pathTooLong)); return false; } if net.len > 64 then { bgp_large_community.add((ourASN, filteredNumber, prefixTooLong64)); return false; } return true; } ` config = fmt.Sprintf(config, flagRouterID, 208521) } 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; define ourASN = %d; define filteredNumber = 65666; define customerNumber = 65667; define martian = 1; define prefixTooLong64 = 2; define prefixTooLong24 = 3; define pathBogon = 4; define pathTooLong = 5; define pathFirstNotPeer = 6; define prefixFiltered = 9; 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 { bgp_large_community.add((ourASN, filteredNumber, martian)); return false; } if bgp_path.len > 64 then { bgp_large_community.add((ourASN, filteredNumber, pathTooLong)); return false; } if net.len > 24 then { bgp_large_community.add((ourASN, filteredNumber, prefixTooLong24)); return false; } return true; } ` config = fmt.Sprintf(config, flagRouterID, 208521) } if flagUpstream { config += ` protocol kernel { scan time 60; import none; export all; } filter UPSTREAM_C3NOC_IN { if net_martians() then reject; if bgp_path.len > 64 then reject; accept; } filter UPSTREAM_C3NOC_OUT { accept; } ` if flagV6 { config += ` protocol bgp UPSTREAM_C3NOC { import filter UPSTREAM_C3NOC_IN; export filter UPSTREAM_C3NOC_OUT; description "UPSTREAM AS13020"; local 2a0d:eb02:4242:4242::7 as 208521; neighbor 2a0d:eb02:4242:4242::250 as 13020; } ` } else { config += ` protocol bgp UPSTREAM_C3NOC { import filter UPSTREAM_C3NOC_IN; export filter UPSTREAM_C3NOC_OUT; local 185.236.243.7 as 208521; neighbor 185.236.243.250 as 13020; } ` } } 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 bgp_path.first != %d then { bgp_large_community.add((ourASN, filteredNumber, pathFirstNotPeer)); reject; } if bgp_path.last != %d then { bgp_large_community.add((ourASN, filteredNumber, pathFirstNotPeer)); reject; } if ! (net ~ [ %s ]) then { bgp_large_community.add((ourASN, filteredNumber, prefixFiltered)); reject; } bgp_large_community.add((ourASN, customerNumber, 2137)); accept; } ` part = fmt.Sprintf(part, peerid, asc.Asn, asc.Asn, allowed) config += part part = ` filter %s_out { if (ourASN, customerNumber, 2137) ~ bgp_large_community then reject; accept; } ` part = fmt.Sprintf(part, peerid) config += part part = ` protocol bgp %s { local %s as 208521; description "PEER AS%d R%d"; neighbor %s as %d; import filter %s_in; import keep filtered on; export filter %s_out; password "%s"; } ` part = fmt.Sprintf(part, peerid, flagLocalIP, asc.Asn, i, addr, asc.Asn, peerid, peerid, router.Password) config += part } } fmt.Println(config) }