diff --git a/internal/api/network/interfaces.go b/internal/api/network/interfaces.go new file mode 100644 index 0000000..a54a8b9 --- /dev/null +++ b/internal/api/network/interfaces.go @@ -0,0 +1,20 @@ +package network + +import ( + "context" + + "nfsense.net/nfsense/internal/definitions" +) + +type GetInterfacesParameters struct { +} + +type GetInterfacesResult struct { + Interfaces []definitions.Interface +} + +func (f *Network) GetInterfaces(ctx context.Context, params GetInterfacesParameters) (GetInterfacesResult, error) { + return GetInterfacesResult{ + Interfaces: f.Conf.Network.Interfaces, + }, nil +} diff --git a/internal/api/network/network.go b/internal/api/network/network.go new file mode 100644 index 0000000..178778b --- /dev/null +++ b/internal/api/network/network.go @@ -0,0 +1,9 @@ +package network + +import ( + "nfsense.net/nfsense/internal/definitions" +) + +type Network struct { + Conf *definitions.Config +} diff --git a/internal/definitions/address.go b/internal/definitions/address.go index 9394a2f..0a86e99 100644 --- a/internal/definitions/address.go +++ b/internal/definitions/address.go @@ -8,12 +8,12 @@ import ( ) type Address struct { - Type AddressType `json:"type" validate:"min=0,max=3"` - Comment string `json:"comment,omitempty"` - Host *netip.Addr `json:"host,omitempty" validate:"excluded_unless=Type 0"` - Range *netipx.IPRange `json:"range,omitempty" validate:"excluded_unless=Type 1"` - Network *IPNet `json:"network,omitempty" validate:"excluded_unless=Type 2"` - Children *[]string `json:"children,omitempty"` + Type AddressType `json:"type" validate:"min=0,max=3"` + Comment string `json:"comment,omitempty"` + Host *netip.Addr `json:"host,omitempty" validate:"excluded_unless=Type 0"` + Range *netipx.IPRange `json:"range,omitempty" validate:"excluded_unless=Type 1"` + NetworkAddress *IPNet `json:"network,omitempty" validate:"excluded_unless=Type 2"` + Children *[]string `json:"children,omitempty"` } type AddressType int @@ -21,7 +21,7 @@ type AddressType int const ( Host AddressType = iota Range - Network + NetworkAddress AddressGroup ) @@ -33,7 +33,7 @@ func (t *AddressType) FromString(input string) AddressType { return map[string]AddressType{ "host": Host, "range": Range, - "network": Network, + "network": NetworkAddress, "group": AddressGroup, }[input] } diff --git a/internal/definitions/config.go b/internal/definitions/config.go index 8669570..b6f8e5b 100644 --- a/internal/definitions/config.go +++ b/internal/definitions/config.go @@ -11,6 +11,7 @@ type Config struct { ConfigVersion uint64 `json:"config_version" validate:"required,eq=1"` Firewall Firewall `json:"firewall" validate:"required,dive"` Object Object `json:"object" validate:"required,dive"` + Network Network `json:"network" validate:"required,dive"` } func ValidateConfig(conf *Config) error { diff --git a/internal/definitions/interface.go b/internal/definitions/interface.go new file mode 100644 index 0000000..467970a --- /dev/null +++ b/internal/definitions/interface.go @@ -0,0 +1,83 @@ +package definitions + +import ( + "encoding/json" + "net/netip" +) + +type Interface struct { + Type InterfaceType `json:"type" validate:"min=0,max=3"` + AddressingMode InterfaceAddressingMode `json:"addressing_mode" validate:"min=0,max=2"` + Address netip.Addr `json:"address" validate:"min=0,max=2"` + Comment string `json:"comment,omitempty"` +} + +type InterfaceType int + +const ( + Hardware InterfaceType = iota + Vlan + Bond + Bridge +) + +func (t InterfaceType) String() string { + return [...]string{"hardware", "vlan", "bond", "bridge"}[t] +} + +func (t *InterfaceType) FromString(input string) InterfaceType { + return map[string]InterfaceType{ + "hardware": Hardware, + "vlan": Vlan, + "bond": Bond, + "bridge": Bridge, + }[input] +} + +func (t InterfaceType) MarshalJSON() ([]byte, error) { + return json.Marshal(t.String()) +} + +func (t *InterfaceType) UnmarshalJSON(b []byte) error { + var s string + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *t = t.FromString(s) + return nil +} + +type InterfaceAddressingMode int + +const ( + None InterfaceAddressingMode = iota + Static + Dhcp +) + +func (t InterfaceAddressingMode) String() string { + return [...]string{"none", "static", "dhcp"}[t] +} + +func (t *InterfaceAddressingMode) FromString(input string) InterfaceAddressingMode { + return map[string]InterfaceAddressingMode{ + "none": None, + "static": Static, + "dhcp": Dhcp, + }[input] +} + +func (t InterfaceAddressingMode) MarshalJSON() ([]byte, error) { + return json.Marshal(t.String()) +} + +func (t *InterfaceAddressingMode) UnmarshalJSON(b []byte) error { + var s string + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *t = t.FromString(s) + return nil +} diff --git a/internal/definitions/network.go b/internal/definitions/network.go new file mode 100644 index 0000000..6128961 --- /dev/null +++ b/internal/definitions/network.go @@ -0,0 +1,5 @@ +package definitions + +type Network struct { + Interfaces []Interface `json:"interfaces" validate:"required,dive"` +} diff --git a/internal/interfaces/interfaces.go b/internal/interfaces/interfaces.go new file mode 100644 index 0000000..8f24e98 --- /dev/null +++ b/internal/interfaces/interfaces.go @@ -0,0 +1,47 @@ +package interfaces + +import ( + "bytes" + "fmt" + "os" + "os/exec" + + "nfsense.net/nfsense/internal/definitions" +) + +func GenerateInterfacesFile(conf definitions.Config) (string, error) { + buf := new(bytes.Buffer) + err := templates.ExecuteTemplate(buf, "interfaces.tmpl", conf) + if err != nil { + return "", fmt.Errorf("executing template: %w", err) + } + return buf.String(), nil +} + +func ApplyInterfacesFile(content string) (string, error) { + f, err := os.Create("interfaces.conf") + if err != nil { + return "", fmt.Errorf("creating File: %w", err) + } + + _, err = f.WriteString(content + "\n") + if err != nil { + return "", fmt.Errorf("writing File: %w", err) + } + + err = f.Sync() + if err != nil { + return "", fmt.Errorf("syncing File: %w", err) + } + + cmd := exec.Command("ifreload", "-a") + + var out bytes.Buffer + cmd.Stdout = &out + + err = cmd.Run() + if err != nil { + return "", fmt.Errorf("reloading Interfaces: %w", err) + } + return out.String(), nil +} diff --git a/internal/interfaces/template.go b/internal/interfaces/template.go new file mode 100644 index 0000000..b36b0b8 --- /dev/null +++ b/internal/interfaces/template.go @@ -0,0 +1,19 @@ +package interfaces + +import ( + "embed" + "text/template" +) + +//go:embed template +var templateFS embed.FS +var templates *template.Template + +func init() { + + var err error + templates, err = template.New("").ParseFS(templateFS, "template/*.tmpl") + if err != nil { + panic(err) + } +} diff --git a/internal/interfaces/template/interfaces.tmpl b/internal/interfaces/template/interfaces.tmpl new file mode 100644 index 0000000..e69de29 diff --git a/internal/nftables/match.go b/internal/nftables/match.go index da45bfc..6fb6d28 100644 --- a/internal/nftables/match.go +++ b/internal/nftables/match.go @@ -77,8 +77,8 @@ func GenerateAddressMatcher(allAddresses map[string]definitions.Address, match d sourceAddresses = append(sourceAddresses, address.Host.String()) case definitions.Range: sourceAddresses = append(sourceAddresses, address.Range.String()) - case definitions.Network: - sourceAddresses = append(sourceAddresses, address.Network.String()) + case definitions.NetworkAddress: + sourceAddresses = append(sourceAddresses, address.NetworkAddress.String()) default: panic("invalid address type") } @@ -90,8 +90,8 @@ func GenerateAddressMatcher(allAddresses map[string]definitions.Address, match d destinationAddresses = append(destinationAddresses, address.Host.String()) case definitions.Range: destinationAddresses = append(destinationAddresses, address.Range.String()) - case definitions.Network: - destinationAddresses = append(destinationAddresses, address.Network.String()) + case definitions.NetworkAddress: + destinationAddresses = append(destinationAddresses, address.NetworkAddress.String()) default: panic("invalid address type") }