package domain import ( "fmt" "github.com/h44z/wg-portal/internal" "github.com/sirupsen/logrus" "math" "regexp" "strconv" "strings" "time" ) const ( InterfaceTypeServer InterfaceType = "server" InterfaceTypeClient InterfaceType = "client" InterfaceTypeAny InterfaceType = "any" ) type InterfaceIdentifier string type InterfaceType string type Interface struct { BaseModel // WireGuard specific (for the [interface] section of the config file) Identifier InterfaceIdentifier `gorm:"primaryKey"` // device name, for example: wg0 KeyPair // private/public Key of the server interface ListenPort int // the listening port, for example: 51820 Addresses []Cidr `gorm:"many2many:interface_addresses;"` // the interface ip addresses DnsStr string // the dns server that should be set if the interface is up, comma separated DnsSearchStr string // the dns search option string that should be set if the interface is up, will be appended to DnsStr Mtu int // the device MTU FirewallMark int32 // a firewall mark RoutingTable string // the routing table number or "off" if the routing table should not be managed PreUp string // action that is executed before the device is up PostUp string // action that is executed after the device is up PreDown string // action that is executed before the device is down PostDown string // action that is executed after the device is down SaveConfig bool // automatically persist config changes to the wgX.conf file // WG Portal specific DisplayName string // a nice display name/ description for the interface Type InterfaceType // the interface type, either InterfaceTypeServer or InterfaceTypeClient DriverType string // the interface driver type (linux, software, ...) Disabled *time.Time `gorm:"index"` // flag that specifies if the interface is enabled (up) or not (down) DisabledReason string // the reason why the interface has been disabled // Default settings for the peer, used for new peers, those settings will be published to ConfigOption options of // the peer config PeerDefNetworkStr string // the default subnets from which peers will get their IP addresses, comma seperated PeerDefDnsStr string // the default dns server for the peer PeerDefDnsSearchStr string // the default dns search options for the peer PeerDefEndpoint string // the default endpoint for the peer PeerDefAllowedIPsStr string // the default allowed IP string for the peer PeerDefMtu int // the default device MTU PeerDefPersistentKeepalive int // the default persistent keep-alive Value PeerDefFirewallMark int32 // default firewall mark PeerDefRoutingTable string // the default routing table PeerDefPreUp string // default action that is executed before the device is up PeerDefPostUp string // default action that is executed after the device is up PeerDefPreDown string // default action that is executed before the device is down PeerDefPostDown string // default action that is executed after the device is down } func (i *Interface) IsValid() bool { return true // TODO: implement check } func (i *Interface) IsDisabled() bool { if i == nil { return true } return i.Disabled != nil } func (i *Interface) AddressStr() string { return CidrsToString(i.Addresses) } func (i *Interface) CopyCalculatedAttributes(src *Interface) { i.BaseModel = src.BaseModel } func (i *Interface) GetConfigFileName() string { reg := regexp.MustCompile("[^a-zA-Z0-9-_]+") filename := fmt.Sprintf("%s", internal.TruncateString(string(i.Identifier), 8)) filename = reg.ReplaceAllString(filename, "") filename += ".conf" return filename } func (i *Interface) GetAllowedIPs(peers []Peer) []Cidr { var allowedCidrs []Cidr for _, peer := range peers { for _, ip := range peer.Interface.Addresses { allowedCidrs = append(allowedCidrs, ip.HostAddr()) } if peer.ExtraAllowedIPsStr != "" { extraIPs, err := CidrsFromString(peer.ExtraAllowedIPsStr) if err == nil { allowedCidrs = append(allowedCidrs, extraIPs...) } } } return allowedCidrs } func (i *Interface) ManageRoutingTable() bool { routingTableStr := strings.ToLower(i.RoutingTable) return routingTableStr != "off" } // GetRoutingTable returns the routing table number or // // -1 if RoutingTable was set to "off" or an error occurred func (i *Interface) GetRoutingTable() int { routingTableStr := strings.ToLower(i.RoutingTable) switch { case routingTableStr == "": return 0 case routingTableStr == "off": return -1 case strings.HasPrefix(routingTableStr, "0x"): numberStr := strings.ReplaceAll(routingTableStr, "0x", "") routingTable, err := strconv.ParseUint(numberStr, 16, 64) if err != nil { logrus.Errorf("invalid hex routing table %s: %v", routingTableStr, err) return -1 } if routingTable > math.MaxInt32 { logrus.Errorf("invalid routing table %s, too big", routingTableStr) return -1 } return int(routingTable) default: routingTable, err := strconv.Atoi(routingTableStr) if err != nil { logrus.Errorf("invalid routing table %s: %v", routingTableStr, err) return -1 } return routingTable } } type PhysicalInterface struct { Identifier InterfaceIdentifier // device name, for example: wg0 KeyPair // private/public Key of the server interface ListenPort int // the listening port, for example: 51820 Addresses []Cidr // the interface ip addresses Mtu int // the device MTU FirewallMark int32 // a firewall mark DeviceUp bool // device status ImportSource string // import source (wgctrl, file, ...) DeviceType string // device type (Linux kernel, userspace, ...) BytesUpload uint64 BytesDownload uint64 } func ConvertPhysicalInterface(pi *PhysicalInterface) *Interface { iface := &Interface{ Identifier: pi.Identifier, KeyPair: pi.KeyPair, ListenPort: pi.ListenPort, Addresses: pi.Addresses, DnsStr: "", DnsSearchStr: "", Mtu: pi.Mtu, FirewallMark: pi.FirewallMark, RoutingTable: "", PreUp: "", PostUp: "", PreDown: "", PostDown: "", SaveConfig: false, DisplayName: string(pi.Identifier), Type: InterfaceTypeAny, DriverType: pi.DeviceType, Disabled: nil, PeerDefNetworkStr: "", PeerDefDnsStr: "", PeerDefDnsSearchStr: "", PeerDefEndpoint: "", PeerDefAllowedIPsStr: "", PeerDefMtu: pi.Mtu, PeerDefPersistentKeepalive: 0, PeerDefFirewallMark: 0, PeerDefRoutingTable: "", PeerDefPreUp: "", PeerDefPostUp: "", PeerDefPreDown: "", PeerDefPostDown: "", } return iface } func MergeToPhysicalInterface(pi *PhysicalInterface, i *Interface) { pi.Identifier = i.Identifier pi.PublicKey = i.PublicKey pi.PrivateKey = i.PrivateKey pi.ListenPort = i.ListenPort pi.Mtu = i.Mtu pi.FirewallMark = i.FirewallMark pi.DeviceUp = !i.IsDisabled() pi.Addresses = i.Addresses } type RoutingTableInfo struct { FwMark int Table int } func (r RoutingTableInfo) String() string { return fmt.Sprintf("%d -> %d", r.FwMark, r.Table) } func (r RoutingTableInfo) ManagementEnabled() bool { if r.Table == -1 { return false } return true } func (r RoutingTableInfo) GetRoutingTable() int { if r.Table <= 0 { return r.FwMark // use the dynamic routing table which has the same number as the firewall mark } return r.Table }