mirror of
https://github.com/openvswitch/ovs
synced 2025-09-03 07:45:30 +00:00
conntrack: Add support for NAT.
Extend OVS conntrack interface to cover NAT. New nested NAT action may be included with a CT action. A bare NAT action only mangles existing connections. If a NAT action with src or dst range attribute is included, new (non-committed) connections are mangled according to the NAT attributes. This work extends on a branch by Thomas Graf at https://github.com/tgraf/ovs/tree/nat. Signed-off-by: Jarno Rajahalme <jarno@ovn.org> Acked-by: Ben Pfaff <blp@ovn.org>
This commit is contained in:
@@ -478,6 +478,12 @@ struct ovs_key_ct_labels {
|
|||||||
#define OVS_CS_F_REPLY_DIR 0x08 /* Flow is in the reply direction. */
|
#define OVS_CS_F_REPLY_DIR 0x08 /* Flow is in the reply direction. */
|
||||||
#define OVS_CS_F_INVALID 0x10 /* Could not track connection. */
|
#define OVS_CS_F_INVALID 0x10 /* Could not track connection. */
|
||||||
#define OVS_CS_F_TRACKED 0x20 /* Conntrack has occurred. */
|
#define OVS_CS_F_TRACKED 0x20 /* Conntrack has occurred. */
|
||||||
|
#define OVS_CS_F_SRC_NAT 0x40 /* Packet's source address/port was
|
||||||
|
mangled by NAT. */
|
||||||
|
#define OVS_CS_F_DST_NAT 0x80 /* Packet's destination address/port
|
||||||
|
was mangled by NAT. */
|
||||||
|
|
||||||
|
#define OVS_CS_F_NAT_MASK (OVS_CS_F_SRC_NAT | OVS_CS_F_DST_NAT)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* enum ovs_flow_attr - attributes for %OVS_FLOW_* commands.
|
* enum ovs_flow_attr - attributes for %OVS_FLOW_* commands.
|
||||||
@@ -686,11 +692,49 @@ enum ovs_ct_attr {
|
|||||||
OVS_CT_ATTR_LABELS, /* label to associate with this connection. */
|
OVS_CT_ATTR_LABELS, /* label to associate with this connection. */
|
||||||
OVS_CT_ATTR_HELPER, /* netlink helper to assist detection of
|
OVS_CT_ATTR_HELPER, /* netlink helper to assist detection of
|
||||||
related connections. */
|
related connections. */
|
||||||
|
OVS_CT_ATTR_NAT, /* Nested OVS_NAT_ATTR_* */
|
||||||
__OVS_CT_ATTR_MAX
|
__OVS_CT_ATTR_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
#define OVS_CT_ATTR_MAX (__OVS_CT_ATTR_MAX - 1)
|
#define OVS_CT_ATTR_MAX (__OVS_CT_ATTR_MAX - 1)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum ovs_nat_attr - Attributes for %OVS_CT_ATTR_NAT.
|
||||||
|
*
|
||||||
|
* @OVS_NAT_ATTR_SRC: Flag for Source NAT (mangle source address/port).
|
||||||
|
* @OVS_NAT_ATTR_DST: Flag for Destination NAT (mangle destination
|
||||||
|
* address/port). Only one of (@OVS_NAT_ATTR_SRC, @OVS_NAT_ATTR_DST) may be
|
||||||
|
* specified. Effective only for packets for ct_state NEW connections.
|
||||||
|
* Committed connections are mangled by the NAT action according to the
|
||||||
|
* committed NAT type regardless of the flags specified. As a corollary, a NAT
|
||||||
|
* action without a NAT type flag will only mangle packets of committed
|
||||||
|
* connections. The following NAT attributes only apply for NEW connections,
|
||||||
|
* and they may be included only when the CT action has the @OVS_CT_ATTR_COMMIT
|
||||||
|
* flag and either @OVS_NAT_ATTR_SRC, @OVS_NAT_ATTR_DST is also included.
|
||||||
|
* @OVS_NAT_ATTR_IP_MIN: struct in_addr or struct in6_addr
|
||||||
|
* @OVS_NAT_ATTR_IP_MAX: struct in_addr or struct in6_addr
|
||||||
|
* @OVS_NAT_ATTR_PROTO_MIN: u16 L4 protocol specific lower boundary (port)
|
||||||
|
* @OVS_NAT_ATTR_PROTO_MAX: u16 L4 protocol specific upper boundary (port)
|
||||||
|
* @OVS_NAT_ATTR_PERSISTENT: Flag for persistent IP mapping across reboots
|
||||||
|
* @OVS_NAT_ATTR_PROTO_HASH: Flag for pseudo random L4 port mapping (MD5)
|
||||||
|
* @OVS_NAT_ATTR_PROTO_RANDOM: Flag for fully randomized L4 port mapping
|
||||||
|
*/
|
||||||
|
enum ovs_nat_attr {
|
||||||
|
OVS_NAT_ATTR_UNSPEC,
|
||||||
|
OVS_NAT_ATTR_SRC,
|
||||||
|
OVS_NAT_ATTR_DST,
|
||||||
|
OVS_NAT_ATTR_IP_MIN,
|
||||||
|
OVS_NAT_ATTR_IP_MAX,
|
||||||
|
OVS_NAT_ATTR_PROTO_MIN,
|
||||||
|
OVS_NAT_ATTR_PROTO_MAX,
|
||||||
|
OVS_NAT_ATTR_PERSISTENT,
|
||||||
|
OVS_NAT_ATTR_PROTO_HASH,
|
||||||
|
OVS_NAT_ATTR_PROTO_RANDOM,
|
||||||
|
__OVS_NAT_ATTR_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define OVS_NAT_ATTR_MAX (__OVS_NAT_ATTR_MAX - 1)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* enum ovs_action_attr - Action types.
|
* enum ovs_action_attr - Action types.
|
||||||
*
|
*
|
||||||
|
@@ -872,6 +872,10 @@ const char *ct_state_to_string(uint32_t state)
|
|||||||
return "rel";
|
return "rel";
|
||||||
case CS_INVALID:
|
case CS_INVALID:
|
||||||
return "inv";
|
return "inv";
|
||||||
|
case CS_SRC_NAT:
|
||||||
|
return "snat";
|
||||||
|
case CS_DST_NAT:
|
||||||
|
return "dnat";
|
||||||
default:
|
default:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
366
lib/odp-util.c
366
lib/odp-util.c
@@ -542,6 +542,150 @@ format_odp_tnl_push_action(struct ds *ds, const struct nlattr *attr)
|
|||||||
ds_put_format(ds, ",out_port(%"PRIu32"))", data->out_port);
|
ds_put_format(ds, ",out_port(%"PRIu32"))", data->out_port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct nl_policy ovs_nat_policy[] = {
|
||||||
|
[OVS_NAT_ATTR_SRC] = { .type = NL_A_FLAG, .optional = true, },
|
||||||
|
[OVS_NAT_ATTR_DST] = { .type = NL_A_FLAG, .optional = true, },
|
||||||
|
[OVS_NAT_ATTR_IP_MIN] = { .type = NL_A_UNSPEC, .optional = true,
|
||||||
|
.min_len = sizeof(struct in_addr),
|
||||||
|
.max_len = sizeof(struct in6_addr)},
|
||||||
|
[OVS_NAT_ATTR_IP_MAX] = { .type = NL_A_UNSPEC, .optional = true,
|
||||||
|
.min_len = sizeof(struct in_addr),
|
||||||
|
.max_len = sizeof(struct in6_addr)},
|
||||||
|
[OVS_NAT_ATTR_PROTO_MIN] = { .type = NL_A_U16, .optional = true, },
|
||||||
|
[OVS_NAT_ATTR_PROTO_MAX] = { .type = NL_A_U16, .optional = true, },
|
||||||
|
[OVS_NAT_ATTR_PERSISTENT] = { .type = NL_A_FLAG, .optional = true, },
|
||||||
|
[OVS_NAT_ATTR_PROTO_HASH] = { .type = NL_A_FLAG, .optional = true, },
|
||||||
|
[OVS_NAT_ATTR_PROTO_RANDOM] = { .type = NL_A_FLAG, .optional = true, },
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
format_odp_ct_nat(struct ds *ds, const struct nlattr *attr)
|
||||||
|
{
|
||||||
|
struct nlattr *a[ARRAY_SIZE(ovs_nat_policy)];
|
||||||
|
size_t addr_len;
|
||||||
|
ovs_be32 ip_min, ip_max;
|
||||||
|
struct in6_addr ip6_min, ip6_max;
|
||||||
|
uint16_t proto_min, proto_max;
|
||||||
|
|
||||||
|
if (!nl_parse_nested(attr, ovs_nat_policy, a, ARRAY_SIZE(a))) {
|
||||||
|
ds_put_cstr(ds, "nat(error: nl_parse_nested() failed.)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* If no type, then nothing else either. */
|
||||||
|
if (!(a[OVS_NAT_ATTR_SRC] || a[OVS_NAT_ATTR_DST])
|
||||||
|
&& (a[OVS_NAT_ATTR_IP_MIN] || a[OVS_NAT_ATTR_IP_MAX]
|
||||||
|
|| a[OVS_NAT_ATTR_PROTO_MIN] || a[OVS_NAT_ATTR_PROTO_MAX]
|
||||||
|
|| a[OVS_NAT_ATTR_PERSISTENT] || a[OVS_NAT_ATTR_PROTO_HASH]
|
||||||
|
|| a[OVS_NAT_ATTR_PROTO_RANDOM])) {
|
||||||
|
ds_put_cstr(ds, "nat(error: options allowed only with \"src\" or \"dst\")");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Both SNAT & DNAT may not be specified. */
|
||||||
|
if (a[OVS_NAT_ATTR_SRC] && a[OVS_NAT_ATTR_DST]) {
|
||||||
|
ds_put_cstr(ds, "nat(error: Only one of \"src\" or \"dst\" may be present.)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* proto may not appear without ip. */
|
||||||
|
if (!a[OVS_NAT_ATTR_IP_MIN] && a[OVS_NAT_ATTR_PROTO_MIN]) {
|
||||||
|
ds_put_cstr(ds, "nat(error: proto but no IP.)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* MAX may not appear without MIN. */
|
||||||
|
if ((!a[OVS_NAT_ATTR_IP_MIN] && a[OVS_NAT_ATTR_IP_MAX])
|
||||||
|
|| (!a[OVS_NAT_ATTR_PROTO_MIN] && a[OVS_NAT_ATTR_PROTO_MAX])) {
|
||||||
|
ds_put_cstr(ds, "nat(error: range max without min.)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Address sizes must match. */
|
||||||
|
if ((a[OVS_NAT_ATTR_IP_MIN]
|
||||||
|
&& (nl_attr_get_size(a[OVS_NAT_ATTR_IP_MIN]) != sizeof(ovs_be32) &&
|
||||||
|
nl_attr_get_size(a[OVS_NAT_ATTR_IP_MIN]) != sizeof(struct in6_addr)))
|
||||||
|
|| (a[OVS_NAT_ATTR_IP_MIN] && a[OVS_NAT_ATTR_IP_MAX]
|
||||||
|
&& (nl_attr_get_size(a[OVS_NAT_ATTR_IP_MIN])
|
||||||
|
!= nl_attr_get_size(a[OVS_NAT_ATTR_IP_MAX])))) {
|
||||||
|
ds_put_cstr(ds, "nat(error: IP address sizes do not match)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr_len = a[OVS_NAT_ATTR_IP_MIN]
|
||||||
|
? nl_attr_get_size(a[OVS_NAT_ATTR_IP_MIN]) : 0;
|
||||||
|
ip_min = addr_len == sizeof(ovs_be32) && a[OVS_NAT_ATTR_IP_MIN]
|
||||||
|
? nl_attr_get_be32(a[OVS_NAT_ATTR_IP_MIN]) : 0;
|
||||||
|
ip_max = addr_len == sizeof(ovs_be32) && a[OVS_NAT_ATTR_IP_MAX]
|
||||||
|
? nl_attr_get_be32(a[OVS_NAT_ATTR_IP_MAX]) : 0;
|
||||||
|
if (addr_len == sizeof ip6_min) {
|
||||||
|
ip6_min = a[OVS_NAT_ATTR_IP_MIN]
|
||||||
|
? *(struct in6_addr *)nl_attr_get(a[OVS_NAT_ATTR_IP_MIN])
|
||||||
|
: in6addr_any;
|
||||||
|
ip6_max = a[OVS_NAT_ATTR_IP_MAX]
|
||||||
|
? *(struct in6_addr *)nl_attr_get(a[OVS_NAT_ATTR_IP_MAX])
|
||||||
|
: in6addr_any;
|
||||||
|
}
|
||||||
|
proto_min = a[OVS_NAT_ATTR_PROTO_MIN]
|
||||||
|
? nl_attr_get_u16(a[OVS_NAT_ATTR_PROTO_MIN]) : 0;
|
||||||
|
proto_max = a[OVS_NAT_ATTR_PROTO_MAX]
|
||||||
|
? nl_attr_get_u16(a[OVS_NAT_ATTR_PROTO_MAX]) : 0;
|
||||||
|
|
||||||
|
if ((addr_len == sizeof(ovs_be32)
|
||||||
|
&& ip_max && ntohl(ip_min) > ntohl(ip_max))
|
||||||
|
|| (addr_len == sizeof(struct in6_addr)
|
||||||
|
&& !ipv6_mask_is_any(&ip6_max)
|
||||||
|
&& memcmp(&ip6_min, &ip6_max, sizeof ip6_min) > 0)
|
||||||
|
|| (proto_max && proto_min > proto_max)) {
|
||||||
|
ds_put_cstr(ds, "nat(range error)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ds_put_cstr(ds, "nat");
|
||||||
|
if (a[OVS_NAT_ATTR_SRC] || a[OVS_NAT_ATTR_DST]) {
|
||||||
|
ds_put_char(ds, '(');
|
||||||
|
if (a[OVS_NAT_ATTR_SRC]) {
|
||||||
|
ds_put_cstr(ds, "src");
|
||||||
|
} else if (a[OVS_NAT_ATTR_DST]) {
|
||||||
|
ds_put_cstr(ds, "dst");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr_len > 0) {
|
||||||
|
ds_put_cstr(ds, "=");
|
||||||
|
|
||||||
|
if (addr_len == sizeof ip_min) {
|
||||||
|
ds_put_format(ds, IP_FMT, IP_ARGS(ip_min));
|
||||||
|
|
||||||
|
if (ip_max && ip_max != ip_min) {
|
||||||
|
ds_put_format(ds, "-"IP_FMT, IP_ARGS(ip_max));
|
||||||
|
}
|
||||||
|
} else if (addr_len == sizeof ip6_min) {
|
||||||
|
ipv6_format_addr_bracket(&ip6_min, ds, proto_min);
|
||||||
|
|
||||||
|
if (!ipv6_mask_is_any(&ip6_max) &&
|
||||||
|
memcmp(&ip6_max, &ip6_min, sizeof ip6_max) != 0) {
|
||||||
|
ds_put_char(ds, '-');
|
||||||
|
ipv6_format_addr_bracket(&ip6_max, ds, proto_min);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (proto_min) {
|
||||||
|
ds_put_format(ds, ":%"PRIu16, proto_min);
|
||||||
|
|
||||||
|
if (proto_max && proto_max != proto_min) {
|
||||||
|
ds_put_format(ds, "-%"PRIu16, proto_max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ds_put_char(ds, ',');
|
||||||
|
if (a[OVS_NAT_ATTR_PERSISTENT]) {
|
||||||
|
ds_put_cstr(ds, "persistent,");
|
||||||
|
}
|
||||||
|
if (a[OVS_NAT_ATTR_PROTO_HASH]) {
|
||||||
|
ds_put_cstr(ds, "hash,");
|
||||||
|
}
|
||||||
|
if (a[OVS_NAT_ATTR_PROTO_RANDOM]) {
|
||||||
|
ds_put_cstr(ds, "random,");
|
||||||
|
}
|
||||||
|
ds_chomp(ds, ',');
|
||||||
|
ds_put_char(ds, ')');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static const struct nl_policy ovs_conntrack_policy[] = {
|
static const struct nl_policy ovs_conntrack_policy[] = {
|
||||||
[OVS_CT_ATTR_COMMIT] = { .type = NL_A_FLAG, .optional = true, },
|
[OVS_CT_ATTR_COMMIT] = { .type = NL_A_FLAG, .optional = true, },
|
||||||
[OVS_CT_ATTR_ZONE] = { .type = NL_A_U16, .optional = true, },
|
[OVS_CT_ATTR_ZONE] = { .type = NL_A_U16, .optional = true, },
|
||||||
@@ -551,6 +695,7 @@ static const struct nl_policy ovs_conntrack_policy[] = {
|
|||||||
.min_len = sizeof(struct ovs_key_ct_labels) * 2 },
|
.min_len = sizeof(struct ovs_key_ct_labels) * 2 },
|
||||||
[OVS_CT_ATTR_HELPER] = { .type = NL_A_STRING, .optional = true,
|
[OVS_CT_ATTR_HELPER] = { .type = NL_A_STRING, .optional = true,
|
||||||
.min_len = 1, .max_len = 16 },
|
.min_len = 1, .max_len = 16 },
|
||||||
|
[OVS_CT_ATTR_NAT] = { .type = NL_A_UNSPEC, .optional = true },
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -562,6 +707,7 @@ format_odp_conntrack_action(struct ds *ds, const struct nlattr *attr)
|
|||||||
const char *helper;
|
const char *helper;
|
||||||
uint16_t zone;
|
uint16_t zone;
|
||||||
bool commit;
|
bool commit;
|
||||||
|
const struct nlattr *nat;
|
||||||
|
|
||||||
if (!nl_parse_nested(attr, ovs_conntrack_policy, a, ARRAY_SIZE(a))) {
|
if (!nl_parse_nested(attr, ovs_conntrack_policy, a, ARRAY_SIZE(a))) {
|
||||||
ds_put_cstr(ds, "ct(error)");
|
ds_put_cstr(ds, "ct(error)");
|
||||||
@@ -573,9 +719,10 @@ format_odp_conntrack_action(struct ds *ds, const struct nlattr *attr)
|
|||||||
mark = a[OVS_CT_ATTR_MARK] ? nl_attr_get(a[OVS_CT_ATTR_MARK]) : NULL;
|
mark = a[OVS_CT_ATTR_MARK] ? nl_attr_get(a[OVS_CT_ATTR_MARK]) : NULL;
|
||||||
label = a[OVS_CT_ATTR_LABELS] ? nl_attr_get(a[OVS_CT_ATTR_LABELS]): NULL;
|
label = a[OVS_CT_ATTR_LABELS] ? nl_attr_get(a[OVS_CT_ATTR_LABELS]): NULL;
|
||||||
helper = a[OVS_CT_ATTR_HELPER] ? nl_attr_get(a[OVS_CT_ATTR_HELPER]) : NULL;
|
helper = a[OVS_CT_ATTR_HELPER] ? nl_attr_get(a[OVS_CT_ATTR_HELPER]) : NULL;
|
||||||
|
nat = a[OVS_CT_ATTR_NAT];
|
||||||
|
|
||||||
ds_put_format(ds, "ct");
|
ds_put_format(ds, "ct");
|
||||||
if (commit || zone || mark || label || helper) {
|
if (commit || zone || mark || label || helper || nat) {
|
||||||
ds_put_cstr(ds, "(");
|
ds_put_cstr(ds, "(");
|
||||||
if (commit) {
|
if (commit) {
|
||||||
ds_put_format(ds, "commit,");
|
ds_put_format(ds, "commit,");
|
||||||
@@ -595,6 +742,9 @@ format_odp_conntrack_action(struct ds *ds, const struct nlattr *attr)
|
|||||||
if (helper) {
|
if (helper) {
|
||||||
ds_put_format(ds, "helper=%s,", helper);
|
ds_put_format(ds, "helper=%s,", helper);
|
||||||
}
|
}
|
||||||
|
if (nat) {
|
||||||
|
format_odp_ct_nat(ds, nat);
|
||||||
|
}
|
||||||
ds_chomp(ds, ',');
|
ds_chomp(ds, ',');
|
||||||
ds_put_cstr(ds, ")");
|
ds_put_cstr(ds, ")");
|
||||||
}
|
}
|
||||||
@@ -876,15 +1026,15 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
|
|||||||
l4 = ((uint8_t *) l3 + sizeof (struct ip_header));
|
l4 = ((uint8_t *) l3 + sizeof (struct ip_header));
|
||||||
ip = (struct ip_header *) l3;
|
ip = (struct ip_header *) l3;
|
||||||
if (!ovs_scan_len(s, &n, "header(size=%"SCNi32",type=%"SCNi32","
|
if (!ovs_scan_len(s, &n, "header(size=%"SCNi32",type=%"SCNi32","
|
||||||
"eth(dst="ETH_ADDR_SCAN_FMT",",
|
"eth(dst="ETH_ADDR_SCAN_FMT",",
|
||||||
&data->header_len,
|
&data->header_len,
|
||||||
&data->tnl_type,
|
&data->tnl_type,
|
||||||
ETH_ADDR_SCAN_ARGS(eth->eth_dst))) {
|
ETH_ADDR_SCAN_ARGS(eth->eth_dst))) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ovs_scan_len(s, &n, "src="ETH_ADDR_SCAN_FMT",",
|
if (!ovs_scan_len(s, &n, "src="ETH_ADDR_SCAN_FMT",",
|
||||||
ETH_ADDR_SCAN_ARGS(eth->eth_src))) {
|
ETH_ADDR_SCAN_ARGS(eth->eth_src))) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
if (!ovs_scan_len(s, &n, "dl_type=0x%"SCNx16"),", &dl_type)) {
|
if (!ovs_scan_len(s, &n, "dl_type=0x%"SCNx16"),", &dl_type)) {
|
||||||
@@ -894,11 +1044,11 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
|
|||||||
|
|
||||||
/* IPv4 */
|
/* IPv4 */
|
||||||
if (!ovs_scan_len(s, &n, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT",proto=%"SCNi8
|
if (!ovs_scan_len(s, &n, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT",proto=%"SCNi8
|
||||||
",tos=%"SCNi8",ttl=%"SCNi8",frag=0x%"SCNx16"),",
|
",tos=%"SCNi8",ttl=%"SCNi8",frag=0x%"SCNx16"),",
|
||||||
IP_SCAN_ARGS(&sip),
|
IP_SCAN_ARGS(&sip),
|
||||||
IP_SCAN_ARGS(&dip),
|
IP_SCAN_ARGS(&dip),
|
||||||
&ip->ip_proto, &ip->ip_tos,
|
&ip->ip_proto, &ip->ip_tos,
|
||||||
&ip->ip_ttl, &ip->ip_frag_off)) {
|
&ip->ip_ttl, &ip->ip_frag_off)) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
put_16aligned_be32(&ip->ip_src, sip);
|
put_16aligned_be32(&ip->ip_src, sip);
|
||||||
@@ -908,7 +1058,7 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
|
|||||||
udp = (struct udp_header *) l4;
|
udp = (struct udp_header *) l4;
|
||||||
greh = (struct gre_base_hdr *) l4;
|
greh = (struct gre_base_hdr *) l4;
|
||||||
if (ovs_scan_len(s, &n, "udp(src=%"SCNi16",dst=%"SCNi16",csum=0x%"SCNx16"),",
|
if (ovs_scan_len(s, &n, "udp(src=%"SCNi16",dst=%"SCNi16",csum=0x%"SCNx16"),",
|
||||||
&udp_src, &udp_dst, &csum)) {
|
&udp_src, &udp_dst, &csum)) {
|
||||||
uint32_t vx_flags, vni;
|
uint32_t vx_flags, vni;
|
||||||
|
|
||||||
udp->udp_src = htons(udp_src);
|
udp->udp_src = htons(udp_src);
|
||||||
@@ -917,7 +1067,7 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
|
|||||||
udp->udp_csum = htons(csum);
|
udp->udp_csum = htons(csum);
|
||||||
|
|
||||||
if (ovs_scan_len(s, &n, "vxlan(flags=0x%"SCNx32",vni=0x%"SCNx32"))",
|
if (ovs_scan_len(s, &n, "vxlan(flags=0x%"SCNx32",vni=0x%"SCNx32"))",
|
||||||
&vx_flags, &vni)) {
|
&vx_flags, &vni)) {
|
||||||
struct vxlanhdr *vxh = (struct vxlanhdr *) (udp + 1);
|
struct vxlanhdr *vxh = (struct vxlanhdr *) (udp + 1);
|
||||||
|
|
||||||
put_16aligned_be32(&vxh->vx_flags, htonl(vx_flags));
|
put_16aligned_be32(&vxh->vx_flags, htonl(vx_flags));
|
||||||
@@ -968,7 +1118,7 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
} else if (ovs_scan_len(s, &n, "gre((flags=0x%"SCNx16",proto=0x%"SCNx16")",
|
} else if (ovs_scan_len(s, &n, "gre((flags=0x%"SCNx16",proto=0x%"SCNx16")",
|
||||||
&gre_flags, &gre_proto)){
|
&gre_flags, &gre_proto)){
|
||||||
|
|
||||||
tnl_type = OVS_VPORT_TYPE_GRE;
|
tnl_type = OVS_VPORT_TYPE_GRE;
|
||||||
greh->flags = htons(gre_flags);
|
greh->flags = htons(gre_flags);
|
||||||
@@ -1030,6 +1180,176 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ct_nat_params {
|
||||||
|
bool snat;
|
||||||
|
bool dnat;
|
||||||
|
size_t addr_len;
|
||||||
|
union {
|
||||||
|
ovs_be32 ip;
|
||||||
|
struct in6_addr ip6;
|
||||||
|
} addr_min;
|
||||||
|
union {
|
||||||
|
ovs_be32 ip;
|
||||||
|
struct in6_addr ip6;
|
||||||
|
} addr_max;
|
||||||
|
uint16_t proto_min;
|
||||||
|
uint16_t proto_max;
|
||||||
|
bool persistent;
|
||||||
|
bool proto_hash;
|
||||||
|
bool proto_random;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
scan_ct_nat_range(const char *s, int *n, struct ct_nat_params *p)
|
||||||
|
{
|
||||||
|
if (ovs_scan_len(s, n, "=")) {
|
||||||
|
char ipv6_s[IPV6_SCAN_LEN + 1];
|
||||||
|
struct in6_addr ipv6;
|
||||||
|
|
||||||
|
if (ovs_scan_len(s, n, IP_SCAN_FMT, IP_SCAN_ARGS(&p->addr_min.ip))) {
|
||||||
|
p->addr_len = sizeof p->addr_min.ip;
|
||||||
|
if (ovs_scan_len(s, n, "-")) {
|
||||||
|
if (!ovs_scan_len(s, n, IP_SCAN_FMT,
|
||||||
|
IP_SCAN_ARGS(&p->addr_max.ip))) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ((ovs_scan_len(s, n, IPV6_SCAN_FMT, ipv6_s)
|
||||||
|
|| ovs_scan_len(s, n, "["IPV6_SCAN_FMT"]", ipv6_s))
|
||||||
|
&& inet_pton(AF_INET6, ipv6_s, &ipv6) == 1) {
|
||||||
|
p->addr_len = sizeof p->addr_min.ip6;
|
||||||
|
p->addr_min.ip6 = ipv6;
|
||||||
|
if (ovs_scan_len(s, n, "-")) {
|
||||||
|
if ((ovs_scan_len(s, n, IPV6_SCAN_FMT, ipv6_s)
|
||||||
|
|| ovs_scan_len(s, n, "["IPV6_SCAN_FMT"]", ipv6_s))
|
||||||
|
&& inet_pton(AF_INET6, ipv6_s, &ipv6) == 1) {
|
||||||
|
p->addr_max.ip6 = ipv6;
|
||||||
|
} else {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (ovs_scan_len(s, n, ":%"SCNu16, &p->proto_min)) {
|
||||||
|
if (ovs_scan_len(s, n, "-")) {
|
||||||
|
if (!ovs_scan_len(s, n, "%"SCNu16, &p->proto_max)) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
scan_ct_nat(const char *s, struct ct_nat_params *p)
|
||||||
|
{
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
if (ovs_scan_len(s, &n, "nat")) {
|
||||||
|
memset(p, 0, sizeof *p);
|
||||||
|
|
||||||
|
if (ovs_scan_len(s, &n, "(")) {
|
||||||
|
char *end;
|
||||||
|
int end_n;
|
||||||
|
|
||||||
|
end = strchr(s + n, ')');
|
||||||
|
if (!end) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
end_n = end - s;
|
||||||
|
|
||||||
|
while (n < end_n) {
|
||||||
|
n += strspn(s + n, delimiters);
|
||||||
|
if (ovs_scan_len(s, &n, "src")) {
|
||||||
|
int err = scan_ct_nat_range(s, &n, p);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
p->snat = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ovs_scan_len(s, &n, "dst")) {
|
||||||
|
int err = scan_ct_nat_range(s, &n, p);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
p->dnat = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ovs_scan_len(s, &n, "persistent")) {
|
||||||
|
p->persistent = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ovs_scan_len(s, &n, "hash")) {
|
||||||
|
p->proto_hash = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ovs_scan_len(s, &n, "random")) {
|
||||||
|
p->proto_random = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p->snat && p->dnat) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if ((p->addr_len != 0 &&
|
||||||
|
memcmp(&p->addr_max, &in6addr_any, p->addr_len) &&
|
||||||
|
memcmp(&p->addr_max, &p->addr_min, p->addr_len) < 0) ||
|
||||||
|
(p->proto_max && p->proto_max < p->proto_min)) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (p->proto_hash && p->proto_random) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nl_msg_put_ct_nat(struct ct_nat_params *p, struct ofpbuf *actions)
|
||||||
|
{
|
||||||
|
size_t start = nl_msg_start_nested(actions, OVS_CT_ATTR_NAT);
|
||||||
|
|
||||||
|
if (p->snat) {
|
||||||
|
nl_msg_put_flag(actions, OVS_NAT_ATTR_SRC);
|
||||||
|
} else if (p->dnat) {
|
||||||
|
nl_msg_put_flag(actions, OVS_NAT_ATTR_DST);
|
||||||
|
} else {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (p->addr_len != 0) {
|
||||||
|
nl_msg_put_unspec(actions, OVS_NAT_ATTR_IP_MIN, &p->addr_min,
|
||||||
|
p->addr_len);
|
||||||
|
if (memcmp(&p->addr_max, &p->addr_min, p->addr_len) > 0) {
|
||||||
|
nl_msg_put_unspec(actions, OVS_NAT_ATTR_IP_MAX, &p->addr_max,
|
||||||
|
p->addr_len);
|
||||||
|
}
|
||||||
|
if (p->proto_min) {
|
||||||
|
nl_msg_put_u16(actions, OVS_NAT_ATTR_PROTO_MIN, p->proto_min);
|
||||||
|
if (p->proto_max && p->proto_max > p->proto_min) {
|
||||||
|
nl_msg_put_u16(actions, OVS_NAT_ATTR_PROTO_MAX, p->proto_max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (p->persistent) {
|
||||||
|
nl_msg_put_flag(actions, OVS_NAT_ATTR_PERSISTENT);
|
||||||
|
}
|
||||||
|
if (p->proto_hash) {
|
||||||
|
nl_msg_put_flag(actions, OVS_NAT_ATTR_PROTO_HASH);
|
||||||
|
}
|
||||||
|
if (p->proto_random) {
|
||||||
|
nl_msg_put_flag(actions, OVS_NAT_ATTR_PROTO_RANDOM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
nl_msg_end_nested(actions, start);
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
parse_conntrack_action(const char *s_, struct ofpbuf *actions)
|
parse_conntrack_action(const char *s_, struct ofpbuf *actions)
|
||||||
{
|
{
|
||||||
@@ -1048,6 +1368,8 @@ parse_conntrack_action(const char *s_, struct ofpbuf *actions)
|
|||||||
ovs_u128 value;
|
ovs_u128 value;
|
||||||
ovs_u128 mask;
|
ovs_u128 mask;
|
||||||
} ct_label;
|
} ct_label;
|
||||||
|
struct ct_nat_params nat_params;
|
||||||
|
bool have_nat = false;
|
||||||
size_t start;
|
size_t start;
|
||||||
char *end;
|
char *end;
|
||||||
|
|
||||||
@@ -1056,13 +1378,14 @@ parse_conntrack_action(const char *s_, struct ofpbuf *actions)
|
|||||||
s += 2;
|
s += 2;
|
||||||
if (ovs_scan(s, "(")) {
|
if (ovs_scan(s, "(")) {
|
||||||
s++;
|
s++;
|
||||||
|
find_end:
|
||||||
end = strchr(s, ')');
|
end = strchr(s, ')');
|
||||||
if (!end) {
|
if (!end) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (s != end) {
|
while (s != end) {
|
||||||
int n = -1;
|
int n;
|
||||||
|
|
||||||
s += strspn(s, delimiters);
|
s += strspn(s, delimiters);
|
||||||
if (ovs_scan(s, "commit%n", &n)) {
|
if (ovs_scan(s, "commit%n", &n)) {
|
||||||
@@ -1106,6 +1429,16 @@ parse_conntrack_action(const char *s_, struct ofpbuf *actions)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
n = scan_ct_nat(s, &nat_params);
|
||||||
|
if (n > 0) {
|
||||||
|
s += n;
|
||||||
|
have_nat = true;
|
||||||
|
|
||||||
|
/* end points to the end of the nested, nat action.
|
||||||
|
* find the real end. */
|
||||||
|
goto find_end;
|
||||||
|
}
|
||||||
|
/* Nothing matched. */
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
s++;
|
s++;
|
||||||
@@ -1130,6 +1463,9 @@ parse_conntrack_action(const char *s_, struct ofpbuf *actions)
|
|||||||
nl_msg_put_string__(actions, OVS_CT_ATTR_HELPER, helper,
|
nl_msg_put_string__(actions, OVS_CT_ATTR_HELPER, helper,
|
||||||
helper_len);
|
helper_len);
|
||||||
}
|
}
|
||||||
|
if (have_nat) {
|
||||||
|
nl_msg_put_ct_nat(&nat_params, actions);
|
||||||
|
}
|
||||||
nl_msg_end_nested(actions, start);
|
nl_msg_end_nested(actions, start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -28,6 +28,7 @@
|
|||||||
#include "meta-flow.h"
|
#include "meta-flow.h"
|
||||||
#include "multipath.h"
|
#include "multipath.h"
|
||||||
#include "nx-match.h"
|
#include "nx-match.h"
|
||||||
|
#include "odp-netlink.h"
|
||||||
#include "ofp-parse.h"
|
#include "ofp-parse.h"
|
||||||
#include "ofp-util.h"
|
#include "ofp-util.h"
|
||||||
#include "ofpbuf.h"
|
#include "ofpbuf.h"
|
||||||
@@ -291,6 +292,9 @@ enum ofp_raw_action_type {
|
|||||||
/* NX1.0+(35): struct nx_action_conntrack, ... */
|
/* NX1.0+(35): struct nx_action_conntrack, ... */
|
||||||
NXAST_RAW_CT,
|
NXAST_RAW_CT,
|
||||||
|
|
||||||
|
/* NX1.0+(36): struct nx_action_nat, ... */
|
||||||
|
NXAST_RAW_NAT,
|
||||||
|
|
||||||
/* ## ------------------ ## */
|
/* ## ------------------ ## */
|
||||||
/* ## Debugging actions. ## */
|
/* ## Debugging actions. ## */
|
||||||
/* ## ------------------ ## */
|
/* ## ------------------ ## */
|
||||||
@@ -4320,21 +4324,12 @@ encode_NOTE(const struct ofpact_note *note,
|
|||||||
{
|
{
|
||||||
size_t start_ofs = out->size;
|
size_t start_ofs = out->size;
|
||||||
struct nx_action_note *nan;
|
struct nx_action_note *nan;
|
||||||
unsigned int remainder;
|
|
||||||
unsigned int len;
|
|
||||||
|
|
||||||
put_NXAST_NOTE(out);
|
put_NXAST_NOTE(out);
|
||||||
out->size = out->size - sizeof nan->note;
|
out->size = out->size - sizeof nan->note;
|
||||||
|
|
||||||
ofpbuf_put(out, note->data, note->length);
|
ofpbuf_put(out, note->data, note->length);
|
||||||
|
pad_ofpat(out, start_ofs);
|
||||||
len = out->size - start_ofs;
|
|
||||||
remainder = len % OFP_ACTION_ALIGN;
|
|
||||||
if (remainder) {
|
|
||||||
ofpbuf_put_zeros(out, OFP_ACTION_ALIGN - remainder);
|
|
||||||
}
|
|
||||||
nan = ofpbuf_at(out, start_ofs, sizeof *nan);
|
|
||||||
nan->len = htons(out->size - start_ofs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static char * OVS_WARN_UNUSED_RESULT
|
static char * OVS_WARN_UNUSED_RESULT
|
||||||
@@ -4777,9 +4772,18 @@ decode_NXAST_RAW_CT(const struct nx_action_conntrack *nac,
|
|||||||
|
|
||||||
if (conntrack->ofpact.len > sizeof(*conntrack)
|
if (conntrack->ofpact.len > sizeof(*conntrack)
|
||||||
&& !(conntrack->flags & NX_CT_F_COMMIT)) {
|
&& !(conntrack->flags & NX_CT_F_COMMIT)) {
|
||||||
VLOG_WARN_RL(&rl, "CT action requires commit flag if actions are "
|
const struct ofpact *a;
|
||||||
"specified.");
|
size_t ofpacts_len = conntrack->ofpact.len - sizeof(*conntrack);
|
||||||
error = OFPERR_OFPBAC_BAD_ARGUMENT;
|
|
||||||
|
OFPACT_FOR_EACH (a, conntrack->actions, ofpacts_len) {
|
||||||
|
if (a->type != OFPACT_NAT || ofpact_get_NAT(a)->flags
|
||||||
|
|| ofpact_get_NAT(a)->range_af != AF_UNSPEC) {
|
||||||
|
VLOG_WARN_RL(&rl, "CT action requires commit flag if actions "
|
||||||
|
"other than NAT without arguments are specified.");
|
||||||
|
error = OFPERR_OFPBAC_BAD_ARGUMENT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
@@ -4816,6 +4820,9 @@ encode_CT(const struct ofpact_conntrack *conntrack,
|
|||||||
nac->len = htons(len);
|
nac->len = htons(len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char * OVS_WARN_UNUSED_RESULT parse_NAT(char *arg, struct ofpbuf *,
|
||||||
|
enum ofputil_protocol * OVS_UNUSED);
|
||||||
|
|
||||||
/* Parses 'arg' as the argument to a "ct" action, and appends such an
|
/* Parses 'arg' as the argument to a "ct" action, and appends such an
|
||||||
* action to 'ofpacts'.
|
* action to 'ofpacts'.
|
||||||
*
|
*
|
||||||
@@ -4853,12 +4860,20 @@ parse_CT(char *arg, struct ofpbuf *ofpacts,
|
|||||||
}
|
}
|
||||||
} else if (!strcmp(key, "alg")) {
|
} else if (!strcmp(key, "alg")) {
|
||||||
error = str_to_connhelper(value, &oc->alg);
|
error = str_to_connhelper(value, &oc->alg);
|
||||||
|
} else if (!strcmp(key, "nat")) {
|
||||||
|
const size_t nat_offset = ofpacts_pull(ofpacts);
|
||||||
|
|
||||||
|
error = parse_NAT(value, ofpacts, usable_protocols);
|
||||||
|
ofpact_pad(ofpacts);
|
||||||
|
/* Update CT action pointer and length. */
|
||||||
|
ofpacts->header = ofpbuf_push_uninit(ofpacts, nat_offset);
|
||||||
|
oc = ofpacts->header;
|
||||||
} else if (!strcmp(key, "exec")) {
|
} else if (!strcmp(key, "exec")) {
|
||||||
/* Hide existing actions from ofpacts_parse_copy(), so the
|
/* Hide existing actions from ofpacts_parse_copy(), so the
|
||||||
* nesting can be handled transparently. */
|
* nesting can be handled transparently. */
|
||||||
enum ofputil_protocol usable_protocols2;
|
enum ofputil_protocol usable_protocols2;
|
||||||
|
const size_t exec_offset = ofpacts_pull(ofpacts);
|
||||||
|
|
||||||
ofpbuf_pull(ofpacts, sizeof(*oc));
|
|
||||||
/* Initializes 'usable_protocol2', fold it back to
|
/* Initializes 'usable_protocol2', fold it back to
|
||||||
* '*usable_protocols' afterwards, so that we do not lose
|
* '*usable_protocols' afterwards, so that we do not lose
|
||||||
* restrictions already in there. */
|
* restrictions already in there. */
|
||||||
@@ -4866,7 +4881,7 @@ parse_CT(char *arg, struct ofpbuf *ofpacts,
|
|||||||
false, OFPACT_CT);
|
false, OFPACT_CT);
|
||||||
*usable_protocols &= usable_protocols2;
|
*usable_protocols &= usable_protocols2;
|
||||||
ofpact_pad(ofpacts);
|
ofpact_pad(ofpacts);
|
||||||
ofpacts->header = ofpbuf_push_uninit(ofpacts, sizeof(*oc));
|
ofpacts->header = ofpbuf_push_uninit(ofpacts, exec_offset);
|
||||||
oc = ofpacts->header;
|
oc = ofpacts->header;
|
||||||
} else {
|
} else {
|
||||||
error = xasprintf("invalid argument to \"ct\" action: `%s'", key);
|
error = xasprintf("invalid argument to \"ct\" action: `%s'", key);
|
||||||
@@ -4891,6 +4906,8 @@ format_alg(int port, struct ds *s)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void format_NAT(const struct ofpact_nat *a, struct ds *ds);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
format_CT(const struct ofpact_conntrack *a, struct ds *s)
|
format_CT(const struct ofpact_conntrack *a, struct ds *s)
|
||||||
{
|
{
|
||||||
@@ -4908,15 +4925,365 @@ format_CT(const struct ofpact_conntrack *a, struct ds *s)
|
|||||||
} else if (a->zone_imm) {
|
} else if (a->zone_imm) {
|
||||||
ds_put_format(s, "zone=%"PRIu16",", a->zone_imm);
|
ds_put_format(s, "zone=%"PRIu16",", a->zone_imm);
|
||||||
}
|
}
|
||||||
if (ofpact_ct_get_action_len(a)) {
|
/* If the first action is a NAT action, format it outside of the 'exec'
|
||||||
|
* envelope. */
|
||||||
|
const struct ofpact *action = a->actions;
|
||||||
|
size_t actions_len = ofpact_ct_get_action_len(a);
|
||||||
|
if (actions_len && action->type == OFPACT_NAT) {
|
||||||
|
format_NAT(ofpact_get_NAT(action), s);
|
||||||
|
ds_put_char(s, ',');
|
||||||
|
actions_len -= OFPACT_ALIGN(action->len);
|
||||||
|
action = ofpact_next(action);
|
||||||
|
}
|
||||||
|
if (actions_len) {
|
||||||
ds_put_cstr(s, "exec(");
|
ds_put_cstr(s, "exec(");
|
||||||
ofpacts_format(a->actions, ofpact_ct_get_action_len(a), s);
|
ofpacts_format(action, actions_len, s);
|
||||||
ds_put_format(s, "),");
|
ds_put_cstr(s, "),");
|
||||||
}
|
}
|
||||||
format_alg(a->alg, s);
|
format_alg(a->alg, s);
|
||||||
ds_chomp(s, ',');
|
ds_chomp(s, ',');
|
||||||
ds_put_char(s, ')');
|
ds_put_char(s, ')');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* NAT action. */
|
||||||
|
|
||||||
|
/* Which optional fields are present? */
|
||||||
|
enum nx_nat_range {
|
||||||
|
NX_NAT_RANGE_IPV4_MIN = 1 << 0, /* ovs_be32 */
|
||||||
|
NX_NAT_RANGE_IPV4_MAX = 1 << 1, /* ovs_be32 */
|
||||||
|
NX_NAT_RANGE_IPV6_MIN = 1 << 2, /* struct in6_addr */
|
||||||
|
NX_NAT_RANGE_IPV6_MAX = 1 << 3, /* struct in6_addr */
|
||||||
|
NX_NAT_RANGE_PROTO_MIN = 1 << 4, /* ovs_be16 */
|
||||||
|
NX_NAT_RANGE_PROTO_MAX = 1 << 5, /* ovs_be16 */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Action structure for NXAST_NAT. */
|
||||||
|
struct nx_action_nat {
|
||||||
|
ovs_be16 type; /* OFPAT_VENDOR. */
|
||||||
|
ovs_be16 len; /* At least 16. */
|
||||||
|
ovs_be32 vendor; /* NX_VENDOR_ID. */
|
||||||
|
ovs_be16 subtype; /* NXAST_NAT. */
|
||||||
|
uint8_t pad[2]; /* Must be zero. */
|
||||||
|
ovs_be16 flags; /* Zero or more NX_NAT_F_* flags.
|
||||||
|
* Unspecified flag bits must be zero. */
|
||||||
|
ovs_be16 range_present; /* NX_NAT_RANGE_* */
|
||||||
|
/* Followed by optional parameters as specified by 'range_present' */
|
||||||
|
};
|
||||||
|
OFP_ASSERT(sizeof(struct nx_action_nat) == 16);
|
||||||
|
|
||||||
|
static void
|
||||||
|
encode_NAT(const struct ofpact_nat *nat,
|
||||||
|
enum ofp_version ofp_version OVS_UNUSED,
|
||||||
|
struct ofpbuf *out)
|
||||||
|
{
|
||||||
|
struct nx_action_nat *nan;
|
||||||
|
const size_t ofs = out->size;
|
||||||
|
uint16_t range_present = 0;
|
||||||
|
|
||||||
|
nan = put_NXAST_NAT(out);
|
||||||
|
nan->flags = htons(nat->flags);
|
||||||
|
if (nat->range_af == AF_INET) {
|
||||||
|
if (nat->range.addr.ipv4.min) {
|
||||||
|
ovs_be32 *min = ofpbuf_put_uninit(out, sizeof *min);
|
||||||
|
*min = nat->range.addr.ipv4.min;
|
||||||
|
range_present |= NX_NAT_RANGE_IPV4_MIN;
|
||||||
|
}
|
||||||
|
if (nat->range.addr.ipv4.max) {
|
||||||
|
ovs_be32 *max = ofpbuf_put_uninit(out, sizeof *max);
|
||||||
|
*max = nat->range.addr.ipv4.max;
|
||||||
|
range_present |= NX_NAT_RANGE_IPV4_MAX;
|
||||||
|
}
|
||||||
|
} else if (nat->range_af == AF_INET6) {
|
||||||
|
if (!ipv6_mask_is_any(&nat->range.addr.ipv6.min)) {
|
||||||
|
struct in6_addr *min = ofpbuf_put_uninit(out, sizeof *min);
|
||||||
|
*min = nat->range.addr.ipv6.min;
|
||||||
|
range_present |= NX_NAT_RANGE_IPV6_MIN;
|
||||||
|
}
|
||||||
|
if (!ipv6_mask_is_any(&nat->range.addr.ipv6.max)) {
|
||||||
|
struct in6_addr *max = ofpbuf_put_uninit(out, sizeof *max);
|
||||||
|
*max = nat->range.addr.ipv6.max;
|
||||||
|
range_present |= NX_NAT_RANGE_IPV6_MAX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nat->range_af != AF_UNSPEC) {
|
||||||
|
if (nat->range.proto.min) {
|
||||||
|
ovs_be16 *min = ofpbuf_put_uninit(out, sizeof *min);
|
||||||
|
*min = htons(nat->range.proto.min);
|
||||||
|
range_present |= NX_NAT_RANGE_PROTO_MIN;
|
||||||
|
}
|
||||||
|
if (nat->range.proto.max) {
|
||||||
|
ovs_be16 *max = ofpbuf_put_uninit(out, sizeof *max);
|
||||||
|
*max = htons(nat->range.proto.max);
|
||||||
|
range_present |= NX_NAT_RANGE_PROTO_MAX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pad_ofpat(out, ofs);
|
||||||
|
nan = ofpbuf_at(out, ofs, sizeof *nan);
|
||||||
|
nan->range_present = htons(range_present);
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum ofperr
|
||||||
|
decode_NXAST_RAW_NAT(const struct nx_action_nat *nan,
|
||||||
|
enum ofp_version ofp_version OVS_UNUSED,
|
||||||
|
struct ofpbuf *out)
|
||||||
|
{
|
||||||
|
struct ofpact_nat *nat;
|
||||||
|
uint16_t range_present = ntohs(nan->range_present);
|
||||||
|
const char *opts = (char *)(nan + 1);
|
||||||
|
uint16_t len = ntohs(nan->len) - sizeof *nan;
|
||||||
|
|
||||||
|
nat = ofpact_put_NAT(out);
|
||||||
|
nat->flags = ntohs(nan->flags);
|
||||||
|
|
||||||
|
#define NX_NAT_GET_OPT(DST, SRC, LEN, TYPE) \
|
||||||
|
(LEN >= sizeof(TYPE) \
|
||||||
|
? (memcpy(DST, SRC, sizeof(TYPE)), LEN -= sizeof(TYPE), \
|
||||||
|
SRC += sizeof(TYPE)) \
|
||||||
|
: NULL)
|
||||||
|
|
||||||
|
nat->range_af = AF_UNSPEC;
|
||||||
|
if (range_present & NX_NAT_RANGE_IPV4_MIN) {
|
||||||
|
if (range_present & (NX_NAT_RANGE_IPV6_MIN | NX_NAT_RANGE_IPV6_MAX)) {
|
||||||
|
return OFPERR_OFPBAC_BAD_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NX_NAT_GET_OPT(&nat->range.addr.ipv4.min, opts, len, ovs_be32)
|
||||||
|
|| !nat->range.addr.ipv4.min) {
|
||||||
|
return OFPERR_OFPBAC_BAD_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
nat->range_af = AF_INET;
|
||||||
|
|
||||||
|
if (range_present & NX_NAT_RANGE_IPV4_MAX) {
|
||||||
|
if (!NX_NAT_GET_OPT(&nat->range.addr.ipv4.max, opts, len,
|
||||||
|
ovs_be32)) {
|
||||||
|
return OFPERR_OFPBAC_BAD_ARGUMENT;
|
||||||
|
}
|
||||||
|
if (ntohl(nat->range.addr.ipv4.max)
|
||||||
|
< ntohl(nat->range.addr.ipv4.min)) {
|
||||||
|
return OFPERR_OFPBAC_BAD_ARGUMENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (range_present & NX_NAT_RANGE_IPV4_MAX) {
|
||||||
|
return OFPERR_OFPBAC_BAD_ARGUMENT;
|
||||||
|
} else if (range_present & NX_NAT_RANGE_IPV6_MIN) {
|
||||||
|
if (!NX_NAT_GET_OPT(&nat->range.addr.ipv6.min, opts, len,
|
||||||
|
struct in6_addr)
|
||||||
|
|| ipv6_mask_is_any(&nat->range.addr.ipv6.min)) {
|
||||||
|
return OFPERR_OFPBAC_BAD_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
nat->range_af = AF_INET6;
|
||||||
|
|
||||||
|
if (range_present & NX_NAT_RANGE_IPV6_MAX) {
|
||||||
|
if (!NX_NAT_GET_OPT(&nat->range.addr.ipv6.max, opts, len,
|
||||||
|
struct in6_addr)) {
|
||||||
|
return OFPERR_OFPBAC_BAD_ARGUMENT;
|
||||||
|
}
|
||||||
|
if (memcmp(&nat->range.addr.ipv6.max, &nat->range.addr.ipv6.min,
|
||||||
|
sizeof(struct in6_addr)) < 0) {
|
||||||
|
return OFPERR_OFPBAC_BAD_ARGUMENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (range_present & NX_NAT_RANGE_IPV6_MAX) {
|
||||||
|
return OFPERR_OFPBAC_BAD_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (range_present & NX_NAT_RANGE_PROTO_MIN) {
|
||||||
|
ovs_be16 proto;
|
||||||
|
|
||||||
|
if (nat->range_af == AF_UNSPEC) {
|
||||||
|
return OFPERR_OFPBAC_BAD_ARGUMENT;
|
||||||
|
}
|
||||||
|
if (!NX_NAT_GET_OPT(&proto, opts, len, ovs_be16) || proto == 0) {
|
||||||
|
return OFPERR_OFPBAC_BAD_ARGUMENT;
|
||||||
|
}
|
||||||
|
nat->range.proto.min = ntohs(proto);
|
||||||
|
if (range_present & NX_NAT_RANGE_PROTO_MAX) {
|
||||||
|
if (!NX_NAT_GET_OPT(&proto, opts, len, ovs_be16)) {
|
||||||
|
return OFPERR_OFPBAC_BAD_ARGUMENT;
|
||||||
|
}
|
||||||
|
nat->range.proto.max = ntohs(proto);
|
||||||
|
if (nat->range.proto.max < nat->range.proto.min) {
|
||||||
|
return OFPERR_OFPBAC_BAD_ARGUMENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (range_present & NX_NAT_RANGE_PROTO_MAX) {
|
||||||
|
return OFPERR_OFPBAC_BAD_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
format_NAT(const struct ofpact_nat *a, struct ds *ds)
|
||||||
|
{
|
||||||
|
ds_put_cstr(ds, "nat");
|
||||||
|
|
||||||
|
if (a->flags & (NX_NAT_F_SRC | NX_NAT_F_DST)) {
|
||||||
|
ds_put_char(ds, '(');
|
||||||
|
ds_put_cstr(ds, a->flags & NX_NAT_F_SRC ? "src" : "dst");
|
||||||
|
|
||||||
|
if (a->range_af != AF_UNSPEC) {
|
||||||
|
ds_put_cstr(ds, "=");
|
||||||
|
|
||||||
|
if (a->range_af == AF_INET) {
|
||||||
|
ds_put_format(ds, IP_FMT, IP_ARGS(a->range.addr.ipv4.min));
|
||||||
|
|
||||||
|
if (a->range.addr.ipv4.max
|
||||||
|
&& a->range.addr.ipv4.max != a->range.addr.ipv4.min) {
|
||||||
|
ds_put_format(ds, "-"IP_FMT,
|
||||||
|
IP_ARGS(a->range.addr.ipv4.max));
|
||||||
|
}
|
||||||
|
} else if (a->range_af == AF_INET6) {
|
||||||
|
ipv6_format_addr_bracket(&a->range.addr.ipv6.min, ds,
|
||||||
|
a->range.proto.min);
|
||||||
|
|
||||||
|
if (!ipv6_mask_is_any(&a->range.addr.ipv6.max)
|
||||||
|
&& memcmp(&a->range.addr.ipv6.max, &a->range.addr.ipv6.min,
|
||||||
|
sizeof(struct in6_addr)) != 0) {
|
||||||
|
ds_put_char(ds, '-');
|
||||||
|
ipv6_format_addr_bracket(&a->range.addr.ipv6.max, ds,
|
||||||
|
a->range.proto.min);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (a->range.proto.min) {
|
||||||
|
ds_put_char(ds, ':');
|
||||||
|
ds_put_format(ds, "%"PRIu16, a->range.proto.min);
|
||||||
|
|
||||||
|
if (a->range.proto.max
|
||||||
|
&& a->range.proto.max != a->range.proto.min) {
|
||||||
|
ds_put_format(ds, "-%"PRIu16, a->range.proto.max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ds_put_char(ds, ',');
|
||||||
|
|
||||||
|
if (a->flags & NX_NAT_F_PERSISTENT) {
|
||||||
|
ds_put_cstr(ds, "persistent,");
|
||||||
|
}
|
||||||
|
if (a->flags & NX_NAT_F_PROTO_HASH) {
|
||||||
|
ds_put_cstr(ds, "hash,");
|
||||||
|
}
|
||||||
|
if (a->flags & NX_NAT_F_PROTO_RANDOM) {
|
||||||
|
ds_put_cstr(ds, "random,");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ds_chomp(ds, ',');
|
||||||
|
ds_put_char(ds, ')');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static char * OVS_WARN_UNUSED_RESULT
|
||||||
|
str_to_nat_range(const char *s, struct ofpact_nat *on)
|
||||||
|
{
|
||||||
|
char ipv6_s[IPV6_SCAN_LEN + 1];
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
on->range_af = AF_UNSPEC;
|
||||||
|
if (ovs_scan_len(s, &n, IP_SCAN_FMT,
|
||||||
|
IP_SCAN_ARGS(&on->range.addr.ipv4.min))) {
|
||||||
|
on->range_af = AF_INET;
|
||||||
|
|
||||||
|
if (s[n] == '-') {
|
||||||
|
n++;
|
||||||
|
if (!ovs_scan_len(s, &n, IP_SCAN_FMT,
|
||||||
|
IP_SCAN_ARGS(&on->range.addr.ipv4.max))
|
||||||
|
|| (ntohl(on->range.addr.ipv4.max)
|
||||||
|
< ntohl(on->range.addr.ipv4.min))) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ((ovs_scan_len(s, &n, IPV6_SCAN_FMT, ipv6_s)
|
||||||
|
|| ovs_scan_len(s, &n, "["IPV6_SCAN_FMT"]", ipv6_s))
|
||||||
|
&& inet_pton(AF_INET6, ipv6_s, &on->range.addr.ipv6.min) == 1) {
|
||||||
|
on->range_af = AF_INET6;
|
||||||
|
|
||||||
|
if (s[n] == '-') {
|
||||||
|
n++;
|
||||||
|
if (!(ovs_scan_len(s, &n, IPV6_SCAN_FMT, ipv6_s)
|
||||||
|
|| ovs_scan_len(s, &n, "["IPV6_SCAN_FMT"]", ipv6_s))
|
||||||
|
|| inet_pton(AF_INET6, ipv6_s, &on->range.addr.ipv6.max) != 1
|
||||||
|
|| memcmp(&on->range.addr.ipv6.max, &on->range.addr.ipv6.min,
|
||||||
|
sizeof on->range.addr.ipv6.max) < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (on->range_af != AF_UNSPEC && s[n] == ':') {
|
||||||
|
n++;
|
||||||
|
if (!ovs_scan_len(s, &n, "%"SCNu16, &on->range.proto.min)) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (s[n] == '-') {
|
||||||
|
n++;
|
||||||
|
if (!ovs_scan_len(s, &n, "%"SCNu16, &on->range.proto.max)
|
||||||
|
|| on->range.proto.max < on->range.proto.min) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (strlen(s) != n) {
|
||||||
|
return xasprintf("garbage (%s) after nat range \"%s\" (pos: %d)",
|
||||||
|
&s[n], s, n);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
error:
|
||||||
|
return xasprintf("invalid nat range \"%s\"", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Parses 'arg' as the argument to a "nat" action, and appends such an
|
||||||
|
* action to 'ofpacts'.
|
||||||
|
*
|
||||||
|
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
||||||
|
* error. The caller is responsible for freeing the returned string. */
|
||||||
|
static char * OVS_WARN_UNUSED_RESULT
|
||||||
|
parse_NAT(char *arg, struct ofpbuf *ofpacts,
|
||||||
|
enum ofputil_protocol *usable_protocols OVS_UNUSED)
|
||||||
|
{
|
||||||
|
struct ofpact_nat *on = ofpact_put_NAT(ofpacts);
|
||||||
|
char *key, *value;
|
||||||
|
|
||||||
|
on->flags = 0;
|
||||||
|
on->range_af = AF_UNSPEC;
|
||||||
|
|
||||||
|
while (ofputil_parse_key_value(&arg, &key, &value)) {
|
||||||
|
char *error = NULL;
|
||||||
|
|
||||||
|
if (!strcmp(key, "src")) {
|
||||||
|
on->flags |= NX_NAT_F_SRC;
|
||||||
|
error = str_to_nat_range(value, on);
|
||||||
|
} else if (!strcmp(key, "dst")) {
|
||||||
|
on->flags |= NX_NAT_F_DST;
|
||||||
|
error = str_to_nat_range(value, on);
|
||||||
|
} else if (!strcmp(key, "persistent")) {
|
||||||
|
on->flags |= NX_NAT_F_PERSISTENT;
|
||||||
|
} else if (!strcmp(key, "hash")) {
|
||||||
|
on->flags |= NX_NAT_F_PROTO_HASH;
|
||||||
|
} else if (!strcmp(key, "random")) {
|
||||||
|
on->flags |= NX_NAT_F_PROTO_RANDOM;
|
||||||
|
} else {
|
||||||
|
error = xasprintf("invalid key \"%s\" in \"nat\" argument",
|
||||||
|
key);
|
||||||
|
}
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (on->flags & NX_NAT_F_SRC && on->flags & NX_NAT_F_DST) {
|
||||||
|
return xasprintf("May only specify one of \"snat\" or \"dnat\".");
|
||||||
|
}
|
||||||
|
if (!(on->flags & NX_NAT_F_SRC || on->flags & NX_NAT_F_DST)) {
|
||||||
|
if (on->flags) {
|
||||||
|
return xasprintf("Flags allowed only with \"snat\" or \"dnat\".");
|
||||||
|
}
|
||||||
|
if (on->range_af != AF_UNSPEC) {
|
||||||
|
return xasprintf("Range allowed only with \"snat\" or \"dnat\".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Meter instruction. */
|
/* Meter instruction. */
|
||||||
|
|
||||||
@@ -5298,6 +5665,7 @@ ofpact_is_set_or_move_action(const struct ofpact *a)
|
|||||||
case OFPACT_BUNDLE:
|
case OFPACT_BUNDLE:
|
||||||
case OFPACT_CLEAR_ACTIONS:
|
case OFPACT_CLEAR_ACTIONS:
|
||||||
case OFPACT_CT:
|
case OFPACT_CT:
|
||||||
|
case OFPACT_NAT:
|
||||||
case OFPACT_CONTROLLER:
|
case OFPACT_CONTROLLER:
|
||||||
case OFPACT_DEC_MPLS_TTL:
|
case OFPACT_DEC_MPLS_TTL:
|
||||||
case OFPACT_DEC_TTL:
|
case OFPACT_DEC_TTL:
|
||||||
@@ -5373,6 +5741,7 @@ ofpact_is_allowed_in_actions_set(const struct ofpact *a)
|
|||||||
case OFPACT_BUNDLE:
|
case OFPACT_BUNDLE:
|
||||||
case OFPACT_CONTROLLER:
|
case OFPACT_CONTROLLER:
|
||||||
case OFPACT_CT:
|
case OFPACT_CT:
|
||||||
|
case OFPACT_NAT:
|
||||||
case OFPACT_ENQUEUE:
|
case OFPACT_ENQUEUE:
|
||||||
case OFPACT_EXIT:
|
case OFPACT_EXIT:
|
||||||
case OFPACT_UNROLL_XLATE:
|
case OFPACT_UNROLL_XLATE:
|
||||||
@@ -5603,6 +5972,7 @@ ovs_instruction_type_from_ofpact_type(enum ofpact_type type)
|
|||||||
case OFPACT_SAMPLE:
|
case OFPACT_SAMPLE:
|
||||||
case OFPACT_DEBUG_RECIRC:
|
case OFPACT_DEBUG_RECIRC:
|
||||||
case OFPACT_CT:
|
case OFPACT_CT:
|
||||||
|
case OFPACT_NAT:
|
||||||
default:
|
default:
|
||||||
return OVSINST_OFPIT11_APPLY_ACTIONS;
|
return OVSINST_OFPIT11_APPLY_ACTIONS;
|
||||||
}
|
}
|
||||||
@@ -6178,6 +6548,18 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case OFPACT_NAT: {
|
||||||
|
struct ofpact_nat *on = ofpact_get_NAT(a);
|
||||||
|
|
||||||
|
if (!dl_type_is_ip_any(flow->dl_type) ||
|
||||||
|
(on->range_af == AF_INET && flow->dl_type != htons(ETH_TYPE_IP)) ||
|
||||||
|
(on->range_af == AF_INET6
|
||||||
|
&& flow->dl_type != htons(ETH_TYPE_IPV6))) {
|
||||||
|
inconsistent_match(usable_protocols);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
case OFPACT_CLEAR_ACTIONS:
|
case OFPACT_CLEAR_ACTIONS:
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -6322,6 +6704,13 @@ ofpacts_verify_nested(const struct ofpact *a, enum ofpact_type outer_action)
|
|||||||
VLOG_WARN("cannot set CT fields outside of ct action");
|
VLOG_WARN("cannot set CT fields outside of ct action");
|
||||||
return OFPERR_OFPBAC_BAD_SET_ARGUMENT;
|
return OFPERR_OFPBAC_BAD_SET_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
if (a->type == OFPACT_NAT) {
|
||||||
|
if (outer_action != OFPACT_CT) {
|
||||||
|
VLOG_WARN("Cannot have NAT action outside of \"ct\" action");
|
||||||
|
return OFPERR_OFPBAC_BAD_SET_ARGUMENT;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (outer_action) {
|
if (outer_action) {
|
||||||
ovs_assert(outer_action == OFPACT_WRITE_ACTIONS
|
ovs_assert(outer_action == OFPACT_WRITE_ACTIONS
|
||||||
@@ -6693,6 +7082,7 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port)
|
|||||||
case OFPACT_GROUP:
|
case OFPACT_GROUP:
|
||||||
case OFPACT_DEBUG_RECIRC:
|
case OFPACT_DEBUG_RECIRC:
|
||||||
case OFPACT_CT:
|
case OFPACT_CT:
|
||||||
|
case OFPACT_NAT:
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -7359,7 +7749,8 @@ pad_ofpat(struct ofpbuf *openflow, size_t start_ofs)
|
|||||||
{
|
{
|
||||||
struct ofp_action_header *oah;
|
struct ofp_action_header *oah;
|
||||||
|
|
||||||
ofpbuf_put_zeros(openflow, PAD_SIZE(openflow->size - start_ofs, 8));
|
ofpbuf_put_zeros(openflow, PAD_SIZE(openflow->size - start_ofs,
|
||||||
|
OFP_ACTION_ALIGN));
|
||||||
|
|
||||||
oah = ofpbuf_at_assert(openflow, start_ofs, sizeof *oah);
|
oah = ofpbuf_at_assert(openflow, start_ofs, sizeof *oah);
|
||||||
oah->len = htons(openflow->size - start_ofs);
|
oah->len = htons(openflow->size - start_ofs);
|
||||||
|
@@ -107,6 +107,7 @@
|
|||||||
OFPACT(SAMPLE, ofpact_sample, ofpact, "sample") \
|
OFPACT(SAMPLE, ofpact_sample, ofpact, "sample") \
|
||||||
OFPACT(UNROLL_XLATE, ofpact_unroll_xlate, ofpact, "unroll_xlate") \
|
OFPACT(UNROLL_XLATE, ofpact_unroll_xlate, ofpact, "unroll_xlate") \
|
||||||
OFPACT(CT, ofpact_conntrack, ofpact, "ct") \
|
OFPACT(CT, ofpact_conntrack, ofpact, "ct") \
|
||||||
|
OFPACT(NAT, ofpact_nat, ofpact, "nat") \
|
||||||
\
|
\
|
||||||
/* Debugging actions. \
|
/* Debugging actions. \
|
||||||
* \
|
* \
|
||||||
@@ -529,6 +530,42 @@ ofpact_nest_get_action_len(const struct ofpact_nest *on)
|
|||||||
void ofpacts_execute_action_set(struct ofpbuf *action_list,
|
void ofpacts_execute_action_set(struct ofpbuf *action_list,
|
||||||
const struct ofpbuf *action_set);
|
const struct ofpbuf *action_set);
|
||||||
|
|
||||||
|
/* Bits for 'flags' in struct nx_action_nat.
|
||||||
|
*/
|
||||||
|
enum nx_nat_flags {
|
||||||
|
NX_NAT_F_SRC = 1 << 0,
|
||||||
|
NX_NAT_F_DST = 1 << 1,
|
||||||
|
NX_NAT_F_PERSISTENT = 1 << 2,
|
||||||
|
NX_NAT_F_PROTO_HASH = 1 << 3,
|
||||||
|
NX_NAT_F_PROTO_RANDOM = 1 << 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* OFPACT_NAT.
|
||||||
|
*
|
||||||
|
* Used for NXAST_NAT. */
|
||||||
|
struct ofpact_nat {
|
||||||
|
struct ofpact ofpact;
|
||||||
|
uint8_t range_af; /* AF_UNSPEC, AF_INET, or AF_INET6 */
|
||||||
|
uint16_t flags; /* NX_NAT_F_* */
|
||||||
|
struct {
|
||||||
|
struct {
|
||||||
|
uint16_t min;
|
||||||
|
uint16_t max;
|
||||||
|
} proto;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
ovs_be32 min;
|
||||||
|
ovs_be32 max;
|
||||||
|
} ipv4;
|
||||||
|
struct {
|
||||||
|
struct in6_addr min;
|
||||||
|
struct in6_addr max;
|
||||||
|
} ipv6;
|
||||||
|
} addr;
|
||||||
|
} range;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/* OFPACT_RESUBMIT.
|
/* OFPACT_RESUBMIT.
|
||||||
*
|
*
|
||||||
* Used for NXAST_RESUBMIT, NXAST_RESUBMIT_TABLE. */
|
* Used for NXAST_RESUBMIT, NXAST_RESUBMIT_TABLE. */
|
||||||
@@ -844,7 +881,7 @@ void *ofpact_put(struct ofpbuf *, enum ofpact_type, size_t len);
|
|||||||
*
|
*
|
||||||
* Appends a new 'ofpact', of length OFPACT_<ENUM>_RAW_SIZE, to 'ofpacts',
|
* Appends a new 'ofpact', of length OFPACT_<ENUM>_RAW_SIZE, to 'ofpacts',
|
||||||
* initializes it with ofpact_init_<ENUM>(), and returns it. Also sets
|
* initializes it with ofpact_init_<ENUM>(), and returns it. Also sets
|
||||||
* 'ofpacts->l2' to the returned action.
|
* 'ofpacts->header' to the returned action.
|
||||||
*
|
*
|
||||||
* After using this function to add a variable-length action, add the
|
* After using this function to add a variable-length action, add the
|
||||||
* elements of the flexible array (e.g. with ofpbuf_put()), then use
|
* elements of the flexible array (e.g. with ofpbuf_put()), then use
|
||||||
|
@@ -447,6 +447,21 @@ ipv6_format_addr(const struct in6_addr *addr, struct ds *s)
|
|||||||
s->length += strlen(dst);
|
s->length += strlen(dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Same as print_ipv6_addr, but optionally encloses the address in square
|
||||||
|
* brackets. */
|
||||||
|
void
|
||||||
|
ipv6_format_addr_bracket(const struct in6_addr *addr, struct ds *s,
|
||||||
|
bool bracket)
|
||||||
|
{
|
||||||
|
if (bracket) {
|
||||||
|
ds_put_char(s, '[');
|
||||||
|
}
|
||||||
|
ipv6_format_addr(addr, s);
|
||||||
|
if (bracket) {
|
||||||
|
ds_put_char(s, ']');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ipv6_format_mapped(const struct in6_addr *addr, struct ds *s)
|
ipv6_format_mapped(const struct in6_addr *addr, struct ds *s)
|
||||||
{
|
{
|
||||||
|
@@ -725,10 +725,13 @@ BUILD_ASSERT_DECL(TCP_HEADER_LEN == sizeof(struct tcp_header));
|
|||||||
#define CS_REPLY_DIR 0x08
|
#define CS_REPLY_DIR 0x08
|
||||||
#define CS_INVALID 0x10
|
#define CS_INVALID 0x10
|
||||||
#define CS_TRACKED 0x20
|
#define CS_TRACKED 0x20
|
||||||
|
#define CS_SRC_NAT 0x40
|
||||||
|
#define CS_DST_NAT 0x80
|
||||||
|
|
||||||
/* Undefined connection state bits. */
|
/* Undefined connection state bits. */
|
||||||
#define CS_SUPPORTED_MASK (CS_NEW | CS_ESTABLISHED | CS_RELATED \
|
#define CS_SUPPORTED_MASK (CS_NEW | CS_ESTABLISHED | CS_RELATED \
|
||||||
| CS_INVALID | CS_REPLY_DIR | CS_TRACKED)
|
| CS_INVALID | CS_REPLY_DIR | CS_TRACKED \
|
||||||
|
| CS_SRC_NAT | CS_DST_NAT)
|
||||||
#define CS_UNSUPPORTED_MASK (~(uint32_t)CS_SUPPORTED_MASK)
|
#define CS_UNSUPPORTED_MASK (~(uint32_t)CS_SUPPORTED_MASK)
|
||||||
|
|
||||||
#define ARP_HRD_ETHERNET 1
|
#define ARP_HRD_ETHERNET 1
|
||||||
@@ -951,6 +954,8 @@ struct vxlanhdr {
|
|||||||
#define VXLAN_FLAGS 0x08000000 /* struct vxlanhdr.vx_flags required value. */
|
#define VXLAN_FLAGS 0x08000000 /* struct vxlanhdr.vx_flags required value. */
|
||||||
|
|
||||||
void ipv6_format_addr(const struct in6_addr *addr, struct ds *);
|
void ipv6_format_addr(const struct in6_addr *addr, struct ds *);
|
||||||
|
void ipv6_format_addr_bracket(const struct in6_addr *addr, struct ds *,
|
||||||
|
bool bracket);
|
||||||
void ipv6_format_mapped(const struct in6_addr *addr, struct ds *);
|
void ipv6_format_mapped(const struct in6_addr *addr, struct ds *);
|
||||||
void ipv6_format_masked(const struct in6_addr *addr,
|
void ipv6_format_masked(const struct in6_addr *addr,
|
||||||
const struct in6_addr *mask, struct ds *);
|
const struct in6_addr *mask, struct ds *);
|
||||||
|
@@ -305,6 +305,9 @@ struct xlate_ctx {
|
|||||||
* state from the datapath should be honored after recirculation. */
|
* state from the datapath should be honored after recirculation. */
|
||||||
bool conntracked;
|
bool conntracked;
|
||||||
|
|
||||||
|
/* Pointer to an embedded NAT action in a conntrack action, or NULL. */
|
||||||
|
struct ofpact_nat *ct_nat_action;
|
||||||
|
|
||||||
/* OpenFlow 1.1+ action set.
|
/* OpenFlow 1.1+ action set.
|
||||||
*
|
*
|
||||||
* 'action_set' accumulates "struct ofpact"s added by OFPACT_WRITE_ACTIONS.
|
* 'action_set' accumulates "struct ofpact"s added by OFPACT_WRITE_ACTIONS.
|
||||||
@@ -4164,6 +4167,7 @@ recirc_unroll_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
|
|||||||
case OFPACT_SAMPLE:
|
case OFPACT_SAMPLE:
|
||||||
case OFPACT_DEBUG_RECIRC:
|
case OFPACT_DEBUG_RECIRC:
|
||||||
case OFPACT_CT:
|
case OFPACT_CT:
|
||||||
|
case OFPACT_NAT:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* These need not be copied for restoration. */
|
/* These need not be copied for restoration. */
|
||||||
@@ -4235,6 +4239,61 @@ put_ct_helper(struct ofpbuf *odp_actions, struct ofpact_conntrack *ofc)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
put_ct_nat(struct xlate_ctx *ctx)
|
||||||
|
{
|
||||||
|
struct ofpact_nat *ofn = ctx->ct_nat_action;
|
||||||
|
size_t nat_offset;
|
||||||
|
|
||||||
|
if (!ofn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nat_offset = nl_msg_start_nested(ctx->odp_actions, OVS_CT_ATTR_NAT);
|
||||||
|
if (ofn->flags & NX_NAT_F_SRC || ofn->flags & NX_NAT_F_DST) {
|
||||||
|
nl_msg_put_flag(ctx->odp_actions, ofn->flags & NX_NAT_F_SRC
|
||||||
|
? OVS_NAT_ATTR_SRC : OVS_NAT_ATTR_DST);
|
||||||
|
if (ofn->flags & NX_NAT_F_PERSISTENT) {
|
||||||
|
nl_msg_put_flag(ctx->odp_actions, OVS_NAT_ATTR_PERSISTENT);
|
||||||
|
}
|
||||||
|
if (ofn->flags & NX_NAT_F_PROTO_HASH) {
|
||||||
|
nl_msg_put_flag(ctx->odp_actions, OVS_NAT_ATTR_PROTO_HASH);
|
||||||
|
} else if (ofn->flags & NX_NAT_F_PROTO_RANDOM) {
|
||||||
|
nl_msg_put_flag(ctx->odp_actions, OVS_NAT_ATTR_PROTO_RANDOM);
|
||||||
|
}
|
||||||
|
if (ofn->range_af == AF_INET) {
|
||||||
|
nl_msg_put_u32(ctx->odp_actions, OVS_NAT_ATTR_IP_MIN,
|
||||||
|
ofn->range.addr.ipv4.min);
|
||||||
|
if (ofn->range.addr.ipv4.max &&
|
||||||
|
ofn->range.addr.ipv4.max > ofn->range.addr.ipv4.min) {
|
||||||
|
nl_msg_put_u32(ctx->odp_actions, OVS_NAT_ATTR_IP_MAX,
|
||||||
|
ofn->range.addr.ipv4.max);
|
||||||
|
}
|
||||||
|
} else if (ofn->range_af == AF_INET6) {
|
||||||
|
nl_msg_put_unspec(ctx->odp_actions, OVS_NAT_ATTR_IP_MIN,
|
||||||
|
&ofn->range.addr.ipv6.min,
|
||||||
|
sizeof ofn->range.addr.ipv6.min);
|
||||||
|
if (!ipv6_mask_is_any(&ofn->range.addr.ipv6.max) &&
|
||||||
|
memcmp(&ofn->range.addr.ipv6.max, &ofn->range.addr.ipv6.min,
|
||||||
|
sizeof ofn->range.addr.ipv6.max) > 0) {
|
||||||
|
nl_msg_put_unspec(ctx->odp_actions, OVS_NAT_ATTR_IP_MAX,
|
||||||
|
&ofn->range.addr.ipv6.max,
|
||||||
|
sizeof ofn->range.addr.ipv6.max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ofn->range_af != AF_UNSPEC && ofn->range.proto.min) {
|
||||||
|
nl_msg_put_u16(ctx->odp_actions, OVS_NAT_ATTR_PROTO_MIN,
|
||||||
|
ofn->range.proto.min);
|
||||||
|
if (ofn->range.proto.max &&
|
||||||
|
ofn->range.proto.max > ofn->range.proto.min) {
|
||||||
|
nl_msg_put_u16(ctx->odp_actions, OVS_NAT_ATTR_PROTO_MAX,
|
||||||
|
ofn->range.proto.max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nl_msg_end_nested(ctx->odp_actions, nat_offset);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc)
|
compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc)
|
||||||
{
|
{
|
||||||
@@ -4248,6 +4307,7 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc)
|
|||||||
xlate_commit_actions(ctx);
|
xlate_commit_actions(ctx);
|
||||||
|
|
||||||
/* Process nested actions first, to populate the key. */
|
/* Process nested actions first, to populate the key. */
|
||||||
|
ctx->ct_nat_action = NULL;
|
||||||
do_xlate_actions(ofc->actions, ofpact_ct_get_action_len(ofc), ctx);
|
do_xlate_actions(ofc->actions, ofpact_ct_get_action_len(ofc), ctx);
|
||||||
|
|
||||||
if (ofc->zone_src.field) {
|
if (ofc->zone_src.field) {
|
||||||
@@ -4264,6 +4324,8 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc)
|
|||||||
put_ct_mark(&ctx->xin->flow, &ctx->base_flow, ctx->odp_actions, ctx->wc);
|
put_ct_mark(&ctx->xin->flow, &ctx->base_flow, ctx->odp_actions, ctx->wc);
|
||||||
put_ct_label(&ctx->xin->flow, &ctx->base_flow, ctx->odp_actions, ctx->wc);
|
put_ct_label(&ctx->xin->flow, &ctx->base_flow, ctx->odp_actions, ctx->wc);
|
||||||
put_ct_helper(ctx->odp_actions, ofc);
|
put_ct_helper(ctx->odp_actions, ofc);
|
||||||
|
put_ct_nat(ctx);
|
||||||
|
ctx->ct_nat_action = NULL;
|
||||||
nl_msg_end_nested(ctx->odp_actions, ct_offset);
|
nl_msg_end_nested(ctx->odp_actions, ct_offset);
|
||||||
|
|
||||||
/* Restore the original ct fields in the key. These should only be exposed
|
/* Restore the original ct fields in the key. These should only be exposed
|
||||||
@@ -4656,6 +4718,11 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
|
|||||||
compose_conntrack_action(ctx, ofpact_get_CT(a));
|
compose_conntrack_action(ctx, ofpact_get_CT(a));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OFPACT_NAT:
|
||||||
|
/* This will be processed by compose_conntrack_action(). */
|
||||||
|
ctx->ct_nat_action = ofpact_get_NAT(a);
|
||||||
|
break;
|
||||||
|
|
||||||
case OFPACT_DEBUG_RECIRC:
|
case OFPACT_DEBUG_RECIRC:
|
||||||
ctx_trigger_recirculation(ctx);
|
ctx_trigger_recirculation(ctx);
|
||||||
a = ofpact_next(a);
|
a = ofpact_next(a);
|
||||||
@@ -4976,6 +5043,8 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
|
|||||||
.was_mpls = false,
|
.was_mpls = false,
|
||||||
.conntracked = false,
|
.conntracked = false,
|
||||||
|
|
||||||
|
.ct_nat_action = NULL,
|
||||||
|
|
||||||
.action_set_has_group = false,
|
.action_set_has_group = false,
|
||||||
.action_set = OFPBUF_STUB_INITIALIZER(action_set_stub),
|
.action_set = OFPBUF_STUB_INITIALIZER(action_set_stub),
|
||||||
};
|
};
|
||||||
|
11
tests/odp.at
11
tests/odp.at
@@ -312,6 +312,17 @@ ct(commit,zone=5)
|
|||||||
ct(commit,mark=0xa0a0a0a0/0xfefefefe)
|
ct(commit,mark=0xa0a0a0a0/0xfefefefe)
|
||||||
ct(commit,label=0x1234567890abcdef1234567890abcdef/0xf1f2f3f4f5f6f7f8f9f0fafbfcfdfeff)
|
ct(commit,label=0x1234567890abcdef1234567890abcdef/0xf1f2f3f4f5f6f7f8f9f0fafbfcfdfeff)
|
||||||
ct(commit,helper=ftp)
|
ct(commit,helper=ftp)
|
||||||
|
ct(nat)
|
||||||
|
ct(commit,nat(src))
|
||||||
|
ct(commit,nat(dst))
|
||||||
|
ct(commit,nat(src=10.0.0.240,random))
|
||||||
|
ct(commit,nat(src=10.0.0.240:32768-65535,random))
|
||||||
|
ct(commit,nat(dst=10.0.0.128-10.0.0.254,hash))
|
||||||
|
ct(commit,nat(src=10.0.0.240-10.0.0.254:32768-65535,persistent))
|
||||||
|
ct(commit,nat(src=fe80::20c:29ff:fe88:a18b,random))
|
||||||
|
ct(commit,nat(src=fe80::20c:29ff:fe88:1-fe80::20c:29ff:fe88:a18b,random))
|
||||||
|
ct(commit,nat(src=[[fe80::20c:29ff:fe88:1]]-[[fe80::20c:29ff:fe88:a18b]]:255-4096,random))
|
||||||
|
ct(commit,helper=ftp,nat(src=10.1.1.240-10.1.1.255))
|
||||||
])
|
])
|
||||||
AT_CHECK_UNQUOTED([ovstest test-odp parse-actions < actions.txt], [0],
|
AT_CHECK_UNQUOTED([ovstest test-odp parse-actions < actions.txt], [0],
|
||||||
[`cat actions.txt`
|
[`cat actions.txt`
|
||||||
|
@@ -187,6 +187,53 @@ ffff 0018 00002320 0007 001f 00010004 000000000000f009
|
|||||||
# actions=ct(alg=ftp)
|
# actions=ct(alg=ftp)
|
||||||
ffff 0018 00002320 0023 0000 00000000 0000 FF 000000 0015
|
ffff 0018 00002320 0023 0000 00000000 0000 FF 000000 0015
|
||||||
|
|
||||||
|
# actions=ct(commit,nat(src))
|
||||||
|
ffff 0028 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl
|
||||||
|
ffff 0010 00002320 0024 00 00 0001 0000
|
||||||
|
|
||||||
|
# actions=ct(commit,nat(dst))
|
||||||
|
ffff 0028 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl
|
||||||
|
ffff 0010 00002320 0024 00 00 0002 0000
|
||||||
|
|
||||||
|
# actions=ct(nat)
|
||||||
|
ffff 0028 00002320 0023 0000 00000000 0000 FF 000000 0000 dnl
|
||||||
|
ffff 0010 00002320 0024 00 00 0000 0000
|
||||||
|
|
||||||
|
# actions=ct(commit,nat(src=10.0.0.240,random))
|
||||||
|
ffff 0030 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl
|
||||||
|
ffff 0018 00002320 0024 00 00 0011 0001 0a0000f0 00000000
|
||||||
|
|
||||||
|
# actions=ct(commit,nat(src=10.0.0.240:32768-65535,random))
|
||||||
|
ffff 0030 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl
|
||||||
|
ffff 0018 00002320 0024 00 00 0011 0031 0a0000f0 8000ffff
|
||||||
|
|
||||||
|
# actions=ct(commit,nat(dst=10.0.0.128-10.0.0.254,hash))
|
||||||
|
ffff 0030 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl
|
||||||
|
ffff 0018 00002320 0024 00 00 000a 0003 0a000080 0a0000fe
|
||||||
|
|
||||||
|
# actions=ct(commit,nat(src=10.0.0.240-10.0.0.254:32768-65535,persistent))
|
||||||
|
ffff 0038 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl
|
||||||
|
ffff 0020 00002320 0024 00 00 0005 0033 0a0000f0 0a0000fe 8000ffff 00000000
|
||||||
|
|
||||||
|
# actions=ct(commit,nat(src=fe80::20c:29ff:fe88:a18b,random))
|
||||||
|
ffff 0038 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl
|
||||||
|
ffff 0020 00002320 0024 00 00 0011 0004 fe800000 00000000 020c 29ff fe88 a18b
|
||||||
|
|
||||||
|
# actions=ct(commit,nat(src=fe80::20c:29ff:fe88:1-fe80::20c:29ff:fe88:a18b,random))
|
||||||
|
ffff 0048 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl
|
||||||
|
ffff 0030 00002320 0024 00 00 0011 000c fe800000 00000000 020c 29ff fe88 0001 fe800000 00000000 020c 29ff fe88 a18b
|
||||||
|
|
||||||
|
# actions=ct(commit,nat(src=[fe80::20c:29ff:fe88:1]-[fe80::20c:29ff:fe88:a18b]:255-4096,random))
|
||||||
|
ffff 0050 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl
|
||||||
|
ffff 0038 00002320 0024 00 00 0011 003c dnl
|
||||||
|
fe800000 00000000 020c 29ff fe88 0001 dnl
|
||||||
|
fe800000 00000000 020c 29ff fe88 a18b dnl
|
||||||
|
00ff1000 00000000
|
||||||
|
|
||||||
|
# bad OpenFlow10 actions: OFPBAC_BAD_ARGUMENT
|
||||||
|
ffff 0048 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl
|
||||||
|
ffff 0030 00002320 0024 00 00 0011 000c fe800000 00000000 020c 29ff fe88 a18b fe800000 00000000 020c 29ff fe88 0001
|
||||||
|
|
||||||
])
|
])
|
||||||
sed '/^[[#&]]/d' < test-data > input.txt
|
sed '/^[[#&]]/d' < test-data > input.txt
|
||||||
sed -n 's/^# //p; /^$/p' < test-data > expout
|
sed -n 's/^# //p; /^$/p' < test-data > expout
|
||||||
|
@@ -160,12 +160,23 @@ sctp actions=drop
|
|||||||
sctp actions=drop
|
sctp actions=drop
|
||||||
in_port=0 actions=resubmit:0
|
in_port=0 actions=resubmit:0
|
||||||
actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
|
actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
|
||||||
|
actions=ct(nat)
|
||||||
|
actions=ct(commit,nat(dst))
|
||||||
|
actions=ct(commit,nat(src))
|
||||||
|
actions=ct(commit,nat(src=10.0.0.240,random))
|
||||||
|
actions=ct(commit,nat(src=10.0.0.240:32768-65535,random))
|
||||||
|
actions=ct(commit,nat(dst=10.0.0.128-10.0.0.254,hash))
|
||||||
|
actions=ct(commit,nat(src=10.0.0.240-10.0.0.254:32768-65535,persistent))
|
||||||
|
actions=ct(commit,nat(src=fe80::20c:29ff:fe88:a18b,random))
|
||||||
|
actions=ct(commit,nat(src=fe80::20c:29ff:fe88:1-fe80::20c:29ff:fe88:a18b,random))
|
||||||
|
actions=ct(commit,nat(src=[fe80::20c:29ff:fe88:1]-[fe80::20c:29ff:fe88:a18b]:255-4096,random))
|
||||||
|
actions=ct(commit,nat(src=10.1.1.240-10.1.1.255),alg=ftp)
|
||||||
]])
|
]])
|
||||||
|
|
||||||
AT_CHECK([ovs-ofctl parse-flows flows.txt
|
AT_CHECK([ovs-ofctl parse-flows flows.txt
|
||||||
], [0], [stdout])
|
], [0], [stdout])
|
||||||
AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0],
|
AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0],
|
||||||
[[usable protocols: any
|
[[usable protocols: OpenFlow10,NXM
|
||||||
chosen protocol: OpenFlow10-table_id
|
chosen protocol: OpenFlow10-table_id
|
||||||
OFPT_FLOW_MOD: ADD tcp,tp_src=123 actions=FLOOD
|
OFPT_FLOW_MOD: ADD tcp,tp_src=123 actions=FLOOD
|
||||||
OFPT_FLOW_MOD: ADD in_port=LOCAL,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop
|
OFPT_FLOW_MOD: ADD in_port=LOCAL,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop
|
||||||
@@ -179,6 +190,17 @@ OFPT_FLOW_MOD: ADD sctp actions=drop
|
|||||||
OFPT_FLOW_MOD: ADD sctp actions=drop
|
OFPT_FLOW_MOD: ADD sctp actions=drop
|
||||||
OFPT_FLOW_MOD: ADD in_port=0 actions=resubmit:0
|
OFPT_FLOW_MOD: ADD in_port=0 actions=resubmit:0
|
||||||
OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
|
OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
|
||||||
|
OFPT_FLOW_MOD: ADD actions=ct(nat)
|
||||||
|
OFPT_FLOW_MOD: ADD actions=ct(commit,nat(dst))
|
||||||
|
OFPT_FLOW_MOD: ADD actions=ct(commit,nat(src))
|
||||||
|
OFPT_FLOW_MOD: ADD actions=ct(commit,nat(src=10.0.0.240,random))
|
||||||
|
OFPT_FLOW_MOD: ADD actions=ct(commit,nat(src=10.0.0.240:32768-65535,random))
|
||||||
|
OFPT_FLOW_MOD: ADD actions=ct(commit,nat(dst=10.0.0.128-10.0.0.254,hash))
|
||||||
|
OFPT_FLOW_MOD: ADD actions=ct(commit,nat(src=10.0.0.240-10.0.0.254:32768-65535,persistent))
|
||||||
|
OFPT_FLOW_MOD: ADD actions=ct(commit,nat(src=fe80::20c:29ff:fe88:a18b,random))
|
||||||
|
OFPT_FLOW_MOD: ADD actions=ct(commit,nat(src=fe80::20c:29ff:fe88:1-fe80::20c:29ff:fe88:a18b,random))
|
||||||
|
OFPT_FLOW_MOD: ADD actions=ct(commit,nat(src=[fe80::20c:29ff:fe88:1]-[fe80::20c:29ff:fe88:a18b]:255-4096,random))
|
||||||
|
OFPT_FLOW_MOD: ADD actions=ct(commit,nat(src=10.1.1.240-10.1.1.255),alg=ftp)
|
||||||
]])
|
]])
|
||||||
AT_CLEANUP
|
AT_CLEANUP
|
||||||
|
|
||||||
|
@@ -1005,7 +1005,7 @@ AT_CHECK([conntrack -L 2>&1 | FORMAT_CT(10.1.1.1)], [0], [dnl
|
|||||||
])
|
])
|
||||||
|
|
||||||
dnl Active FTP requests from p0->p1 should work fine.
|
dnl Active FTP requests from p0->p1 should work fine.
|
||||||
NS_CHECK_EXEC([at_ns0], [wget ftp://10.1.1.2 --no-passive-ftp -t 3 -T 1 --retry-connrefused -v -o wget0.log])
|
NS_CHECK_EXEC([at_ns0], [wget ftp://10.1.1.2 --no-passive-ftp -t 3 -T 1 --retry-connrefused -v -o wget0-1.log])
|
||||||
AT_CHECK([conntrack -L 2>&1 | FORMAT_CT(10.1.1.2) | grep -v "FIN"], [0], [dnl
|
AT_CHECK([conntrack -L 2>&1 | FORMAT_CT(10.1.1.2) | grep -v "FIN"], [0], [dnl
|
||||||
TIME_WAIT src=10.1.1.1 dst=10.1.1.2 sport=<cleared> dport=<cleared> src=10.1.1.2 dst=10.1.1.1 sport=<cleared> dport=<cleared> [[ASSURED]] mark=0 helper=ftp use=2
|
TIME_WAIT src=10.1.1.1 dst=10.1.1.2 sport=<cleared> dport=<cleared> src=10.1.1.2 dst=10.1.1.1 sport=<cleared> dport=<cleared> [[ASSURED]] mark=0 helper=ftp use=2
|
||||||
TIME_WAIT src=10.1.1.2 dst=10.1.1.1 sport=<cleared> dport=<cleared> src=10.1.1.1 dst=10.1.1.2 sport=<cleared> dport=<cleared> [[ASSURED]] mark=0 use=1
|
TIME_WAIT src=10.1.1.2 dst=10.1.1.1 sport=<cleared> dport=<cleared> src=10.1.1.1 dst=10.1.1.2 sport=<cleared> dport=<cleared> [[ASSURED]] mark=0 use=1
|
||||||
@@ -1014,7 +1014,7 @@ TIME_WAIT src=10.1.1.2 dst=10.1.1.1 sport=<cleared> dport=<cleared> src=10.1.1.1
|
|||||||
AT_CHECK([conntrack -F 2>/dev/null])
|
AT_CHECK([conntrack -F 2>/dev/null])
|
||||||
|
|
||||||
dnl Passive FTP requests from p0->p1 should work fine.
|
dnl Passive FTP requests from p0->p1 should work fine.
|
||||||
NS_CHECK_EXEC([at_ns0], [wget ftp://10.1.1.2 -t 3 -T 1 --retry-connrefused -v -o wget0.log])
|
NS_CHECK_EXEC([at_ns0], [wget ftp://10.1.1.2 -t 3 -T 1 --retry-connrefused -v -o wget0-2.log])
|
||||||
AT_CHECK([conntrack -L 2>&1 | FORMAT_CT(10.1.1.2) | grep -v "FIN"], [0], [dnl
|
AT_CHECK([conntrack -L 2>&1 | FORMAT_CT(10.1.1.2) | grep -v "FIN"], [0], [dnl
|
||||||
TIME_WAIT src=10.1.1.1 dst=10.1.1.2 sport=<cleared> dport=<cleared> src=10.1.1.2 dst=10.1.1.1 sport=<cleared> dport=<cleared> [[ASSURED]] mark=0 helper=ftp use=2
|
TIME_WAIT src=10.1.1.1 dst=10.1.1.2 sport=<cleared> dport=<cleared> src=10.1.1.2 dst=10.1.1.1 sport=<cleared> dport=<cleared> [[ASSURED]] mark=0 helper=ftp use=2
|
||||||
TIME_WAIT src=10.1.1.1 dst=10.1.1.2 sport=<cleared> dport=<cleared> src=10.1.1.2 dst=10.1.1.1 sport=<cleared> dport=<cleared> [[ASSURED]] mark=0 use=1
|
TIME_WAIT src=10.1.1.1 dst=10.1.1.2 sport=<cleared> dport=<cleared> src=10.1.1.2 dst=10.1.1.1 sport=<cleared> dport=<cleared> [[ASSURED]] mark=0 use=1
|
||||||
@@ -1352,6 +1352,7 @@ NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -w 2 10.1.1.100 | FORMAT_PI
|
|||||||
OVS_TRAFFIC_VSWITCHD_STOP
|
OVS_TRAFFIC_VSWITCHD_STOP
|
||||||
AT_CLEANUP
|
AT_CLEANUP
|
||||||
|
|
||||||
|
|
||||||
AT_SETUP([conntrack - resubmit to ct multiple times])
|
AT_SETUP([conntrack - resubmit to ct multiple times])
|
||||||
CHECK_CONNTRACK()
|
CHECK_CONNTRACK()
|
||||||
|
|
||||||
@@ -1390,3 +1391,561 @@ NXST_FLOW reply:
|
|||||||
|
|
||||||
OVS_TRAFFIC_VSWITCHD_STOP
|
OVS_TRAFFIC_VSWITCHD_STOP
|
||||||
AT_CLEANUP
|
AT_CLEANUP
|
||||||
|
|
||||||
|
|
||||||
|
AT_SETUP([conntrack - simple SNAT])
|
||||||
|
CHECK_CONNTRACK()
|
||||||
|
OVS_TRAFFIC_VSWITCHD_START()
|
||||||
|
|
||||||
|
ADD_NAMESPACES(at_ns0, at_ns1)
|
||||||
|
|
||||||
|
ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
|
||||||
|
NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address 80:88:88:88:88:88])
|
||||||
|
ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
|
||||||
|
|
||||||
|
dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0.
|
||||||
|
AT_DATA([flows.txt], [dnl
|
||||||
|
in_port=1,ip,action=ct(commit,zone=1,nat(src=10.1.1.240-10.1.1.255)),2
|
||||||
|
in_port=2,ct_state=-trk,ip,action=ct(table=0,zone=1,nat)
|
||||||
|
in_port=2,ct_state=+trk,ct_zone=1,ip,action=1
|
||||||
|
dnl
|
||||||
|
dnl ARP
|
||||||
|
priority=100 arp arp_op=1 action=move:OXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10
|
||||||
|
priority=10 arp action=normal
|
||||||
|
priority=0,action=drop
|
||||||
|
dnl
|
||||||
|
dnl MAC resolution table for IP in reg2, stores mac in OXM_OF_PKT_REG0
|
||||||
|
table=8,reg2=0x0a0101f0/0xfffffff0,action=load:0x808888888888->OXM_OF_PKT_REG0[[]]
|
||||||
|
table=8,priority=0,action=load:0->OXM_OF_PKT_REG0[[]]
|
||||||
|
dnl ARP responder mac filled in at OXM_OF_PKT_REG0, or 0 for normal action.
|
||||||
|
dnl TPA IP in reg2.
|
||||||
|
dnl Swaps the fields of the ARP message to turn a query to a response.
|
||||||
|
table=10 priority=100 arp xreg0=0 action=normal
|
||||||
|
table=10 priority=10,arp,arp_op=1,action=load:2->OXM_OF_ARP_OP[[]],move:OXM_OF_ARP_SHA[[]]->OXM_OF_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->OXM_OF_ARP_SHA[[]],move:OXM_OF_ARP_SPA[[]]->OXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->OXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],load:0->NXM_OF_IN_PORT[[]],output:NXM_NX_REG3[[0..15]]
|
||||||
|
table=10 priority=0 action=drop
|
||||||
|
])
|
||||||
|
|
||||||
|
AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
|
||||||
|
|
||||||
|
dnl HTTP requests from p0->p1 should work fine.
|
||||||
|
NETNS_DAEMONIZE([at_ns1], [[$PYTHON $srcdir/test-l7.py]], [http0.pid])
|
||||||
|
NS_CHECK_EXEC([at_ns0], [wget 10.1.1.2 -t 5 -T 1 --retry-connrefused -v -o wget0.log])
|
||||||
|
|
||||||
|
AT_CHECK([conntrack -L 2>&1 | FORMAT_CT(10.1.1.2) | sed -e 's/dst=10.1.1.2[[45]][[0-9]]/dst=10.1.1.2XX/'], [0], [dnl
|
||||||
|
TIME_WAIT src=10.1.1.1 dst=10.1.1.2 sport=<cleared> dport=<cleared> src=10.1.1.2 dst=10.1.1.2XX sport=<cleared> dport=<cleared> [[ASSURED]] mark=0 zone=1 use=1
|
||||||
|
])
|
||||||
|
|
||||||
|
OVS_TRAFFIC_VSWITCHD_STOP
|
||||||
|
AT_CLEANUP
|
||||||
|
|
||||||
|
|
||||||
|
AT_SETUP([conntrack - SNAT with port range])
|
||||||
|
CHECK_CONNTRACK()
|
||||||
|
OVS_TRAFFIC_VSWITCHD_START()
|
||||||
|
|
||||||
|
ADD_NAMESPACES(at_ns0, at_ns1)
|
||||||
|
|
||||||
|
ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
|
||||||
|
NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address 80:88:88:88:88:88])
|
||||||
|
ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
|
||||||
|
|
||||||
|
dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0.
|
||||||
|
AT_DATA([flows.txt], [dnl
|
||||||
|
in_port=1,tcp,action=ct(commit,zone=1,nat(src=10.1.1.240-10.1.1.255:34567-34568,random)),2
|
||||||
|
in_port=2,ct_state=-trk,tcp,tp_dst=34567,action=ct(table=0,zone=1,nat)
|
||||||
|
in_port=2,ct_state=-trk,tcp,tp_dst=34568,action=ct(table=0,zone=1,nat)
|
||||||
|
in_port=2,ct_state=+trk,ct_zone=1,tcp,action=1
|
||||||
|
dnl
|
||||||
|
dnl ARP
|
||||||
|
priority=100 arp arp_op=1 action=move:OXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10
|
||||||
|
priority=10 arp action=normal
|
||||||
|
priority=0,action=drop
|
||||||
|
dnl
|
||||||
|
dnl MAC resolution table for IP in reg2, stores mac in OXM_OF_PKT_REG0
|
||||||
|
table=8,reg2=0x0a0101f0/0xfffffff0,action=load:0x808888888888->OXM_OF_PKT_REG0[[]]
|
||||||
|
table=8,priority=0,action=load:0->OXM_OF_PKT_REG0[[]]
|
||||||
|
dnl ARP responder mac filled in at OXM_OF_PKT_REG0, or 0 for normal action.
|
||||||
|
dnl TPA IP in reg2.
|
||||||
|
dnl Swaps the fields of the ARP message to turn a query to a response.
|
||||||
|
table=10 priority=100 arp xreg0=0 action=normal
|
||||||
|
table=10 priority=10,arp,arp_op=1,action=load:2->OXM_OF_ARP_OP[[]],move:OXM_OF_ARP_SHA[[]]->OXM_OF_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->OXM_OF_ARP_SHA[[]],move:OXM_OF_ARP_SPA[[]]->OXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->OXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],load:0->NXM_OF_IN_PORT[[]],output:NXM_NX_REG3[[0..15]]
|
||||||
|
table=10 priority=0 action=drop
|
||||||
|
])
|
||||||
|
|
||||||
|
AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
|
||||||
|
|
||||||
|
dnl HTTP requests from p0->p1 should work fine.
|
||||||
|
NETNS_DAEMONIZE([at_ns1], [[$PYTHON $srcdir/test-l7.py]], [http0.pid])
|
||||||
|
NS_CHECK_EXEC([at_ns0], [wget 10.1.1.2 -t 5 -T 1 --retry-connrefused -v -o wget0.log])
|
||||||
|
|
||||||
|
AT_CHECK([conntrack -L 2>&1 | FORMAT_CT(10.1.1.2) | sed -e 's/dst=10.1.1.2[[45]][[0-9]]/dst=10.1.1.2XX/'], [0], [dnl
|
||||||
|
TIME_WAIT src=10.1.1.1 dst=10.1.1.2 sport=<cleared> dport=<cleared> src=10.1.1.2 dst=10.1.1.2XX sport=<cleared> dport=<cleared> [[ASSURED]] mark=0 zone=1 use=1
|
||||||
|
])
|
||||||
|
|
||||||
|
OVS_TRAFFIC_VSWITCHD_STOP
|
||||||
|
AT_CLEANUP
|
||||||
|
|
||||||
|
|
||||||
|
AT_SETUP([conntrack - more complex SNAT])
|
||||||
|
CHECK_CONNTRACK()
|
||||||
|
OVS_TRAFFIC_VSWITCHD_START()
|
||||||
|
|
||||||
|
ADD_NAMESPACES(at_ns0, at_ns1)
|
||||||
|
|
||||||
|
ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
|
||||||
|
NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address 80:88:88:88:88:88])
|
||||||
|
ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
|
||||||
|
|
||||||
|
AT_DATA([flows.txt], [dnl
|
||||||
|
dnl Track all IP traffic, NAT existing connections.
|
||||||
|
priority=100 ip action=ct(table=1,zone=1,nat)
|
||||||
|
dnl
|
||||||
|
dnl Allow ARP, but generate responses for NATed addresses
|
||||||
|
priority=100 arp arp_op=1 action=move:OXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10
|
||||||
|
priority=10 arp action=normal
|
||||||
|
priority=0 action=drop
|
||||||
|
dnl
|
||||||
|
dnl Allow any traffic from ns0->ns1. SNAT ns0 to 10.1.1.240-10.1.1.255
|
||||||
|
table=1 priority=100 in_port=1 ip ct_state=+trk+new-est action=ct(commit,zone=1,nat(src=10.1.1.240-10.1.1.255)),2
|
||||||
|
table=1 priority=100 in_port=1 ip ct_state=+trk-new+est action=2
|
||||||
|
dnl Only allow established traffic from ns1->ns0.
|
||||||
|
table=1 priority=100 in_port=2 ip ct_state=+trk-new+est action=1
|
||||||
|
table=1 priority=0 action=drop
|
||||||
|
dnl
|
||||||
|
dnl MAC resolution table for IP in reg2, stores mac in OXM_OF_PKT_REG0
|
||||||
|
table=8 priority=100 reg2=0x0a0101f0/0xfffffff0 action=load:0x808888888888->OXM_OF_PKT_REG0[[]]
|
||||||
|
dnl Zero result means not found.
|
||||||
|
table=8 priority=0 action=load:0->OXM_OF_PKT_REG0[[]]
|
||||||
|
dnl ARP responder mac filled in at OXM_OF_PKT_REG0, or 0 for normal action.
|
||||||
|
dnl ARP TPA IP in reg2.
|
||||||
|
table=10 priority=100 arp xreg0=0 action=normal
|
||||||
|
dnl Swaps the fields of the ARP message to turn a query to a response.
|
||||||
|
table=10 priority=10 arp arp_op=1 action=load:2->OXM_OF_ARP_OP[[]],move:OXM_OF_ARP_SHA[[]]->OXM_OF_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->OXM_OF_ARP_SHA[[]],move:OXM_OF_ARP_SPA[[]]->OXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->OXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],load:0->NXM_OF_IN_PORT[[]],output:NXM_NX_REG3[[0..15]]
|
||||||
|
table=10 priority=0 action=drop
|
||||||
|
])
|
||||||
|
|
||||||
|
AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
|
||||||
|
|
||||||
|
dnl HTTP requests from p0->p1 should work fine.
|
||||||
|
NETNS_DAEMONIZE([at_ns1], [[$PYTHON $srcdir/test-l7.py]], [http0.pid])
|
||||||
|
NS_CHECK_EXEC([at_ns0], [wget 10.1.1.2 -t 5 -T 1 --retry-connrefused -v -o wget0.log])
|
||||||
|
|
||||||
|
AT_CHECK([conntrack -L 2>&1 | FORMAT_CT(10.1.1.2) | sed -e 's/dst=10.1.1.2[[45]][[0-9]]/dst=10.1.1.2XX/'], [0], [dnl
|
||||||
|
TIME_WAIT src=10.1.1.1 dst=10.1.1.2 sport=<cleared> dport=<cleared> src=10.1.1.2 dst=10.1.1.2XX sport=<cleared> dport=<cleared> [[ASSURED]] mark=0 zone=1 use=1
|
||||||
|
])
|
||||||
|
|
||||||
|
OVS_TRAFFIC_VSWITCHD_STOP
|
||||||
|
AT_CLEANUP
|
||||||
|
|
||||||
|
AT_SETUP([conntrack - simple DNAT])
|
||||||
|
CHECK_CONNTRACK()
|
||||||
|
OVS_TRAFFIC_VSWITCHD_START()
|
||||||
|
|
||||||
|
ADD_NAMESPACES(at_ns0, at_ns1)
|
||||||
|
|
||||||
|
ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
|
||||||
|
ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
|
||||||
|
NS_CHECK_EXEC([at_ns1], [ip link set dev p1 address 80:88:88:88:88:88])
|
||||||
|
|
||||||
|
dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0.
|
||||||
|
AT_DATA([flows.txt], [dnl
|
||||||
|
priority=100 in_port=1,ip,nw_dst=10.1.1.64,action=ct(zone=1,nat(dst=10.1.1.2),commit),2
|
||||||
|
priority=10 in_port=1,ip,action=ct(commit,zone=1),2
|
||||||
|
priority=100 in_port=2,ct_state=-trk,ip,action=ct(table=0,nat,zone=1)
|
||||||
|
priority=100 in_port=2,ct_state=+trk+est,ct_zone=1,ip,action=1
|
||||||
|
dnl
|
||||||
|
dnl ARP
|
||||||
|
priority=100 arp arp_op=1 action=move:OXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10
|
||||||
|
priority=10 arp action=normal
|
||||||
|
priority=0,action=drop
|
||||||
|
dnl
|
||||||
|
dnl MAC resolution table for IP in reg2, stores mac in OXM_OF_PKT_REG0
|
||||||
|
table=8,reg2=0x0a010140,action=load:0x808888888888->OXM_OF_PKT_REG0[[]]
|
||||||
|
dnl Zero result means not found.
|
||||||
|
table=8,priority=0,action=load:0->OXM_OF_PKT_REG0[[]]
|
||||||
|
dnl ARP responder mac filled in at OXM_OF_PKT_REG0, or 0 for normal action.
|
||||||
|
dnl TPA IP in reg2.
|
||||||
|
table=10 priority=100 arp xreg0=0 action=normal
|
||||||
|
dnl Swaps the fields of the ARP message to turn a query to a response.
|
||||||
|
table=10 priority=10,arp,arp_op=1,action=load:2->OXM_OF_ARP_OP[[]],move:OXM_OF_ARP_SHA[[]]->OXM_OF_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->OXM_OF_ARP_SHA[[]],move:OXM_OF_ARP_SPA[[]]->OXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->OXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],load:0->NXM_OF_IN_PORT[[]],output:NXM_NX_REG3[[0..15]]
|
||||||
|
table=10 priority=0 action=drop
|
||||||
|
])
|
||||||
|
|
||||||
|
AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
|
||||||
|
|
||||||
|
dnl Should work with the virtual IP address through NAT
|
||||||
|
NETNS_DAEMONIZE([at_ns1], [[$PYTHON $srcdir/test-l7.py]], [http0.pid])
|
||||||
|
NS_CHECK_EXEC([at_ns0], [wget 10.1.1.64 -t 5 -T 1 --retry-connrefused -v -o wget0.log])
|
||||||
|
|
||||||
|
AT_CHECK([conntrack -L 2>&1 | FORMAT_CT(10.1.1.64) ], [0], [dnl
|
||||||
|
TIME_WAIT src=10.1.1.1 dst=10.1.1.64 sport=<cleared> dport=<cleared> src=10.1.1.2 dst=10.1.1.1 sport=<cleared> dport=<cleared> [[ASSURED]] mark=0 zone=1 use=1
|
||||||
|
])
|
||||||
|
|
||||||
|
dnl Should work with the assigned IP address as well
|
||||||
|
NS_CHECK_EXEC([at_ns0], [wget 10.1.1.2 -t 3 -T 1 --retry-connrefused -v -o wget0.log])
|
||||||
|
|
||||||
|
AT_CHECK([conntrack -L 2>&1 | FORMAT_CT(10.1.1.2) ], [0], [dnl
|
||||||
|
TIME_WAIT src=10.1.1.1 dst=10.1.1.2 sport=<cleared> dport=<cleared> src=10.1.1.2 dst=10.1.1.1 sport=<cleared> dport=<cleared> [[ASSURED]] mark=0 zone=1 use=1
|
||||||
|
])
|
||||||
|
|
||||||
|
OVS_TRAFFIC_VSWITCHD_STOP
|
||||||
|
AT_CLEANUP
|
||||||
|
|
||||||
|
AT_SETUP([conntrack - more complex DNAT])
|
||||||
|
CHECK_CONNTRACK()
|
||||||
|
OVS_TRAFFIC_VSWITCHD_START()
|
||||||
|
|
||||||
|
ADD_NAMESPACES(at_ns0, at_ns1)
|
||||||
|
|
||||||
|
ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
|
||||||
|
ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
|
||||||
|
NS_CHECK_EXEC([at_ns1], [ip link set dev p1 address 80:88:88:88:88:88])
|
||||||
|
|
||||||
|
dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0.
|
||||||
|
AT_DATA([flows.txt], [dnl
|
||||||
|
dnl Track all IP traffic
|
||||||
|
table=0 priority=100 ip action=ct(table=1,zone=1,nat)
|
||||||
|
dnl
|
||||||
|
dnl Allow ARP, but generate responses for NATed addresses
|
||||||
|
table=0 priority=100 arp arp_op=1 action=move:OXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10
|
||||||
|
table=0 priority=10 arp action=normal
|
||||||
|
table=0 priority=0 action=drop
|
||||||
|
dnl
|
||||||
|
dnl Allow any IP traffic from ns0->ns1. DNAT ns0 from 10.1.1.64 to 10.1.1.2
|
||||||
|
table=1 priority=100 in_port=1 ct_state=+new ip nw_dst=10.1.1.64 action=ct(zone=1,nat(dst=10.1.1.2),commit),2
|
||||||
|
table=1 priority=10 in_port=1 ct_state=+new ip action=ct(commit,zone=1),2
|
||||||
|
table=1 priority=100 in_port=1 ct_state=+est ct_zone=1 action=2
|
||||||
|
dnl Only allow established traffic from ns1->ns0.
|
||||||
|
table=1 priority=100 in_port=2 ct_state=+est ct_zone=1 action=1
|
||||||
|
table=1 priority=0 action=drop
|
||||||
|
dnl
|
||||||
|
dnl MAC resolution table for IP in reg2, stores mac in OXM_OF_PKT_REG0
|
||||||
|
table=8,reg2=0x0a010140,action=load:0x808888888888->OXM_OF_PKT_REG0[[]]
|
||||||
|
dnl Zero result means not found.
|
||||||
|
table=8,priority=0,action=load:0->OXM_OF_PKT_REG0[[]]
|
||||||
|
dnl ARP responder mac filled in at OXM_OF_PKT_REG0, or 0 for normal action.
|
||||||
|
dnl TPA IP in reg2.
|
||||||
|
table=10 priority=100 arp xreg0=0 action=normal
|
||||||
|
dnl Swaps the fields of the ARP message to turn a query to a response.
|
||||||
|
table=10 priority=10,arp,arp_op=1,action=load:2->OXM_OF_ARP_OP[[]],move:OXM_OF_ARP_SHA[[]]->OXM_OF_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->OXM_OF_ARP_SHA[[]],move:OXM_OF_ARP_SPA[[]]->OXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->OXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],load:0->NXM_OF_IN_PORT[[]],output:NXM_NX_REG3[[0..15]]
|
||||||
|
table=10 priority=0 action=drop
|
||||||
|
])
|
||||||
|
|
||||||
|
AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
|
||||||
|
|
||||||
|
dnl Should work with the virtual IP address through NAT
|
||||||
|
NETNS_DAEMONIZE([at_ns1], [[$PYTHON $srcdir/test-l7.py]], [http0.pid])
|
||||||
|
NS_CHECK_EXEC([at_ns0], [wget 10.1.1.64 -t 5 -T 1 --retry-connrefused -v -o wget0.log])
|
||||||
|
|
||||||
|
AT_CHECK([conntrack -L 2>&1 | FORMAT_CT(10.1.1.64) ], [0], [dnl
|
||||||
|
TIME_WAIT src=10.1.1.1 dst=10.1.1.64 sport=<cleared> dport=<cleared> src=10.1.1.2 dst=10.1.1.1 sport=<cleared> dport=<cleared> [[ASSURED]] mark=0 zone=1 use=1
|
||||||
|
])
|
||||||
|
|
||||||
|
dnl Should work with the assigned IP address as well
|
||||||
|
NS_CHECK_EXEC([at_ns0], [wget 10.1.1.2 -t 3 -T 1 --retry-connrefused -v -o wget0.log])
|
||||||
|
|
||||||
|
AT_CHECK([conntrack -L 2>&1 | FORMAT_CT(10.1.1.2) ], [0], [dnl
|
||||||
|
TIME_WAIT src=10.1.1.1 dst=10.1.1.2 sport=<cleared> dport=<cleared> src=10.1.1.2 dst=10.1.1.1 sport=<cleared> dport=<cleared> [[ASSURED]] mark=0 zone=1 use=1
|
||||||
|
])
|
||||||
|
|
||||||
|
OVS_TRAFFIC_VSWITCHD_STOP
|
||||||
|
AT_CLEANUP
|
||||||
|
|
||||||
|
AT_SETUP([conntrack - ICMP related with NAT])
|
||||||
|
CHECK_CONNTRACK()
|
||||||
|
OVS_TRAFFIC_VSWITCHD_START()
|
||||||
|
|
||||||
|
ADD_NAMESPACES(at_ns0, at_ns1)
|
||||||
|
|
||||||
|
ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
|
||||||
|
NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address 80:88:88:88:88:88])
|
||||||
|
ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
|
||||||
|
|
||||||
|
dnl Allow UDP traffic from ns0->ns1. Only allow related ICMP responses back.
|
||||||
|
dnl Make sure ICMP responses are reverse-NATted.
|
||||||
|
AT_DATA([flows.txt], [dnl
|
||||||
|
in_port=1,udp,action=ct(commit,nat(src=10.1.1.240-10.1.1.255),exec(set_field:1->ct_mark)),2
|
||||||
|
in_port=2,icmp,ct_state=-trk,action=ct(table=0,nat)
|
||||||
|
in_port=2,icmp,nw_dst=10.1.1.1,ct_state=+trk+rel,ct_mark=1,action=1
|
||||||
|
dnl
|
||||||
|
dnl ARP
|
||||||
|
priority=100 arp arp_op=1 action=move:OXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10
|
||||||
|
priority=10 arp action=normal
|
||||||
|
priority=0,action=drop
|
||||||
|
dnl
|
||||||
|
dnl MAC resolution table for IP in reg2, stores mac in OXM_OF_PKT_REG0
|
||||||
|
table=8,reg2=0x0a0101f0/0xfffffff0,action=load:0x808888888888->OXM_OF_PKT_REG0[[]]
|
||||||
|
table=8,priority=0,action=load:0->OXM_OF_PKT_REG0[[]]
|
||||||
|
dnl ARP responder mac filled in at OXM_OF_PKT_REG0, or 0 for normal action.
|
||||||
|
dnl TPA IP in reg2.
|
||||||
|
dnl Swaps the fields of the ARP message to turn a query to a response.
|
||||||
|
table=10 priority=100 arp xreg0=0 action=normal
|
||||||
|
table=10 priority=10,arp,arp_op=1,action=load:2->OXM_OF_ARP_OP[[]],move:OXM_OF_ARP_SHA[[]]->OXM_OF_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->OXM_OF_ARP_SHA[[]],move:OXM_OF_ARP_SPA[[]]->OXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->OXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],load:0->NXM_OF_IN_PORT[[]],output:NXM_NX_REG3[[0..15]]
|
||||||
|
table=10 priority=0 action=drop
|
||||||
|
])
|
||||||
|
|
||||||
|
AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
|
||||||
|
|
||||||
|
dnl UDP packets from ns0->ns1 should solicit "destination unreachable" response.
|
||||||
|
dnl We pass "-q 1" here to handle openbsd-style nc that can't quit immediately.
|
||||||
|
NS_CHECK_EXEC([at_ns0], [bash -c "echo a | nc -q 1 -u 10.1.1.2 10000"])
|
||||||
|
|
||||||
|
AT_CHECK([ovs-appctl revalidator/purge], [0])
|
||||||
|
AT_CHECK([ovs-ofctl -O OpenFlow15 dump-flows br0 | ofctl_strip | sort | grep -v drop], [0], [dnl
|
||||||
|
n_packets=1, n_bytes=42, priority=10,arp actions=NORMAL
|
||||||
|
n_packets=1, n_bytes=44, udp,in_port=1 actions=ct(commit,nat(src=10.1.1.240-10.1.1.255),exec(set_field:0x1->ct_mark)),output:2
|
||||||
|
n_packets=1, n_bytes=72, ct_state=+rel+trk,ct_mark=0x1,icmp,in_port=2,nw_dst=10.1.1.1 actions=output:1
|
||||||
|
n_packets=1, n_bytes=72, ct_state=-trk,icmp,in_port=2 actions=ct(table=0,nat)
|
||||||
|
n_packets=2, n_bytes=84, priority=100,arp,arp_op=1 actions=move:NXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10
|
||||||
|
table=10, n_packets=1, n_bytes=42, priority=10,arp,arp_op=1 actions=set_field:2->arp_op,move:NXM_NX_ARP_SHA[[]]->NXM_NX_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_NX_ARP_SHA[[]],move:NXM_OF_ARP_SPA[[]]->NXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->NXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],set_field:0->in_port,output:NXM_NX_REG3[[0..15]]
|
||||||
|
table=10, n_packets=1, n_bytes=42, priority=100,arp,reg0=0,reg1=0 actions=NORMAL
|
||||||
|
table=8, n_packets=1, n_bytes=42, priority=0 actions=set_field:0->xreg0
|
||||||
|
table=8, n_packets=1, n_bytes=42, reg2=0xa0101f0/0xfffffff0 actions=set_field:0x808888888888->xreg0
|
||||||
|
OFPST_FLOW reply (OF1.5):
|
||||||
|
])
|
||||||
|
|
||||||
|
AT_CHECK([conntrack -L 2>&1 | FORMAT_CT(10.1.1.2) | sed -e 's/dst=10.1.1.2[[45]][[0-9]]/dst=10.1.1.2XX/'], [0], [dnl
|
||||||
|
src=10.1.1.1 dst=10.1.1.2 sport=<cleared> dport=<cleared> [[UNREPLIED]] src=10.1.1.2 dst=10.1.1.2XX sport=<cleared> dport=<cleared> mark=1 use=1
|
||||||
|
])
|
||||||
|
|
||||||
|
OVS_TRAFFIC_VSWITCHD_STOP
|
||||||
|
AT_CLEANUP
|
||||||
|
|
||||||
|
|
||||||
|
AT_SETUP([conntrack - FTP with NAT])
|
||||||
|
AT_SKIP_IF([test $HAVE_PYFTPDLIB = no])
|
||||||
|
CHECK_CONNTRACK()
|
||||||
|
|
||||||
|
OVS_TRAFFIC_VSWITCHD_START()
|
||||||
|
|
||||||
|
ADD_NAMESPACES(at_ns0, at_ns1)
|
||||||
|
|
||||||
|
ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
|
||||||
|
NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address 80:88:88:88:88:88])
|
||||||
|
ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
|
||||||
|
|
||||||
|
dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0.
|
||||||
|
|
||||||
|
AT_DATA([flows.txt], [dnl
|
||||||
|
dnl track all IP traffic, de-mangle non-NEW connections
|
||||||
|
table=0 in_port=1, ip, action=ct(table=1,nat)
|
||||||
|
table=0 in_port=2, ip, action=ct(table=2,nat)
|
||||||
|
dnl
|
||||||
|
dnl ARP
|
||||||
|
dnl
|
||||||
|
table=0 priority=100 arp arp_op=1 action=move:OXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10
|
||||||
|
table=0 priority=10 arp action=normal
|
||||||
|
table=0 priority=0 action=drop
|
||||||
|
dnl
|
||||||
|
dnl Table 1: port 1 -> 2
|
||||||
|
dnl
|
||||||
|
dnl Allow new FTP connections. These need to be commited.
|
||||||
|
table=1 ct_state=+new, tcp, tp_dst=21, nw_src=10.1.1.1, action=ct(alg=ftp,commit,nat(src=10.1.1.240)),2
|
||||||
|
dnl Allow established TCP connections, make sure they are NATted already.
|
||||||
|
table=1 ct_state=+est, tcp, nw_src=10.1.1.240, action=2
|
||||||
|
dnl
|
||||||
|
dnl Table 1: droppers
|
||||||
|
dnl
|
||||||
|
table=1 priority=10, tcp, action=drop
|
||||||
|
table=1 priority=0,action=drop
|
||||||
|
dnl
|
||||||
|
dnl Table 2: port 2 -> 1
|
||||||
|
dnl
|
||||||
|
dnl Allow established TCP connections, make sure they are reverse NATted
|
||||||
|
table=2 ct_state=+est, tcp, nw_dst=10.1.1.1, action=1
|
||||||
|
dnl Allow (new) related (data) connections. These need to be commited.
|
||||||
|
table=2 ct_state=+new+rel, tcp, nw_dst=10.1.1.240, action=ct(commit,nat),1
|
||||||
|
dnl Allow related ICMP packets, make sure they are reverse NATted
|
||||||
|
table=2 ct_state=+rel, icmp, nw_dst=10.1.1.1, action=1
|
||||||
|
dnl
|
||||||
|
dnl Table 2: droppers
|
||||||
|
dnl
|
||||||
|
table=2 priority=10, tcp, action=drop
|
||||||
|
table=2 priority=0, action=drop
|
||||||
|
dnl
|
||||||
|
dnl MAC resolution table for IP in reg2, stores mac in OXM_OF_PKT_REG0
|
||||||
|
dnl
|
||||||
|
table=8,reg2=0x0a0101f0/0xfffffff0,action=load:0x808888888888->OXM_OF_PKT_REG0[[]]
|
||||||
|
table=8,priority=0,action=load:0->OXM_OF_PKT_REG0[[]]
|
||||||
|
dnl ARP responder mac filled in at OXM_OF_PKT_REG0, or 0 for normal action.
|
||||||
|
dnl TPA IP in reg2.
|
||||||
|
dnl Swaps the fields of the ARP message to turn a query to a response.
|
||||||
|
table=10 priority=100 arp xreg0=0 action=normal
|
||||||
|
table=10 priority=10,arp,arp_op=1,action=load:2->OXM_OF_ARP_OP[[]],move:OXM_OF_ARP_SHA[[]]->OXM_OF_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->OXM_OF_ARP_SHA[[]],move:OXM_OF_ARP_SPA[[]]->OXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->OXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],load:0->NXM_OF_IN_PORT[[]],output:NXM_NX_REG3[[0..15]]
|
||||||
|
table=10 priority=0 action=drop
|
||||||
|
])
|
||||||
|
|
||||||
|
AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
|
||||||
|
|
||||||
|
dnl NETNS_DAEMONIZE([at_ns0], [[$PYTHON $srcdir/test-l7.py ftp]], [ftp1.pid])
|
||||||
|
NETNS_DAEMONIZE([at_ns1], [[$PYTHON $srcdir/test-l7.py ftp]], [ftp0.pid])
|
||||||
|
|
||||||
|
dnl FTP requests from p0->p1 should work fine.
|
||||||
|
NS_CHECK_EXEC([at_ns0], [wget ftp://10.1.1.2 -4 --no-passive-ftp -t 3 -T 1 --retry-connrefused -v --server-response --no-proxy --no-remove-listing -o wget0.log -d])
|
||||||
|
|
||||||
|
AT_CHECK([conntrack -L 2>&1 | FORMAT_CT(10.1.1.2) | grep -v "FIN"], [0], [dnl
|
||||||
|
TIME_WAIT src=10.1.1.1 dst=10.1.1.2 sport=<cleared> dport=<cleared> src=10.1.1.2 dst=10.1.1.240 sport=<cleared> dport=<cleared> [[ASSURED]] mark=0 helper=ftp use=2
|
||||||
|
TIME_WAIT src=10.1.1.2 dst=10.1.1.240 sport=<cleared> dport=<cleared> src=10.1.1.1 dst=10.1.1.2 sport=<cleared> dport=<cleared> [[ASSURED]] mark=0 use=1
|
||||||
|
])
|
||||||
|
|
||||||
|
OVS_TRAFFIC_VSWITCHD_STOP
|
||||||
|
AT_CLEANUP
|
||||||
|
|
||||||
|
|
||||||
|
AT_SETUP([conntrack - FTP with NAT 2])
|
||||||
|
AT_SKIP_IF([test $HAVE_PYFTPDLIB = no])
|
||||||
|
CHECK_CONNTRACK()
|
||||||
|
OVS_TRAFFIC_VSWITCHD_START()
|
||||||
|
|
||||||
|
ADD_NAMESPACES(at_ns0, at_ns1)
|
||||||
|
|
||||||
|
ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
|
||||||
|
NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address 80:88:88:88:88:88])
|
||||||
|
ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
|
||||||
|
|
||||||
|
dnl Allow any traffic from ns0->ns1.
|
||||||
|
dnl Only allow nd, return traffic from ns1->ns0.
|
||||||
|
AT_DATA([flows.txt], [dnl
|
||||||
|
dnl track all IP traffic (this includes a helper call to non-NEW packets.)
|
||||||
|
table=0 ip, action=ct(table=1)
|
||||||
|
dnl
|
||||||
|
dnl ARP
|
||||||
|
dnl
|
||||||
|
table=0 priority=100 arp arp_op=1 action=move:OXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10
|
||||||
|
table=0 priority=10 arp action=normal
|
||||||
|
table=0 priority=0 action=drop
|
||||||
|
dnl
|
||||||
|
dnl Table 1
|
||||||
|
dnl
|
||||||
|
dnl Allow new FTP connections. These need to be commited.
|
||||||
|
dnl This does helper for new packets.
|
||||||
|
table=1 in_port=1 ct_state=+new, tcp, tp_dst=21, action=ct(alg=ftp,commit,nat(src=10.1.1.240)),2
|
||||||
|
dnl Allow and NAT established TCP connections
|
||||||
|
table=1 in_port=1 ct_state=+est, tcp, action=ct(nat),2
|
||||||
|
table=1 in_port=2 ct_state=+est, tcp, action=ct(nat),1
|
||||||
|
dnl Allow and NAT (new) related active (data) connections.
|
||||||
|
dnl These need to be commited.
|
||||||
|
table=1 in_port=2 ct_state=+new+rel, tcp, action=ct(commit,nat),1
|
||||||
|
dnl Allow related ICMP packets.
|
||||||
|
table=1 in_port=2 ct_state=+rel, icmp, action=ct(nat),1
|
||||||
|
dnl Drop everything else.
|
||||||
|
table=1 priority=0, action=drop
|
||||||
|
dnl
|
||||||
|
dnl MAC resolution table for IP in reg2, stores mac in OXM_OF_PKT_REG0
|
||||||
|
dnl
|
||||||
|
table=8,reg2=0x0a0101f0/0xfffffff0,action=load:0x808888888888->OXM_OF_PKT_REG0[[]]
|
||||||
|
table=8,priority=0,action=load:0->OXM_OF_PKT_REG0[[]]
|
||||||
|
dnl ARP responder mac filled in at OXM_OF_PKT_REG0, or 0 for normal action.
|
||||||
|
dnl TPA IP in reg2.
|
||||||
|
dnl Swaps the fields of the ARP message to turn a query to a response.
|
||||||
|
table=10 priority=100 arp xreg0=0 action=normal
|
||||||
|
table=10 priority=10,arp,arp_op=1,action=load:2->OXM_OF_ARP_OP[[]],move:OXM_OF_ARP_SHA[[]]->OXM_OF_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->OXM_OF_ARP_SHA[[]],move:OXM_OF_ARP_SPA[[]]->OXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->OXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],load:0->NXM_OF_IN_PORT[[]],output:NXM_NX_REG3[[0..15]]
|
||||||
|
table=10 priority=0 action=drop
|
||||||
|
])
|
||||||
|
|
||||||
|
AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
|
||||||
|
|
||||||
|
NETNS_DAEMONIZE([at_ns1], [[$PYTHON $srcdir/test-l7.py ftp]], [ftp0.pid])
|
||||||
|
|
||||||
|
dnl FTP requests from p0->p1 should work fine.
|
||||||
|
NS_CHECK_EXEC([at_ns0], [wget ftp://10.1.1.2 -4 --no-passive-ftp -t 3 -T 1 --retry-connrefused -v --server-response --no-proxy --no-remove-listing -o wget0.log -d])
|
||||||
|
|
||||||
|
AT_CHECK([conntrack -L 2>&1 | FORMAT_CT(10.1.1.2) | grep -v "FIN" | grep -v "CLOSE"], [0], [dnl
|
||||||
|
TIME_WAIT src=10.1.1.1 dst=10.1.1.2 sport=<cleared> dport=<cleared> src=10.1.1.2 dst=10.1.1.240 sport=<cleared> dport=<cleared> [[ASSURED]] mark=0 helper=ftp use=2
|
||||||
|
TIME_WAIT src=10.1.1.2 dst=10.1.1.240 sport=<cleared> dport=<cleared> src=10.1.1.1 dst=10.1.1.2 sport=<cleared> dport=<cleared> [[ASSURED]] mark=0 use=1
|
||||||
|
])
|
||||||
|
|
||||||
|
OVS_TRAFFIC_VSWITCHD_STOP
|
||||||
|
AT_CLEANUP
|
||||||
|
|
||||||
|
AT_SETUP([conntrack - IPv6 HTTP with NAT])
|
||||||
|
CHECK_CONNTRACK()
|
||||||
|
OVS_TRAFFIC_VSWITCHD_START()
|
||||||
|
|
||||||
|
ADD_NAMESPACES(at_ns0, at_ns1)
|
||||||
|
|
||||||
|
ADD_VETH(p0, at_ns0, br0, "fc00::1/96")
|
||||||
|
NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address 80:88:88:88:88:88])
|
||||||
|
ADD_VETH(p1, at_ns1, br0, "fc00::2/96")
|
||||||
|
NS_CHECK_EXEC([at_ns1], [ip -6 neigh add fc00::240 lladdr 80:88:88:88:88:88 dev p1])
|
||||||
|
|
||||||
|
dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0.
|
||||||
|
AT_DATA([flows.txt], [dnl
|
||||||
|
priority=1,action=drop
|
||||||
|
priority=10,icmp6,action=normal
|
||||||
|
priority=100,in_port=1,ip6,action=ct(commit,nat(src=fc00::240)),2
|
||||||
|
priority=100,in_port=2,ct_state=-trk,ip6,action=ct(nat,table=0)
|
||||||
|
priority=100,in_port=2,ct_state=+trk+est,ip6,action=1
|
||||||
|
priority=200,in_port=2,ct_state=+trk+new,icmp6,icmpv6_code=0,icmpv6_type=135,nd_target=fc00::240,action=ct(commit,nat(dst=fc00::1)),1
|
||||||
|
])
|
||||||
|
|
||||||
|
AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
|
||||||
|
|
||||||
|
dnl Without this sleep, we get occasional failures due to the following error:
|
||||||
|
dnl "connect: Cannot assign requested address"
|
||||||
|
sleep 2;
|
||||||
|
|
||||||
|
dnl HTTP requests from ns0->ns1 should work fine.
|
||||||
|
NETNS_DAEMONIZE([at_ns1], [[$PYTHON $srcdir/test-l7.py http6]], [http0.pid])
|
||||||
|
|
||||||
|
NS_CHECK_EXEC([at_ns0], [wget http://[[fc00::2]] -t 3 -T 1 --retry-connrefused -v -o wget0.log])
|
||||||
|
|
||||||
|
dnl HTTP requests from ns1->ns0 should fail due to network failure.
|
||||||
|
dnl Try 3 times, in 1 second intervals.
|
||||||
|
NETNS_DAEMONIZE([at_ns0], [[$PYTHON $srcdir/test-l7.py http6]], [http1.pid])
|
||||||
|
NS_CHECK_EXEC([at_ns1], [wget http://[[fc00::1]] -t 3 -T 1 -v -o wget1.log], [4])
|
||||||
|
|
||||||
|
OVS_TRAFFIC_VSWITCHD_STOP
|
||||||
|
AT_CLEANUP
|
||||||
|
|
||||||
|
|
||||||
|
AT_SETUP([conntrack - IPv6 FTP with NAT])
|
||||||
|
AT_SKIP_IF([test $HAVE_PYFTPDLIB = no])
|
||||||
|
CHECK_CONNTRACK()
|
||||||
|
OVS_TRAFFIC_VSWITCHD_START()
|
||||||
|
|
||||||
|
ADD_NAMESPACES(at_ns0, at_ns1)
|
||||||
|
|
||||||
|
ADD_VETH(p0, at_ns0, br0, "fc00::1/96")
|
||||||
|
NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address 80:88:88:88:88:88])
|
||||||
|
ADD_VETH(p1, at_ns1, br0, "fc00::2/96")
|
||||||
|
dnl Would be nice if NAT could translate neighbor discovery messages, too.
|
||||||
|
NS_CHECK_EXEC([at_ns1], [ip -6 neigh add fc00::240 lladdr 80:88:88:88:88:88 dev p1])
|
||||||
|
|
||||||
|
dnl Allow any traffic from ns0->ns1.
|
||||||
|
dnl Only allow nd, return traffic from ns1->ns0.
|
||||||
|
AT_DATA([flows.txt], [dnl
|
||||||
|
dnl Allow other ICMPv6 both ways (without commit).
|
||||||
|
table=1 priority=100 in_port=1 icmp6, action=2
|
||||||
|
table=1 priority=100 in_port=2 icmp6, action=1
|
||||||
|
dnl track all IPv6 traffic (this includes NAT & help to non-NEW packets.)
|
||||||
|
table=0 priority=10 ip6, action=ct(nat,table=1)
|
||||||
|
table=0 priority=0 action=drop
|
||||||
|
dnl
|
||||||
|
dnl Table 1
|
||||||
|
dnl
|
||||||
|
dnl Allow new TCPv6 FTP control connections.
|
||||||
|
table=1 in_port=1 ct_state=+new tcp6 ipv6_src=fc00::1 tp_dst=21 action=ct(alg=ftp,commit,nat(src=fc00::240)),2
|
||||||
|
dnl Allow related TCPv6 connections from port 2 to the NATted address.
|
||||||
|
table=1 in_port=2 ct_state=+new+rel tcp6 ipv6_dst=fc00::240 action=ct(commit,nat),1
|
||||||
|
dnl Allow established TCPv6 connections both ways, enforce NATting
|
||||||
|
table=1 in_port=1 ct_state=+est tcp6 ipv6_src=fc00::240 action=2
|
||||||
|
table=1 in_port=2 ct_state=+est tcp6 ipv6_dst=fc00::1 action=1
|
||||||
|
dnl Drop everything else.
|
||||||
|
table=1 priority=0, action=drop
|
||||||
|
])
|
||||||
|
|
||||||
|
AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
|
||||||
|
|
||||||
|
NETNS_DAEMONIZE([at_ns1], [[$PYTHON $srcdir/test-l7.py ftp]], [ftp0.pid])
|
||||||
|
|
||||||
|
dnl FTP requests from p0->p1 should work fine.
|
||||||
|
NS_CHECK_EXEC([at_ns0], [wget ftp://[[fc00::2]] -6 --no-passive-ftp -t 3 -T 1 --retry-connrefused -v --server-response --no-proxy --no-remove-listing -o wget0.log -d])
|
||||||
|
|
||||||
|
AT_CHECK([conntrack -L -f ipv6 2>&1 | FORMAT_CT(fc00::2) | grep -v "FIN" | grep -v "CLOSE"], [0], [dnl
|
||||||
|
TIME_WAIT src=fc00::1 dst=fc00::2 sport=<cleared> dport=<cleared> src=fc00::2 dst=fc00::240 sport=<cleared> dport=<cleared> [[ASSURED]] mark=0 helper=ftp use=2
|
||||||
|
TIME_WAIT src=fc00::2 dst=fc00::240 sport=<cleared> dport=<cleared> src=fc00::1 dst=fc00::2 sport=<cleared> dport=<cleared> [[ASSURED]] mark=0 use=1
|
||||||
|
])
|
||||||
|
|
||||||
|
OVS_TRAFFIC_VSWITCHD_STOP
|
||||||
|
AT_CLEANUP
|
||||||
|
Reference in New Issue
Block a user