@@ -776,6 +776,39 @@ parse_tc_flower_to_match(struct tc_flower *flower,
ct_label->mask = action->ct.label_mask;
}
+ if (action->ct.nat_type) {
+ size_t nat_offset = nl_msg_start_nested(buf, OVS_CT_ATTR_NAT);
+
+ if (action->ct.nat_type == TC_NAT_SRC) {
+ nl_msg_put_flag(buf, OVS_NAT_ATTR_SRC);
+ } else if (action->ct.nat_type == TC_NAT_DST) {
+ nl_msg_put_flag(buf, OVS_NAT_ATTR_DST);
+ }
+
+ if (action->ct.range.ip_family == AF_INET) {
+ nl_msg_put_be32(buf, OVS_NAT_ATTR_IP_MIN,
+ action->ct.range.min_addr.ipv4);
+ nl_msg_put_be32(buf, OVS_NAT_ATTR_IP_MAX,
+ action->ct.range.max_addr.ipv4);
+ } else if (action->ct.range.ip_family == AF_INET6) {
+ nl_msg_put_in6_addr(buf, OVS_NAT_ATTR_IP_MIN,
+ &action->ct.range.min_addr.ipv6);
+ nl_msg_put_in6_addr(buf, OVS_NAT_ATTR_IP_MAX,
+ &action->ct.range.max_addr.ipv6);
+ }
+
+ if (action->ct.range.min_port) {
+ nl_msg_put_be16(buf, OVS_NAT_ATTR_PROTO_MIN,
+ action->ct.range.min_port);
+ if (action->ct.range.max_port) {
+ nl_msg_put_be16(buf, OVS_NAT_ATTR_PROTO_MAX,
+ action->ct.range.max_port);
+ }
+ }
+
+ nl_msg_end_nested(buf, nat_offset);
+ }
+
nl_msg_end_nested(buf, ct_offset);
}
break;
@@ -851,6 +884,66 @@ netdev_tc_flow_dump_next(struct netdev_flow_dump *dump,
}
static int
+parse_put_flow_nat_action(struct tc_action *action,
+ const struct nlattr *nat,
+ size_t nat_len)
+{
+ const struct nlattr *nat_attr;
+ size_t nat_left;
+
+ action->ct.nat_type = TC_NAT_RESTORE;
+ NL_ATTR_FOR_EACH_UNSAFE(nat_attr, nat_left, nat, nat_len) {
+ switch (nl_attr_type(nat_attr)) {
+ case OVS_NAT_ATTR_SRC: {
+ action->ct.nat_type = TC_NAT_SRC;
+ };
+ break;
+ case OVS_NAT_ATTR_DST: {
+ action->ct.nat_type = TC_NAT_DST;
+ };
+ break;
+ case OVS_NAT_ATTR_IP_MIN: {
+ if (nl_attr_get_size(nat_attr) == sizeof(ovs_be32)) {
+ ovs_be32 addr = nl_attr_get_be32(nat_attr);
+
+ action->ct.range.min_addr.ipv4 = addr;
+ action->ct.range.ip_family = AF_INET;
+ } else {
+ struct in6_addr addr = nl_attr_get_in6_addr(nat_attr);
+
+ action->ct.range.min_addr.ipv6 = addr;
+ action->ct.range.ip_family = AF_INET6;
+ }
+ };
+ break;
+ case OVS_NAT_ATTR_IP_MAX: {
+ if (nl_attr_get_size(nat_attr) == sizeof(ovs_be32)) {
+ ovs_be32 addr = nl_attr_get_be32(nat_attr);
+
+ action->ct.range.max_addr.ipv4 = addr;
+ action->ct.range.ip_family = AF_INET;
+ } else {
+ struct in6_addr addr = nl_attr_get_in6_addr(nat_attr);
+
+ action->ct.range.max_addr.ipv6 = addr;
+ action->ct.range.ip_family = AF_INET6;
+ }
+ };
+ break;
+ case OVS_NAT_ATTR_PROTO_MIN: {
+ action->ct.range.min_port = nl_attr_get_be16(nat_attr);
+ };
+ break;
+ case OVS_NAT_ATTR_PROTO_MAX: {
+ action->ct.range.max_port = nl_attr_get_be16(nat_attr);
+ };
+ break;
+ }
+ }
+ return 0;
+}
+
+static int
parse_put_flow_ct_action(struct tc_flower *flower,
struct tc_action *action,
const struct nlattr *ct,
@@ -870,6 +963,16 @@ parse_put_flow_ct_action(struct tc_flower *flower,
action->ct.zone = nl_attr_get_u16(ct_attr);
}
break;
+ case OVS_CT_ATTR_NAT: {
+ const struct nlattr *nat = nl_attr_get(ct_attr);
+ const size_t nat_len = nl_attr_get_size(ct_attr);
+
+ err = parse_put_flow_nat_action(action, nat, nat_len);
+ if (err) {
+ return err;
+ }
+ }
+ break;
case OVS_CT_ATTR_MARK: {
const struct {
uint32_t key;
@@ -1264,6 +1264,18 @@ static const struct nl_policy ct_policy[] = {
.optional = true, },
[TCA_CT_LABELS_MASK] = { .type = NL_A_UNSPEC,
.optional = true, },
+ [TCA_CT_NAT_IPV4_MIN] = { .type = NL_A_U32,
+ .optional = true, },
+ [TCA_CT_NAT_IPV4_MAX] = { .type = NL_A_U32,
+ .optional = true, },
+ [TCA_CT_NAT_IPV6_MIN] = { .min_len = sizeof(struct in6_addr),
+ .optional = true, },
+ [TCA_CT_NAT_IPV6_MAX] = { .min_len = sizeof(struct in6_addr),
+ .optional = true, },
+ [TCA_CT_NAT_PORT_MIN] = { .type = NL_A_U16,
+ .optional = true, },
+ [TCA_CT_NAT_PORT_MAX] = { .type = NL_A_U16,
+ .optional = true, },
};
static int
@@ -1306,6 +1318,47 @@ nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower)
action->ct.label_mask = label_mask ?
nl_attr_get_u128(label_mask) : OVS_U128_ZERO;
+ if (ct_action & TCA_CT_ACT_NAT) {
+ struct nlattr *ipv4_min = ct_attrs[TCA_CT_NAT_IPV4_MIN];
+ struct nlattr *ipv4_max = ct_attrs[TCA_CT_NAT_IPV4_MAX];
+ struct nlattr *ipv6_min = ct_attrs[TCA_CT_NAT_IPV6_MIN];
+ struct nlattr *ipv6_max = ct_attrs[TCA_CT_NAT_IPV6_MAX];
+ struct nlattr *min_port = ct_attrs[TCA_CT_NAT_PORT_MIN];
+ struct nlattr *max_port = ct_attrs[TCA_CT_NAT_PORT_MAX];
+
+ action->ct.nat_type = TC_NAT_RESTORE;
+ if (ct_action & TCA_CT_ACT_NAT_SRC) {
+ action->ct.nat_type = TC_NAT_SRC;
+ } else if (ct_action & TCA_CT_ACT_NAT_DST) {
+ action->ct.nat_type = TC_NAT_DST;
+ }
+
+ if (ipv4_min) {
+ action->ct.range.ip_family = AF_INET;
+ action->ct.range.min_addr.ipv4 = nl_attr_get_be32(ipv4_min);
+ if (ipv4_max) {
+ ovs_be32 port = nl_attr_get_be32(ipv4_max);
+
+ action->ct.range.max_addr.ipv4 = port;
+ }
+ } else if (ipv6_min) {
+ action->ct.range.ip_family = AF_INET6;
+ action->ct.range.min_addr.ipv6
+ = nl_attr_get_in6_addr(ipv6_min);
+ if (ipv6_max) {
+ struct in6_addr addr = nl_attr_get_in6_addr(ipv6_max);
+
+ action->ct.range.max_addr.ipv6 = addr;
+ }
+ }
+
+ if (min_port) {
+ action->ct.range.min_port = nl_attr_get_be16(min_port);
+ if (max_port) {
+ action->ct.range.max_port = nl_attr_get_be16(max_port);
+ }
+ }
+ }
}
action->type = TC_ACT_CT;
@@ -1882,6 +1935,37 @@ nl_msg_put_act_ct(struct ofpbuf *request, struct tc_action *action)
ct_action |= TCA_CT_ACT_FORCE;
}
}
+
+ if (action->ct.nat_type) {
+ ct_action |= TCA_CT_ACT_NAT;
+
+ if (action->ct.nat_type == TC_NAT_SRC) {
+ ct_action |= TCA_CT_ACT_NAT_SRC;
+ } else if (action->ct.nat_type == TC_NAT_DST) {
+ ct_action |= TCA_CT_ACT_NAT_DST;
+ }
+
+ if (action->ct.range.ip_family == AF_INET) {
+ nl_msg_put_be32(request, TCA_CT_NAT_IPV4_MIN,
+ action->ct.range.min_addr.ipv4);
+ nl_msg_put_be32(request, TCA_CT_NAT_IPV4_MAX,
+ action->ct.range.max_addr.ipv4);
+ } else if (action->ct.range.ip_family == AF_INET6) {
+ nl_msg_put_in6_addr(request, TCA_CT_NAT_IPV6_MIN,
+ &action->ct.range.min_addr.ipv6);
+ nl_msg_put_in6_addr(request, TCA_CT_NAT_IPV6_MAX,
+ &action->ct.range.max_addr.ipv6);
+ }
+
+ if (action->ct.range.min_port) {
+ nl_msg_put_be16(request, TCA_CT_NAT_PORT_MIN,
+ action->ct.range.min_port);
+ if (action->ct.range.max_port) {
+ nl_msg_put_be16(request, TCA_CT_NAT_PORT_MAX,
+ action->ct.range.max_port);
+ }
+ }
+ }
} else {
ct_action = TCA_CT_ACT_CLEAR;
}
@@ -162,6 +162,13 @@ enum tc_action_type {
TC_ACT_CT,
};
+enum nat_type {
+ TC_NO_NAT = 0,
+ TC_NAT_SRC,
+ TC_NAT_DST,
+ TC_NAT_RESTORE,
+};
+
struct tc_action {
union {
int chain;
@@ -202,6 +209,23 @@ struct tc_action {
uint32_t mark_mask;
ovs_u128 label;
ovs_u128 label_mask;
+ uint8_t nat_type;
+ struct {
+ uint8_t ip_family;
+
+ union {
+ struct in6_addr ipv6;
+ ovs_be32 ipv4;
+ } min_addr;
+
+ union {
+ struct in6_addr ipv6;
+ ovs_be32 ipv4;
+ } max_addr;
+
+ ovs_be16 min_port;
+ ovs_be16 max_port;
+ } range;
bool clear;
bool force;
bool commit;
Signed-off-by: Paul Blakey <paulb@mellanox.com> --- lib/netdev-offload-tc.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/tc.c | 84 +++++++++++++++++++++++++++++++++++++++ lib/tc.h | 24 +++++++++++ 3 files changed, 211 insertions(+)