It is targetted to Rocky but it was merged accidentally before Rocky directory is prepared. Change-Id: Ia73a333b7544dd1bc4ab8027dd69f7dcc92c8e36
18 KiB
Port Forwarding API
https://blueprints.launchpad.net/neutron/+spec/port-forwarding
Port forwarding is a common feature in networking and more specifically in PaaS and SaaS cloud systems which aim at reusing the same public IP for different clients that use different VMs for their services.
This is especially relevant for deployments which lack a large number of public IPs they can assign.
Common use case for this feature is a client requesting a specific service, where the serving platform (PaaS, SaaS) allocates a VM to run the service and then allocates a client port to access this service. This means that various clients use the same public IP, but the TCP/UDP destination port is used to distinguish between the end point VMs.
Example, a mapping for web servers:
- client1 172.24.4.2:4001 TCP => maps to 10.0.0.2 port 80 TCP (VM1)
- client2 172.24.4.2:4002 TCP => maps to 10.0.0.3 port 80 TCP (VM2)
This spec will focus on port forwarding based on Floating IPs. A future spec will be submitted for port forwarding based on routers external gateway interface.
Problem Description
- In environments constrained with limited IPs, operators would like to reuse public IPs instead of assigning to each VM its own public IP (Floating IP).
- Docker supports a port-mapping feature and hence a big eco-system of automation orchestration and management plugins leverage it. We would like to make Neutron compatible for these tools and systems and provide a similar API1.
Proposed Change
Introduce port forwarding API and implementation to Floating IPs.
The user can define various port forwarding rules on the Floating IPs containing the internal/client port and the external/destination port, connected with the VM they want to expose. And users would be allowed to create port forwarding rules only on a "free" Floating IP, i.e. a Floating IP which is not directly associated with a Fixed IP of a tenant's VM.
We will have four deployment variants:
a) Legacy Router: For the generic Router deployment, the port forwarding rules would be installed in the Router namespace on the network node and forwarding functions would be performed.
b) HA Router: For the HA Router deployment, the port forwarding rule will be installed in both the ACTIVE and the BACKUP Router namespaces on the network nodes they are located on.
c) DVR: As2 has been merged, we now have the
ability to create centralized Floating IPs in a DVR supported
deployment. This helps in mapping the Compute nodes where the
destination VM is present with the centralized FIP for Port forwarding.
This mechanism will centralize a floating IP not only when it is
associated with a port bound to a host with the
dvr_no_external
option enabled, but also when port
forwarding attributes are added to it.
d) DVR + HA: If the created router is not an HA router, then we can proceed with option (c). While if the router is an HA router, then we will use the centralized HA router to install the port forwarding rules.
In all deployment variants, the port forwarding entry NATs a specific Floating IP:Port and protocol to a specific Neutron port (and a private IP that is attached to this port). That means it will maintain a mapping like "FIP:extport protocol" to "Neutron Port Fixed IP:intport protocol". So if a Neutron Port contains multiple Fixed IPs, then it will be allowed to create multiple port forwarding entries for a particular Neutron port, with different external ports, such as:
- FIPX:EXTPORTX PROTOCOLA => Neutron PortQ Fixed IPA:INTPORTA PROTOCOLA (VM1)
- FIPX:EXTPORTY PROTOCOLA => Neutron PortQ Fixed IPB:INTPORTA PROTOCOLA (VM1)
However, the same Fixed IP:intport socket cannot be mapped with different protocols.
If the Neutron port is deleted, the port forwarding entries that match this port are also deleted. Same is applicable in case the Floating IP is deleted.
Data Model Impact
The following new table is added as part of the port forwarding feature:
CREATE TABLE port_forwarding (
id CHAR(36) NOT NULL PRI KEY,
floating_ip_id VARCHAR(36) NOT NULL,
external_port INT NOT NULL,
internal_neutron_port_id VARCHAR(36) NOT NULL FOREIGN KEY,
protocol CHAR(4) NOT NULL,
socket VARCHAR(20) NOT NULL,
CONSTRAINT floating_ip_id_external_port_constraint UNIQUE (floating_ip_id, external_port),
CONSTRAINT internal_neutron_port_id_socket_constraint UNIQUE (
internal_neutron_port_id, socket)
);
The socket
column will store the string like 'Fixed
IP:Port'.
Note
This table lacks project_id
, as the owner of this
port_forwarding
must be the owner of associated Floating
IP. So there is a project_id check for preventing association of
Floating IP to internal Neutron Port if their project_id
are different. Also, allow the association that Floating IP/internal
Neutron Port exists on a shared network for admin users in different
project_id cases, such as FloatingIP from a shared public network
created by a admin user and a Neutron Port from a particular internal
tenant network created by the tenant user, then admin user want a
association of them which have different project_ids. For general users,
there is only the same project_id case.
Sub Resource Extension
Neutron floatingips
will be extended with a sub resource
port_forwarding
, it will contain some fields to expose the
port_forwarding
assigned to the floatingip
resource.
For this new feature, a new service plugin will be introduced, and the following methods will be added:
- 'create_floatingip_port_forwarding()'
- 'delete_floatingip_port_forwarding()'
- 'get_floatingip_port_forwarding()'
- 'get_floatingip_port_forwardings()'
For update operation, we will extend the function in the future if possible. But for now, we will just support create/delete/get functions.
So the attributes map of new sub resource would be like:
= {
SUB_RESOURCE_ATTRIBUTE_MAP 'port_forwarding': {
'parent': {'collection_name': 'floatingips',
'member_name': 'floatingip'},
'parameters': {
'external_port': {'allow_post': True, 'allow_put': False,
'convert_to':
convert_validate_port_value,'is_visible': True},
'internal_port': {'allow_post': True, 'allow_put': False,
'convert_to':
convert_validate_port_value,'is_visible': True},
'internal_ip_address': {'allow_post': True,
'allow_put': False,
'validate': {
'type:ip_address_or_none': None},
'is_visible': True},
'protocol': {'allow_post': True, 'allow_put': False,
'validate': {
'type:values': constants.IPTABLES_PROTOCOL_MAP.keys()},
'is_visible': True,
'convert_to': converters.convert_to_protocol},
'internal_port_id': {'allow_post': True,
'allow_put': False,
'validate': {'type:int':None},
'is_visible': True},
}
} }
REST API Impact
The idea is to extend the Floating IP Rest API with a new extension
floating_ip_port_forwarding
with the below defined
attributes.
Attribute Name | Type | CRUD | Default Value | Description |
port_forwardings | List | R | None | The associated 'port-forwarding' sub resource with the particular Floating IP resource. |
The Floating IP extension definition would be expanded as :
= {
RESOURCE_ATTRIBUTE_MAP 'floatingips': {
'port_forwardings': {'allow_post': False,
'allow_put': False,
'is_visible': True, 'default': None}
} }
This new field will be exposed in the response during GET/POST/PUT requests of Floating IP resource. That means users can not change the forwarding resources through CRU FloatingIP, only can create the forwardings one by one with the new port forwarding API which will be introduced below.
For example, GET a Floating IP:
GET /v2.0/floatingips/<floatingip-uuid>
{
"floatingip": {
"floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57",
"router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f",
"fixed_ip_address": "",
"floating_ip_address": "172.24.4.228",
"project_id": "4969c491a3c74ee4af974e6d800c62de",
"tenant_id": "4969c491a3c74ee4af974e6d800c62de",
"status": "ACTIVE",
"port_id": "",
"id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7",
"port_forwardings": [
{
"internal_ip_address": "10.0.0.3",
"protocol": "tcp",
"internal_port": "22",
"external_port": "7001"
},
{
"internal_ip_address": "192.168.4.32",
"protocol": "tcp",
"internal_port": "22",
"external_port": "7002"
}
]
}
}
For the new sub resource 'port_forwarding', a new url will be introduced:
- /v2.0/floatingips/<floatingip-uuid>/port_forwardings
List Port Forwardings
GET /v2.0/floatingips/<floatingip-uuid>/port_forwardings
{
"port_forwardings": [
{
"id": "ae34051f-aa6c-4c75-abf5-50dc9ac99ef3",
"external_port": "7003",
"internal_port": "22",
"internal_ip_address": "10.0.0.10",
"protocol": "tcp",
"internal_port_id": "b930d7f6-ceb7-40a0-8b81-a425dd994ccf"
},
{
"id": "915a14a6-867b-4af7-83d1-70efceb146f9",
"external_port": "7004",
"internal_port": "22",
"internal_ip_address": "10.0.0.11",
"protocol": "tcp",
"internal_port_id": "0c56df5d-ace5-46c8-8f4c-45fa4e334d18"
}
]
}
Parameter | Style | Type | Description |
---|---|---|---|
port_forwardings | plain | xsd:list | A list of port_forwarding objects |
More parameters see show_port_forwarding
Show Port Forwarding
GET /v2.0/floatingips/<floatingip-uuid>/port_forwardings/<port-forwarding-id>
{
"port_forwarding": {
"id": "ae34051f-aa6c-4c75-abf5-50dc9ac99ef3",
"external_port": "7003",
"internal_port": "22",
"internal_ip_address": "10.0.0.10",
"protocol": "tcp",
"internal_port_id": "b930d7f6-ceb7-40a0-8b81-a425dd994ccf"
}
}
Parameter | Style | Type | Description |
---|---|---|---|
port_forwarding | plain | xsd:dict | A port_forwarding object |
id | plain | xsd:string | The ID of port_forwarding object |
external_port | plain | xsd:string | The exposed external protocol port number |
internal_port | plain | xsd:string | The port forwarding mapped internal protocol port number |
internal_ip_address | plain | xsd:string | The IP Address from the fixed ips of a particular port |
protocol | plain | xsd:string | The traffic protocol type, such as TCP or UDP. Default value is 'TCP'. |
internal_port_id | plain | xsd:string | The Neutron internal Port ID. |
Create Port Forwarding
POST /v2.0/floatingips/<floatingip-uuid>/port_forwardings
Parameter | Style | Type | Description |
---|---|---|---|
port_forwarding | plain | xsd:dict | A port_forwarding Object |
external_port (mandatory) | plain | xsd:string | The exposed external protocol port number |
internal_port (mandatory) | plain | xsd:string | The port forwarding mapped internal protocol port number |
protocol (optional, default = 'TCP') | plain | xsd:string | The traffic protocol type, such as TCP or UDP. Default value is 'TCP'. |
internal_port_id (mandatory) | plain | xsd:string | The Neutron internal Port ID. |
internal_ip_address (optional, default = The first fixed ip address of port) | plain | xsd:string | The IP Address from the fixed ips of a particular port |
{
"port_forwarding": {
"external_port": "7233",
"internal_port": "22",
"internal_port_id": "b930d7f6-ceb7-40a0-8b81-a425dd994ccf"
}
}
Response parameters
see show_port_forwarding
{
"port_forwarding": {
"id": "f8a44de0-fc8e-45df-93c7-f79bf3b01c95",
"external_port": "7233",
"internal_port": "22",
"internal_ip_address": "192.168.43.33",
"protocol": "tcp",
"internal_port_id": "b930d7f6-ceb7-40a0-8b81-a425dd994ccf"
}
}
Delete Port Forwarding
DELETE /v2.0/floatingips/<floatingip-uuid>/port_forwardings/<port-forwarding-id>
This operation does not accept a request body and does not return a response body.
Effects on Existing Floating IP APIs
Slight adjustment to existing Floating IP APIs:
- Create a Floating IP just the same with current behavior.
- Associate a Neutron internal port with a Floating IP, if the requested url is the same as previous, the Floating IP will work as 1:1 DNAT like current behavior. If the request with the new url, that means the request needs a port-forwarding function towards the Floating IP.
- Get a Floating IP resource will be the same as before if the
Floating IP resource had already associated with a neutron port for 1:1
DNAT. If a Floating IP resource contains more than 1 port-forwarding sub
resource, it is better to show the
port-forwardings
summary in the Floating IP response body to distinguish which Floating IP resource is available for different requirements, such as 1:1 DNAT, port-forwarding.
Command Line Client Impact
Openstack Client would have additional options
portforwarding
for Floating IP CLI, which would define the
port-forwarding characteristics.
Security Impact
Port forwarding is similar in nature to centralized DNAT, so should not pose additional security implications. But if users actually want to use Port forwarding, they must make sure to allow the associated ingress Security Group towards the internal ip which is used by the neutron port of their VMs.
Notifications Impact
Depends on the implementation spec
Other End User Impact
None
Performance Impact
Performance testing must be conducted to see what is the overhead of enabling this feature, of course that if the feature is disabled no performance impact should be noticed.
IPv6 Impact
IPv6 is not supported
Other Deployer Impact
Deployer will be able to leverage port forwarding for a unique way to reach a private VM/Container without wasting new public IPs
Developer Impact
Future SNAT distribution plans should take port forwarding into consideration. Kuryr can leverage port forwarding for feature compatibility with Docker port mapping.
Alternatives
Users can use an external VM that provide this NAT capability or assign a new Floating IP for each VM.
Implementation
Assignee(s)
- Primary assignees:
-
reedip <reedip.banerjee@nectechnologies.in>
- Other contributors:
-
gal-sagie <gal.sagie@gmail.com>
tian-mingming <tian.mingming@h3c.com>
zhaobo <zhaobo6@huawei.com>
Work Items
- API Implementation
- DB Implementation
- Reference implementation
- Tests
- Documentation
Dependencies
None
Testing
Tempest Tests
Need to add tempest tests
Functional Tests
Need to add functional tests
API Tests
Need to add API tests
Fullstack Tests
Need to add Fullstack tests.
Documentation Impact
User Documentation
Needs user documentation
Developer Documentation
Needs devref documentation