package host import ( "lux/proto" "net" "net/netip" "strconv" "strings" ) // Option containing all network interfaces of host (excluding loopbacks) // IPv6 GUA // IPv6 ULA // IPv6 LL // IPv4 type LuxNetAddrType int const ( LuxNetAddrType4 = 0 LuxNetAddrType6LL = 1 LuxNetAddrType6ULA = 2 LuxNetAddrType6GUA = 3 ) func (addrType LuxNetAddrType) String() string { switch addrType { case LuxNetAddrType4: return "ip4" case LuxNetAddrType6LL: return "ip6ll" case LuxNetAddrType6ULA: return "ip6ula" case LuxNetAddrType6GUA: return "ip6gua" default: return strconv.Itoa(int(addrType)) } } type LuxNetAddr struct { Type LuxNetAddrType Addr netip.Addr } func (addr *LuxNetAddr) Read(rd *proto.LuxBuffer) error { addrType, err := rd.ReadUint16() if err != nil { return err } addr.Type = LuxNetAddrType(addrType) ipAddr, err := rd.ReadIP() if err != nil { return err } addr.Addr = ipAddr return nil } func (addr *LuxNetAddr) Write(wd *proto.LuxBuffer) { wd.WriteUint16(uint16(addr.Type)) wd.WriteIP(addr.Addr) } type LuxNetInterface struct { Name string Index int Addrs []LuxNetAddr } func NewLuxNetInterface() LuxNetInterface { return LuxNetInterface{ Addrs: make([]LuxNetAddr, 0), } } func parseNetlinkIp(addr string) string { if strings.ContainsRune(addr, '/') { return strings.Split(addr, "/")[0] } return addr } func (luxNet *LuxNetInterface) FromNetlink(netif *net.Interface) error { luxNet.Name = netif.Name luxNet.Index = netif.Index // enumerate interface ips addrs, err := netif.Addrs() if err != nil { return err } for _, addr := range addrs { ip := net.ParseIP(parseNetlinkIp(addr.String())) if ip == nil { return err } luxAddr := LuxNetAddr{} if ip.IsLoopback() { continue // loopbacks are useless to LUX } else if ip.To4() == nil { // got IPv6 if ip.IsLinkLocalUnicast() { luxAddr.Type = LuxNetAddrType6LL } else if ip[0] == 0xFD || ip[0] == 0xFC { // ULA - fc00::/7 with L = 1 -> fd00::/8, // tho fc L 0 is not defined we still gonna assume its ULA luxAddr.Type = LuxNetAddrType6ULA } else { // anything else is global unicast luxAddr.Type = LuxNetAddrType6GUA } } else { // got IPv4 luxAddr.Type = LuxNetAddrType4 } luxAddr.Addr = proto.LuxProtoIPToAddr(ip) luxNet.Addrs = append(luxNet.Addrs, luxAddr) } return nil } func (netif *LuxNetInterface) Read(rd *proto.LuxBuffer) error { name, err := rd.ReadString() if err != nil { return err } netif.Name = name idx, err := rd.ReadUint16() if err != nil { return err } netif.Index = int(idx) addrNum, err := rd.ReadUint16() if err != nil { return err } for i := 0; i < int(addrNum); i++ { addr := LuxNetAddr{} if addr.Read(rd) != nil { return err } netif.Addrs = append(netif.Addrs, addr) } return nil } func (netif *LuxNetInterface) Write(wd *proto.LuxBuffer) { wd.WriteString(netif.Name) wd.WriteUint16(uint16(netif.Index)) wd.WriteUint16(uint16(len(netif.Addrs))) for _, addr := range netif.Addrs { addr.Write(wd) } } type LuxOptionNetIf struct { Interfaces map[string]*LuxNetInterface } func NewLuxOptionNetIf() LuxOptionNetIf { return LuxOptionNetIf{ Interfaces: make(map[string]*LuxNetInterface), } } func (opt *LuxOptionNetIf) EnumerateNetlink() error { ifaces, err := net.Interfaces() if err != nil { return err } for _, iface := range ifaces { netif := NewLuxNetInterface() if err := netif.FromNetlink(&iface); err != nil { return nil } if len(netif.Addrs) > 0 { // only add if we got atleast 1 ip, since we might want to skip // netif loopback that has no IP because we skip one in FromNetlink opt.Interfaces[netif.Name] = &netif } } return nil } func (opt *LuxOptionNetIf) Read(rd *proto.LuxBuffer) error { ifNum, err := rd.ReadUint16() if err != nil { return err } for i := 0; i < int(ifNum); i++ { netif := NewLuxNetInterface() if err := netif.Read(rd); err != nil { return err } opt.Interfaces[netif.Name] = &netif } return nil } func (opt *LuxOptionNetIf) Write(wd *proto.LuxBuffer) { wd.WriteUint16(uint16(len(opt.Interfaces))) for _, netif := range opt.Interfaces { netif.Write(wd) } } func (*LuxOptionNetIf) Type() LuxOptionType { return LuxOptionTypeNetIf }