mirror of
https://github.com/openvswitch/ovs
synced 2025-08-31 06:15:47 +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:
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);
|
||||
}
|
||||
|
||||
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[] = {
|
||||
[OVS_CT_ATTR_COMMIT] = { .type = NL_A_FLAG, .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 },
|
||||
[OVS_CT_ATTR_HELPER] = { .type = NL_A_STRING, .optional = true,
|
||||
.min_len = 1, .max_len = 16 },
|
||||
[OVS_CT_ATTR_NAT] = { .type = NL_A_UNSPEC, .optional = true },
|
||||
};
|
||||
|
||||
static void
|
||||
@@ -562,6 +707,7 @@ format_odp_conntrack_action(struct ds *ds, const struct nlattr *attr)
|
||||
const char *helper;
|
||||
uint16_t zone;
|
||||
bool commit;
|
||||
const struct nlattr *nat;
|
||||
|
||||
if (!nl_parse_nested(attr, ovs_conntrack_policy, a, ARRAY_SIZE(a))) {
|
||||
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;
|
||||
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;
|
||||
nat = a[OVS_CT_ATTR_NAT];
|
||||
|
||||
ds_put_format(ds, "ct");
|
||||
if (commit || zone || mark || label || helper) {
|
||||
if (commit || zone || mark || label || helper || nat) {
|
||||
ds_put_cstr(ds, "(");
|
||||
if (commit) {
|
||||
ds_put_format(ds, "commit,");
|
||||
@@ -595,6 +742,9 @@ format_odp_conntrack_action(struct ds *ds, const struct nlattr *attr)
|
||||
if (helper) {
|
||||
ds_put_format(ds, "helper=%s,", helper);
|
||||
}
|
||||
if (nat) {
|
||||
format_odp_ct_nat(ds, nat);
|
||||
}
|
||||
ds_chomp(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));
|
||||
ip = (struct ip_header *) l3;
|
||||
if (!ovs_scan_len(s, &n, "header(size=%"SCNi32",type=%"SCNi32","
|
||||
"eth(dst="ETH_ADDR_SCAN_FMT",",
|
||||
&data->header_len,
|
||||
&data->tnl_type,
|
||||
ETH_ADDR_SCAN_ARGS(eth->eth_dst))) {
|
||||
"eth(dst="ETH_ADDR_SCAN_FMT",",
|
||||
&data->header_len,
|
||||
&data->tnl_type,
|
||||
ETH_ADDR_SCAN_ARGS(eth->eth_dst))) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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 */
|
||||
if (!ovs_scan_len(s, &n, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT",proto=%"SCNi8
|
||||
",tos=%"SCNi8",ttl=%"SCNi8",frag=0x%"SCNx16"),",
|
||||
IP_SCAN_ARGS(&sip),
|
||||
IP_SCAN_ARGS(&dip),
|
||||
&ip->ip_proto, &ip->ip_tos,
|
||||
&ip->ip_ttl, &ip->ip_frag_off)) {
|
||||
",tos=%"SCNi8",ttl=%"SCNi8",frag=0x%"SCNx16"),",
|
||||
IP_SCAN_ARGS(&sip),
|
||||
IP_SCAN_ARGS(&dip),
|
||||
&ip->ip_proto, &ip->ip_tos,
|
||||
&ip->ip_ttl, &ip->ip_frag_off)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
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;
|
||||
greh = (struct gre_base_hdr *) l4;
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
} 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;
|
||||
greh->flags = htons(gre_flags);
|
||||
@@ -1030,6 +1180,176 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
|
||||
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
|
||||
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 mask;
|
||||
} ct_label;
|
||||
struct ct_nat_params nat_params;
|
||||
bool have_nat = false;
|
||||
size_t start;
|
||||
char *end;
|
||||
|
||||
@@ -1056,13 +1378,14 @@ parse_conntrack_action(const char *s_, struct ofpbuf *actions)
|
||||
s += 2;
|
||||
if (ovs_scan(s, "(")) {
|
||||
s++;
|
||||
find_end:
|
||||
end = strchr(s, ')');
|
||||
if (!end) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
while (s != end) {
|
||||
int n = -1;
|
||||
int n;
|
||||
|
||||
s += strspn(s, delimiters);
|
||||
if (ovs_scan(s, "commit%n", &n)) {
|
||||
@@ -1106,6 +1429,16 @@ parse_conntrack_action(const char *s_, struct ofpbuf *actions)
|
||||
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;
|
||||
}
|
||||
s++;
|
||||
@@ -1130,6 +1463,9 @@ parse_conntrack_action(const char *s_, struct ofpbuf *actions)
|
||||
nl_msg_put_string__(actions, OVS_CT_ATTR_HELPER, helper,
|
||||
helper_len);
|
||||
}
|
||||
if (have_nat) {
|
||||
nl_msg_put_ct_nat(&nat_params, actions);
|
||||
}
|
||||
nl_msg_end_nested(actions, start);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user