When the type of NAT is dnat, the externally visible IP address
and port number can be DNATted to the IP address and port number
in the logical space. Adds a protocol column for the NAT. Valid
protocols are tcp or udp. This column is useful when a port
number is provided as part of the external_ip column and the type
is set as dnat. If this column is empty and a port number is
provided as part of external_ip column when the type is set as dnat,
OVN assumes the protocol to be tcp.
For example :
ovn-nbctl -- --id=@nat create nat type="dnat" protocol="tcp" \
logical_ip="192.168.1.2\:8080" external_ip="30.0.0.2\:80" \
-- add logical_router R1 nat @nat
This patch improves the "tests/test-l7.py" which can support the
"--port" argument. This patch also provides the unit tests and
the documentation.
Signed-off-by: nickcooper-zhangtonghao <[email protected]>
---
include/ovn/actions.h | 1 +
ovn/lib/actions.c | 13 +++-
ovn/northd/ovn-northd.8.xml | 10 +++
ovn/northd/ovn-northd.c | 149 ++++++++++++++++++++++++++++++++++----------
ovn/ovn-nb.ovsschema | 8 ++-
ovn/ovn-nb.xml | 22 +++++--
tests/system-ovn.at | 113 +++++++++++++++++++++++++++++++++
tests/test-l7.py | 13 +++-
8 files changed, 287 insertions(+), 42 deletions(-)
diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index 0bf6145..ce8aca5 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -170,6 +170,7 @@ struct ovnact_ct_commit {
struct ovnact_ct_nat {
struct ovnact ovnact;
ovs_be32 ip;
+ uint16_t port;
uint8_t ltable; /* Logical table ID of next table. */
};
diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c
index df526c0..90c19f3 100644
--- a/ovn/lib/actions.c
+++ b/ovn/lib/actions.c
@@ -697,13 +697,21 @@ parse_ct_nat(struct action_context *ctx, const char *name,
if (lexer_match(ctx->lexer, LEX_T_LPAREN)) {
if (ctx->lexer->token.type != LEX_T_INTEGER
- || ctx->lexer->token.format != LEX_F_IPV4) {
+ || mf_subvalue_width(&ctx->lexer->token.value) > 32) {
lexer_syntax_error(ctx->lexer, "expecting IPv4 address");
return;
}
cn->ip = ctx->lexer->token.value.ipv4;
lexer_get(ctx->lexer);
+ /* Parse optional port. */
+ uint16_t port = 0;
+ if (lexer_match(ctx->lexer, LEX_T_COLON)
+ && !action_parse_port(ctx, &port)) {
+ return;
+ }
+ cn->port = port;
+
if (!lexer_force_match(ctx->lexer, LEX_T_RPAREN)) {
return;
}
@@ -779,6 +787,9 @@ encode_ct_nat(const struct ovnact_ct_nat *cn,
if (snat) {
nat->flags |= NX_NAT_F_SRC;
} else {
+ if (cn->port) {
+ nat->range.proto.min = cn->port;
+ }
nat->flags |= NX_NAT_F_DST;
}
}
diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
index df53d4c..f141462 100644
--- a/ovn/northd/ovn-northd.8.xml
+++ b/ovn/northd/ovn-northd.8.xml
@@ -1191,6 +1191,16 @@ icmp4 {
<li>
For each configuration in the OVN Northbound database, that asks
+ to change the destination IP address <var>A</var> with a port
+ <var>P1</var> of protocol <var>P</var> to IP address <var>B</var>
+ and port <var>P2</var>, a priority-100 flow matches <code>ip &&
+ ip4.dst == <var>A</var> && <var>P</var> &&
+ <var>P</var>.dst == <var>P1</var></code> with an action
+ <code>flags.loopback = 1; ct_dnat(<var>B</var>:<var>P2</var>);</code>.
+ </li>
+
+ <li>
+ For each configuration in the OVN Northbound database, that asks
to change the destination IP address of a packet from <var>A</var> to
<var>B</var>, a priority-100 flow matches <code>ip &&
ip4.dst == <var>A</var></code> with an action
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index ad0739c..03bd9e4 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -2252,7 +2252,7 @@ build_pre_acls(struct ovn_datapath *od, struct hmap
*lflows)
* 'ip_address'. The caller must free() the memory allocated for
* 'ip_address'. */
static void
-ip_address_and_port_from_lb_key(const char *key, char **ip_address,
+ip_address_and_port(const char *key, char **ip_address,
uint16_t *port)
{
char *ip_str, *start, *next;
@@ -2262,8 +2262,6 @@ ip_address_and_port_from_lb_key(const char *key, char
**ip_address,
next = start = xstrdup(key);
ip_str = strsep(&next, ":");
if (!ip_str || !ip_str[0]) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad ip address for load balancer key %s", key);
free(start);
return;
}
@@ -2271,8 +2269,6 @@ ip_address_and_port_from_lb_key(const char *key, char
**ip_address,
ovs_be32 ip, mask;
char *error = ip_parse_masked(ip_str, &ip, &mask);
if (error || mask != OVS_BE32_MAX) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad ip address for load balancer key %s", key);
free(start);
free(error);
return;
@@ -2281,8 +2277,6 @@ ip_address_and_port_from_lb_key(const char *key, char
**ip_address,
int l4_port = 0;
if (next && next[0]) {
if (!str_to_int(next, 0, &l4_port) || l4_port < 0 || l4_port > 65535) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad ip port for load balancer key %s", key);
free(start);
return;
}
@@ -2313,8 +2307,11 @@ build_pre_lb(struct ovn_datapath *od, struct hmap
*lflows)
/* node->key contains IP:port or just IP. */
char *ip_address = NULL;
uint16_t port;
- ip_address_and_port_from_lb_key(node->key, &ip_address, &port);
+ ip_address_and_port(node->key, &ip_address, &port);
if (!ip_address) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+ VLOG_WARN_RL(&rl, "bad ip address or port for load balancer "
+ "key %s", node->key);
continue;
}
@@ -2719,8 +2716,11 @@ build_stateful(struct ovn_datapath *od, struct hmap
*lflows)
/* node->key contains IP:port or just IP. */
char *ip_address = NULL;
- ip_address_and_port_from_lb_key(node->key, &ip_address, &port);
+ ip_address_and_port(node->key, &ip_address, &port);
if (!ip_address) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+ VLOG_WARN_RL(&rl, "bad ip address or port for load balancer "
+ "key %s", node->key);
continue;
}
@@ -3613,8 +3613,12 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap
*ports,
char *ip_address = NULL;
uint16_t port;
- ip_address_and_port_from_lb_key(node->key, &ip_address, &port);
+ ip_address_and_port(node->key, &ip_address, &port);
if (!ip_address) {
+ static struct vlog_rate_limit rl =
+ VLOG_RATE_LIMIT_INIT(5, 1);
+ VLOG_WARN_RL(&rl, "bad ip address or port for load "
+ "balancer key %s", node->key);
continue;
}
@@ -3669,10 +3673,25 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap
*ports,
ovs_be32 ip;
if (!ip_parse(nat->external_ip, &ip) || !ip) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad ip address %s in nat configuration "
- "for router %s", nat->external_ip, op->key);
- continue;
+ bool is_external_port = false;
+ if (!strcmp(nat->type, "dnat")) {
+ ovs_be16 port;
+ char *error = ip_parse_port(nat->external_ip, &ip, &port);
+ if (error) {
+ free(error);
+ } else {
+ is_external_port = true;
+ }
+ }
+
+ if (!is_external_port) {
+ static struct vlog_rate_limit rl =
+ VLOG_RATE_LIMIT_INIT(5, 1);
+ VLOG_WARN_RL(&rl, "bad external ip address %s in nat "
+ "configuration for router %s",
+ nat->external_ip, op->key);
+ continue;
+ }
}
if (!strcmp(nat->type, "snat")) {
@@ -3845,8 +3864,12 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap
*ports,
/* node->key contains IP:port or just IP. */
char *ip_address = NULL;
- ip_address_and_port_from_lb_key(node->key, &ip_address, &port);
+ ip_address_and_port(node->key, &ip_address, &port);
if (!ip_address) {
+ static struct vlog_rate_limit rl =
+ VLOG_RATE_LIMIT_INIT(5, 1);
+ VLOG_WARN_RL(&rl, "bad ip address or port for load "
+ "balancer key %s", node->key);
continue;
}
@@ -3905,37 +3928,78 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap
*ports,
nat = od->nbr->nat[i];
- ovs_be32 ip, mask;
-
- char *error = ip_parse_masked(nat->external_ip, &ip, &mask);
- if (error || mask != OVS_BE32_MAX) {
+ uint16_t external_port = 0;
+ char *external_ip = NULL;
+ ip_address_and_port(nat->external_ip,
+ &external_ip, &external_port);
+ if (!external_ip) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
VLOG_WARN_RL(&rl, "bad external ip %s for nat",
nat->external_ip);
- free(error);
+ continue;
+ }
+
+ if (strcmp(nat->type, "dnat") && external_port) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+ VLOG_WARN_RL(&rl, "the external ip (%s) with port should not "
+ "be configured for snat or dnat_and_snat",
+ nat->external_ip);
+ free(external_ip);
continue;
}
/* Check the validity of nat->logical_ip. 'logical_ip' can
* be a subnet when the type is "snat". */
- error = ip_parse_masked(nat->logical_ip, &ip, &mask);
- if (!strcmp(nat->type, "snat")) {
- if (error) {
+ uint16_t logical_port = 0;
+ char *error, *logical_ip = NULL;
+ ovs_be32 ip, mask = OVS_BE32_MAX;
+ ip_address_and_port(nat->logical_ip,
+ &logical_ip, &logical_port);
+ if (!logical_ip) {
+ if (!strcmp(nat->type, "snat")) {
+ error = ip_parse_masked(nat->logical_ip, &ip, &mask);
+ if (error) {
+ static struct vlog_rate_limit rl =
+ VLOG_RATE_LIMIT_INIT(5, 1);
+ VLOG_WARN_RL(&rl, "bad logical ip network or ip %s "
+ "for snat in router "UUID_FMT"",
+ nat->logical_ip, UUID_ARGS(&od->key));
+ free(external_ip);
+ free(error);
+ continue;
+ }
+ } else {
static struct vlog_rate_limit rl =
VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad ip network or ip %s for snat "
- "in router "UUID_FMT"",
+ VLOG_WARN_RL(&rl, "bad logical ip %s for dnat or "
+ "dnat_and_snat in router "UUID_FMT"",
nat->logical_ip, UUID_ARGS(&od->key));
- free(error);
+ free(external_ip);
+ continue;
+ }
+ }
+
+ if (!strcmp(nat->type, "dnat")) {
+ if ((external_port != 0 && logical_port == 0)
+ || (external_port == 0 && logical_port != 0)) {
+ static struct vlog_rate_limit rl =
+ VLOG_RATE_LIMIT_INIT(5, 1);
+ VLOG_WARN_RL(&rl, "the external/logical ip (%s, %s) with "
+ "port should be configured at same time for "
+ "dnat", nat->external_ip, nat->logical_ip);
+ free(external_ip);
+ free(logical_ip);
continue;
}
} else {
- if (error || mask != OVS_BE32_MAX) {
+ if (logical_port) {
static struct vlog_rate_limit rl =
VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad ip %s for dnat in router "
- ""UUID_FMT"", nat->logical_ip, UUID_ARGS(&od->key));
- free(error);
+ VLOG_WARN_RL(&rl, "the logical ip (%s) with port should "
+ "not be configured for snat or dnat_and_snat",
+ nat->logical_ip);
+ free(external_ip);
+ free(logical_ip);
continue;
}
}
@@ -3966,10 +4030,27 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap
*ports,
* We need to zero the inport because the router can
* send the packet back through the same interface. */
ds_clear(&match);
- ds_put_format(&match, "ip && ip4.dst == %s", nat->external_ip);
ds_clear(&actions);
- ds_put_format(&actions,"flags.loopback = 1; ct_dnat(%s);",
- nat->logical_ip);
+ if (external_port && logical_port) {
+ /* The external_port and logical port should be configured
+ * only for DNAT.*/
+ char *protocol = "tcp";
+ if (nat->protocol && !strcmp(nat->protocol, "udp")) {
+ protocol = "udp";
+ }
+ ds_put_format(&match, "ip && ip4.dst == %s "
+ "&& %s && %s.dst == %d",
+ external_ip, protocol,
+ protocol, external_port);
+ ds_put_format(&actions,"flags.loopback = 1; "
+ "ct_dnat(%s:%d);",
+ logical_ip, logical_port);
+ } else {
+ ds_put_format(&match, "ip && ip4.dst == %s",
+ external_ip);
+ ds_put_format(&actions,"flags.loopback = 1; ct_dnat(%s);",
+ logical_ip);
+ }
ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 100,
ds_cstr(&match), ds_cstr(&actions));
}
@@ -3991,6 +4072,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap
*ports,
count_1bits(ntohl(mask)) + 1,
ds_cstr(&match), ds_cstr(&actions));
}
+ free(external_ip);
+ free(logical_ip);
}
/* Re-circulate every packet through the DNAT zone.
diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema
index 865dd34..a290c67 100644
--- a/ovn/ovn-nb.ovsschema
+++ b/ovn/ovn-nb.ovsschema
@@ -1,7 +1,7 @@
{
"name": "OVN_Northbound",
- "version": "5.4.0",
- "cksum": "4176761817 11225",
+ "version": "5.4.1",
+ "cksum": "944843976 11424",
"tables": {
"NB_Global": {
"columns": {
@@ -203,6 +203,10 @@
"columns": {
"external_ip": {"type": "string"},
"logical_ip": {"type": "string"},
+ "protocol": {
+ "type": {"key": {"type": "string",
+ "enum": ["set", ["tcp", "udp"]]},
+ "min": 0, "max": 1}},
"type": {"type": {"key": {"type": "string",
"enum": ["set", ["dnat",
"snat",
diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml
index e1b3136..ec13278 100644
--- a/ovn/ovn-nb.xml
+++ b/ovn/ovn-nb.xml
@@ -1116,8 +1116,10 @@
<ul>
<li>
When <ref column="type"/> is <code>dnat</code>, the externally
- visible IP address <ref column="external_ip"/> is DNATted to the IP
- address <ref column="logical_ip"/> in the logical space.
+ visible IP address (and an optional port number with <code>:</code>
+ as a separator) <ref column="external_ip"/> is DNATted to the IP
+ address (and an optional port number with <code>:</code> as a
+ separator) <ref column="logical_ip"/> in the logical space.
</li>
<li>
When <ref column="type"/> is <code>snat</code>, IP packets
@@ -1138,11 +1140,23 @@
</column>
<column name="external_ip">
- An IPv4 address.
+ An IPv4 address (and an optional port number with <code>:</code>
+ as a separator).
</column>
<column name="logical_ip">
- An IPv4 network (e.g 192.168.1.0/24) or an IPv4 address.
+ An IPv4 network (e.g 192.168.1.0/24) or an IPv4 address (and an optional
+ port number with <code>:</code> as a separator).
+ </column>
+
+ <column name="protocol">
+ Valid protocols are <code>tcp</code> or <code>udp</code>. This column
+ is useful when a port number is provided as part of the
+ <code>external_ip</code> column and the <code>type</code> is set
+ as <code>dnat</code>. If this column is empty and a port number
+ is provided as part of <code>external_ip</code> column when
+ the <code>type</code> is set as <code>dnat</code>, OVN assumes the
+ <code>protocol</code> to be <code>tcp</code>.
</column>
</table>
diff --git a/tests/system-ovn.at b/tests/system-ovn.at
index 21226d9..ba69df6 100644
--- a/tests/system-ovn.at
+++ b/tests/system-ovn.at
@@ -279,6 +279,119 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port
patch-.*/d
/connection dropped.*/d"])
AT_CLEANUP
+AT_SETUP([ovn -- 2 LRs connected via LS, gateway router, DNAT with port])
+AT_KEYWORDS([ovnnat])
+
+CHECK_CONNTRACK()
+CHECK_CONNTRACK_NAT()
+ovn_start
+OVS_TRAFFIC_VSWITCHD_START()
+ADD_BR([br-int])
+
+# Set external-ids in br-int needed for ovn-controller
+ovs-vsctl \
+ -- set Open_vSwitch . external-ids:system-id=hv1 \
+ -- set Open_vSwitch .
external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
+ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
+ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
+ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
+
+# Start ovn-controller
+start_daemon ovn-controller
+
+# Logical network:
+# Two LRs - R1 and R2 that are connected to each other via LS "join"
+# in 20.0.0.0/24 network. R1 has switchess foo (192.168.1.0/24) connected
+# to it. R2 has alice (172.16.1.0/24) connected to it.
+# R2 is a gateway router on which we add NAT rules.
+#
+# foo -- R1 -- join - R2 -- alice
+
+ovn-nbctl lr-add R1
+ovn-nbctl lr-add R2 -- set Logical_Router R2 options:chassis=hv1
+
+ovn-nbctl ls-add foo
+ovn-nbctl ls-add alice
+ovn-nbctl ls-add join
+
+ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24
+ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 172.16.1.1/24
+ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 20.0.0.1/24
+ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 20.0.0.2/24
+
+# Connect foo to R1
+ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \
+ type=router options:router-port=foo addresses=\"00:00:01:01:02:03\"
+
+# Connect alice to R2
+ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
+ type=router options:router-port=alice addresses=\"00:00:02:01:02:03\"
+
+# Connect R1 to join
+ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \
+ type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"'
+
+# Connect R2 to join
+ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \
+ type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"'
+
+# Static routes.
+ovn-nbctl lr-route-add R1 172.16.1.0/24 20.0.0.2
+ovn-nbctl lr-route-add R2 192.168.0.0/16 20.0.0.1
+
+# Logical port 'foo1' in switch 'foo'.
+ADD_NAMESPACES(foo1)
+ADD_VETH(foo1, foo1, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \
+ "192.168.1.1")
+ovn-nbctl lsp-add foo foo1 \
+-- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
+
+# Logical port 'alice1' in switch 'alice'.
+ADD_NAMESPACES(alice1)
+ADD_VETH(alice1, alice1, br-int, "172.16.1.2/24", "f0:00:00:01:02:04", \
+ "172.16.1.1")
+ovn-nbctl lsp-add alice alice1 \
+-- lsp-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.2"
+
+# Add a DNAT with port rule
+ovn-nbctl -- --id=@nat create nat type="dnat" protocol="tcp"
logical_ip="192.168.1.2\:8080" \
+ external_ip="30.0.0.2\:80" -- add logical_router R2 nat @nat
+ovn-nbctl -- --id=@nat create nat type="dnat" protocol="udp"
logical_ip="192.168.1.2\:8181" \
+ external_ip="30.0.0.3\:81" -- add logical_router R2 nat @nat
+
+OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep ct\( | grep nat])
+
+# South-North DNAT
+NETNS_DAEMONIZE([foo1], [[$PYTHON $srcdir/test-l7.py --port 8080]],
[http0.pid])
+NS_CHECK_EXEC([alice1], [wget 30.0.0.2 -t 3 -T 1 --retry-connrefused -v -o
wget0.log])
+AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br-int ovs-alice1 normal
'000002010203F0000001020408004500001C000100004011AFBBAC1001021E0000030035005100083443'])
+
+# We verify that DNAT indeed happened via 'dump-conntrack' command.
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.2) | \
+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
+tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
+])
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.3) | \
+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
+udp,orig=(src=172.16.1.2,dst=30.0.0.3,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>
+])
+
+OVS_APP_EXIT_AND_WAIT([ovn-controller])
+
+as ovn-sb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as ovn-nb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as northd
+OVS_APP_EXIT_AND_WAIT([ovn-northd])
+
+as
+OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
+/connection dropped.*/d"])
+AT_CLEANUP
+
AT_SETUP([ovn -- load-balancing])
AT_KEYWORDS([ovnlb])
diff --git a/tests/test-l7.py b/tests/test-l7.py
index aed34f4..c6aae4e 100755
--- a/tests/test-l7.py
+++ b/tests/test-l7.py
@@ -61,8 +61,14 @@ def main():
protocols = [srv for srv in SERVERS]
parser = argparse.ArgumentParser(
description='Run basic application servers.')
- parser.add_argument('proto', default='http', nargs='?',
- help='protocol to serve (%s)' % protocols)
+ parser.add_argument('proto',
+ default='http',
+ nargs='?',
+ help='protocol to serve (%s)' % protocols)
+ parser.add_argument('-o','--port',
+ type=int,
+ action='store',
+ help='Port to connect on')
args = parser.parse_args()
if args.proto not in SERVERS:
@@ -72,6 +78,9 @@ def main():
constructor = SERVERS[args.proto][0]
handler = SERVERS[args.proto][1]
port = SERVERS[args.proto][2]
+ if args.port:
+ port = args.port
+
srv = constructor(('', port), handler)
srv.serve_forever()
--
1.8.3.1
_______________________________________________
dev mailing list
[email protected]
http://openvswitch.org/mailman/listinfo/dev