Skip to content

Commit 13b81c2

Browse files
feat: add Models and Endpoints for GRE interfaces #156
1 parent ef2075f commit 13b81c2

3 files changed

Lines changed: 214 additions & 0 deletions

File tree

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace RESTAPI\Endpoints;
4+
5+
require_once 'RESTAPI/autoloader.inc';
6+
7+
use RESTAPI\Core\Endpoint;
8+
9+
/**
10+
* Defines an Endpoint for interacting with a singular InterfaceGRE Model object at
11+
* /api/v2/interface/gre.
12+
*/
13+
class InterfaceGREEndpoint extends Endpoint {
14+
public function __construct() {
15+
# Set Endpoint attributes
16+
$this->url = '/api/v2/interface/gre';
17+
$this->model_name = 'InterfaceGRE';
18+
$this->request_method_options = ['GET', 'POST', 'PATCH', 'DELETE'];
19+
20+
# Construct the parent Endpoint object
21+
parent::__construct();
22+
}
23+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace RESTAPI\Endpoints;
4+
5+
require_once 'RESTAPI/autoloader.inc';
6+
7+
use RESTAPI\Core\Endpoint;
8+
9+
/**
10+
* Defines an Endpoint for interacting with multiple InterfaceGRE Model objects at
11+
* /api/v2/interface/gres.
12+
*/
13+
class InterfaceGREsEndpoint extends Endpoint {
14+
public function __construct() {
15+
# Set Endpoint attributes
16+
$this->url = '/api/v2/interface/gres';
17+
$this->model_name = 'InterfaceGRE';
18+
$this->many = true;
19+
$this->request_method_options = ['GET', 'DELETE'];
20+
21+
# Construct the parent Endpoint object
22+
parent::__construct();
23+
}
24+
}
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
<?php
2+
3+
namespace RESTAPI\Models;
4+
5+
use RESTAPI\Core\Model;
6+
use RESTAPI\Fields\BooleanField;
7+
use RESTAPI\Fields\IntegerField;
8+
use RESTAPI\Fields\InterfaceField;
9+
use RESTAPI\Fields\StringField;
10+
use RESTAPI\Responses\ConflictError;
11+
use RESTAPI\Responses\ValidationError;
12+
use RESTAPI\Validators\IPAddressValidator;
13+
14+
/**
15+
* Defines a Model for interacting with Interface GRE Tunnels.
16+
*/
17+
class InterfaceGRE extends Model {
18+
public InterfaceField $if;
19+
public StringField $greif;
20+
public StringField $descr;
21+
public BooleanField $add_static_route;
22+
public StringField $remote_addr;
23+
public StringField $tunnel_local_addr;
24+
public StringField $tunnel_remote_addr;
25+
public IntegerField $tunnel_remote_net;
26+
public StringField $tunnel_local_addr6;
27+
public StringField $tunnel_remote_addr6;
28+
public IntegerField $tunnel_remote_net6;
29+
30+
public function __construct(mixed $id = null, mixed $parent_id = null, mixed $data = [], ...$options) {
31+
# Set model attributes
32+
$this->config_path = 'gres/gre';
33+
$this->many = true;
34+
$this->always_apply = true;
35+
36+
# Set model fields
37+
$this->if = new InterfaceField(
38+
required: true,
39+
help_text: 'The pfSense interface interface serving as the local address to be used for the GRE tunnel.',
40+
);
41+
$this->greif = new StringField(
42+
default: null,
43+
allow_null: true,
44+
read_only: true,
45+
help_text: 'The real interface name for this GRE interface.',
46+
);
47+
$this->descr = new StringField(
48+
default: '',
49+
allow_empty: true,
50+
help_text: 'A description for this GRE interface.',
51+
);
52+
$this->add_static_route = new BooleanField(
53+
default: false,
54+
internal_name: 'link1',
55+
help_text: 'Whether to add an explicit static route for the remote inner tunnel address/subnet via the ' .
56+
'local tunnel address.',
57+
);
58+
$this->remote_addr = new StringField(
59+
required: true,
60+
internal_name: 'remote-addr',
61+
validators: [new IPAddressValidator(allow_ipv4: true, allow_ipv6: true)],
62+
help_text: 'The remote address to use for the GRE tunnel.',
63+
);
64+
$this->tunnel_local_addr = new StringField(
65+
default: null,
66+
allow_null: true,
67+
internal_name: 'tunnel-local-addr',
68+
validators: [new IPAddressValidator(allow_ipv4: true, allow_ipv6: false)],
69+
help_text: 'The local IPv4 address to use for the GRE tunnel.',
70+
);
71+
$this->tunnel_remote_addr = new StringField(
72+
required: true,
73+
unique: true,
74+
internal_name: 'tunnel-remote-addr',
75+
conditions: ['!tunnel_local_addr' => null],
76+
validators: [new IPAddressValidator(allow_ipv4: true, allow_ipv6: false)],
77+
help_text: 'The remote IPv4 address to use for the GRE tunnel.',
78+
);
79+
$this->tunnel_remote_net = new IntegerField(
80+
default: 32,
81+
minimum: 1,
82+
maximum: 32,
83+
internal_name: 'tunnel-remote-net',
84+
conditions: ['!tunnel_local_addr' => null],
85+
help_text: 'The remote IPv4 subnet bitmask to use for the GRE tunnel.',
86+
);
87+
$this->tunnel_local_addr6 = new StringField(
88+
default: null,
89+
allow_null: true,
90+
internal_name: 'tunnel-local-addr6',
91+
validators: [new IPAddressValidator(allow_ipv4: false, allow_ipv6: true)],
92+
help_text: 'The local IPv6 address to use for the GRE tunnel.',
93+
);
94+
$this->tunnel_remote_addr6 = new StringField(
95+
required: true,
96+
unique: true,
97+
internal_name: 'tunnel-remote-addr6',
98+
conditions: ['!tunnel_local_addr6' => null],
99+
validators: [new IPAddressValidator(allow_ipv4: false, allow_ipv6: true)],
100+
help_text: 'The remote IPv6 address to use for the GRE tunnel.',
101+
);
102+
$this->tunnel_remote_net6 = new IntegerField(
103+
default: 128,
104+
minimum: 1,
105+
maximum: 128,
106+
internal_name: 'tunnel-remote-net6',
107+
conditions: ['!tunnel_local_addr6' => null],
108+
help_text: 'The remote IPv6 subnet bitmask to use for the GRE tunnel.',
109+
);
110+
111+
parent::__construct($id, $parent_id, $data, ...$options);
112+
}
113+
114+
/**
115+
* Adds extra validation to this entire Model.
116+
* @throws ValidationError If neither a local IPv4 nor IPv6 tunnel address is present.
117+
*/
118+
public function validate_extra(): void {
119+
# Require either a local IPv4 and/or IPv6 address to present
120+
if (!$this->tunnel_local_addr->value and !$this->tunnel_local_addr6->value) {
121+
throw new ValidationError(
122+
message: 'GRE tunnel must have a `tunnel_local_addr` and/or `tunnel_local_addr6`.',
123+
response_id: 'INTERFACE_GRE_NO_LOCAL_ADDRESS',
124+
);
125+
}
126+
}
127+
128+
/**
129+
* Applies changes to this Interface GRE Tunnel.
130+
*/
131+
public function apply(): void {
132+
# Create the GRE interface if no greif exists
133+
if (!$this->greif->value) {
134+
interface_gre_configure($this->to_internal());
135+
}
136+
137+
# If the greif is already assigned to an interface, reconfigure the interface
138+
$if_q = NetworkInterface::query(if: $this->greif->value);
139+
if ($if_q->exists()) {
140+
interface_configure($if_q->first()->id);
141+
}
142+
}
143+
144+
/**
145+
* Applies the deletion of this Interface GRE Tunnel.
146+
*/
147+
public function apply_delete(): void {
148+
pfSense_interface_destroy($this->greif->value);
149+
}
150+
151+
/**
152+
* Extend the default _delete method to prevent deletion of the GRE interface while in use.
153+
* @throws ConflictError If the GRE interface is in use.
154+
*/
155+
public function _delete(): void {
156+
# If the greif is in use, don't allow deletion
157+
$if_q = NetworkInterface::query(if: $this->greif->value);
158+
if ($if_q->exists()) {
159+
throw new ConflictError(
160+
message: 'Cannot delete GRE interface while it is in use.',
161+
response_id: 'INTERFACE_GRE_CANNOT_DELETE_WHILE_IN_USE',
162+
);
163+
}
164+
165+
parent::_delete();
166+
}
167+
}

0 commit comments

Comments
 (0)