diff --git a/docs/resources/routeros_interface_eoip.md b/docs/resources/routeros_interface_eoip.md new file mode 100644 index 00000000..7cfea4e9 --- /dev/null +++ b/docs/resources/routeros_interface_eoip.md @@ -0,0 +1,62 @@ +# routeros_interface_eoip (Resource) + + +## Example Usage +```terraform +resource "routeros_interface_eoip" "eoip_tunnel1" { + name = "eoip-tunnel1" + local_address = "192.168.88.1" + remote_address = "192.168.88.2" + disabled = true +} +``` + + +## Schema + +### Required + +- `name` (String) Changing the name of this resource will force it to be recreated. + > The links of other configuration properties to this resource may be lost! + > Changing the name of the resource outside of a Terraform will result in a loss of control integrity for that resource! + +### Optional + +- `allow_fast_path` (Boolean) Whether to allow FastPath processing. Must be disabled if IPsec tunneling is used. +- `arp` (String) Address Resolution Protocol mode: + * disabled - the interface will not use ARP + * enabled - the interface will use ARP + * local-proxy-arp - the router performs proxy ARP on the interface and sends replies to the same interface + * proxy-arp - the router performs proxy ARP on the interface and sends replies to other interfaces + * reply-only - the interface will only reply to requests originated from matching IP address/MAC address combinations which are entered as static entries in the ARP table. No dynamic entries will be automatically stored in the ARP table. Therefore for communications to be successful, a valid static entry must already exist. +- `arp_timeout` (String) 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. Can use postfix ms, s, M, h, d for milliseconds, seconds, minutes, hours or days. If no postfix is set then seconds (s) is used. +- `clamp_tcp_mss` (Boolean) Controls whether to change MSS size for received TCP SYN packets. When enabled, a router will change the MSS size for received TCP SYN packets if the current MSS size exceeds the tunnel interface MTU (taking into account the TCP/IP overhead). The received encapsulated packet will still contain the original MSS, and only after decapsulation the MSS is changed. +- `comment` (String) +- `disabled` (Boolean) +- `dont_fragment` (String) +- `dscp` (String) Set dscp value in GRE header to a fixed value '0..63' or 'inherit' from dscp value taken from tunnelled traffic. +- `ipsec_secret` (String, Sensitive) When secret is specified, router adds dynamic IPsec peer to remote-address with pre-shared key and policy (by default phase2 uses sha1/aes128cbc). +- `keepalive` (String) Tunnel keepalive parameter sets the time interval in which the tunnel running flag will remain even if the remote end of tunnel goes down. If configured time,retries fail, interface running flag is removed. Parameters are written in following format: KeepaliveInterval,KeepaliveRetries where KeepaliveInterval is time interval and KeepaliveRetries - number of retry attempts. KeepaliveInterval is integer 0..4294967295 +- `local_address` (String) Source address of the tunnel packets, local on the router. +- `loop_protect` (String) +- `loop_protect_disable_time` (String) +- `loop_protect_send_interval` (String) +- `mtu` (String) Layer3 Maximum transmission unit ('auto', 0 .. 65535) +- `remote_address` (String) IP address of the remote end of the tunnel. +- `tunnel_id` (String) Unique tunnel identifier, which must match the other side of the tunnel. + +### Read-Only + +- `actual_mtu` (Number) +- `id` (String) The ID of this resource. +- `l2mtu` (Number) Layer2 Maximum transmission unit. [See](https://wiki.mikrotik.com/wiki/Maximum_Transmission_Unit_on_RouterBoards). +- `loop_protect_status` (String) +- `mac_address` (String) Current mac address. +- `running` (Boolean) + +## Import +Import is supported using the following syntax: +```shell +# Import with the name of the EoIP interface in case of the example, use `eoip-tunnel1` +terraform import routeros_interface_eoip.eoip_tunnel1 eoip-tunnel1 +``` diff --git a/examples/resources/routeros_interface_eoip/import.sh b/examples/resources/routeros_interface_eoip/import.sh new file mode 100644 index 00000000..733a2b71 --- /dev/null +++ b/examples/resources/routeros_interface_eoip/import.sh @@ -0,0 +1,2 @@ +# Import with the name of the EoIP interface in case of the example, use `eoip-tunnel1` +terraform import routeros_interface_eoip.eoip_tunnel1 eoip-tunnel1 \ No newline at end of file diff --git a/examples/resources/routeros_interface_eoip/resource.tf b/examples/resources/routeros_interface_eoip/resource.tf new file mode 100644 index 00000000..03e3e3eb --- /dev/null +++ b/examples/resources/routeros_interface_eoip/resource.tf @@ -0,0 +1,6 @@ +resource "routeros_interface_eoip" "eoip_tunnel1" { + name = "eoip-tunnel1" + local_address = "192.168.88.1" + remote_address = "192.168.88.2" + disabled = true +} diff --git a/routeros/provider.go b/routeros/provider.go index 1e16dc15..1f8b9e9e 100644 --- a/routeros/provider.go +++ b/routeros/provider.go @@ -103,6 +103,7 @@ func Provider() *schema.Provider { "routeros_interface_bridge_port": ResourceInterfaceBridgePort(), "routeros_interface_bridge_vlan": ResourceInterfaceBridgeVlan(), "routeros_interface_bridge_settings": ResourceInterfaceBridgeSettings(), + "routeros_interface_eoip": ResourceInterfaceEoip(), "routeros_interface_gre": ResourceInterfaceGre(), "routeros_interface_vlan": ResourceInterfaceVlan(), "routeros_interface_vrrp": ResourceInterfaceVrrp(), diff --git a/routeros/provider_schema_helpers.go b/routeros/provider_schema_helpers.go index 7d35064f..99b69386 100644 --- a/routeros/provider_schema_helpers.go +++ b/routeros/provider_schema_helpers.go @@ -21,23 +21,35 @@ const ( ) const ( - KeyActualMtu = "actual_mtu" - KeyArp = "arp" - KeyArpTimeout = "arp_timeout" - KeyComment = "comment" - KeyDynamic = "dynamic" - KeyDisabled = "disabled" - KeyFilter = "filter" - KeyInactive = "inactive" - KeyInterface = "interface" - KeyInvalid = "invalid" - KeyL2Mtu = "l2mtu" - KeyMacAddress = "mac_address" - KeyMtu = "mtu" - KeyName = "name" - KeyPlaceBefore = "place_before" - KeyRunning = "running" - KeyVrf = "vrf" + KeyActualMtu = "actual_mtu" + KeyAllowFastPath = "allow_fast_path" + KeyArp = "arp" + KeyArpTimeout = "arp_timeout" + KeyClampTcpMss = "clamp_tcp_mss" + KeyComment = "comment" + KeyDynamic = "dynamic" + KeyDisabled = "disabled" + KeyDontFragment = "dont_fragment" + KeyDscp = "dscp" + KeyFilter = "filter" + KeyInactive = "inactive" + KeyInterface = "interface" + KeyInvalid = "invalid" + KeyIpsecSecret = "ipsec_secret" + KeyKeepalive = "keepalive" + KeyL2Mtu = "l2mtu" + KeyLocalAddress = "local_address" + KeyLoopProtect = "loop_protect" + KeyLoopProtectDisableTime = "loop_protect_disable_time" + KeyLoopProtectSendInterval = "loop_protect_send_interval" + KeyLoopProtectStatus = "loop_protect_status" + KeyMacAddress = "mac_address" + KeyMtu = "mtu" + KeyName = "name" + KeyPlaceBefore = "place_before" + KeyRemoteAddress = "remote_address" + KeyRunning = "running" + KeyVrf = "vrf" ) // PropResourcePath Resource path property. @@ -107,6 +119,12 @@ var ( Type: schema.TypeInt, Computed: true, } + PropAllowFastPathRw = &schema.Schema{ + Type: schema.TypeBool, + Optional: true, // Must be present in the request so that the IPSEC PSK can be set correctly. + Default: true, + Description: "Whether to allow FastPath processing. Must be disabled if IPsec tunneling is used.", + } PropArpRw = &schema.Schema{ Type: schema.TypeString, Optional: true, @@ -130,6 +148,15 @@ var ( ValidateFunc: validation.StringMatch(regexp.MustCompile(`^$|auto$|(\d+(ms|s|M|h|d)?)+$`), "expected arp_timout value to be 'auto' string or time value"), } + PropClampTcpMssRw = &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "Controls whether to change MSS size for received TCP SYN packets. When enabled, a " + + "router will change the MSS size for received TCP SYN packets if the current MSS size exceeds the " + + "tunnel interface MTU (taking into account the TCP/IP overhead). The received encapsulated packet " + + "will still contain the original MSS, and only after decapsulation the MSS is changed.", + } PropCommentRw = &schema.Schema{ Type: schema.TypeString, Optional: true, @@ -139,6 +166,42 @@ var ( Optional: true, Default: false, } + PropDontFragmentRw = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "no", + ValidateFunc: validation.StringInSlice([]string{"inherit", "no"}, false), + } + PropDscpRw = &schema.Schema{ + // dscp (inherit | integer [0-63]; Default: '') + Type: schema.TypeString, + Optional: true, + Default: "inherit", + ValidateDiagFunc: func(v interface{}, p cty.Path) (diags diag.Diagnostics) { + value := v.(string) + + if value == "" || value == "inherit" { + return + } + + i, err := strconv.Atoi(value) + if err != nil { + diags = diag.Errorf( + "expected dscp value (%s) to be empty string or 'inherit' or integer 0..63", value) + return + } + + if i < 0 || i > 63 { + diags = diag.Errorf( + "expected %s to be in the range 0 - 63, got %d", value, i) + return + } + + return + }, + Description: "Set dscp value in GRE header to a fixed value '0..63' or 'inherit' from dscp value taken " + + "from tunnelled traffic.", + } PropDynamicRo = &schema.Schema{ Type: schema.TypeBool, Computed: true, @@ -164,12 +227,96 @@ var ( Type: schema.TypeBool, Computed: true, } + PropIpsecSecretRw = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "", + Sensitive: true, + Description: "When secret is specified, router adds dynamic IPsec peer to remote-address with " + + "pre-shared key and policy (by default phase2 uses sha1/aes128cbc).", + } + PropKeepaliveRw = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "10s,10", + ValidateFunc: validation.StringMatch(regexp.MustCompile(`^(\d+[smhdw]?)+(,\d+)?$`), + "value must be integer[/time],integer 0..4294967295 (https://help.mikrotik.com/docs/display/ROS/GRE)"), + Description: "Tunnel keepalive parameter sets the time interval in which the tunnel running flag will " + + "remain even if the remote end of tunnel goes down. If configured time,retries fail, interface " + + "running flag is removed. Parameters are written in following format: " + + "KeepaliveInterval,KeepaliveRetries where KeepaliveInterval is time interval and " + + "KeepaliveRetries - number of retry attempts. KeepaliveInterval is integer 0..4294967295", + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + if old == new { + return true + } + + if old == "" || new == "" { + return false + } + + o := strings.Split(old, ",") + n := strings.Split(new, ",") + if len(o) != 2 || len(n) != 2 { + panic(fmt.Sprintf("[GRE keepalive] wrong keepalive format, old: '%v', new: '%v'", old, new)) + } + + // Compare keepalive retries. + if o[1] != n[1] { + return false + } + + // Compare keepalive intervals. + oDuration, err := ParseDuration(o[0]) + if err != nil { + panic("[GRE keepalive] parse 'old' duration error: " + err.Error()) + } + + nDuration, err := ParseDuration(n[0]) + if err != nil { + panic("[GRE keepalive] parse 'new' duration error: " + err.Error()) + } + + return oDuration.Seconds() == nDuration.Seconds() + }, + } PropL2MtuRo = &schema.Schema{ Type: schema.TypeInt, Computed: true, Description: "Layer2 Maximum transmission unit. " + "[See](https://wiki.mikrotik.com/wiki/Maximum_Transmission_Unit_on_RouterBoards).", } + PropLocalAddressRw = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "0.0.0.0", + Description: "Source address of the tunnel packets, local on the router.", + ValidateFunc: validation.IsIPv4Address, + } + PropLoopProtectRw = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "default", + ValidateFunc: validation.StringInSlice([]string{"default", "on", "off"}, false), + } + PropLoopProtectDisableTimeRw = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "5m", + ValidateFunc: ValidationTime, + DiffSuppressFunc: TimeEquall, + } + PropLoopProtectSendIntervalRw = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "5s", + ValidateFunc: ValidationTime, + DiffSuppressFunc: TimeEquall, + } + PropLoopProtectStatusRo = &schema.Schema{ + Type: schema.TypeString, + Computed: true, + } PropMacAddressRo = &schema.Schema{ Type: schema.TypeString, Computed: true, @@ -193,6 +340,13 @@ var ( > Best way to use in conjunction with a data source. See [example](../data-sources/firewall.md#example-usage). `, } + PropRemoteAddressRw = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "0.0.0.0", + Description: "IP address of the remote end of the tunnel.", + ValidateFunc: validation.IsIPv4Address, + } PropRunningRo = &schema.Schema{ Type: schema.TypeBool, Computed: true, diff --git a/routeros/resource_interface_eoip.go b/routeros/resource_interface_eoip.go new file mode 100644 index 00000000..81a6ccb6 --- /dev/null +++ b/routeros/resource_interface_eoip.go @@ -0,0 +1,66 @@ +package routeros + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// https://help.mikrotik.com/docs/display/ROS/EoIP +func ResourceInterfaceEoip() *schema.Resource { + resSchema := map[string]*schema.Schema{ + MetaResourcePath: PropResourcePath("/interface/eoip"), + MetaId: PropId(Name), + + KeyActualMtu: PropActualMtuRo, + KeyArp: PropArpRw, + KeyArpTimeout: PropArpTimeoutRw, + KeyAllowFastPath: PropAllowFastPathRw, + KeyClampTcpMss: PropClampTcpMssRw, + KeyComment: PropCommentRw, + KeyDisabled: PropDisabledRw, + KeyDontFragment: PropDontFragmentRw, + KeyDscp: PropDscpRw, + KeyIpsecSecret: PropIpsecSecretRw, + KeyKeepalive: PropKeepaliveRw, + KeyL2Mtu: PropL2MtuRo, + KeyLocalAddress: PropLocalAddressRw, + KeyLoopProtect: PropLoopProtectRw, + KeyLoopProtectDisableTime: PropLoopProtectDisableTimeRw, + KeyLoopProtectSendInterval: PropLoopProtectSendIntervalRw, + KeyLoopProtectStatus: PropLoopProtectStatusRo, + KeyMacAddress: PropMacAddressRo, + KeyMtu: PropMtuRw(), + KeyName: PropNameForceNewRw, + KeyRemoteAddress: PropRemoteAddressRw, + KeyRunning: PropRunningRo, + "tunnel_id": { + Type: schema.TypeString, + Optional: true, + Default: "0", + Description: "Unique tunnel identifier, which must match the other side of the tunnel.", + }, + } + + return &schema.Resource{ + CreateContext: DefaultValidateCreate(resSchema, func(d *schema.ResourceData) diag.Diagnostics { + if d.Get("allow_fast_path").(bool) && d.Get("ipsec_secret").(string) != "" { + return diag.Errorf("can't enable fastpath together with ipsec") + } + return nil + }), + ReadContext: DefaultRead(resSchema), + UpdateContext: DefaultValidateUpdate(resSchema, func(d *schema.ResourceData) diag.Diagnostics { + if d.Get("allow_fast_path").(bool) && d.Get("ipsec_secret").(string) != "" { + return diag.Errorf("can't enable fastpath together with ipsec") + } + return nil + }), + DeleteContext: DefaultDelete(resSchema), + + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: resSchema, + } +} diff --git a/routeros/resource_interface_ethernet.go b/routeros/resource_interface_ethernet.go index b039e368..4e44be54 100644 --- a/routeros/resource_interface_ethernet.go +++ b/routeros/resource_interface_ethernet.go @@ -153,30 +153,10 @@ func ResourceInterfaceEthernet() *schema.Resource { Description: "Layer2 Maximum transmission unit. " + "[See](https://wiki.mikrotik.com/wiki/Maximum_Transmission_Unit_on_RouterBoards).", }, - "loop_protect": { - Type: schema.TypeString, - Optional: true, - Default: "default", - ValidateFunc: validation.StringInSlice([]string{"default", "on", "off"}, false), - }, - "loop_protect_disable_time": { - Type: schema.TypeString, - Optional: true, - Default: "5m", - ValidateFunc: ValidationTime, - DiffSuppressFunc: TimeEquall, - }, - "loop_protect_send_interval": { - Type: schema.TypeString, - Optional: true, - Default: "5s", - ValidateFunc: ValidationTime, - DiffSuppressFunc: TimeEquall, - }, - "loop_protect_status": { - Type: schema.TypeString, - Computed: true, - }, + KeyLoopProtect: PropLoopProtectRw, + KeyLoopProtectDisableTime: PropLoopProtectDisableTimeRw, + KeyLoopProtectSendInterval: PropLoopProtectSendIntervalRw, + KeyLoopProtectStatus: PropLoopProtectStatusRo, "mac_address": { Type: schema.TypeString, Description: `Media Access Control number of an interface.`, diff --git a/routeros/resource_interface_gre.go b/routeros/resource_interface_gre.go index b9e496c4..336f26fd 100644 --- a/routeros/resource_interface_gre.go +++ b/routeros/resource_interface_gre.go @@ -1,16 +1,8 @@ package routeros import ( - "fmt" - "regexp" - "strconv" - "strings" - - "github.com/hashicorp/go-cty/cty" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) // ResourceInterfaceGre https://wiki.mikrotik.com/wiki/Manual:Interface/Gre @@ -19,126 +11,21 @@ func ResourceInterfaceGre() *schema.Resource { MetaResourcePath: PropResourcePath("/interface/gre"), MetaId: PropId(Name), - KeyActualMtu: PropActualMtuRo, - "allow_fast_path": { - Type: schema.TypeBool, - Optional: true, // Must be present in the request so that the IPSEC PSK can be set correctly. - Default: true, - Description: "Whether to allow FastPath processing. Must be disabled if IPsec tunneling is used.", - }, - "clamp_tcp_mss": { - Type: schema.TypeBool, - Optional: true, - Default: true, - Description: "Controls whether to change MSS size for received TCP SYN packets. When enabled, a " + - "router will change the MSS size for received TCP SYN packets if the current MSS size exceeds the " + - "tunnel interface MTU (taking into account the TCP/IP overhead). The received encapsulated packet " + - "will still contain the original MSS, and only after decapsulation the MSS is changed.", - }, - KeyComment: PropCommentRw, - KeyDisabled: PropDisabledRw, - "dont_fragment": { - Type: schema.TypeString, - Optional: true, - Default: "no", - ValidateFunc: validation.StringInSlice([]string{"inherit", "no"}, false), - }, - "dscp": { - Type: schema.TypeString, - Optional: true, - Default: "inherit", - ValidateDiagFunc: func(v interface{}, p cty.Path) (diags diag.Diagnostics) { - value := v.(string) - - // dscp (inherit | integer [0-63]; Default: '') - if value == "" || value == "inherit" { - return - } - - i, err := strconv.Atoi(value) - if err != nil { - diags = diag.Errorf( - "expected dscp value (%s) to be empty string or 'inherit' or integer 0..63", value) - return - } - if i < 0 || i > 63 { - diags = diag.Errorf( - "expected %s to be in the range 0 - 63, got %d", value, i) - return - } - return - }, - Description: "Set dscp value in GRE header to a fixed value '0..63' or 'inherit' from dscp value taken " + - "from tunnelled traffic.", - }, - "ipsec_secret": { - Type: schema.TypeString, - Optional: true, - Default: "", - Sensitive: true, - Description: "When secret is specified, router adds dynamic IPsec peer to remote-address with " + - "pre-shared key and policy (by default phase2 uses sha1/aes128cbc).", - }, - "keepalive": { - Type: schema.TypeString, - Optional: true, - Default: "10s,10", - ValidateFunc: validation.StringMatch(regexp.MustCompile(`^(\d+[smhdw]?)+(,\d+)?$`), - "value must be integer[/time],integer 0..4294967295 (https://help.mikrotik.com/docs/display/ROS/GRE)"), - Description: "Tunnel keepalive parameter sets the time interval in which the tunnel running flag will " + - "remain even if the remote end of tunnel goes down. If configured time,retries fail, interface " + - "running flag is removed. Parameters are written in following format: " + - "KeepaliveInterval,KeepaliveRetries where KeepaliveInterval is time interval and " + - "KeepaliveRetries - number of retry attempts. KeepaliveInterval is integer 0..4294967295", - DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - if old == new { - return true - } - - if old == "" || new == "" { - return false - } - - o := strings.Split(old, ",") - n := strings.Split(new, ",") - if len(o) != 2 || len(n) != 2 { - panic(fmt.Sprintf("[GRE keepalive] wrong keepalive format, old: '%v', new: '%v'", old, new)) - } - - // Compare keepalive retries. - if o[1] != n[1] { - return false - } - - // Compare keepalive intervals. - oDuration, err := ParseDuration(o[0]) - if err != nil { - panic("[GRE keepalive] parse 'old' duration error: " + err.Error()) - } - - nDuration, err := ParseDuration(n[0]) - if err != nil { - panic("[GRE keepalive] parse 'new' duration error: " + err.Error()) - } - - return oDuration.Seconds() == nDuration.Seconds() - }, - }, - KeyL2Mtu: PropL2MtuRo, - "local_address": { - Type: schema.TypeString, - Optional: true, - Default: "0.0.0.0", - ValidateFunc: validation.IsIPv4Address, - }, - KeyMtu: PropMtuRw(), - KeyName: PropNameForceNewRw, - "remote_address": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.IsIPv4Address, - }, - KeyRunning: PropRunningRo, + KeyActualMtu: PropActualMtuRo, + KeyAllowFastPath: PropAllowFastPathRw, + KeyClampTcpMss: PropClampTcpMssRw, + KeyComment: PropCommentRw, + KeyDisabled: PropDisabledRw, + KeyDontFragment: PropDontFragmentRw, + KeyDscp: PropDscpRw, + KeyIpsecSecret: PropIpsecSecretRw, + KeyKeepalive: PropKeepaliveRw, + KeyL2Mtu: PropL2MtuRo, + KeyLocalAddress: PropLocalAddressRw, + KeyMtu: PropMtuRw(), + KeyName: PropNameForceNewRw, + KeyRemoteAddress: PropRemoteAddressRw, + KeyRunning: PropRunningRo, } return &schema.Resource{ diff --git a/routeros/resource_interface_vlan.go b/routeros/resource_interface_vlan.go index 4d9641f5..47e8f4fb 100644 --- a/routeros/resource_interface_vlan.go +++ b/routeros/resource_interface_vlan.go @@ -2,49 +2,28 @@ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) // ResourceInterfaceVlan https://wiki.mikrotik.com/wiki/Manual:Interface/VLAN func ResourceInterfaceVlan() *schema.Resource { resSchema := map[string]*schema.Schema{ - MetaResourcePath: PropResourcePath("/interface/vlan"), - MetaId: PropId(Name), + MetaResourcePath: PropResourcePath("/interface/vlan"), + MetaId: PropId(Name), - KeyArp: PropArpRw, - KeyArpTimeout: PropArpTimeoutRw, - KeyComment: PropCommentRw, - KeyDisabled: PropDisabledRw, - KeyInterface: PropInterfaceRw, - KeyL2Mtu: PropL2MtuRo, - "loop_protect": { - Type: schema.TypeString, - Optional: true, - Default: "default", - ValidateFunc: validation.StringInSlice([]string{"default", "on", "off"}, false), - }, - "loop_protect_disable_time": { - Type: schema.TypeString, - Optional: true, - Default: "5m", - ValidateFunc: ValidationTime, - DiffSuppressFunc: TimeEquall, - }, - "loop_protect_send_interval": { - Type: schema.TypeString, - Optional: true, - Default: "5s", - ValidateFunc: ValidationTime, - DiffSuppressFunc: TimeEquall, - }, - "loop_protect_status": { - Type: schema.TypeString, - Computed: true, - }, - KeyMacAddress: PropMacAddressRo, - KeyMtu: PropMtuRw(), - KeyName: PropNameForceNewRw, - KeyRunning: PropRunningRo, + KeyArp: PropArpRw, + KeyArpTimeout: PropArpTimeoutRw, + KeyComment: PropCommentRw, + KeyDisabled: PropDisabledRw, + KeyInterface: PropInterfaceRw, + KeyL2Mtu: PropL2MtuRo, + KeyLoopProtect: PropLoopProtectRw, + KeyLoopProtectDisableTime: PropLoopProtectDisableTimeRw, + KeyLoopProtectSendInterval: PropLoopProtectSendIntervalRw, + KeyLoopProtectStatus: PropLoopProtectStatusRo, + KeyMacAddress: PropMacAddressRo, + KeyMtu: PropMtuRw(), + KeyName: PropNameForceNewRw, + KeyRunning: PropRunningRo, "use_service_tag": { Type: schema.TypeBool, Optional: true,