Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add EoIP tunnel support #283

Merged
merged 1 commit into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions docs/resources/routeros_interface_eoip.md
Original file line number Diff line number Diff line change
@@ -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 generated by tfplugindocs -->
## 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
```
2 changes: 2 additions & 0 deletions examples/resources/routeros_interface_eoip/import.sh
Original file line number Diff line number Diff line change
@@ -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
6 changes: 6 additions & 0 deletions examples/resources/routeros_interface_eoip/resource.tf
Original file line number Diff line number Diff line change
@@ -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
}
1 change: 1 addition & 0 deletions routeros/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
188 changes: 171 additions & 17 deletions routeros/provider_schema_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand Down
66 changes: 66 additions & 0 deletions routeros/resource_interface_eoip.go
Original file line number Diff line number Diff line change
@@ -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,
}
}
Loading
Loading