summaryrefslogtreecommitdiff
path: root/lib/netlink-conntrack.c
diff options
context:
space:
mode:
authorYi-Hung Wei <yihung.wei@gmail.com>2019-08-28 15:14:24 -0700
committerJustin Pettit <jpettit@ovn.org>2019-09-26 13:50:17 -0700
commit1f1613183733781084f001ce0116bd4d6eee3d32 (patch)
tree7c27f96969efc00434571bb727230dea5020c6b1 /lib/netlink-conntrack.c
parent934f54a1bcb6823e38db8c7dc6dda5af2fb38b0c (diff)
downloadopenvswitch-1f1613183733781084f001ce0116bd4d6eee3d32.tar.gz
ct-dpif, dpif-netlink: Add conntrack timeout policy support
This patch first defines the dpif interface for a datapath to support adding, deleting, getting and dumping conntrack timeout policy. The timeout policy is identified by a 4 bytes unsigned integer in datapath, and it currently support timeout for TCP, UDP, and ICMP protocols. Moreover, this patch provides the implementation for Linux kernel datapath in dpif-netlink. In Linux kernel, the timeout policy is maintained per L3/L4 protocol, and it is identified by 32 bytes null terminated string. On the other hand, in vswitchd, the timeout policy is a generic one that consists of all the supported L4 protocols. Therefore, one of the main task in dpif-netlink is to break down the generic timeout policy into 6 sub policies (ipv4 tcp, udp, icmp, and ipv6 tcp, udp, icmp), and push down the configuration using the netlink API in netlink-conntrack.c. This patch also adds missing symbols in the windows datapath so that the build on windows can pass. Appveyor CI: * https://ci.appveyor.com/project/YiHungWei/ovs/builds/26387754 Signed-off-by: Yi-Hung Wei <yihung.wei@gmail.com> Acked-by: Alin Gabriel Serdean <aserdean@ovn.org> Signed-off-by: Justin Pettit <jpettit@ovn.org>
Diffstat (limited to 'lib/netlink-conntrack.c')
-rw-r--r--lib/netlink-conntrack.c301
1 files changed, 301 insertions, 0 deletions
diff --git a/lib/netlink-conntrack.c b/lib/netlink-conntrack.c
index 7631ba5d5..86ab866cf 100644
--- a/lib/netlink-conntrack.c
+++ b/lib/netlink-conntrack.c
@@ -840,6 +840,307 @@ nl_ct_parse_helper(struct nlattr *nla, struct ct_dpif_helper *helper)
return parsed;
}
+static int nl_ct_timeout_policy_max_attr[] = {
+ [IPPROTO_TCP] = CTA_TIMEOUT_TCP_MAX,
+ [IPPROTO_UDP] = CTA_TIMEOUT_UDP_MAX,
+ [IPPROTO_ICMP] = CTA_TIMEOUT_ICMP_MAX,
+ [IPPROTO_ICMPV6] = CTA_TIMEOUT_ICMPV6_MAX
+};
+
+static void
+nl_ct_set_timeout_policy_attr(struct nl_ct_timeout_policy *nl_tp,
+ uint32_t attr, uint32_t val)
+{
+ nl_tp->present |= 1 << attr;
+ nl_tp->attrs[attr] = val;
+}
+
+static int
+nl_ct_parse_tcp_timeout_policy_data(struct nlattr *nla,
+ struct nl_ct_timeout_policy *nl_tp)
+{
+ static const struct nl_policy policy[] = {
+ [CTA_TIMEOUT_TCP_SYN_SENT] = { .type = NL_A_BE32,
+ .optional = false },
+ [CTA_TIMEOUT_TCP_SYN_RECV] = { .type = NL_A_BE32,
+ .optional = false },
+ [CTA_TIMEOUT_TCP_ESTABLISHED] = { .type = NL_A_BE32,
+ .optional = false },
+ [CTA_TIMEOUT_TCP_FIN_WAIT] = { .type = NL_A_BE32,
+ .optional = false },
+ [CTA_TIMEOUT_TCP_CLOSE_WAIT] = { .type = NL_A_BE32,
+ .optional = false },
+ [CTA_TIMEOUT_TCP_LAST_ACK] = { .type = NL_A_BE32,
+ .optional = false },
+ [CTA_TIMEOUT_TCP_TIME_WAIT] = { .type = NL_A_BE32,
+ .optional = false },
+ [CTA_TIMEOUT_TCP_CLOSE] = { .type = NL_A_BE32,
+ .optional = false },
+ [CTA_TIMEOUT_TCP_SYN_SENT2] = { .type = NL_A_BE32,
+ .optional = false },
+ [CTA_TIMEOUT_TCP_RETRANS] = { .type = NL_A_BE32,
+ .optional = false },
+ [CTA_TIMEOUT_TCP_UNACK] = { .type = NL_A_BE32,
+ .optional = false },
+ };
+ struct nlattr *attrs[ARRAY_SIZE(policy)];
+
+ if (!nl_parse_nested(nla, policy, attrs, ARRAY_SIZE(policy))) {
+ VLOG_ERR_RL(&rl, "Could not parse nested tcp timeout options. "
+ "Possibly incompatible Linux kernel version.");
+ return EINVAL;
+ }
+
+ for (int i = CTA_TIMEOUT_TCP_SYN_SENT; i <= CTA_TIMEOUT_TCP_UNACK; i++) {
+ nl_ct_set_timeout_policy_attr(nl_tp, i,
+ ntohl(nl_attr_get_be32(attrs[i])));
+ }
+ return 0;
+}
+
+static int
+nl_ct_parse_udp_timeout_policy_data(struct nlattr *nla,
+ struct nl_ct_timeout_policy *nl_tp)
+{
+ static const struct nl_policy policy[] = {
+ [CTA_TIMEOUT_UDP_UNREPLIED] = { .type = NL_A_BE32,
+ .optional = false },
+ [CTA_TIMEOUT_UDP_REPLIED] = { .type = NL_A_BE32,
+ .optional = false },
+ };
+ struct nlattr *attrs[ARRAY_SIZE(policy)];
+
+ if (!nl_parse_nested(nla, policy, attrs, ARRAY_SIZE(policy))) {
+ VLOG_ERR_RL(&rl, "Could not parse nested tcp timeout options. "
+ "Possibly incompatible Linux kernel version.");
+ return EINVAL;
+ }
+
+ for (int i = CTA_TIMEOUT_UDP_UNREPLIED; i <= CTA_TIMEOUT_UDP_REPLIED;
+ i++) {
+ nl_ct_set_timeout_policy_attr(nl_tp, i,
+ ntohl(nl_attr_get_be32(attrs[i])));
+ }
+ return 0;
+}
+
+static int
+nl_ct_parse_icmp_timeout_policy_data(struct nlattr *nla,
+ struct nl_ct_timeout_policy *nl_tp)
+{
+ static const struct nl_policy policy[] = {
+ [CTA_TIMEOUT_ICMP_TIMEOUT] = { .type = NL_A_BE32,
+ .optional = false },
+ };
+ struct nlattr *attrs[ARRAY_SIZE(policy)];
+
+ if (!nl_parse_nested(nla, policy, attrs, ARRAY_SIZE(policy))) {
+ VLOG_ERR_RL(&rl, "Could not parse nested icmp timeout options. "
+ "Possibly incompatible Linux kernel version.");
+ return EINVAL;
+ }
+
+ nl_ct_set_timeout_policy_attr(
+ nl_tp, CTA_TIMEOUT_ICMP_TIMEOUT,
+ ntohl(nl_attr_get_be32(attrs[CTA_TIMEOUT_ICMP_TIMEOUT])));
+ return 0;
+}
+
+static int
+nl_ct_parse_icmpv6_timeout_policy_data(struct nlattr *nla,
+ struct nl_ct_timeout_policy *nl_tp)
+{
+ static const struct nl_policy policy[] = {
+ [CTA_TIMEOUT_ICMPV6_TIMEOUT] = { .type = NL_A_BE32,
+ .optional = false },
+ };
+ struct nlattr *attrs[ARRAY_SIZE(policy)];
+
+ if (!nl_parse_nested(nla, policy, attrs, ARRAY_SIZE(policy))) {
+ VLOG_ERR_RL(&rl, "Could not parse nested icmpv6 timeout options. "
+ "Possibly incompatible Linux kernel version.");
+ return EINVAL;
+ }
+
+ nl_ct_set_timeout_policy_attr(
+ nl_tp, CTA_TIMEOUT_ICMPV6_TIMEOUT,
+ ntohl(nl_attr_get_be32(attrs[CTA_TIMEOUT_ICMPV6_TIMEOUT])));
+ return 0;
+}
+
+static int
+nl_ct_parse_timeout_policy_data(struct nlattr *nla,
+ struct nl_ct_timeout_policy *nl_tp)
+{
+ switch (nl_tp->l4num) {
+ case IPPROTO_TCP:
+ return nl_ct_parse_tcp_timeout_policy_data(nla, nl_tp);
+ case IPPROTO_UDP:
+ return nl_ct_parse_udp_timeout_policy_data(nla, nl_tp);
+ case IPPROTO_ICMP:
+ return nl_ct_parse_icmp_timeout_policy_data(nla, nl_tp);
+ case IPPROTO_ICMPV6:
+ return nl_ct_parse_icmpv6_timeout_policy_data(nla, nl_tp);
+ default:
+ return EINVAL;
+ }
+}
+
+static int
+nl_ct_timeout_policy_from_ofpbuf(struct ofpbuf *buf,
+ struct nl_ct_timeout_policy *nl_tp,
+ bool default_tp)
+{
+ static const struct nl_policy policy[] = {
+ [CTA_TIMEOUT_NAME] = { .type = NL_A_STRING, .optional = false },
+ [CTA_TIMEOUT_L3PROTO] = { .type = NL_A_BE16, .optional = false },
+ [CTA_TIMEOUT_L4PROTO] = { .type = NL_A_U8, .optional = false },
+ [CTA_TIMEOUT_DATA] = { .type = NL_A_NESTED, .optional = false }
+ };
+ static const struct nl_policy policy_default_tp[] = {
+ [CTA_TIMEOUT_L3PROTO] = { .type = NL_A_BE16, .optional = false },
+ [CTA_TIMEOUT_L4PROTO] = { .type = NL_A_U8, .optional = false },
+ [CTA_TIMEOUT_DATA] = { .type = NL_A_NESTED, .optional = false }
+ };
+
+ struct nlattr *attrs[ARRAY_SIZE(policy)];
+ struct ofpbuf b = ofpbuf_const_initializer(buf->data, buf->size);
+ struct nlmsghdr *nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg);
+ struct nfgenmsg *nfmsg = ofpbuf_try_pull(&b, sizeof *nfmsg);
+
+ if (!nlmsg || !nfmsg
+ || NFNL_SUBSYS_ID(nlmsg->nlmsg_type) != NFNL_SUBSYS_CTNETLINK_TIMEOUT
+ || nfmsg->version != NFNETLINK_V0
+ || !nl_policy_parse(&b, 0, default_tp ? policy_default_tp : policy,
+ attrs, default_tp ? ARRAY_SIZE(policy_default_tp) :
+ ARRAY_SIZE(policy))) {
+ return EINVAL;
+ }
+
+ if (!default_tp) {
+ ovs_strlcpy(nl_tp->name, nl_attr_get_string(attrs[CTA_TIMEOUT_NAME]),
+ sizeof nl_tp->name);
+ }
+ nl_tp->l3num = ntohs(nl_attr_get_be16(attrs[CTA_TIMEOUT_L3PROTO]));
+ nl_tp->l4num = nl_attr_get_u8(attrs[CTA_TIMEOUT_L4PROTO]);
+ nl_tp->present = 0;
+
+ return nl_ct_parse_timeout_policy_data(attrs[CTA_TIMEOUT_DATA], nl_tp);
+}
+
+int
+nl_ct_set_timeout_policy(const struct nl_ct_timeout_policy *nl_tp)
+{
+ struct ofpbuf buf;
+ size_t offset;
+
+ ofpbuf_init(&buf, 512);
+ nl_msg_put_nfgenmsg(&buf, 0, AF_UNSPEC, NFNL_SUBSYS_CTNETLINK_TIMEOUT,
+ IPCTNL_MSG_TIMEOUT_NEW, NLM_F_REQUEST | NLM_F_CREATE
+ | NLM_F_ACK | NLM_F_REPLACE);
+
+ nl_msg_put_string(&buf, CTA_TIMEOUT_NAME, nl_tp->name);
+ nl_msg_put_be16(&buf, CTA_TIMEOUT_L3PROTO, htons(nl_tp->l3num));
+ nl_msg_put_u8(&buf, CTA_TIMEOUT_L4PROTO, nl_tp->l4num);
+
+ offset = nl_msg_start_nested(&buf, CTA_TIMEOUT_DATA);
+ for (int i = 1; i <= nl_ct_timeout_policy_max_attr[nl_tp->l4num]; ++i) {
+ if (nl_tp->present & 1 << i) {
+ nl_msg_put_be32(&buf, i, htonl(nl_tp->attrs[i]));
+ }
+ }
+ nl_msg_end_nested(&buf, offset);
+
+ int err = nl_transact(NETLINK_NETFILTER, &buf, NULL);
+ ofpbuf_uninit(&buf);
+ return err;
+}
+
+int
+nl_ct_get_timeout_policy(const char *tp_name,
+ struct nl_ct_timeout_policy *nl_tp)
+{
+ struct ofpbuf request, *reply;
+
+ ofpbuf_init(&request, 512);
+ nl_msg_put_nfgenmsg(&request, 0, AF_UNSPEC, NFNL_SUBSYS_CTNETLINK_TIMEOUT,
+ IPCTNL_MSG_TIMEOUT_GET, NLM_F_REQUEST | NLM_F_ACK);
+ nl_msg_put_string(&request, CTA_TIMEOUT_NAME, tp_name);
+ int err = nl_transact(NETLINK_NETFILTER, &request, &reply);
+ if (err) {
+ goto out;
+ }
+
+ err = nl_ct_timeout_policy_from_ofpbuf(reply, nl_tp, false);
+
+out:
+ ofpbuf_uninit(&request);
+ ofpbuf_delete(reply);
+ return err;
+}
+
+int
+nl_ct_del_timeout_policy(const char *tp_name)
+{
+ struct ofpbuf buf;
+
+ ofpbuf_init(&buf, 64);
+ nl_msg_put_nfgenmsg(&buf, 0, AF_UNSPEC, NFNL_SUBSYS_CTNETLINK_TIMEOUT,
+ IPCTNL_MSG_TIMEOUT_DELETE, NLM_F_REQUEST | NLM_F_ACK);
+
+ nl_msg_put_string(&buf, CTA_TIMEOUT_NAME, tp_name);
+ int err = nl_transact(NETLINK_NETFILTER, &buf, NULL);
+ ofpbuf_uninit(&buf);
+ return err;
+}
+
+struct nl_ct_timeout_policy_dump_state {
+ struct nl_dump dump;
+ struct ofpbuf buf;
+};
+
+int
+nl_ct_timeout_policy_dump_start(
+ struct nl_ct_timeout_policy_dump_state **statep)
+{
+ struct ofpbuf request;
+ struct nl_ct_timeout_policy_dump_state *state;
+
+ *statep = state = xzalloc(sizeof *state);
+ ofpbuf_init(&request, 512);
+ nl_msg_put_nfgenmsg(&request, 0, AF_UNSPEC, NFNL_SUBSYS_CTNETLINK_TIMEOUT,
+ IPCTNL_MSG_TIMEOUT_GET,
+ NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);
+
+ nl_dump_start(&state->dump, NETLINK_NETFILTER, &request);
+ ofpbuf_uninit(&request);
+ ofpbuf_init(&state->buf, NL_DUMP_BUFSIZE);
+ return 0;
+}
+
+int
+nl_ct_timeout_policy_dump_next(struct nl_ct_timeout_policy_dump_state *state,
+ struct nl_ct_timeout_policy *nl_tp)
+{
+ struct ofpbuf reply;
+
+ if (!nl_dump_next(&state->dump, &reply, &state->buf)) {
+ return EOF;
+ }
+ int err = nl_ct_timeout_policy_from_ofpbuf(&reply, nl_tp, false);
+ ofpbuf_uninit(&reply);
+ return err;
+}
+
+int
+nl_ct_timeout_policy_dump_done(struct nl_ct_timeout_policy_dump_state *state)
+{
+ int err = nl_dump_done(&state->dump);
+ ofpbuf_uninit(&state->buf);
+ free(state);
+ return err;
+}
+
/* Translate netlink entry status flags to CT_DPIF_TCP status flags. */
static uint32_t
ips_status_to_dpif_flags(uint32_t status)