File: nic.go

package info (click to toggle)
incus 6.0.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 24,392 kB
  • sloc: sh: 16,313; ansic: 3,121; python: 457; makefile: 337; ruby: 51; sql: 50; lisp: 6
file content (146 lines) | stat: -rw-r--r-- 6,264 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package device

import (
	"errors"
	"fmt"
	"slices"
	"strings"

	"github.com/lxc/incus/v6/internal/server/instance"
	"github.com/lxc/incus/v6/internal/server/instance/instancetype"
	"github.com/lxc/incus/v6/internal/server/network/acl"
	"github.com/lxc/incus/v6/shared/validate"
)

// nicValidationRules returns config validation rules for nic devices.
func nicValidationRules(requiredFields []string, optionalFields []string, instConf instance.ConfigReader) map[string]func(value string) error {
	// Define a set of default validators for each field name.
	defaultValidators := map[string]func(value string) error{
		"acceleration":                         validate.Optional(validate.IsOneOf("none", "sriov", "vdpa")),
		"name":                                 validate.Optional(validate.IsInterfaceName, func(_ string) error { return nicCheckNamesUnique(instConf) }),
		"parent":                               validate.IsAny,
		"network":                              validate.IsAny,
		"mtu":                                  validate.Optional(validate.IsNetworkMTU),
		"vlan":                                 validate.IsNetworkVLAN,
		"gvrp":                                 validate.Optional(validate.IsBool),
		"hwaddr":                               validate.IsNetworkMAC,
		"host_name":                            validate.IsAny,
		"limits.ingress":                       validate.IsAny,
		"limits.egress":                        validate.IsAny,
		"limits.max":                           validate.IsAny,
		"limits.priority":                      validate.Optional(validate.IsUint32),
		"security.mac_filtering":               validate.IsAny,
		"security.ipv4_filtering":              validate.IsAny,
		"security.ipv6_filtering":              validate.IsAny,
		"security.port_isolation":              validate.Optional(validate.IsBool),
		"ipv4.address":                         validate.Optional(validate.IsNetworkAddressV4),
		"ipv6.address":                         validate.Optional(validate.IsNetworkAddressV6),
		"ipv4.routes":                          validate.Optional(validate.IsListOf(validate.IsNetworkV4)),
		"ipv6.routes":                          validate.Optional(validate.IsListOf(validate.IsNetworkV6)),
		"boot.priority":                        validate.Optional(validate.IsUint32),
		"ipv4.gateway":                         networkValidGateway,
		"ipv6.gateway":                         networkValidGateway,
		"ipv4.host_address":                    validate.Optional(validate.IsNetworkAddressV4),
		"ipv6.host_address":                    validate.Optional(validate.IsNetworkAddressV6),
		"ipv4.host_table":                      validate.Optional(validate.IsUint32),
		"ipv6.host_table":                      validate.Optional(validate.IsUint32),
		"queue.tx.length":                      validate.Optional(validate.IsUint32),
		"ipv4.routes.external":                 validate.Optional(validate.IsListOf(validate.IsNetworkV4)),
		"ipv6.routes.external":                 validate.Optional(validate.IsListOf(validate.IsNetworkV6)),
		"nested":                               validate.IsAny,
		"security.acls":                        validate.IsAny,
		"security.acls.default.ingress.action": validate.Optional(validate.IsOneOf(acl.ValidActions...)),
		"security.acls.default.egress.action":  validate.Optional(validate.IsOneOf(acl.ValidActions...)),
		"security.acls.default.ingress.logged": validate.Optional(validate.IsBool),
		"security.acls.default.egress.logged":  validate.Optional(validate.IsBool),
		"security.promiscuous":                 validate.Optional(validate.IsBool),
		"mode":                                 validate.Optional(validate.IsOneOf("bridge", "vepa", "passthru", "private")),
		"io.bus":                               validate.Optional(func(_ string) error { return nicCheckIsVM(instConf) }, validate.IsOneOf("virtio", "usb")),
	}

	validators := map[string]func(value string) error{}

	for _, k := range optionalFields {
		defaultValidator := defaultValidators[k]

		// If field doesn't have a known validator, it is an unknown field, skip.
		if defaultValidator == nil {
			continue
		}

		// Wrap the default validator in an empty check as field is optional.
		validators[k] = func(value string) error {
			if value == "" {
				return nil
			}

			return defaultValidator(value)
		}
	}

	// Add required fields last, that way if they are specified in both required and optional
	// field sets, the required one will overwrite the optional validators.
	for _, k := range requiredFields {
		defaultValidator := defaultValidators[k]

		// If field doesn't have a known validator, it is an unknown field, skip.
		if defaultValidator == nil {
			continue
		}

		// Wrap the default validator in a not empty check as field is required.
		validators[k] = func(value string) error {
			err := validate.IsNotEmpty(value)
			if err != nil {
				return err
			}

			return defaultValidator(value)
		}
	}

	return validators
}

// nicHasAutoGateway takes the value of the "ipv4.gateway" or "ipv6.gateway" config keys and returns whether they
// specify whether the gateway mode is automatic or not.
func nicHasAutoGateway(value string) bool {
	if value == "" || value == "auto" {
		return true
	}

	return false
}

// nicCheckNamesUnique checks that all the NICs in the instConf's expanded devices have a unique (or unset) name.
func nicCheckNamesUnique(instConf instance.ConfigReader) error {
	seenNICNames := []string{}

	for _, devConfig := range instConf.ExpandedDevices() {
		if devConfig["type"] != "nic" || devConfig["name"] == "" {
			continue
		}

		if slices.Contains(seenNICNames, devConfig["name"]) {
			return fmt.Errorf("Duplicate NIC name detected %q", devConfig["name"])
		}

		seenNICNames = append(seenNICNames, devConfig["name"])
	}

	return nil
}

// nicCheckDNSNameConflict returns if instNameA matches instNameB (case insensitive).
func nicCheckDNSNameConflict(instNameA string, instNameB string) bool {
	return strings.EqualFold(instNameA, instNameB)
}

// nicCheckIsVM returns if the given instance is a VM.
func nicCheckIsVM(instConf instance.ConfigReader) error {
	if instConf.Type() != instancetype.VM {
		return errors.New("This option is only supported on virtual machines")
	}

	return nil
}