package ipam import ( "cmp" "fmt" "log" "net/netip" "slices" "utils/ipam/yaml_model" ) func LoadIpam(bs []byte) (*Ipam, error) { parsed, err := yaml_model.Parse(bs) if err != nil { return nil, err } ipam := Ipam{} err = processNetworks(parsed.Ipam6.Networks, &ipam) if err != nil { return nil, err } err = processRouters(parsed.Routers, &ipam) if err != nil { return nil, err } ipam.ResolveParents() return &ipam, nil } type Ipam struct { Networks []*Network Routers []*Router } func contains(a, b netip.Prefix) bool { return b.Bits() > a.Bits() && b.Overlaps(a) } func (ipam *Ipam) ResolveParents() { for _, n := range ipam.Networks { log.Printf("network %s/%s", n.Name, n.Prefix) for _, p := range ipam.Networks { if n == p { continue } log.Printf(" candidate %s/%s", p.Name, p.Prefix) if contains(p.Prefix, n.Prefix) { if n.Parent == nil { log.Printf(" found parent %s/%s", p.Name, p.Prefix) n.Parent = p } else { if n.Parent.Prefix.Bits() < p.Prefix.Bits() { log.Printf(" found better parent %s/%s", p.Name, p.Prefix) n.Parent = p } } } } } return } func (ipam *Ipam) FindRoots() []*Network { var ns []*Network for _, n := range ipam.Networks { if n.Parent == nil { ns = append(ns, n) } } return ns } type Network struct { Name string Parent *Network Prefix netip.Prefix hosts []networkHost } func CompareNetwork(a, b *Network) int { return cmp.Compare(a.Name, b.Name) } func (n *Network) Compare(other *Network) int { return cmp.Compare(n.Name, other.Name) } type networkHost struct { name string address netip.Addr } func processNetworks(networks map[string]yaml_model.Network6Yaml, ipam *Ipam) error { var ns []*Network for name, net := range networks { log.Printf("Processing net %v\n", name) prefix, err := netip.ParsePrefix(net.Range) if err != nil { return fmt.Errorf("error parsing net range: %v", err) } log.Printf("prefix: %s", prefix.String()) n := Network{Name: name, Prefix: prefix} for hostname, a := range net.Hosts { addr, err := netip.ParseAddr(a) if err != nil { return fmt.Errorf("network: %s, unable to parse host address %s", n.Name, a) } n.hosts = append(n.hosts, networkHost{ name: hostname, address: addr, }) } ns = append(ns, &n) } ipam.Networks = ns return nil } type Router struct { Name string As string Peers []Peer } func (r *Router) Compare(other *Router) int { return cmp.Compare(r.Name, other.Name) } type Peer struct { Name string /// Router is set if this is a known peer Router *Router As string } func processRouters(routers map[string]yaml_model.Router, ipam *Ipam) error { var rs []*Router rsIndex := make(map[string]*Router) for name, router := range routers { r := &Router{ Name: name, As: router.As, } rs = append(rs, r) rsIndex[name] = r } for _, r := range rs { router := routers[r.Name] for name, peer := range router.Peers { remote := rsIndex[name] r.Peers = append(r.Peers, Peer{ Name: name, Router: remote, As: peer.As, }) } } compare := func(a *Router, b *Router) int { return a.Compare(b) } ipam.Routers = slices.SortedStableFunc(slices.Values(rs), compare) return nil }