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 API [1].
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: As [2] 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
{
"port_forwarding": {
"external_port": "7233",
"internal_port": "22",
"internal_port_id": "b930d7f6-ceb7-40a0-8b81-a425dd994ccf"
}
}
Response parameters
{
"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