From 27eae48e5ce9cf1c455792a2953299922a5d873a Mon Sep 17 00:00:00 2001 From: Vaerh Date: Fri, 27 Sep 2024 09:07:56 +0300 Subject: [PATCH 01/13] Add a helper for the `default` attribute --- routeros/provider_schema_helpers.go | 6 ++++++ routeros/resource_bgp_template.go | 5 +---- routeros/resource_capsman_manager.go | 7 ++----- routeros/resource_interface_lte_apn.go | 5 +---- routeros/resource_ip_hotspot_user.go | 8 ++------ routeros/resource_ip_hotspot_user_profile.go | 6 +----- routeros/resource_ipv6_neighbor_discovery.go | 6 +----- routeros/resource_ppp_profile.go | 6 +----- routeros/resource_snmp_community.go | 12 ++++-------- routeros/resource_snmp_community_v0.go | 14 +++++--------- routeros/resource_system_led.go | 5 +---- routeros/resource_system_logging.go | 5 +---- routeros/resource_system_logging_actions.go | 6 +----- routeros/resource_user_manager_attribute.go | 5 +---- routeros/resource_user_manager_user_group.go | 5 +---- 15 files changed, 29 insertions(+), 72 deletions(-) diff --git a/routeros/provider_schema_helpers.go b/routeros/provider_schema_helpers.go index 71b71299..7078a5b1 100644 --- a/routeros/provider_schema_helpers.go +++ b/routeros/provider_schema_helpers.go @@ -31,6 +31,7 @@ const ( KeyArpTimeout = "arp_timeout" KeyClampTcpMss = "clamp_tcp_mss" KeyComment = "comment" + KeyDefault = "default" KeyDynamic = "dynamic" KeyDefaultName = "default_name" KeyDisabled = "disabled" @@ -255,6 +256,11 @@ var ( Optional: true, DiffSuppressFunc: AlwaysPresentNotUserProvided, } + PropDefaultRo = &schema.Schema{ + Type: schema.TypeBool, + Computed: true, + Description: "It's the default item.", + } PropDontFragmentRw = &schema.Schema{ Type: schema.TypeString, Optional: true, diff --git a/routeros/resource_bgp_template.go b/routeros/resource_bgp_template.go index 5b51d611..459133b0 100644 --- a/routeros/resource_bgp_template.go +++ b/routeros/resource_bgp_template.go @@ -105,10 +105,7 @@ func ResourceRoutingBGPTemplate() *schema.Resource { ValidateFunc: validation.IsIPv4Address, }, KeyComment: PropCommentRw, - "default": { - Type: schema.TypeBool, - Computed: true, - }, + KeyDefault: PropDefaultRo, KeyDisabled: PropDisabledRw, // hold-time ( time[3s..1h] | infinity ; Default: 3m ) "hold_time": { diff --git a/routeros/resource_capsman_manager.go b/routeros/resource_capsman_manager.go index 7834e4fb..edc75219 100644 --- a/routeros/resource_capsman_manager.go +++ b/routeros/resource_capsman_manager.go @@ -101,11 +101,8 @@ func ResourceCapsManManagerInterface() *schema.Resource { MetaResourcePath: PropResourcePath("/caps-man/manager/interface"), MetaId: PropId(Id), - KeyComment: PropCommentRw, - "default": { - Type: schema.TypeBool, - Computed: true, - }, + KeyComment: PropCommentRw, + KeyDefault: PropDefaultRo, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "forbid": { diff --git a/routeros/resource_interface_lte_apn.go b/routeros/resource_interface_lte_apn.go index e0e45f97..acbc126a 100644 --- a/routeros/resource_interface_lte_apn.go +++ b/routeros/resource_interface_lte_apn.go @@ -48,10 +48,7 @@ func ResourceInterfaceLteApn() *schema.Resource { DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyComment: PropCommentRw, - "default": { - Type: schema.TypeBool, - Computed: true, - }, + KeyDefault: PropDefaultRo, "default_route_distance": { Type: schema.TypeInt, Optional: true, diff --git a/routeros/resource_ip_hotspot_user.go b/routeros/resource_ip_hotspot_user.go index 30ab4349..7333f746 100644 --- a/routeros/resource_ip_hotspot_user.go +++ b/routeros/resource_ip_hotspot_user.go @@ -41,12 +41,8 @@ func ResourceIpHotspotUser() *schema.Resource { Description: "IP address, when specified client will get the address from the HotSpot one-to-one NAT translations. " + "Address does not restrict HotSpot login only from this address.", }, - KeyComment: PropCommentRw, - "default": { - Type: schema.TypeBool, - Computed: true, - Description: "It's the default rule.", - }, + KeyComment: PropCommentRw, + KeyDefault: PropDefaultRo, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "email": { diff --git a/routeros/resource_ip_hotspot_user_profile.go b/routeros/resource_ip_hotspot_user_profile.go index 2866d570..b7711089 100644 --- a/routeros/resource_ip_hotspot_user_profile.go +++ b/routeros/resource_ip_hotspot_user_profile.go @@ -92,11 +92,7 @@ func ResourceIpHotspotUserProfile() *schema.Resource { "from the begining.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, - "default": { - Type: schema.TypeBool, - Computed: true, - Description: "It's the default rule.", - }, + KeyDefault: PropDefaultRo, "idle_timeout": { Type: schema.TypeString, Optional: true, diff --git a/routeros/resource_ipv6_neighbor_discovery.go b/routeros/resource_ipv6_neighbor_discovery.go index fad89ac8..0571cac6 100644 --- a/routeros/resource_ipv6_neighbor_discovery.go +++ b/routeros/resource_ipv6_neighbor_discovery.go @@ -50,11 +50,7 @@ func ResourceIPv6NeighborDiscovery() *schema.Resource { Default: true, }, KeyComment: PropCommentRw, - "default": { - Type: schema.TypeBool, - Computed: true, - Description: "Neighbor discovery entry is the default configuration.", - }, + KeyDefault: PropDefaultRo, "dns_servers": { Type: schema.TypeString, Optional: true, diff --git a/routeros/resource_ppp_profile.go b/routeros/resource_ppp_profile.go index a7407dab..91442241 100644 --- a/routeros/resource_ppp_profile.go +++ b/routeros/resource_ppp_profile.go @@ -91,11 +91,7 @@ func ResourcePPPProfile() *schema.Resource { ValidateFunc: validation.StringInSlice([]string{"yes", "no", "default"}, false), }, KeyComment: PropCommentRw, - "default": { - Type: schema.TypeString, - Computed: true, - Description: "Default profile sign.", - }, + KeyDefault: PropDefaultRo, "dhcpv6_pd_pool": { Type: schema.TypeString, Optional: true, diff --git a/routeros/resource_snmp_community.go b/routeros/resource_snmp_community.go index 1379332e..74c07030 100644 --- a/routeros/resource_snmp_community.go +++ b/routeros/resource_snmp_community.go @@ -56,12 +56,8 @@ func ResourceSNMPCommunity() *schema.Resource { Description: "The protocol used for authentication (SNMPv3).", ValidateFunc: validation.StringInSlice([]string{"MD5", "SHA1"}, false), }, - KeyComment: PropCommentRw, - "default": { - Type: schema.TypeBool, - Computed: true, - Description: "It's a default community.", - }, + KeyComment: PropCommentRw, + KeyDefault: PropDefaultRo, KeyDisabled: PropDisabledRw, "encryption_password": { Type: schema.TypeString, @@ -113,11 +109,11 @@ func ResourceSNMPCommunity() *schema.Resource { StateContext: schema.ImportStatePassthroughContext, }, - Schema: resSchema, + Schema: resSchema, SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ { - Type: ResourceSNMPCommunityV0().CoreConfigSchema().ImpliedType(), + Type: ResourceSNMPCommunityV0().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationScalarToList("addresses"), Version: 0, }, diff --git a/routeros/resource_snmp_community_v0.go b/routeros/resource_snmp_community_v0.go index 1ca0794a..80d39d56 100644 --- a/routeros/resource_snmp_community_v0.go +++ b/routeros/resource_snmp_community_v0.go @@ -30,9 +30,9 @@ func ResourceSNMPCommunityV0() *schema.Resource { MetaId: PropId(Id), "addresses": { - Type: schema.TypeString, - Optional: true, - Description: "Set of IP (v4 or v6) addresses or CIDR networks from which connections to SNMP server are allowed.", + Type: schema.TypeString, + Optional: true, + Description: "Set of IP (v4 or v6) addresses or CIDR networks from which connections to SNMP server are allowed.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "authentication_password": { @@ -48,12 +48,8 @@ func ResourceSNMPCommunityV0() *schema.Resource { Description: "The protocol used for authentication (SNMPv3).", ValidateFunc: validation.StringInSlice([]string{"MD5", "SHA1"}, false), }, - KeyComment: PropCommentRw, - "default": { - Type: schema.TypeBool, - Computed: true, - Description: "It's a default community.", - }, + KeyComment: PropCommentRw, + KeyDefault: PropDefaultRo, KeyDisabled: PropDisabledRw, "encryption_password": { Type: schema.TypeString, diff --git a/routeros/resource_system_led.go b/routeros/resource_system_led.go index 832e92ea..ee3a2c04 100644 --- a/routeros/resource_system_led.go +++ b/routeros/resource_system_led.go @@ -22,10 +22,7 @@ func ResourceSystemLed() *schema.Resource { MetaResourcePath: PropResourcePath("/system/leds"), MetaId: PropId(Id), - "default": { - Type: schema.TypeBool, - Computed: true, - }, + KeyDefault: PropDefaultRo, KeyDisabled: PropDisabledRw, "interface": { Type: schema.TypeString, diff --git a/routeros/resource_system_logging.go b/routeros/resource_system_logging.go index 229c9fd8..2740cf53 100644 --- a/routeros/resource_system_logging.go +++ b/routeros/resource_system_logging.go @@ -38,10 +38,7 @@ func ResourceSystemLogging() *schema.Resource { Required: true, Description: "specifies one of the system default actions or user specified action listed in actions menu", }, - "default": { - Type: schema.TypeString, - Computed: true, - }, + KeyDefault: PropDefaultRo, "prefix": { Type: schema.TypeString, Optional: true, diff --git a/routeros/resource_system_logging_actions.go b/routeros/resource_system_logging_actions.go index f49e1c64..70d16952 100644 --- a/routeros/resource_system_logging_actions.go +++ b/routeros/resource_system_logging_actions.go @@ -36,11 +36,7 @@ func ResourceSystemLoggingAction() *schema.Resource { Optional: true, Description: `Whether to use bsd-syslog as defined in RFC 3164.`, }, - "default": { - Type: schema.TypeBool, - Computed: true, - Description: "This is a default action.", - }, + KeyDefault: PropDefaultRo, "disk_file_count": { Type: schema.TypeInt, Optional: true, diff --git a/routeros/resource_user_manager_attribute.go b/routeros/resource_user_manager_attribute.go index 0a3dd3e0..ef5bb3ad 100644 --- a/routeros/resource_user_manager_attribute.go +++ b/routeros/resource_user_manager_attribute.go @@ -25,10 +25,7 @@ func ResourceUserManagerAttribute() *schema.Resource { MetaResourcePath: PropResourcePath("/user-manager/attribute"), MetaId: PropId(Id), - "default": { - Type: schema.TypeBool, - Computed: true, - }, + KeyDefault: PropDefaultRo, KeyDefaultName: PropDefaultNameRo("The attribute's default name."), KeyName: PropName("The attribute's name."), "packet_types": { diff --git a/routeros/resource_user_manager_user_group.go b/routeros/resource_user_manager_user_group.go index 78d95010..b09f76eb 100644 --- a/routeros/resource_user_manager_user_group.go +++ b/routeros/resource_user_manager_user_group.go @@ -29,10 +29,7 @@ func ResourceUserManagerUserGroup() *schema.Resource { Elem: &schema.Schema{Type: schema.TypeString}, Description: "A custom set of colon-separated attributes with their values will be added to `Access-Accept` messages for users in this group.", }, - "default": { - Type: schema.TypeBool, - Computed: true, - }, + KeyDefault: PropDefaultRo, KeyDefaultName: PropDefaultNameRo("The default name of the group."), "inner_auths": { Type: schema.TypeSet, From 99e74657b9e8cfb1bd7875bb79418c69db69ad6a Mon Sep 17 00:00:00 2001 From: Vaerh Date: Fri, 27 Sep 2024 10:49:14 +0300 Subject: [PATCH 02/13] feat(wireless): Add new resource `routeros_interface_wireless_security_profiles` --- .../import.sh | 3 + .../resource.tf | 7 + ...ce_interface_wireless_security_profiles.go | 366 ++++++++++++++++++ ...terface_wireless_security_profiles_test.go | 53 +++ 4 files changed, 429 insertions(+) create mode 100644 examples/resources/routeros_interface_wireless_security_profiles/import.sh create mode 100644 examples/resources/routeros_interface_wireless_security_profiles/resource.tf create mode 100644 routeros/resource_interface_wireless_security_profiles.go create mode 100644 routeros/resource_interface_wireless_security_profiles_test.go diff --git a/examples/resources/routeros_interface_wireless_security_profiles/import.sh b/examples/resources/routeros_interface_wireless_security_profiles/import.sh new file mode 100644 index 00000000..4ae79052 --- /dev/null +++ b/examples/resources/routeros_interface_wireless_security_profiles/import.sh @@ -0,0 +1,3 @@ +#The ID can be found via API or the terminal +#The command for the terminal is -> :put [/interface/wireless/security-profiles get [print show-ids]] +terraform import routeros_interface_wireless_security_profiles.test *3 \ No newline at end of file diff --git a/examples/resources/routeros_interface_wireless_security_profiles/resource.tf b/examples/resources/routeros_interface_wireless_security_profiles/resource.tf new file mode 100644 index 00000000..ca349563 --- /dev/null +++ b/examples/resources/routeros_interface_wireless_security_profiles/resource.tf @@ -0,0 +1,7 @@ +resource "routeros_interface_wireless_security_profiles" "test" { + name = "test-profile" + mode = "dynamic-keys" + authentication_types = ["wpa-psk", "wpa2-psk"] + wpa_pre_shared_key = "wpa_psk_key" + wpa2_pre_shared_key = "wpa2_psk_key" +} \ No newline at end of file diff --git a/routeros/resource_interface_wireless_security_profiles.go b/routeros/resource_interface_wireless_security_profiles.go new file mode 100644 index 00000000..a1067368 --- /dev/null +++ b/routeros/resource_interface_wireless_security_profiles.go @@ -0,0 +1,366 @@ +package routeros + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +/* + { + ".id": "*0", + "authentication-types": "wpa2-psk", + "comment": "defconf", + "default": "true", + "disable-pmkid": "true", + "eap-methods": "passthrough", + "group-ciphers": "aes-ccm", + "group-key-update": "5m", + "interim-update": "0s", + "management-protection": "disabled", + "management-protection-key": "", + "mode": "dynamic-keys", + "mschapv2-password": "", + "mschapv2-username": "", + "name": "default", + "radius-called-format": "mac:ssid", + "radius-eap-accounting": "false", + "radius-mac-accounting": "false", + "radius-mac-authentication": "false", + "radius-mac-caching": "disabled", + "radius-mac-format": "XX:XX:XX:XX:XX:XX", + "radius-mac-mode": "as-username", + "static-algo-0": "none", + "static-algo-1": "none", + "static-algo-2": "none", + "static-algo-3": "none", + "static-key-0": "", + "static-key-1": "", + "static-key-2": "", + "static-key-3": "", + "static-sta-private-algo": "none", + "static-sta-private-key": "", + "static-transmit-key": "key-0", + "supplicant-identity": "MikroTik", + "tls-certificate": "none", + "tls-mode": "no-certificates", + "unicast-ciphers": "aes-ccm", + "wpa-pre-shared-key": "", + "wpa2-pre-shared-key": "XXX" + } +*/ + +// https://help.mikrotik.com/docs/display/ROS/Wireless+Interface#WirelessInterface-SecurityProfiles +func ResourceInterfaceWirelessSecurityProfiles() *schema.Resource { + resSchema := map[string]*schema.Schema{ + MetaResourcePath: PropResourcePath("/interface/wireless/security-profiles"), + MetaId: PropId(Id), + + "authentication_types": { + Type: schema.TypeSet, + Optional: true, + Description: "Set of supported authentication types, multiple values can be selected. Access Point will " + + "advertise supported authentication types, and client will connect to Access Point only if it supports " + + "any of the advertised authentication types.", + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"wpa-psk", "wpa2-psk", "wpa-eap", "wpa2-eap"}, false), + }, + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + KeyComment: PropCommentRw, + KeyDefault: PropDefaultRo, + "disable_pmkid": { + Type: schema.TypeBool, + Optional: true, + Description: "Whether to include `PMKID` into the `EAPOL` frame sent out by the Access Point. Disabling PMKID " + + "can cause compatibility issues with devices that use the PMKID to connect to an Access Point. `yes` - removes " + + "PMKID from EAPOL frames (improves security, reduces compatibility). `no` - includes PMKID into EAPOL frames " + + "(reduces security, improves compatibility).This property only has effect on Access Points.", + }, + "eap_methods": { + Type: schema.TypeString, + Optional: true, + Description: "Allowed types of authentication methods, multiple values can be selected. This property only " + + "has effect on Access Points. `eap-tls` - Use built-in EAP TLS authentication. Both client and server certificates " + + "are supported. See description of tls-mode and tls-certificate properties. `eap-ttls-mschapv2` - Use EAP-TTLS " + + "with MS-CHAPv2 authentication. `passthrough` - Access Point will relay authentication process to the RADIUS " + + "server. `peap` - Use Protected EAP authentication.", + ValidateFunc: validation.StringInSlice([]string{"eap-tls", "eap-ttls-mschapv2", "passthrough", "peap"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "group_ciphers": { + Type: schema.TypeString, + Optional: true, + Description: "Access Point advertises one of these ciphers, multiple values can be selected. Access Point " + + "uses it to encrypt all broadcast and multicast frames. Client attempts connection only to Access Points " + + "that use one of the specified group ciphers. `tkip` - Temporal Key Integrity Protocol - encryption protocol, " + + "compatible with legacy WEP equipment, but enhanced to correct some of the WEP flaws. `aes-ccm` - more secure " + + "WPA encryption protocol, based on the reliable AES (Advanced Encryption Standard). Networks free of " + + "WEP legacy should use only this cipher.", + ValidateFunc: validation.StringInSlice([]string{"tkip", "aes-ccm"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "group_key_update": { + Type: schema.TypeString, + Optional: true, + Description: "Controls how often Access Point updates the group key. This key is used to encrypt all broadcast " + + "and multicast frames. property only has effect for Access Points.", + DiffSuppressFunc: TimeEquall, + }, + "interim_update": { + Type: schema.TypeString, + Optional: true, + Description: "When RADIUS accounting is used, Access Point periodically sends accounting information updates " + + "to the RADIUS server. This property specifies default update interval that can be overridden by the " + + "RADIUS server using Acct-Interim-Interval attribute.", + DiffSuppressFunc: TimeEquall, + }, + "management_protection": { + Type: schema.TypeString, + Optional: true, + Description: "Management frame protection. Used for: Deauthentication attack prevention, MAC address cloning " + + "issue. Possible values are: `disabled` - management protection is disabled (default), `allowed` - use " + + "management protection if supported by remote party (for AP - allow both, non-management protection " + + "and management protection clients, for client - connect both to APs with and without management " + + "protection), `required` - establish association only with remote devices that support management " + + "protection (for AP - accept only clients that support management protection, for client - connect " + + "only to APs that support management protection).", + ValidateFunc: validation.StringInSlice([]string{"disabled", "allowed", "required"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "management_protection_key": { + Type: schema.TypeString, + Optional: true, + Description: "Management protection shared secret. When interface is in AP mode, default management " + + "protection key (configured in security-profile) can be overridden by key specified in access-list or " + + "RADIUS attribute.", + }, + "mode": { + Type: schema.TypeString, + Optional: true, + Description: "Encryption mode for the security profile. `none` - Encryption is not used. Encrypted frames are " + + "not accepted. `static-keys-required` - WEP mode. Do not accept and do not send unencrypted frames. Station " + + "in static-keys-required mode will not connect to an Access Point in static-keys-optional mode. `static-keys-optional` - " + + "WEP mode. Support encryption and decryption, but allow also to receive and send unencrypted frames. " + + "Device will send unencrypted frames if encryption algorithm is specified as none. Station in static-keys-optional mode " + + "will not connect to an Access Point in `static-keys-required` mode. See also: static-sta-private-algo, " + + "static-transmit-key. `dynamic-keys` - WPA mode.", + ValidateFunc: validation.StringInSlice([]string{"none", "static-keys-optional", "static-keys-required", "dynamic-keys"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "mschapv2_password": { + Type: schema.TypeString, + Optional: true, + Description: "Password to use for authentication when `eap-ttls-mschapv2` or `peap` authentication method is " + + "being used. This property only has effect on Stations.", + }, + "mschapv2_username": { + Type: schema.TypeString, + Optional: true, + Description: "Username to use for authentication when `eap-ttls-mschapv2` or `peap` authentication method is " + + "being used. This property only has effect on Stations.", + }, + KeyName: PropName("Name of the security profile."), + "radius_called_format": { + Type: schema.TypeString, + Optional: true, + Description: "mac | mac:ssid | ssid", + ValidateFunc: validation.StringInSlice([]string{"mac", "mac:ssid", "ssid"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "radius_eap_accounting": { + Type: schema.TypeBool, + Optional: true, + }, + "radius_mac_accounting": { + Type: schema.TypeBool, + Optional: true, + }, + "radius_mac_authentication": { + Type: schema.TypeBool, + Optional: true, + Description: "This property affects the way how Access Point processes clients that are not found in the Access " + + "List.no - allow or reject client authentication based on the value of default-authentication property " + + "of the Wireless interface.yes - Query RADIUS server using MAC address of client as user name. With this " + + "setting the value of default-authentication has no effect.", + }, + "radius_mac_caching": { + Type: schema.TypeString, + Optional: true, + Description: "If this value is set to time interval, the Access Point will cache RADIUS MAC authentication " + + "responses for specified time, and will not contact RADIUS server if matching cache entry already exists. " + + "Value disabled will disable cache, Access Point will always contact RADIUS server.", + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "radius_mac_format": { + Type: schema.TypeString, + Optional: true, + Description: "Controls how MAC address of the client is encoded by Access Point in the User-Name attribute " + + "of the MAC authentication and MAC accounting RADIUS requests.", + ValidateFunc: validation.StringInSlice([]string{"XX:XX:XX:XX:XX:XX", "XXXX:XXXX:XXXX", "XXXXXX:XXXXXX", + "XX-XX-XX-XX-XX-XX", "XXXXXX-XXXXXX", "XXXXXXXXXXXX", "XX XX XX XX XX XX"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "radius_mac_mode": { + Type: schema.TypeString, + Optional: true, + Description: "By default Access Point uses an empty password, when sending Access-Request during MAC authentication. " + + "When this property is set to `as-username-and-password`, Access Point will use the same value for User-Password " + + "attribute as for the User-Name attribute.", + ValidateFunc: validation.StringInSlice([]string{"as-username", "as-username-and-password"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "static_algo_0": { + Type: schema.TypeString, + Optional: true, + Description: "Encryption algorithm to use with the corresponding key.", + ValidateFunc: validation.StringInSlice([]string{"none", "40bit-wep", "104bit-wep", "tkip", "aes-ccm"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "static_algo_1": { + Type: schema.TypeString, + Optional: true, + Description: "Encryption algorithm to use with the corresponding key.", + ValidateFunc: validation.StringInSlice([]string{"none", "40bit-wep", "104bit-wep", "tkip", "aes-ccm"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "static_algo_2": { + Type: schema.TypeString, + Optional: true, + Description: "Encryption algorithm to use with the corresponding key.", + ValidateFunc: validation.StringInSlice([]string{"none", "40bit-wep", "104bit-wep", "tkip", "aes-ccm"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "static_algo_3": { + Type: schema.TypeString, + Optional: true, + Description: "Encryption algorithm to use with the corresponding key.", + ValidateFunc: validation.StringInSlice([]string{"none", "40bit-wep", "104bit-wep", "tkip", "aes-ccm"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "static_key_0": { + Type: schema.TypeString, + Optional: true, + Description: "Hexadecimal representation of the key. Length of key must be appropriate for selected algorithm. " + + "See the Statically configured WEP keys section.", + }, + "static_key_1": { + Type: schema.TypeString, + Optional: true, + Description: "Hexadecimal representation of the key. Length of key must be appropriate for selected algorithm. " + + "See the Statically configured WEP keys section.", + }, + "static_key_2": { + Type: schema.TypeString, + Optional: true, + Description: "Hexadecimal representation of the key. Length of key must be appropriate for selected algorithm. " + + "See the Statically configured WEP keys section.", + }, + "static_key_3": { + Type: schema.TypeString, + Optional: true, + Description: "Hexadecimal representation of the key. Length of key must be appropriate for selected algorithm. " + + "See the Statically configured WEP keys section.", + }, + "static_sta_private_algo": { + Type: schema.TypeString, + Optional: true, + Description: "Encryption algorithm to use with station private key. Value none disables use of the private " + + "key. This property is only used on Stations. Access Point has to get corresponding value either from private-algo property, " + + "or from Mikrotik-Wireless-Enc-Algo attribute. Station private key replaces key 0 for unicast frames. " + + "Station will not use private key to decrypt broadcast frames.", + ValidateFunc: validation.StringInSlice([]string{"none", "40bit-wep", "104bit-wep", "tkip", "aes-ccm"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "static_sta_private_key": { + Type: schema.TypeString, + Optional: true, + Description: "Length of key must be appropriate for selected algorithm, see the Statically configured WEP " + + "keys section. This property is used only on Stations. Access Point uses corresponding key either from " + + "private-key property, or from Mikrotik-Wireless-Enc-Key attribute.", + }, + "static_transmit_key": { + Type: schema.TypeString, + Optional: true, + Description: "Access Point will use the specified key to encrypt frames for clients that do not use private " + + "key. Access Point will also use this key to encrypt broadcast and multicast frames. Client will use " + + "the specified key to encrypt frames if static-sta-private-algo is set to none. If corresponding static-algo-N property " + + "has value set to none, then frame will be sent unencrypted (when mode is set to static-keys-optional) " + + "or will not be sent at all (when mode is set to static-keys-required).", + ValidateFunc: validation.StringInSlice([]string{"key-0", "key-1", "key-2", "key-3"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "supplicant_identity": { + Type: schema.TypeString, + Optional: true, + Description: "EAP identity that is sent by client at the beginning of EAP authentication. This value is " + + "used as a value for User-Name attribute in RADIUS messages sent by RADIUS EAP accounting and RADIUS " + + "EAP pass-through authentication.", + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "tls_certificate": { + Type: schema.TypeString, + Optional: true, + Description: "Access Point always needs a certificate when configured when tls-mode is set to verify-certificate, " + + "or is set to dont-verify-certificate. Client needs a certificate only if Access Point is configured " + + "with tls-mode set to verify-certificate. In this case client needs a valid certificate that is signed " + + "by a CA known to the Access Point. This property only has effect when tls-mode is not set to " + + "no-certificates and eap-methods contains eap-tls.", + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "tls_mode": { + Type: schema.TypeString, + Optional: true, + Description: "This property has effect only when eap-methods contains eap-tls. `verify-certificate` - Require " + + "remote device to have valid certificate. Check that it is signed by known certificate authority. No " + + "additional identity verification is done. Certificate may include information about time period during " + + "which it is valid. If router has incorrect time and date, it may reject valid certificate because router's " + + "clock is outside that period. See also the Certificates configuration. `dont-verify-certificate` - Do not " + + "check certificate of the remote device. Access Point will not require client to provide certificate. " + + "`no-certificates` - Do not use certificates. TLS session is established using 2048 bit anonymous " + + "Diffie-Hellman key exchange. `verify-certificate-with-crl` - Same as verify-certificate but also " + + "checks if the certificate is valid by checking the Certificate Revocation List.", + ValidateFunc: validation.StringInSlice([]string{"verify-certificate", "dont-verify-certificate", + "no-certificates", "verify-certificate-with-crl"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "unicast_ciphers": { + Type: schema.TypeString, + Optional: true, + Description: "Access Point advertises that it supports specified ciphers, multiple values can be selected. " + + "Client attempts connection only to Access Points that supports at least one of the specified ciphers. " + + "One of the ciphers will be used to encrypt unicast frames that are sent between Access Point and Station.", + ValidateFunc: validation.StringInSlice([]string{"tkip", "aes-ccm"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "wpa_pre_shared_key": { + Type: schema.TypeString, + Optional: true, + Description: "`WPA` pre-shared key mode requires all devices in a BSS to have common secret key. Value of " + + "this key can be an arbitrary text. Commonly referred to as the network password for WPA mode. property " + + "only has effect when wpa-psk is added to authentication-types.", + ValidateFunc: validation.StringLenBetween(8, 64), + }, + "wpa2_pre_shared_key": { + Type: schema.TypeString, + Optional: true, + Description: "`WPA2` pre-shared key mode requires all devices in a BSS to have common secret key. Value of " + + "this key can be an arbitrary text. Commonly referred to as the network password for WPA2 mode. property " + + "only has effect when wpa2-psk is added to authentication-types.", + ValidateFunc: validation.StringLenBetween(8, 64), + }, + } + + return &schema.Resource{ + CreateContext: DefaultCreate(resSchema), + ReadContext: DefaultRead(resSchema), + UpdateContext: DefaultUpdate(resSchema), + DeleteContext: DefaultDelete(resSchema), + + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: resSchema, + } +} diff --git a/routeros/resource_interface_wireless_security_profiles_test.go b/routeros/resource_interface_wireless_security_profiles_test.go new file mode 100644 index 00000000..ebf5ab90 --- /dev/null +++ b/routeros/resource_interface_wireless_security_profiles_test.go @@ -0,0 +1,53 @@ +package routeros + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +const testInterfaceWirelessSecurityProfiles = "routeros_interface_wireless_security_profiles.test" + +func TestAccInterfaceWirelessSecurityProfilesTest_basic(t *testing.T) { + t.Parallel() + for _, name := range testNames { + t.Run(name, func(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testSetTransportEnv(t, name) + }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testCheckResourceDestroy("/interface/wireless/security-profiles", + "routeros_interface_wireless_security_profiles"), + Steps: []resource.TestStep{ + { + Config: testAccInterfaceWirelessSecurityProfilesConfig(), + Check: resource.ComposeTestCheckFunc( + testResourcePrimaryInstanceId(testInterfaceWirelessSecurityProfiles), + resource.TestCheckResourceAttr(testInterfaceWirelessSecurityProfiles, "name", "test-profile"), + resource.TestCheckResourceAttr(testInterfaceWirelessSecurityProfiles, "mode", "dynamic-keys"), + resource.TestCheckResourceAttr(testInterfaceWirelessSecurityProfiles, "wpa_pre_shared_key", "wpa_psk_key"), + resource.TestCheckResourceAttr(testInterfaceWirelessSecurityProfiles, "wpa2_pre_shared_key", "wpa2_psk_key"), + ), + }, + }, + }) + + }) + } +} + +func testAccInterfaceWirelessSecurityProfilesConfig() string { + return fmt.Sprintf(`%v + +resource "routeros_interface_wireless_security_profiles" "test" { + name = "test-profile" + mode = "dynamic-keys" + authentication_types = ["wpa-psk", "wpa2-psk"] + wpa_pre_shared_key = "wpa_psk_key" + wpa2_pre_shared_key = "wpa2_psk_key" +} +`, providerConfig) +} From 39805886f68213a66ccf52408819bc878659521e Mon Sep 17 00:00:00 2001 From: Vaerh Date: Fri, 27 Sep 2024 10:49:44 +0300 Subject: [PATCH 03/13] feat(wireless): Add new resource `routeros_interface_wireless_access_list` --- .../import.sh | 3 + .../resource.tf | 5 + ...resource_interface_wireless_access_list.go | 143 ++++++++++++++++++ ...rce_interface_wireless_access_list_test.go | 50 ++++++ 4 files changed, 201 insertions(+) create mode 100644 examples/resources/routeros_interface_wireless_access_list/import.sh create mode 100644 examples/resources/routeros_interface_wireless_access_list/resource.tf create mode 100644 routeros/resource_interface_wireless_access_list.go create mode 100644 routeros/resource_interface_wireless_access_list_test.go diff --git a/examples/resources/routeros_interface_wireless_access_list/import.sh b/examples/resources/routeros_interface_wireless_access_list/import.sh new file mode 100644 index 00000000..02e7efc8 --- /dev/null +++ b/examples/resources/routeros_interface_wireless_access_list/import.sh @@ -0,0 +1,3 @@ +#The ID can be found via API or the terminal +#The command for the terminal is -> :put [/interface/wireless/access-list get [print show-ids]] +terraform import routeros_interface_wireless_access_list.test *3 \ No newline at end of file diff --git a/examples/resources/routeros_interface_wireless_access_list/resource.tf b/examples/resources/routeros_interface_wireless_access_list/resource.tf new file mode 100644 index 00000000..9060eb2b --- /dev/null +++ b/examples/resources/routeros_interface_wireless_access_list/resource.tf @@ -0,0 +1,5 @@ +resource "routeros_interface_wireless_access_list" "test" { + signal_range = "-100..100" + time = "3h3m-5h,mon,tue,wed,thu,fri" + mac_address = "00:AA:BB:CC:DD:EE" +} \ No newline at end of file diff --git a/routeros/resource_interface_wireless_access_list.go b/routeros/resource_interface_wireless_access_list.go new file mode 100644 index 00000000..8afc6da8 --- /dev/null +++ b/routeros/resource_interface_wireless_access_list.go @@ -0,0 +1,143 @@ +package routeros + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +/* + { + ".id": "*1", + "allow-signal-out-of-range": "10s", + "ap-tx-limit": "0", + "authentication": "true", + "client-tx-limit": "0", + "disabled": "false", + "forwarding": "true", + "interface": "any", + "mac-address": "00:00:00:00:00:00", + "management-protection-key": "", + "private-algo": "none", + "private-key": "", + "private-pre-shared-key": "", + "signal-range": "-120..120", + "time": "3h3m-5h,mon,tue,wed,thu,fri", + "vlan-id": "1", + "vlan-mode": "default" + } +*/ + +// https://help.mikrotik.com/docs/display/ROS/ +func ResourceInterfaceWirelessAccessList() *schema.Resource { + resSchema := map[string]*schema.Schema{ + MetaResourcePath: PropResourcePath("/interface/wireless/access-list"), + MetaId: PropId(Id), + + "allow_signal_out_of_range": { + Type: schema.TypeString, + Optional: true, + Description: "Option which permits client's signal to be out of the range always or for some time interval.", + DiffSuppressFunc: TimeEquall, + }, + "ap_tx_limit": { + Type: schema.TypeInt, + Optional: true, + Description: "Limit rate of data transmission to this client. Value 0 means no limit. Value is in bits per " + + "second.", + }, + "authentication": { + Type: schema.TypeBool, + Optional: true, + Description: "No - Client association will always fail.yes - Use authentication procedure that is specified " + + "in the security-profile of the interface.", + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "client_tx_limit": { + Type: schema.TypeInt, + Optional: true, + Description: "Ask client to limit rate of data transmission. Value 0 means no limit.This is a proprietary " + + "extension that is supported by RouterOS clients.Value is in bits per second.", + }, + KeyComment: PropCommentRw, + KeyDisabled: PropDisabledRw, + "forwarding": { + Type: schema.TypeBool, + Optional: true, + Description: "`No` - Client cannot send frames to other station that are connected to same access point.yes - " + + "Client can send frames to other stations on the same access point.", + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "interface": { + Type: schema.TypeString, + Optional: true, + Description: "Rules with interface=any are used for any wireless interface and the interface=all defines interface-list `all` " + + "name. To make rule that applies only to one wireless interface, specify that interface as a value of " + + "this property.", + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + KeyMacAddress: PropMacAddressRw("Rule matches client with the specified MAC address. Value 00:00:00:00:00:00 matches always.", false), + "management_protection_key": { + Type: schema.TypeString, + Description: "Management protection shared secret.", + Optional: true, + }, + "private_algo": { + Type: schema.TypeString, + Optional: true, + Description: "Only for `WEP` modes.", + ValidateFunc: validation.StringInSlice([]string{"104bit-wep", "40bit-wep", "aes-ccm", "none", "tkip"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "private_key": { + Type: schema.TypeString, + Optional: true, + Description: "Only for `WEP` modes (HEX).", + }, + "private_pre_shared_key": { + Type: schema.TypeString, + Optional: true, + Description: "Used in `WPA PSK` mode.", + }, + "signal_range": { + Type: schema.TypeString, + Optional: true, + Description: "Rule matches if signal strength of the station is within the range.If signal strength of the " + + "station will go out of the range that is specified in the rule, access point will disconnect that station.", + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "time": { + Type: schema.TypeString, + Optional: true, + Description: "Rule will match only during specified time.Station will be disconnected after specified time " + + "ends. Both start and end time is expressed as time since midnight, 00:00. Rule will match only during " + + "specified days of the week. Ex: \"3h3m-5h,mon,tue,wed,thu,fri\"", + }, + "vlan_id": { + Type: schema.TypeInt, + Optional: true, + Description: "VLAN ID to use if doing VLAN tagging.", + ValidateFunc: validation.IntBetween(0, 4094), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "vlan_mode": { + Type: schema.TypeString, + Optional: true, + Description: "VLAN tagging mode specifies if traffic coming from client should get tagged (and untagged when going to client).", + ValidateFunc: validation.StringInSlice([]string{"default", "no-tag", "use-service-tag", "use-tag"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + } + + return &schema.Resource{ + CreateContext: DefaultCreate(resSchema), + ReadContext: DefaultRead(resSchema), + UpdateContext: DefaultUpdate(resSchema), + DeleteContext: DefaultDelete(resSchema), + + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: resSchema, + } +} diff --git a/routeros/resource_interface_wireless_access_list_test.go b/routeros/resource_interface_wireless_access_list_test.go new file mode 100644 index 00000000..bc7cedf7 --- /dev/null +++ b/routeros/resource_interface_wireless_access_list_test.go @@ -0,0 +1,50 @@ +package routeros + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +const testInterfaceWirelessAccessList = "routeros_interface_wireless_access_list.test" + +func TestAccInterfaceWirelessAccessListTest_basic(t *testing.T) { + t.Parallel() + for _, name := range testNames { + t.Run(name, func(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testSetTransportEnv(t, name) + }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testCheckResourceDestroy("/interface/wireless/access-list", + "routeros_interface_wireless_access_list"), + Steps: []resource.TestStep{ + { + Config: testAccInterfaceWirelessAccessListConfig(), + Check: resource.ComposeTestCheckFunc( + testResourcePrimaryInstanceId(testInterfaceWirelessAccessList), + resource.TestCheckResourceAttr(testInterfaceWirelessAccessList, "signal_range", "-100..100"), + resource.TestCheckResourceAttr(testInterfaceWirelessAccessList, "time", "3h3m-5h,mon,tue,wed,thu,fri"), + resource.TestCheckResourceAttr(testInterfaceWirelessAccessList, "mac_address", "00:AA:BB:CC:DD:EE"), + ), + }, + }, + }) + + }) + } +} + +func testAccInterfaceWirelessAccessListConfig() string { + return fmt.Sprintf(`%v + +resource "routeros_interface_wireless_access_list" "test" { + signal_range = "-100..100" + time = "3h3m-5h,mon,tue,wed,thu,fri" + mac_address = "00:AA:BB:CC:DD:EE" +} +`, providerConfig) +} From 59b0da58e9087e1f77e3983cc248f1639d6bd4c6 Mon Sep 17 00:00:00 2001 From: Vaerh Date: Fri, 27 Sep 2024 11:21:41 +0300 Subject: [PATCH 04/13] fix(wireless_security_profile): Add `Sensitive` flag to attributes --- ...ce_interface_wireless_security_profiles.go | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/routeros/resource_interface_wireless_security_profiles.go b/routeros/resource_interface_wireless_security_profiles.go index a1067368..a2c4791e 100644 --- a/routeros/resource_interface_wireless_security_profiles.go +++ b/routeros/resource_interface_wireless_security_profiles.go @@ -129,8 +129,9 @@ func ResourceInterfaceWirelessSecurityProfiles() *schema.Resource { DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "management_protection_key": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + Sensitive: true, Description: "Management protection shared secret. When interface is in AP mode, default management " + "protection key (configured in security-profile) can be overridden by key specified in access-list or " + "RADIUS attribute.", @@ -239,26 +240,30 @@ func ResourceInterfaceWirelessSecurityProfiles() *schema.Resource { DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "static_key_0": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + Sensitive: true, Description: "Hexadecimal representation of the key. Length of key must be appropriate for selected algorithm. " + "See the Statically configured WEP keys section.", }, "static_key_1": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + Sensitive: true, Description: "Hexadecimal representation of the key. Length of key must be appropriate for selected algorithm. " + "See the Statically configured WEP keys section.", }, "static_key_2": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + Sensitive: true, Description: "Hexadecimal representation of the key. Length of key must be appropriate for selected algorithm. " + "See the Statically configured WEP keys section.", }, "static_key_3": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + Sensitive: true, Description: "Hexadecimal representation of the key. Length of key must be appropriate for selected algorithm. " + "See the Statically configured WEP keys section.", }, @@ -273,8 +278,9 @@ func ResourceInterfaceWirelessSecurityProfiles() *schema.Resource { DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "static_sta_private_key": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + Sensitive: true, Description: "Length of key must be appropriate for selected algorithm, see the Statically configured WEP " + "keys section. This property is used only on Stations. Access Point uses corresponding key either from " + "private-key property, or from Mikrotik-Wireless-Enc-Key attribute.", @@ -291,8 +297,9 @@ func ResourceInterfaceWirelessSecurityProfiles() *schema.Resource { DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "supplicant_identity": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + Sensitive: true, Description: "EAP identity that is sent by client at the beginning of EAP authentication. This value is " + "used as a value for User-Name attribute in RADIUS messages sent by RADIUS EAP accounting and RADIUS " + "EAP pass-through authentication.", @@ -334,16 +341,18 @@ func ResourceInterfaceWirelessSecurityProfiles() *schema.Resource { DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "wpa_pre_shared_key": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + Sensitive: true, Description: "`WPA` pre-shared key mode requires all devices in a BSS to have common secret key. Value of " + "this key can be an arbitrary text. Commonly referred to as the network password for WPA mode. property " + "only has effect when wpa-psk is added to authentication-types.", ValidateFunc: validation.StringLenBetween(8, 64), }, "wpa2_pre_shared_key": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + Sensitive: true, Description: "`WPA2` pre-shared key mode requires all devices in a BSS to have common secret key. Value of " + "this key can be an arbitrary text. Commonly referred to as the network password for WPA2 mode. property " + "only has effect when wpa2-psk is added to authentication-types.", From 182ec0f4be7d66240b8d4ca37d7e56590e13baea Mon Sep 17 00:00:00 2001 From: Vaerh Date: Fri, 27 Sep 2024 13:40:38 +0300 Subject: [PATCH 05/13] fix(serialize): Fix the transformation of attribute names. It looks like the transformation was done incorrectly. In `zerotier` the order of names was reversed. In serialization functions the order of map filling was mixed up. Added ability to transform common attribute names, since the wifi interface contains a `basic-rates-a/g` parameter. --- routeros/mikrotik_serialize.go | 13 ++++++++++--- routeros/resource_zerotier.go | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/routeros/mikrotik_serialize.go b/routeros/mikrotik_serialize.go index fb291321..9b05d37f 100644 --- a/routeros/mikrotik_serialize.go +++ b/routeros/mikrotik_serialize.go @@ -121,9 +121,9 @@ func TerraformResourceDataToMikrotik(s map[string]*schema.Schema, d *schema.Reso var transformSet map[string]string var skipFields, setUnsetFields map[string]struct{} - // {"channel.config": "channel", "schema-field-name": "mikrotik-field-name"} + // {"channel: channel.config", "datapath: datapath.config", "schema-field-name": "mikrotik-field-name"} if ts, ok := s[MetaTransformSet]; ok { - transformSet = loadTransformSet(ts.Default.(string), true) + transformSet = loadTransformSet(ts.Default.(string), false) } // "field_first", "field_second", "field_third" @@ -196,6 +196,13 @@ func TerraformResourceDataToMikrotik(s map[string]*schema.Schema, d *schema.Reso mikrotikKebabName := SnakeToKebab(terraformSnakeName) value := d.Get(terraformSnakeName) + // WiFi basic_rates_ag -> basic-rates-a/g + if transformSet != nil && terraformMetadata.Type != schema.TypeMap { + if new, ok := transformSet[terraformSnakeName]; ok { + mikrotikKebabName = SnakeToKebab(new) + } + } + switch terraformMetadata.Type { case schema.TypeString: if _, ok := setUnsetFields[terraformSnakeName]; ok && value.(string) == "" { @@ -322,7 +329,7 @@ func MikrotikResourceDataToTerraform(item MikrotikItem, s map[string]*schema.Sch // {"channel": "channel.config", "mikrotik-field-name": "schema-field-name"} if ts, ok := s[MetaTransformSet]; ok { - transformSet = loadTransformSet(ts.Default.(string), false) + transformSet = loadTransformSet(ts.Default.(string), true) } // "field_first", "field_second", "field_third" diff --git a/routeros/resource_zerotier.go b/routeros/resource_zerotier.go index a8437210..afafc7b9 100644 --- a/routeros/resource_zerotier.go +++ b/routeros/resource_zerotier.go @@ -26,7 +26,7 @@ func ResourceZerotier() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/zerotier"), MetaId: PropId(Id), - MetaTransformSet: PropTransformSet("identity.public: identity_public"), + MetaTransformSet: PropTransformSet("identity_public: identity.public"), KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, From e615db998c5c0eb18b737b73412f4bfc9cf01b84 Mon Sep 17 00:00:00 2001 From: Vaerh Date: Fri, 27 Sep 2024 13:42:05 +0300 Subject: [PATCH 06/13] feat(wireless): Add new resource `routeros_interface_wireless` --- .../routeros_interface_wireless/import.sh | 3 + .../routeros_interface_wireless/resource.tf | 17 + routeros/provider.go | 29 +- routeros/resource_interface_wireless.go | 926 ++++++++++++++++++ routeros/resource_interface_wireless_test.go | 61 ++ 5 files changed, 1023 insertions(+), 13 deletions(-) create mode 100644 examples/resources/routeros_interface_wireless/import.sh create mode 100644 examples/resources/routeros_interface_wireless/resource.tf create mode 100644 routeros/resource_interface_wireless.go create mode 100644 routeros/resource_interface_wireless_test.go diff --git a/examples/resources/routeros_interface_wireless/import.sh b/examples/resources/routeros_interface_wireless/import.sh new file mode 100644 index 00000000..d2e99051 --- /dev/null +++ b/examples/resources/routeros_interface_wireless/import.sh @@ -0,0 +1,3 @@ +#The ID can be found via API or the terminal +#The command for the terminal is -> :put [/interface/wireless get [print show-ids]] +terraform import routeros_interface_wireless.test *3 \ No newline at end of file diff --git a/examples/resources/routeros_interface_wireless/resource.tf b/examples/resources/routeros_interface_wireless/resource.tf new file mode 100644 index 00000000..6175aedd --- /dev/null +++ b/examples/resources/routeros_interface_wireless/resource.tf @@ -0,0 +1,17 @@ +resource "routeros_interface_wireless_security_profiles" "test" { + name = "test-profile" + mode = "dynamic-keys" + authentication_types = ["wpa-psk", "wpa2-psk"] + wpa_pre_shared_key = "wpa_psk_key" + wpa2_pre_shared_key = "wpa2_psk_key" +} + +resource "routeros_interface_wireless" "test" { + depends_on = [resource.routeros_interface_wireless_security_profiles.test] + security_profile = resource.routeros_interface_wireless_security_profiles.test.name + mode = "ap-bridge" + master_interface = "wlan1" + name = "wlan-guest" + ssid = "guests" + basic_rates_ag = ["6Mbps", "9Mbps"] +} \ No newline at end of file diff --git a/routeros/provider.go b/routeros/provider.go index 31b6ddb5..f5fb3c19 100644 --- a/routeros/provider.go +++ b/routeros/provider.go @@ -137,37 +137,40 @@ func Provider() *schema.Provider { "routeros_dns_record": ResourceDnsRecord(), // Interface Objects - "routeros_interface_bridge": ResourceInterfaceBridge(), + "routeros_interface_bonding": ResourceInterfaceBonding(), "routeros_interface_bridge_port": ResourceInterfaceBridgePort(), - "routeros_interface_bridge_vlan": ResourceInterfaceBridgeVlan(), "routeros_interface_bridge_settings": ResourceInterfaceBridgeSettings(), + "routeros_interface_bridge_vlan": ResourceInterfaceBridgeVlan(), + "routeros_interface_bridge": ResourceInterfaceBridge(), "routeros_interface_dot1x_client": ResourceInterfaceDot1xClient(), "routeros_interface_dot1x_server": ResourceInterfaceDot1xServer(), "routeros_interface_eoip": ResourceInterfaceEoip(), + "routeros_interface_ethernet": ResourceInterfaceEthernet(), "routeros_interface_ethernet_switch": ResourceInterfaceEthernetSwitch(), "routeros_interface_ethernet_switch_host": ResourceInterfaceEthernetSwitchHost(), "routeros_interface_ethernet_switch_port": ResourceInterfaceEthernetSwitchPort(), "routeros_interface_ethernet_switch_port_isolation": ResourceInterfaceEthernetSwitchPortIsolation(), - "routeros_interface_ethernet_switch_vlan": ResourceInterfaceEthernetSwitchVlan(), "routeros_interface_ethernet_switch_rule": ResourceInterfaceEthernetSwitchRule(), + "routeros_interface_ethernet_switch_vlan": ResourceInterfaceEthernetSwitchVlan(), "routeros_interface_gre": ResourceInterfaceGre(), - "routeros_interface_macvlan": ResourceInterfaceMacVlan(), "routeros_interface_ipip": ResourceInterfaceIPIP(), - "routeros_interface_vlan": ResourceInterfaceVlan(), - "routeros_interface_vrrp": ResourceInterfaceVrrp(), - "routeros_interface_wireguard": ResourceInterfaceWireguard(), - "routeros_interface_wireguard_peer": ResourceInterfaceWireguardPeer(), - "routeros_interface_wireless_cap": ResourceInterfaceWirelessCap(), "routeros_interface_list": ResourceInterfaceList(), "routeros_interface_list_member": ResourceInterfaceListMember(), "routeros_interface_lte": ResourceInterfaceLte(), "routeros_interface_lte_apn": ResourceInterfaceLteApn(), - "routeros_interface_ovpn_server": ResourceInterfaceOpenVPNServer(), + "routeros_interface_macvlan": ResourceInterfaceMacVlan(), "routeros_interface_ovpn_client": ResourceOpenVPNClient(), - "routeros_interface_veth": ResourceInterfaceVeth(), - "routeros_interface_bonding": ResourceInterfaceBonding(), + "routeros_interface_ovpn_server": ResourceInterfaceOpenVPNServer(), "routeros_interface_pppoe_client": ResourceInterfacePPPoEClient(), - "routeros_interface_ethernet": ResourceInterfaceEthernet(), + "routeros_interface_veth": ResourceInterfaceVeth(), + "routeros_interface_vlan": ResourceInterfaceVlan(), + "routeros_interface_vrrp": ResourceInterfaceVrrp(), + "routeros_interface_wireguard": ResourceInterfaceWireguard(), + "routeros_interface_wireguard_peer": ResourceInterfaceWireguardPeer(), + "routeros_interface_wireless": ResourceInterfaceWireless(), + "routeros_interface_wireless_access_list": ResourceInterfaceWirelessAccessList(), + "routeros_interface_wireless_cap": ResourceInterfaceWirelessCap(), + "routeros_interface_wireless_security_profiles": ResourceInterfaceWirelessSecurityProfiles(), // Aliases for interface objects to retain compatibility between original and fork "routeros_bridge": ResourceInterfaceBridge(), diff --git a/routeros/resource_interface_wireless.go b/routeros/resource_interface_wireless.go new file mode 100644 index 00000000..299c592e --- /dev/null +++ b/routeros/resource_interface_wireless.go @@ -0,0 +1,926 @@ +package routeros + +import ( + "regexp" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +/* +{ + ".id": "*7", + "adaptive-noise-immunity": "none", + "allow-sharedkey": "false", + "ampdu-priorities": "0", + "amsdu-limit": "8192", + "amsdu-threshold": "8192", + "antenna-gain": "3", + "area": "", + "arp": "enabled", + "arp-timeout": "auto", + "band": "2ghz-b/g/n", + "basic-rates-a/g": "6Mbps", + "basic-rates-b": "1Mbps", + "bridge-mode": "enabled", + "channel-width": "20/40mhz-XX", + "compression": "false", + "country": "etsi", + "default-ap-tx-limit": "0", + "default-authentication": "true", + "default-client-tx-limit": "0", + "default-forwarding": "true", + "default-name": "wlan1", + "disable-running-check": "false", + "disabled": "true", + "disconnect-timeout": "3s", + "distance": "indoors", + "frame-lifetime": "0", + "frequency": "auto", + "frequency-mode": "regulatory-domain", + "frequency-offset": "0", + "guard-interval": "any", + "hide-ssid": "false", + "ht-basic-mcs": "mcs-0,mcs-1,mcs-2,mcs-3,mcs-4,mcs-5,mcs-6,mcs-7", + "ht-supported-mcs": "mcs-0,mcs-1,mcs-2,mcs-3,mcs-4,mcs-5,mcs-6,mcs-7,mcs-8,mcs-9,mcs-10,mcs-11,mcs-12,mcs-13,mcs-14,mcs-15,mcs-16,mcs-17,mcs-18,mcs-19,mcs-20,mcs-21,mcs-22,mcs-23", + "hw-fragmentation-threshold": "disabled", + "hw-protection-mode": "none", + "hw-protection-threshold": "0", + "hw-retries": "7", + "installation": "indoor", + "interface-type": "Atheros AR9300", + "interworking-profile": "disabled", + "keepalive-frames": "enabled", + "l2mtu": "1600", + "mac-address": "D4:01:C3:30:E2:87", + "max-station-count": "2007", + "mode": "ap-bridge", + "mtu": "1500", + "multicast-buffering": "enabled", + "multicast-helper": "default", + "name": "wlan1", + "noise-floor-threshold": "default", + "nv2-cell-radius": "30", + "nv2-downlink-ratio": "50", + "nv2-mode": "dynamic-downlink", + "nv2-noise-floor-offset": "default", + "nv2-preshared-key": "", + "nv2-qos": "default", + "nv2-queue-count": "2", + "nv2-security": "disabled", + "nv2-sync-secret": "", + "on-fail-retry-time": "100ms", + "preamble-mode": "both", + "radio-name": "D401C330E287", + "rate-selection": "advanced", + "rate-set": "default", + "running": "false", + "rx-chains": "0,1,2", + "scan-list": "default", + "secondary-frequency": "", + "security-profile": "default", + "skip-dfs-channels": "disabled", + "ssid": "MikroTik-30E287", + "station-bridge-clone-mac": "00:00:00:00:00:00", + "station-roaming": "disabled", + "supported-rates-a/g": "6Mbps,9Mbps,12Mbps,18Mbps,24Mbps,36Mbps,48Mbps,54Mbps", + "supported-rates-b": "1Mbps,2Mbps,5.5Mbps,11Mbps", + "tdma-period-size": "2", + "tx-chains": "0,1,2", + "tx-power-mode": "default", + "update-stats-interval": "disabled", + "vlan-id": "1", + "vlan-mode": "no-tag", + "wds-cost-range": "50-150", + "wds-default-bridge": "none", + "wds-default-cost": "100", + "wds-ignore-ssid": "false", + "wds-mode": "disabled", + "wireless-protocol": "802.11", + "wmm-support": "disabled", + "wps-mode": "push-button-virtual-only" +} +*/ + +// https://help.mikrotik.com/docs/display/ROS/Wireless+Interface#WirelessInterface-Overview +func ResourceInterfaceWireless() *schema.Resource { + resSchema := map[string]*schema.Schema{ + MetaResourcePath: PropResourcePath("/interface/wireless"), + MetaId: PropId(Id), + MetaTransformSet: PropTransformSet("basic_rates_ag: basic-rates-a/g", "supported_rates_ag: supported-rates-a/g"), + MetaSetUnsetFields: PropSetUnsetFields("secondary_frequency"), + + "adaptive_noise_immunity": { + Type: schema.TypeString, + Optional: true, + Description: "This property is only effective for cards based on Atheros chipset.", + ValidateFunc: validation.StringInSlice([]string{"ap-and-client-mode", "client-mode", "none"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "allow_sharedkey": { + Type: schema.TypeBool, + Optional: true, + Description: "Allow WEP Shared Key clients to connect. Note that no authentication is done for these clients " + + "(WEP Shared keys are not compared to anything) - they are just accepted at once (if access list allows " + + "that).", + }, + "amsdu_limit": { + Type: schema.TypeInt, + Optional: true, + Description: "Max AMSDU that device is allowed to prepare when negotiated. AMSDU aggregation may significantly " + + "increase throughput especially for small frames, but may increase latency in case of packet loss due " + + "to retransmission of aggregated frame. Sending and receiving AMSDUs will also increase CPU usage.", + ValidateFunc: validation.IntBetween(0, 8192), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "ampdu_priorities": { + Type: schema.TypeSet, + Optional: true, + Description: "Frame priorities for which AMPDU sending (aggregating frames and sending using block acknowledgment) " + + "should get negotiated and used. Using AMPDUs will increase throughput, but may increase latency, therefore, " + + "may not be desirable for real-time traffic (voice, video). Due to this, by default AMPDUs are enabled " + + "only for best-effort traffic.", + Elem: &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validation.IntBetween(0, 7), + }, + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "amsdu_threshold": { + Type: schema.TypeInt, + Optional: true, + Description: "Max frame size to allow including in AMSDU.", + ValidateFunc: validation.IntBetween(0, 8192), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "antenna_gain": { + Type: schema.TypeInt, + Optional: true, + Description: "Antenna gain in dBi, used to calculate maximum transmit power according to country regulations.", + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "antenna_mode": { + Type: schema.TypeString, + Optional: true, + Description: "Select antenna to use for transmitting and for receiving: `ant-a` - use only 'a'; antenna `ant-b` " + + "- use only 'b'; antenna `txa-rxb` - use antenna 'a' for transmitting, antenna 'b' for receiving; `rxa-txb` - " + + "use antenna 'b' for transmitting, antenna 'a' for receiving.", + ValidateFunc: validation.StringInSlice([]string{"ant-a", "ant-b", "rxa-txb", "txa-rxb"}, false), + }, + "area": { + Type: schema.TypeString, + Optional: true, + Description: "Identifies group of wireless networks. This value is announced by AP, and can be matched in " + + " connect-list by area-prefix. This is a proprietary extension.", + }, + "arp": { + Type: schema.TypeString, + Optional: true, + Description: "ARP Mode.", + ValidateFunc: validation.StringInSlice([]string{"disabled", "enabled", "proxy-arp", "reply-only"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "arp_timeout": { + Type: schema.TypeString, + Optional: true, + Description: "ARP timeout is time how long ARP record is kept in ARP table after no packets are received " + + "from IP. Value auto equals to the value of arp-timeout in `/ip settings`, default is 30s.", + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "band": { + Type: schema.TypeString, + Optional: true, + Description: "Defines set of used data rates, channel frequencies and widths.", + ValidateFunc: validation.StringInSlice([]string{"2ghz-b", "2ghz-b/g", "2ghz-b/g/n", "2ghz-onlyg", + "2ghz-onlyn", "5ghz-a", "5ghz-a/n", "5ghz-onlyn", "5ghz-a/n/ac", "5ghz-onlyac", "5ghz-n/ac"}, false), + }, + "basic_rates_ag": { + Type: schema.TypeSet, + Optional: true, + Description: "Similar to the basic-rates-b property, but used for 5ghz, 5ghz-10mhz, 5ghz-5mhz, 5ghz-turbo, " + + "2.4ghz-b/g, 2.4ghz-onlyg, 2ghz-10mhz, 2ghz-5mhz and 2.4ghz-g-turbo bands.", + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateDiagFunc: ValidationMultiValInSlice([]string{"12Mbps", "18Mbps", "24Mbps", "36Mbps", "48Mbps", + "54Mbps", "6Mbps", "9Mbps"}, false, false), + }, + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "basic_rates_b": { + Type: schema.TypeSet, + Optional: true, + Description: "List of basic rates, used for 2.4ghz-b, 2.4ghz-b/g and 2.4ghz-onlyg bands.Client will connect " + + "to AP only if it supports all basic rates announced by the AP. AP will establish WDS link only if it " + + "supports all basic rates of the other AP.This property has effect only in AP modes, and when value of " + + "rate-set is configured.", + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateDiagFunc: ValidationMultiValInSlice([]string{"1Mbps", "2Mbps", "5Mbps", "11Mbps"}, false, false), + }, + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "bridge_mode": { + Type: schema.TypeString, + Optional: true, + Description: "Allows to use station-bridge mode.", + ValidateFunc: validation.StringInSlice([]string{"disabled", "enabled"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "burst_time": { + Type: schema.TypeString, + Optional: true, + Description: "Time in microseconds which will be used to send data without stopping. Note that no other " + + "wireless cards in that network will be able to transmit data during burst-time microseconds. This setting " + + "is available only for AR5000, AR5001X, and AR5001X+ chipset based cards.", + }, + "channel_width": { + Type: schema.TypeString, + Optional: true, + Description: "Use of extension channels (e.g. Ce, eC etc) allows additional 20MHz extension channels and " + + "if it should be located below or above the control (main) channel. Extension channel allows 802.11n " + + "devices to use up to 40MHz (802.11ac up to 160MHz) of spectrum in total thus increasing max throughput. " + + "Channel widths with XX and XXXX extensions automatically scan for a less crowded control channel frequency " + + "based on the number of concurrent devices running in every frequency and chooses the `C` - Control channel " + + "frequency automatically.", + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + KeyComment: PropCommentRw, + "compression": { + Type: schema.TypeBool, + Optional: true, + Description: "Setting this property to yes will allow the use of the hardware compression. Wireless interface " + + "must have support for hardware compression. Connections with devices that do not use compression will " + + "still work.", + }, + "country": { + Type: schema.TypeString, + Optional: true, + Description: "Limits available bands, frequencies and maximum transmit power for each frequency. Also specifies " + + "default value of scan-list. Value no_country_set is an FCC compliant set of channels.", + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "default_ap_tx_limit": { + Type: schema.TypeInt, + Optional: true, + Description: "This is the value of ap-tx-limit for clients that do not match any entry in the access-list. " + + "0 means no limit.", + }, + "default_authentication": { + Type: schema.TypeBool, + Optional: true, + Description: "For AP mode, this is the value of authentication for clients that do not match any entry in " + + "the access-list. For station mode, this is the value of connect for APs that do not match any entry " + + "in the connect-list.", + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "default_client_tx_limit": { + Type: schema.TypeInt, + Optional: true, + Description: "This is the value of client-tx-limit for clients that do not match any entry in the access-list. " + + "0 means no limit.", + }, + "default_forwarding": { + Type: schema.TypeBool, + Optional: true, + Description: "This is the value of forwarding for clients that do not match any entry in the access-list.", + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "default_name": { + Type: schema.TypeString, + Computed: true, + }, + "disable_running_check": { + Type: schema.TypeBool, + Optional: true, + Description: "When set to yes interface will always have running flag. If value is set to no', the router " + + "determines whether the card is up and running - for AP one or more clients have to be registered to " + + "it, for station, it should be connected to an AP.", + }, + KeyDisabled: PropDisabledRw, + "disconnect_timeout": { + Type: schema.TypeString, + Optional: true, + Description: "This interval is measured from third sending failure on the lowest data rate. At this point " + + "3 * (hw-retries + 1) frame transmits on the lowest data rate had failed. During disconnect-timeout packet " + + "transmission will be retried with on-fail-retry-time interval. If no frame can be transmitted successfully " + + "during disconnect-timeout, the connection is closed, and this event is logged as `extensive data loss`. " + + "Successful frame transmission resets this timer.", + DiffSuppressFunc: TimeEquall, + }, + "distance": { + Type: schema.TypeString, + Optional: true, + Description: "How long to wait for confirmation of unicast frames (ACKs) before considering transmission " + + "unsuccessful, or in short ACK-Timeout. Distance value has these behaviors:Dynamic - causes AP to detect " + + "and use the smallest timeout that works with all connected clients.Indoor - uses the default ACK timeout " + + "value that the hardware chip manufacturer has set.Number - uses the input value in formula: ACK-timeout " + + "= ((distance * 1000) + 299) / 300 us;Acknowledgments are not used in Nstreme/NV2 protocols.", + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "frame_lifetime": { + Type: schema.TypeInt, + Optional: true, + Description: "Discard frames that have been queued for sending longer than frame-lifetime. By default, when " + + "value of this property is 0, frames are discarded only after connection is closed.", + }, + "frequency": { + Type: schema.TypeString, + Optional: true, + Description: "Channel frequency value in MHz on which AP will operate.Allowed values depend on the selected " + + "band, and are restricted by country setting and wireless card capabilities. This setting has no effect " + + "if interface is in any of station modes, or in wds-slave mode, or if DFS is active.Note: If using mode " + + "`superchannel.", + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "frequency_mode": { + Type: schema.TypeString, + Optional: true, + Description: "Three frequency modes are available: `regulatory-domain` - Limit available channels and maximum " + + "transmit power for each channel according to the value of country `manual-txpower` - Same as above, but " + + "do not limit maximum transmit power.`superchannel` - Conformance Testing Mode. Allow all channels supported " + + "by the card.List of available channels for each band can be seen in `/interface wireless` info allowed-channels. " + + "This mode allows you to test wireless channels outside the default scan-list and/or regulatory domain. " + + "This mode should only be used in controlled environments, or if you have special permission to use it " + + "in your region. Before v4.3 this was called Custom Frequency Upgrade, or Superchannel. Since RouterOS " + + "v4.3 this mode is available without special key upgrades to all installations.", + ValidateFunc: validation.StringInSlice([]string{"manual-txpower", "regulatory-domain", "superchannel"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "frequency_offset": { + Type: schema.TypeInt, + Optional: true, + Description: "Allows to specify offset if the used wireless card operates at a different frequency than " + + "is shown in RouterOS, in case a frequency converter is used in the card. So if your card works at 4000MHz " + + "but RouterOS shows 5000MHz, set offset to 1000MHz and it will be displayed correctly. The value is in " + + "MHz and can be positive or negative.", + }, + "guard_interval": { + Type: schema.TypeString, + Optional: true, + Description: "Whether to allow use of short guard interval (refer to 802.11n MCS specification to see how " + + "this may affect throughput). `any` will use either short or long, depending on data rate, `long` will " + + "use long.", + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "hide_ssid": { + Type: schema.TypeBool, + Optional: true, + Description: "Yes - AP does not include SSID in the beacon frames, and does not reply to probe requests " + + "that have broadcast SSID.no - AP includes SSID in the beacon frames, and replies to probe requests that " + + "have broadcast SSID.This property has an effect only in AP mode. Setting it to yes can remove this network " + + "from the list of wireless networks that are shown by some client software. Changing this setting does " + + "not improve the security of the wireless network, because SSID is included in other frames sent by the " + + "AP.", + }, + "ht_basic_mcs": { + Type: schema.TypeSet, + Optional: true, + Description: "Modulation and Coding Schemes that every connecting client must support. Refer to 802.11n " + + "for MCS specification.", + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringMatch(regexp.MustCompile(`mcs-\d+`), + `ht_basic_mcs format is "mcs-[0..23]": mcs-"12"`), + }, + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "ht_supported_mcs": { + Type: schema.TypeSet, + Optional: true, + Description: "Modulation and Coding Schemes that this device advertises as supported. Refer to 802.11n for " + + "MCS specification.", + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringMatch(regexp.MustCompile(`mcs-\d+`), + `ht_basic_mcs format is "mcs-[0..23]": mcs-"12"`), + }, + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "hw_fragmentation_threshold": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies maximum fragment size in bytes when transmitted over the wireless medium. 802.11 " + + "standard packet (MSDU in 802.11 terminologies) fragmentation allows packets to be fragmented before " + + "transmitting over a wireless medium to increase the probability of successful transmission (only fragments " + + "that did not transmit correctly are retransmitted). Note that transmission of a fragmented packet is " + + "less efficient than transmitting unfragmented packet because of protocol overhead and increased resource " + + "usage at both - transmitting and receiving party.", + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "hw_protection_mode": { + Type: schema.TypeString, + Optional: true, + Description: "Frame protection support property.", + ValidateFunc: validation.StringInSlice([]string{"cts-to-self", "none", "rts-cts"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "hw_protection_threshold": { + Type: schema.TypeInt, + Optional: true, + Description: "Frame protection support property read more >>.", + ValidateFunc: Validation64k, + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "hw_retries": { + Type: schema.TypeInt, + Optional: true, + Description: "Number of times sending frame is retried without considering it a transmission failure. Data-rate " + + "is decreased upon failure and the frame is sent again. Three sequential failures on the lowest supported " + + "rate suspend transmission to this destination for the duration of on-fail-retry-time. After that, the " + + "frame is sent again. The frame is being retransmitted until transmission success, or until the client " + + "is disconnected after disconnect-timeout. The frame can be discarded during this time if frame-lifetime " + + "is exceeded.", + ValidateFunc: validation.IntBetween(0, 15), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "installation": { + Type: schema.TypeString, + Optional: true, + Description: "Adjusts scan-list to use indoor, outdoor or all frequencies for the country that is set.", + ValidateFunc: validation.StringInSlice([]string{"any", "indoor", "outdoor"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "interworking_profile": { + Type: schema.TypeString, + Optional: true, + Description: "", + ValidateFunc: validation.StringInSlice([]string{"enabled", "disabled"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "keepalive_frames": { + Type: schema.TypeString, + Optional: true, + Description: "Applies only if wireless interface is in mode=ap-bridge. If a client has not communicated " + + "for around 20 seconds, AP sends a `keepalive-frame`. Note, disabling the feature can lead to `ghost` " + + "clients in registration-table.", + ValidateFunc: validation.StringInSlice([]string{"enabled", "disabled"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + KeyL2Mtu: PropL2MtuRw, + KeyMacAddress: PropMacAddressRw("MAC address.", false), + "master_interface": { + Type: schema.TypeString, + Optional: true, + Description: "Name of wireless interface that has virtual-ap capability. Virtual AP interface will only " + + "work if master interface is in ap-bridge, bridge, station or wds-slave mode. This property is only for " + + "virtual AP interfaces.", + }, + "max_station_count": { + Type: schema.TypeInt, + Optional: true, + Description: "Maximum number of associated clients. WDS links also count toward this limit.", + ValidateFunc: validation.IntBetween(1, 2007), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "mode": { + Type: schema.TypeString, + Required: true, + Description: "Selection between different station and access point (AP) modes. **Station modes**: `station` - Basic " + + "station mode. Find and connect to acceptable AP. `station-wds` - Same as station, but create WDS link with " + + "AP, using proprietary extension. AP configuration has to allow WDS links with this device. Note that " + + "this mode does not use entries in wds. `station-pseudobridge` - Same as station, but additionally perform " + + "MAC address translation of all traffic. Allows interface to be bridged. `station-pseudobridge-clone` - " + + "Same as station-pseudobridge, but use station-bridge-clone-mac address to connect to AP. `station-bridge` " + + "- Provides support for transparent protocol-independent L2 bridging on the station device. RouterOS " + + "AP accepts clients in station-bridge mode when enabled using bridge-mode parameter. In this mode, the " + + "AP maintains a forwarding table with information on which MAC addresses are reachable over which station " + + "device. Only works with RouterOS APs. With station-bridge mode, it is not possible to connect to CAPsMAN " + + "controlled CAP. **AP modes**: `ap-bridge` - Basic access point mode. `bridge` - Same as ap-bridge, but limited " + + "to one associated client. `wds-slave` - Same as ap-bridge, but scan for AP with the same ssid and establishes " + + "WDS link. If this link is lost or cannot be established, then continue scanning. If dfs-mode is radar-detect, " + + "then APs with enabled hide-ssid will not be found during scanning. **Special modes**: `alignment-only` - Put " + + "the interface in a continuous transmit mode that is used for aiming the remote antenna. `nstreme-dual-slave` " + + "- allow this interface to be used in nstreme-dual setup. MAC address translation in pseudobridge modes " + + "works by inspecting packets and building a table of corresponding IP and MAC addresses. All packets " + + "are sent to AP with the MAC address used by pseudobridge, and MAC addresses of received packets are " + + "restored from the address translation table. There is a single entry in the address translation table " + + "for all non-IP packets, hence more than one host in the bridged network cannot reliably use non-IP protocols. " + + "Note: Currently IPv6 doesn't work over Pseudobridge.", + ValidateFunc: validation.StringInSlice([]string{"station", "station-wds", "ap-bridge", "bridge", + "alignment-only", "nstreme-dual-slave", "wds-slave", "station-pseudobridge", "station-pseudobridge-clone", + "station-bridge"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + KeyMtu: PropMtuRw(), + "multicast_buffering": { + Type: schema.TypeString, + Optional: true, + Description: "For a client that has power saving, buffer multicast packets until next beacon time. A client " + + "should wake up to receive a beacon, by receiving beacon it sees that there are multicast packets pending, " + + "and it should wait for multicast packets to be sent.", + ValidateFunc: validation.StringInSlice([]string{"disabled", "enabled"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "multicast_helper": { + Type: schema.TypeString, + Optional: true, + Description: "When set to full, multicast packets will be sent with a unicast destination MAC address, resolving " + + " multicast problem on the wireless link. This option should be enabled only on the access point, clients " + + "should be configured in station-bridge mode. Available starting from v5.15.disabled - disables the helper " + + "and sends multicast packets with multicast destination MAC addressesdhcp - dhcp packet mac addresses " + + "are changed to unicast mac addresses prior to sending them outfull - all multicast packet mac address " + + "are changed to unicast mac addresses prior to sending them outdefault - default choice that currently " + + "is set to dhcp. Value can be changed in future releases.", + ValidateFunc: validation.StringInSlice([]string{"default", "disabled", "full"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + KeyName: PropName("Name of the interface."), + "noise_floor_threshold": { + Type: schema.TypeString, + Optional: true, + Description: "For advanced use only, as it can badly affect the performance of the interface. It is possible " + + "to manually set noise floor threshold value. By default, it is dynamically calculated. This property " + + "also affects received signal strength. This property is only effective on non-AC chips.", + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "nv2_cell_radius": { + Type: schema.TypeInt, + Optional: true, + Description: "Setting affects the size of contention time slot that AP allocates for clients to initiate " + + "connection and also size of time slots used for estimating distance to client. When setting is too small, " + + "clients that are farther away may have trouble connecting and/or disconnect with `ranging timeout` error. " + + "Although during normal operation the effect of this setting should be negligible, in order to maintain " + + "maximum performance, it is advised to not increase this setting if not necessary, so AP is not reserving " + + "time that is actually never used, but instead allocates it for actual data transfer.on AP: distance " + + "to farthest client in kmon station: no effect.", + ValidateFunc: validation.IntBetween(10, 200), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "nv2_noise_floor_offset": { + Type: schema.TypeString, + Optional: true, + Description: "", + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "nv2_preshared_key": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + Description: "", + }, + "nv2_qos": { + Type: schema.TypeString, + Optional: true, + Description: "Sets the packet priority mechanism, firstly data from high priority queue is sent, then lower " + + "queue priority data until 0 queue priority is reached. When link is full with high priority queue data, " + + "lower priority data is not sent. Use it very carefully, setting works on APframe-priority - manual setting " + + "that can be tuned with Mangle rules.default - default setting where small packets receive priority for " + + "best latency.", + ValidateFunc: validation.StringInSlice([]string{"default", "frame-priority"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "nv2_queue_count": { + Type: schema.TypeInt, + Optional: true, + Description: "", + ValidateFunc: validation.IntBetween(2, 8), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "nv2_security": { + Type: schema.TypeString, + Optional: true, + Description: "", + ValidateFunc: validation.StringInSlice([]string{"disabled", "enabled"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "on_fail_retry_time": { + Type: schema.TypeString, + Optional: true, + Description: "After third sending failure on the lowest data rate, wait for specified time interval before " + + "retrying.", + DiffSuppressFunc: TimeEquall, + }, + "periodic_calibration": { + Type: schema.TypeString, + Optional: true, + Description: "Setting default enables periodic calibration if info default-periodic-calibration property " + + "is enabled. Value of that property depends on the type of wireless card. This property is only effective " + + "for cards based on Atheros chipset.", + ValidateFunc: validation.StringInSlice([]string{"default", "disabled", "enabled"}, false), + }, + "periodic_calibration_interval": { + Type: schema.TypeInt, + Optional: true, + Description: "This property is only effective for cards based on Atheros chipset.", + ValidateFunc: validation.IntBetween(1, 10000), + }, + "preamble_mode": { + Type: schema.TypeString, + Optional: true, + Description: "Short preamble mode is an option of 802.11b standard that reduces per-frame overhead.On AP:long " + + "- Do not use short preamble.short - Announce short preamble capability. Do not accept connections from " + + "clients that do not have this capability.both - Announce short preamble capability.On station:long - " + + "do not use short preamble.short - do not connect to AP if it does not support short preamble.both - " + + "Use short preamble if AP supports it.", + ValidateFunc: validation.StringInSlice([]string{"both", "long", "short"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "prism_cardtype": { + Type: schema.TypeString, + Optional: true, + Description: "Specify type of the installed Prism wireless card.", + ValidateFunc: validation.StringInSlice([]string{"100mW", "200mW", "30mW"}, false), + }, + "proprietary_extensions": { + Type: schema.TypeString, + Optional: true, + Description: "RouterOS includes proprietary information in an information element of management frames. " + + "This parameter controls how this information is included. `pre-2.9.25` - This is older method. It can interoperate " + + "with newer versions of RouterOS. This method is incompatible with some clients, for example, Centrino " + + "based ones. `post-2.9.25` - This uses standardized way of including vendor specific information, that is " + + "compatible with newer wireless clients.", + ValidateFunc: validation.StringInSlice([]string{"post-2.9.25", "pre-2.9.25"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "radio_name": { + Type: schema.TypeString, + Computed: true, + Description: "Descriptive name of the device, that is shown in registration table entries on the remote " + + "devices. This is a proprietary extension.", + }, + "rate_selection": { + Type: schema.TypeString, + Optional: true, + Description: "Starting from v5.9 default value is advanced since legacy mode was inefficient.", + ValidateFunc: validation.StringInSlice([]string{"advanced", "legacy"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "rate_set": { + Type: schema.TypeString, + Optional: true, + Description: "Two options are available: `default` - default basic and supported rate sets are used. Values " + + "from basic-rates and supported-rates parameters have no effect. `configured` - use values from basic-rates, " + + "supported-rates, basic-mcs, mcs.", + ValidateFunc: validation.StringInSlice([]string{"configured", "default"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + KeyRunning: PropRunningRo, + "rx_chains": { + Type: schema.TypeSet, + Optional: true, + Description: "Which antennas to use for receive. In current MikroTik routers, both RX and TX chain must " + + "be enabled, for the chain to be enabled.", + Elem: &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validation.IntBetween(0, 3), + }, + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "scan_list": { + Type: schema.TypeString, + Optional: true, + Description: "The default value is all channels from selected band that are supported by card and allowed " + + "by the country and frequency-mode settings (this list can be seen in info). For default scan list in " + + "5ghz band channels are taken with 20MHz step, in 5ghz-turbo band - with 40MHz step, for all other bands " + + "- with 5MHz step. If scan-list is specified manually, then all matching channels are taken. (Example: " + + "scan-list=default,5200-5245,2412-2427 - This will use the default value of scan list for current band, " + + "and add to it supported frequencies from 5200-5245 or 2412-2427 range.) Since RouterOS v6.0 with Winbox " + + "or Webfig, for inputting of multiple frequencies, add each frequency or range of frequencies into separate " + + "multiple scan-lists. Using a comma to separate frequencies is no longer supported in Winbox/Webfig since " + + "v6.0.Since RouterOS v6.35 (wireless-rep) scan-list support step feature where it is possible to manually " + + "specify the scan step. Example: scan-list=5500-5600:20 will generate such scan-list values 5500,5520,5540,5560,5580,5600.", + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "security_profile": { + Type: schema.TypeString, + Optional: true, + Description: "Name of profile from security-profiles.", + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "secondary_frequency": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies secondary channel, required to enable 80+80MHz transmission. To disable 80+80MHz " + + "functionality, set secondary-frequency to `` or unset the value via CLI/GUI.", + ValidateFunc: validation.StringInSlice([]string{"integer"}, false), + }, + "ssid": { + Type: schema.TypeString, + Required: true, + Description: "SSID (service set identifier) is a name that identifies wireless network.", + }, + "skip_dfs_channels": { + Type: schema.TypeString, + Optional: true, + Description: "These values are used to skip all DFS channels or specifically skip DFS CAC channels in range " + + "5600-5650MHz which detection could go up to 10min.", + ValidateFunc: validation.StringInSlice([]string{"string", "10min-cac", "all", "disabled"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "station_bridge_clone_mac": { + Type: schema.TypeString, + Optional: true, + Description: "This property has effect only in the station-pseudobridge-clone mode.Use this MAC address " + + "when connection to AP. If this value is 00:00:00:00:00:00, station will initially use MAC address of " + + "the wireless interface.As soon as packet with MAC address of another device needs to be transmitted, " + + "station will reconnect to AP using that address.", + ValidateFunc: validation.IsMACAddress, + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "station_roaming": { + Type: schema.TypeString, + Optional: true, + Description: "Station Roaming feature is available only for 802.11 wireless protocol and only for station " + + "modes.", + ValidateFunc: validation.StringInSlice([]string{"disabled", "enabled"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "supported_rates_ag": { + Type: schema.TypeString, + Optional: true, + Description: "List of supported rates, used for all bands except 2ghz-b.", + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateDiagFunc: ValidationMultiValInSlice([]string{"12Mbps", "18Mbps", "24Mbps", "36Mbps", "48Mbps", + "54Mbps", "6Mbps", "9Mbps"}, false, false), + }, + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "supported_rates_b": { + Type: schema.TypeString, + Optional: true, + Description: "List of supported rates, used for 2ghz-b, 2ghz-b/g and 2ghz-b/g/n bands. Two devices will " + + "communicate only using rates that are supported by both devices. This property has effect only when " + + "value of rate-set is configured.", + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateDiagFunc: ValidationMultiValInSlice([]string{"1Mbps", "2Mbps", "5Mbps", "11Mbps"}, false, false), + }, + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "tdma_period_size": { + Type: schema.TypeInt, + Optional: true, + Description: "Specifies TDMA period in milliseconds. It could help on the longer distance links, it could " + + "slightly increase bandwidth, while latency is increased too.", + ValidateFunc: validation.IntBetween(1, 10), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "tx_chains": { + Type: schema.TypeSet, + Optional: true, + Description: "Which antennas to use for transmitting. In current MikroTik routers, both RX and TX chain " + + "must be enabled, for the chain to be enabled.", + Elem: &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validation.IntBetween(0, 3), + }, + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "tx_power": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(-30, 40), + Description: "For 802.11ac wireless interface it's total power but for 802.11a/b/g/n it's power per chain.", + }, + "tx_power_mode": { + Type: schema.TypeString, + Optional: true, + Description: "Sets up tx-power mode for wireless card `default` - use values stored in the card `all-rates-fixed` " + + "- use same transmit power for all data rates. Can damage the card if transmit power is set above rated " + + "value of the card for used rate. `manual-table` - define transmit power for each rate separately. Can damage " + + "the card if transmit power is set above rated value of the card for used rate. `card-rates` - use transmit " + + "power calculated for each rate based on value of tx-power parameter. Legacy mode only compatible with " + + "currently discontinued products.", + ValidateFunc: validation.StringInSlice([]string{"default", "card-rates", "all-rates-fixed", "manual-table"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "update_stats_interval": { + Type: schema.TypeString, + Optional: true, + Description: "How often to request update of signals strength and ccq values from clients. Access to registration-table " + + " also triggers update of these values.This is proprietary extension.", + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "vlan_id": { + Type: schema.TypeInt, + Optional: true, + Description: "VLAN ID to use if doing VLAN tagging.", + ValidateFunc: validation.IntBetween(0, 4094), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "vlan_mode": { + Type: schema.TypeString, + Optional: true, + Description: "VLAN tagging mode specifies if traffic coming from client should get tagged (and untagged when going to client).", + ValidateFunc: validation.StringInSlice([]string{"default", "no-tag", "use-service-tag", "use-tag"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "vht_basic_mcs": { + Type: schema.TypeString, + Optional: true, + Description: "Modulation and Coding Schemes that every connecting client must support. Refer to 802.11ac " + + "for MCS specification. You can set MCS interval for each of Spatial Stream `none` - will not use selected; " + + "Spatial Stream `mcs0-7` - client must support MCS-0 to MCS-7; `mcs0-8` - client must support MCS-0 to MCS-8; " + + "`mcs0-9` - client must support MCS-0 to MCS-9.", + ValidateDiagFunc: ValidationMultiValInSlice([]string{"none", "mcs0-7", "`mcs0-8`", "mcs0-9"}, false, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "vht_supported_mcs": { + Type: schema.TypeString, + Optional: true, + Description: "Modulation and Coding Schemes that this device advertises as supported. Refer to 802.11ac " + + "for MCS specification. You can set MCS interval for each of Spatial Stream `none` - will not use selected; " + + "Spatial Stream `mcs0-7` - devices will advertise as supported MCS-0 to MCS-7; `mcs0-8` - devices will advertise " + + "as supported MCS-0 to MCS-8; `mcs0-9` - devices will advertise as supported MCS-0 to MCS-9.", + ValidateDiagFunc: ValidationMultiValInSlice([]string{"none", "mcs0-7", "`mcs0-8`", "mcs0-9"}, false, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "wds_cost_range": { + Type: schema.TypeString, + Optional: true, + Description: "Bridge port cost of WDS links are automatically adjusted, depending on measured link throughput. " + + "Port cost is recalculated and adjusted every 5 seconds if it has changed by more than 10%, or if more " + + "than 20 seconds have passed since the last adjustment.Setting this property to 0 disables automatic " + + "cost adjustment.Automatic adjustment does not work for WDS links that are manually configured as a bridge " + + "port.", + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "wds_default_bridge": { + Type: schema.TypeString, + Optional: true, + Description: "When WDS link is established and status of the wds interface becomes running, it will be added " + + "as a bridge port to the bridge interface specified by this property. When WDS link is lost, wds interface " + + "is removed from the bridge. If wds interface is already included in a bridge setup when WDS link becomes " + + "active, it will not be added to bridge specified by , and will (needs editing).", + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "wds_default_cost": { + Type: schema.TypeInt, + Optional: true, + Description: "Initial bridge port cost of the WDS links.", + ValidateFunc: validation.IntBetween(1, 200000000), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "wds_ignore_ssid": { + Type: schema.TypeBool, + Optional: true, + Description: "By default, WDS link between two APs can be created only when they work on the same frequency " + + "and have the same SSID value. If this property is set to yes, then SSID of the remote AP will not be " + + "checked. This property has no effect on connections from clients in station-wds mode. It also does not " + + "work if wds-mode is static-mesh or dynamic-mesh.", + }, + "wds_mode": { + Type: schema.TypeString, + Optional: true, + Description: "Controls how WDS links with other devices (APs and clients in station-wds mode) are established. `disabled` " + + "does not allow WDS links. `static` only allows WDS links that are manually configured in WDS. `dynamic` also " + + "allows WDS links with devices that are not configured in WDS, by creating required entries dynamically. " + + "Such dynamic WDS entries are removed automatically after the connection with the other AP is lost. `-mesh` " + + "modes use different (better) method for establishing link between AP, that is not compatible with APs " + + "in non-mesh mode. This method avoids one-sided WDS links that are created only by one of the two APs. " + + "Such links cannot pass any data.When AP or station is establishing WDS connection with another AP, it " + + "uses connect-list to check whether this connection is allowed. If station in station-wds mode is establishing " + + "connection with AP, AP uses access-list to check whether this connection is allowed.If mode is station-wds, " + + "then this property has no effect.", + ValidateFunc: validation.StringInSlice([]string{"disabled", "dynamic", "dynamic-mesh", "static", "static-mesh"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "wireless_protocol": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies protocol used on wireless interface; `unspecified` - protocol mode used on previous " + + "RouterOS versions (v3.x, v4.x). Nstreme is enabled by old enable-nstreme setting, Nv2 configuration " + + "is not possible. `any` : on AP - regular 802.11 Access Point or Nstreme Access Point; on station - selects " + + "Access Point without specific sequence, it could be changed by connect-list rules. `nstreme` - enables " + + "Nstreme protocol (the same as old enable-nstreme setting). `nv2` - enables Nv2 protocol. `nv2 nstreme` : on " + + "AP - uses first wireless-protocol setting, always Nv2; on station - searches for Nv2 Access Point, then " + + "for Nstreme Access Point. `nv2 nstreme 802.11` - on AP - uses first wireless-protocol setting, always Nv2; " + + "on station - searches for Nv2 Access Point, then for Nstreme Access Point, then for regular 802.11 Access " + + "Point.Warning! Nv2 doesn't have support for Virtual AP.", + ValidateFunc: validation.StringInSlice([]string{"802.11", "any", "nstreme", "nv2", "nv2-nstreme", + "nv2-nstreme-802.11", "unspecified"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + // ----------------------- + "wmm_support": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies whether to enable WMM. Only applies to bands B and G. Other bands will have it " + + "enabled regardless of this setting.", + ValidateFunc: validation.StringInSlice([]string{"disabled", "enabled", "required"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + "wps_mode": { + Type: schema.TypeString, + Optional: true, + Description: "WPS Server allows to connect wireless clients that support WPS to AP protected with the " + + "Pre-Shared Key without specifying that key in the clients configuration.", + ValidateFunc: validation.StringInSlice([]string{"disabled", "push-button", "push-button-virtual-only"}, false), + DiffSuppressFunc: AlwaysPresentNotUserProvided, + }, + } + + return &schema.Resource{ + CreateContext: DefaultCreate(resSchema), + ReadContext: DefaultRead(resSchema), + UpdateContext: DefaultUpdate(resSchema), + DeleteContext: DefaultDelete(resSchema), + + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: resSchema, + } +} diff --git a/routeros/resource_interface_wireless_test.go b/routeros/resource_interface_wireless_test.go new file mode 100644 index 00000000..35aa9918 --- /dev/null +++ b/routeros/resource_interface_wireless_test.go @@ -0,0 +1,61 @@ +package routeros + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +const testInterfaceWireless = "routeros_interface_wireless.test" + +func TestAccInterfaceWirelessTest_basic(t *testing.T) { + t.Logf("A device with WiFi interface is required for the test") + return + + for _, name := range testNames { + t.Run(name, func(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testSetTransportEnv(t, name) + }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testCheckResourceDestroy("/interface/wireless", "routeros_interface_wireless"), + Steps: []resource.TestStep{ + { + Config: testAccInterfaceWirelessConfig(), + Check: resource.ComposeTestCheckFunc( + testResourcePrimaryInstanceId(testInterfaceWireless), + resource.TestCheckResourceAttr(testInterfaceWireless, "", ""), + ), + }, + }, + }) + + }) + } +} + +func testAccInterfaceWirelessConfig() string { + return fmt.Sprintf(`%v + +resource "routeros_interface_wireless_security_profiles" "test" { + name = "test-profile" + mode = "dynamic-keys" + authentication_types = ["wpa-psk", "wpa2-psk"] + wpa_pre_shared_key = "wpa_psk_key" + wpa2_pre_shared_key = "wpa2_psk_key" +} + +resource "routeros_interface_wireless" "test" { + depends_on = [resource.routeros_interface_wireless_security_profiles.test] + security_profile=resource.routeros_interface_wireless_security_profiles.test.name + mode="ap-bridge" + master_interface="wlan1" + name="wlan-guest" + ssid="guests" + basic_rates_ag = ["6Mbps", "9Mbps"] +} +`, providerConfig) +} From 6f3d45508e70a19738c51fe074bfe35a66a63dd2 Mon Sep 17 00:00:00 2001 From: Vaerh Date: Fri, 27 Sep 2024 14:50:14 +0300 Subject: [PATCH 07/13] fix(wireless): Add missing field --- routeros/resource_interface_wireless.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/routeros/resource_interface_wireless.go b/routeros/resource_interface_wireless.go index 299c592e..d6c6cf24 100644 --- a/routeros/resource_interface_wireless.go +++ b/routeros/resource_interface_wireless.go @@ -440,6 +440,10 @@ func ResourceInterfaceWireless() *schema.Resource { ValidateFunc: validation.StringInSlice([]string{"any", "indoor", "outdoor"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, + "interface_type": { + Type: schema.TypeString, + Computed: true, + }, "interworking_profile": { Type: schema.TypeString, Optional: true, From 2eb587ff7ee792fea395d3e54637e60d2be373b5 Mon Sep 17 00:00:00 2001 From: Vaerh Date: Fri, 27 Sep 2024 15:55:38 +0300 Subject: [PATCH 08/13] fix(tool_sniffer): Add resource state control Automatically start and stop the tool depending on the state specified in the script. --- routeros/resource_tool_sniffer.go | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/routeros/resource_tool_sniffer.go b/routeros/resource_tool_sniffer.go index 192ba5df..db5e341c 100644 --- a/routeros/resource_tool_sniffer.go +++ b/routeros/resource_tool_sniffer.go @@ -49,8 +49,14 @@ func ResourceToolSniffer() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/tool/sniffer"), MetaId: PropId(Id), - MetaSkipFields: PropSkipFields("quick_rows", "quick_show_frame", "show_frame"), + MetaSkipFields: PropSkipFields("quick_rows", "quick_show_frame", "show_frame", "enabled"), + "enabled": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "Start packet capture.", + }, "file_limit": { Type: schema.TypeInt, Optional: true, @@ -335,12 +341,19 @@ func ResourceToolSniffer() *schema.Resource { return diags } - startSniffer(ctx, resSchema, d, m) + setSnifferState(ctx, resSchema, d, m) return SystemResourceRead(ctx, resSchema, d, m) }, - ReadContext: DefaultSystemRead(resSchema), + ReadContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + if d := SystemResourceRead(ctx, resSchema, d, m); d.HasError() { + return d + } + d.Set("enabled", d.Get(KeyRunning).(bool)) + + return nil + }, UpdateContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { stopSniffer(ctx, resSchema, d, m) @@ -349,7 +362,7 @@ func ResourceToolSniffer() *schema.Resource { if diags.HasError() { return diags } - startSniffer(ctx, resSchema, d, m) + setSnifferState(ctx, resSchema, d, m) return SystemResourceRead(ctx, resSchema, d, m) }, @@ -368,6 +381,13 @@ func ResourceToolSniffer() *schema.Resource { } } +func setSnifferState(ctx context.Context, s map[string]*schema.Schema, d *schema.ResourceData, m interface{}) diag.Diagnostics { + if d.Get("enabled").(bool) { + return startSniffer(ctx, s, d, m) + } + return stopSniffer(ctx, s, d, m) +} + func startSniffer(ctx context.Context, s map[string]*schema.Schema, d *schema.ResourceData, m interface{}) diag.Diagnostics { // Start sniffer. var resUrl = &URL{ From 2fb80bf6a3800ca07717135cb64ae4311d53f0f2 Mon Sep 17 00:00:00 2001 From: Vaerh Date: Fri, 27 Sep 2024 20:37:37 +0300 Subject: [PATCH 09/13] fix(serialize): Fix `PropTransformSet` --- routeros/provider_schema_helpers.go | 2 +- routeros/resource_capsman_configuration.go | 4 ++-- routeros/resource_capsman_configuration_v0.go | 4 ++-- routeros/resource_capsman_interface.go | 4 ++-- routeros/resource_wifi.go | 4 ++-- routeros/resource_wifi_configuration.go | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/routeros/provider_schema_helpers.go b/routeros/provider_schema_helpers.go index 7078a5b1..e9bcd30c 100644 --- a/routeros/provider_schema_helpers.go +++ b/routeros/provider_schema_helpers.go @@ -109,7 +109,7 @@ func PropDropByValue(s ...string) *schema.Schema { } } -// PropTransformSet List of []string{"TF", "MT"} string pairs. +// PropTransformSet List of []string{"TF : MT", "TF : MT", ...} string pairs. func PropTransformSet(s ...string) *schema.Schema { return &schema.Schema{ Type: schema.TypeString, diff --git a/routeros/resource_capsman_configuration.go b/routeros/resource_capsman_configuration.go index 4283a6d8..7980b638 100644 --- a/routeros/resource_capsman_configuration.go +++ b/routeros/resource_capsman_configuration.go @@ -39,8 +39,8 @@ func ResourceCapsManConfiguration() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/configuration"), MetaId: PropId(Id), - MetaTransformSet: PropTransformSet("channel: channel.config", "datapath: datapath.config", - "rates: rates.config", "security: security.config"), + MetaTransformSet: PropTransformSet("channel.config: channel", "datapath.config: datapath", + "rates.config: rates", "security.config: security"), "channel": { Type: schema.TypeMap, diff --git a/routeros/resource_capsman_configuration_v0.go b/routeros/resource_capsman_configuration_v0.go index 606bd32c..0e13cd8e 100644 --- a/routeros/resource_capsman_configuration_v0.go +++ b/routeros/resource_capsman_configuration_v0.go @@ -10,8 +10,8 @@ func ResourceCapsManConfigurationV0() *schema.Resource { Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/configuration"), MetaId: PropId(Name), - MetaTransformSet: PropTransformSet("channel: channel.config", "datapath: datapath.config", - "rates: rates.config", "security: security.config"), + MetaTransformSet: PropTransformSet("channel.config: channel", "datapath.config: datapath", + "rates.config: rates", "security.config: security"), "channel": { Type: schema.TypeMap, diff --git a/routeros/resource_capsman_interface.go b/routeros/resource_capsman_interface.go index 135445cd..485afff6 100644 --- a/routeros/resource_capsman_interface.go +++ b/routeros/resource_capsman_interface.go @@ -36,8 +36,8 @@ func ResourceCapsManInterface() *schema.Resource { MetaId: PropId(Id), MetaSkipFields: PropSkipFields("current_authorized_clients", "current_basic_rate_set", "current_channel", "current_rate_set", "current_registered_clients", "current_state"), - MetaTransformSet: PropTransformSet("channel: channel.config", "configuration: configuration.config", - "datapath: datapath.config", "rates: rates.config", "security: security.config"), + MetaTransformSet: PropTransformSet("channel.config: channel", "datapath.config: datapath", + "rates.config: rates", "security.config: security"), KeyArpTimeout: PropArpTimeoutRw, "bound": { diff --git a/routeros/resource_wifi.go b/routeros/resource_wifi.go index 8a9e979b..5099d2ec 100644 --- a/routeros/resource_wifi.go +++ b/routeros/resource_wifi.go @@ -34,8 +34,8 @@ func ResourceWifi() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/wifi"), MetaId: PropId(Id), - MetaTransformSet: PropTransformSet("aaa: aaa.config", "channel: channel.config", "configuration: configuration.config", - "datapath: datapath.config", "interworking: interworking.config", "security: security.config", "steering: steering.config"), + MetaTransformSet: PropTransformSet("aaa.config: aaa", "channel.config: channel", "configuration.config: configuration", + "datapath.config: datapath", "interworking.config: interworking", "security.config: security", "steering.config: steering"), "aaa": { Type: schema.TypeMap, diff --git a/routeros/resource_wifi_configuration.go b/routeros/resource_wifi_configuration.go index a0be3e37..27065fa2 100644 --- a/routeros/resource_wifi_configuration.go +++ b/routeros/resource_wifi_configuration.go @@ -38,8 +38,8 @@ func ResourceWifiConfiguration() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/wifi/configuration"), MetaId: PropId(Id), - MetaTransformSet: PropTransformSet("aaa: aaa.config", "channel: channel.config", "datapath: datapath.config", - "interworking: interworking.config", "security: security.config", "steering: steering.config"), + MetaTransformSet: PropTransformSet("aaa.config: aaa", "channel.config: channel", "datapath.config: datapath", + "interworking.config: interworking", "security.config: security", "steering.config: steering"), "aaa": { Type: schema.TypeMap, From c59a96f3b928b1d088174bd78deb5c03e1a3bb87 Mon Sep 17 00:00:00 2001 From: Vaerh Date: Fri, 27 Sep 2024 20:41:24 +0300 Subject: [PATCH 10/13] ci: Change ROS versions for autotests --- .github/workflows/module_testing.yml | 4 ++-- README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/module_testing.yml b/.github/workflows/module_testing.yml index b1bede38..8e74c468 100644 --- a/.github/workflows/module_testing.yml +++ b/.github/workflows/module_testing.yml @@ -21,8 +21,8 @@ jobs: os: [ubuntu-latest] routeros_version: - "7.12" - - "7.13" - - "7.14" + - "7.15" + - "7.16" steps: - name: Check out code into the Go module directory diff --git a/README.md b/README.md index ee57e4eb..54500a7c 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ For more in-depth documentation about each of the resources and datasources, ple ### Versions tested -- go 1.21 and ROS 7.12, 7.13, 7.14 (stable) +- go 1.21 and ROS 7.12, 7.15, 7.16 (stable) ## Changelog From 4acd13530763f1a5411996c9f506f5f9292f5aa0 Mon Sep 17 00:00:00 2001 From: Vaerh Date: Fri, 27 Sep 2024 20:55:57 +0300 Subject: [PATCH 11/13] docs(tool_sniffer): Fix the resource example --- examples/resources/routeros_tool_sniffer/resource.tf | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/resources/routeros_tool_sniffer/resource.tf b/examples/resources/routeros_tool_sniffer/resource.tf index ff204865..5ad50fb0 100755 --- a/examples/resources/routeros_tool_sniffer/resource.tf +++ b/examples/resources/routeros_tool_sniffer/resource.tf @@ -1,4 +1,6 @@ resource "routeros_tool_sniffer" "test" { + enabled = true + streaming_enabled = true streaming_server = "192.168.88.5:37008" filter_stream = true @@ -6,4 +8,4 @@ resource "routeros_tool_sniffer" "test" { filter_interface = ["ether2"] filter_direction = "rx" filter_operator_between_entries = "and" -} \ No newline at end of file +} From 603adeff056ace08ec76998cdbb905795c03ff75 Mon Sep 17 00:00:00 2001 From: Vaerh Date: Fri, 27 Sep 2024 20:58:10 +0300 Subject: [PATCH 12/13] test(wireless): Limit the maximum ROS version for tests --- routeros/resource_interface_wireless_access_list_test.go | 5 +++++ .../resource_interface_wireless_security_profiles_test.go | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/routeros/resource_interface_wireless_access_list_test.go b/routeros/resource_interface_wireless_access_list_test.go index bc7cedf7..a77618f3 100644 --- a/routeros/resource_interface_wireless_access_list_test.go +++ b/routeros/resource_interface_wireless_access_list_test.go @@ -10,6 +10,11 @@ import ( const testInterfaceWirelessAccessList = "routeros_interface_wireless_access_list.test" func TestAccInterfaceWirelessAccessListTest_basic(t *testing.T) { + if !testCheckMaxVersion(t, testCapsManChannelMaxVersion) { + t.Logf("Test skipped, the maximum supported version is %v", testCapsManChannelMaxVersion) + return + } + t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { diff --git a/routeros/resource_interface_wireless_security_profiles_test.go b/routeros/resource_interface_wireless_security_profiles_test.go index ebf5ab90..a5e01187 100644 --- a/routeros/resource_interface_wireless_security_profiles_test.go +++ b/routeros/resource_interface_wireless_security_profiles_test.go @@ -10,6 +10,11 @@ import ( const testInterfaceWirelessSecurityProfiles = "routeros_interface_wireless_security_profiles.test" func TestAccInterfaceWirelessSecurityProfilesTest_basic(t *testing.T) { + if !testCheckMaxVersion(t, testCapsManChannelMaxVersion) { + t.Logf("Test skipped, the maximum supported version is %v", testCapsManChannelMaxVersion) + return + } + t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { From db6346c1851ac4eeb5e87c26357fa9892322b752 Mon Sep 17 00:00:00 2001 From: Vaerh Date: Fri, 27 Sep 2024 21:13:39 +0300 Subject: [PATCH 13/13] test(connection_tracking): Fix for ROS 7.16 --- routeros/resource_ip_firewall_connection_tracking_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routeros/resource_ip_firewall_connection_tracking_test.go b/routeros/resource_ip_firewall_connection_tracking_test.go index abbc0a6a..faecbf1d 100644 --- a/routeros/resource_ip_firewall_connection_tracking_test.go +++ b/routeros/resource_ip_firewall_connection_tracking_test.go @@ -35,7 +35,7 @@ func TestAccIPConnectionTrackingTest_basic(t *testing.T) { resource.TestCheckResourceAttr(testIPConnectionTracking, "generic_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "icmp_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "loose_tcp_tracking", "false"), - resource.TestCheckResourceAttr(testIPConnectionTracking, "max_entries", "419840"), + // resource.TestCheckResourceAttr(testIPConnectionTracking, "max_entries", "419840"), ROS 7.16 - 337920 resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_close_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_close_wait_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_established_timeout", "3m"), @@ -61,7 +61,7 @@ func TestAccIPConnectionTrackingTest_basic(t *testing.T) { resource.TestCheckResourceAttr(testIPConnectionTracking, "generic_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "icmp_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "loose_tcp_tracking", "false"), - resource.TestCheckResourceAttr(testIPConnectionTracking, "max_entries", "419840"), + // resource.TestCheckResourceAttr(testIPConnectionTracking, "max_entries", "419840"), ROS 7.16 - 337920 resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_close_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_close_wait_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_established_timeout", "3m"),