diff --git a/lib/conntrack.c b/lib/conntrack.c
index f5a3aa9..b370384 100644
--- a/lib/conntrack.c
+++ b/lib/conntrack.c
@@ -67,6 +67,12 @@ enum ct_alg_mode {
     CT_TFTP_MODE,
 };
 
+enum ct_alg_ctl_type {
+    CT_ALG_CTL_NONE,
+    CT_ALG_CTL_FTP,
+    CT_ALG_CTL_TFTP,
+};
+
 static bool conn_key_extract(struct conntrack *, struct dp_packet *,
                              ovs_be16 dl_type, struct conn_lookup_ctx *,
                              uint16_t zone);
@@ -142,6 +148,13 @@ static enum ftp_ctl_pkt
 detect_ftp_ctl_type(const struct conn_lookup_ctx *ctx,
                     struct dp_packet *pkt);
 
+static struct ct_l4_proto *l4_protos[] = {
+    [IPPROTO_TCP] = &ct_proto_tcp,
+    [IPPROTO_UDP] = &ct_proto_other,
+    [IPPROTO_ICMP] = &ct_proto_icmp4,
+    [IPPROTO_ICMPV6] = &ct_proto_icmp6,
+};
+
 static void
 handle_ftp_ctl(struct conntrack *ct, const struct conn_lookup_ctx *ctx,
                struct dp_packet *pkt,
@@ -150,14 +163,23 @@ handle_ftp_ctl(struct conntrack *ct, const struct conn_lookup_ctx *ctx,
 
 static void
 handle_tftp_ctl(struct conntrack *ct,
+                const struct conn_lookup_ctx *ctx OVS_UNUSED,
+                struct dp_packet *pkt OVS_UNUSED,
                 const struct conn *conn_for_expectation,
-                long long now);
-
-static struct ct_l4_proto *l4_protos[] = {
-    [IPPROTO_TCP] = &ct_proto_tcp,
-    [IPPROTO_UDP] = &ct_proto_other,
-    [IPPROTO_ICMP] = &ct_proto_icmp4,
-    [IPPROTO_ICMPV6] = &ct_proto_icmp6,
+                long long now, enum ftp_ctl_pkt ftp_ctl OVS_UNUSED,
+                bool nat OVS_UNUSED);
+
+typedef void (*alg_helper)(struct conntrack *ct,
+                           const struct conn_lookup_ctx *ctx,
+                           struct dp_packet *pkt,
+                           const struct conn *conn_for_expectation,
+                           long long now, enum ftp_ctl_pkt ftp_ctl,
+                           bool nat);
+
+static alg_helper alg_helpers[] = {
+    [CT_ALG_CTL_NONE] = NULL,
+    [CT_ALG_CTL_FTP] = handle_ftp_ctl,
+    [CT_ALG_CTL_TFTP] = handle_tftp_ctl,
 };
 
 long long ct_timeout_val[] = {
@@ -434,35 +456,45 @@ get_ip_proto(const struct dp_packet *pkt)
 }
 
 static bool
-is_ftp_ctl(const struct dp_packet *pkt)
+is_ftp_ctl(const enum ct_alg_ctl_type ct_alg_ctl)
 {
-    uint8_t ip_proto = get_ip_proto(pkt);
-    struct tcp_header *th = dp_packet_l4(pkt);
-
-    /* CT_IPPORT_FTP is used because IPPORT_FTP in not defined in OSX,
-     * at least in in.h. Since this value will never change, just remove
-     * the external dependency. */
-#define CT_IPPORT_FTP 21
-
-    return (ip_proto == IPPROTO_TCP &&
-            (th->tcp_src == htons(CT_IPPORT_FTP) ||
-             th->tcp_dst == htons(CT_IPPORT_FTP)));
-
+    return ct_alg_ctl == CT_ALG_CTL_FTP;
 }
 
-static bool
-is_tftp_ctl(const struct dp_packet *pkt)
+static enum ct_alg_ctl_type
+get_alg_ctl_type(const struct dp_packet *pkt)
 {
     uint8_t ip_proto = get_ip_proto(pkt);
     struct udp_header *uh = dp_packet_l4(pkt);
+    struct tcp_header *th = dp_packet_l4(pkt);
 
-    /* CT_IPPORT_TFTP is used because IPPORT_TFTP in not defined in OSX,
-     * at least in in.h. Since this value will never change, remove
+    /* CT_IPPORT_FTP/TFTP is used because IPPORT_FTP/TFTP in not defined
+     * in OSX, at least in in.h. Since these values will never change, remove
      * the external dependency. */
-#define CT_IPPORT_TFTP 69
-    return (ip_proto == IPPROTO_UDP &&
-            uh->udp_dst == htons(CT_IPPORT_TFTP));
+    enum { CT_IPPORT_FTP = 21 };
+    enum { CT_IPPORT_TFTP = 69 };
 
+    if (ip_proto == IPPROTO_UDP && uh->udp_dst == htons(CT_IPPORT_TFTP)) {
+        return CT_ALG_CTL_TFTP;
+    } else if (ip_proto == IPPROTO_TCP &&
+               (th->tcp_src == htons(CT_IPPORT_FTP) ||
+                th->tcp_dst == htons(CT_IPPORT_FTP))) {
+        return CT_ALG_CTL_FTP;
+    }
+    return CT_ALG_CTL_NONE;
+}
+
+static void
+handle_alg_ctl(struct conntrack *ct, const struct conn_lookup_ctx *ctx,
+               struct dp_packet *pkt, enum ct_alg_ctl_type ct_alg_ctl,
+               const struct conn *conn, long long now, bool nat,
+               const struct conn *conn_for_expectation)
+{
+    /* ALG control packet handling with expectation creation. */
+    if (OVS_UNLIKELY(alg_helpers[ct_alg_ctl] && conn)) {
+        alg_helpers[ct_alg_ctl](ct, ctx, pkt, conn_for_expectation, now,
+                                CT_FTP_CTL_INTEREST, nat);
+    }
 }
 
 static void
@@ -1069,6 +1101,33 @@ is_un_nat_conn_valid(const struct conn *un_nat_conn)
     return un_nat_conn->conn_type == CT_CONN_TYPE_UN_NAT;
 }
 
+static bool
+conn_update_state_alg(struct conntrack *ct, struct dp_packet *pkt,
+                      struct conn_lookup_ctx *ctx, struct conn *conn,
+                      const struct nat_action_info_t *nat_action_info,
+                      enum ct_alg_ctl_type ct_alg_ctl, long long now,
+                      unsigned bucket, bool *create_new_conn)
+    OVS_REQUIRES(ct->buckets[bucket].lock)
+{
+    if (is_ftp_ctl(ct_alg_ctl)) {
+        /* Keep sequence tracking in sync with the source of the
+         * sequence skew. */
+        if (ctx->reply != conn->seq_skew_dir) {
+            handle_ftp_ctl(ct, ctx, pkt, conn, now, CT_FTP_CTL_OTHER,
+                           !!nat_action_info);
+            *create_new_conn = conn_update_state(ct, pkt, ctx, &conn, now,
+                                                bucket);
+        } else {
+            *create_new_conn = conn_update_state(ct, pkt, ctx, &conn, now,
+                                                bucket);
+            handle_ftp_ctl(ct, ctx, pkt, conn, now, CT_FTP_CTL_OTHER,
+                           !!nat_action_info);
+        }
+        return true;
+    }
+    return false;
+}
+
 static void
 process_one(struct conntrack *ct, struct dp_packet *pkt,
             struct conn_lookup_ctx *ctx, uint16_t zone,
@@ -1121,24 +1180,14 @@ process_one(struct conntrack *ct, struct dp_packet *pkt,
     bool create_new_conn = false;
     struct conn conn_for_un_nat_copy;
     conn_for_un_nat_copy.conn_type = CT_CONN_TYPE_DEFAULT;
-    bool ftp_ctl = is_ftp_ctl(pkt);
+
+    enum ct_alg_ctl_type ct_alg_ctl = get_alg_ctl_type(pkt);
 
     if (OVS_LIKELY(conn)) {
-        if (ftp_ctl) {
-            /* Keep sequence tracking in sync with the source of the
-             * sequence skew. */
-            if (ctx->reply != conn->seq_skew_dir) {
-                handle_ftp_ctl(ct, ctx, pkt, conn, now, CT_FTP_CTL_OTHER,
-                               !!nat_action_info);
-                create_new_conn = conn_update_state(ct, pkt, ctx, &conn, now,
-                                                    bucket);
-            } else {
-                create_new_conn = conn_update_state(ct, pkt, ctx, &conn, now,
-                                                    bucket);
-                handle_ftp_ctl(ct, ctx, pkt, conn, now, CT_FTP_CTL_OTHER,
-                               !!nat_action_info);
-            }
-        } else {
+        if (OVS_LIKELY(!conn_update_state_alg(ct, pkt, ctx, conn,
+                                              nat_action_info,
+                                              ct_alg_ctl, now, bucket,
+                                              &create_new_conn))) {
             create_new_conn = conn_update_state(ct, pkt, ctx, &conn, now,
                                                 bucket);
         }
@@ -1187,9 +1236,8 @@ process_one(struct conntrack *ct, struct dp_packet *pkt,
         set_label(pkt, conn, &setlabel[0], &setlabel[1]);
     }
 
-    bool tftp_ctl = is_tftp_ctl(pkt);
     struct conn conn_for_expectation;
-    if (OVS_UNLIKELY((ftp_ctl || tftp_ctl) && conn)) {
+    if (OVS_UNLIKELY((ct_alg_ctl != CT_ALG_CTL_NONE) && conn)) {
         conn_for_expectation = *conn;
     }
 
@@ -1199,13 +1247,8 @@ process_one(struct conntrack *ct, struct dp_packet *pkt,
         create_un_nat_conn(ct, &conn_for_un_nat_copy, now, !!alg_exp);
     }
 
-    /* FTP control packet handling with expectation creation. */
-    if (OVS_UNLIKELY(ftp_ctl && conn)) {
-        handle_ftp_ctl(ct, ctx, pkt, &conn_for_expectation,
-                       now, CT_FTP_CTL_INTEREST, !!nat_action_info);
-    } else if (OVS_UNLIKELY(tftp_ctl && conn)) {
-        handle_tftp_ctl(ct, &conn_for_expectation, now);
-    }
+    handle_alg_ctl(ct, ctx, pkt, ct_alg_ctl, conn, now, !!nat_action_info,
+                   &conn_for_expectation);
 }
 
 /* Sends the packets in '*pkt_batch' through the connection tracker 'ct'.  All
@@ -1235,8 +1278,8 @@ conntrack_execute(struct conntrack *ct, struct dp_packet_batch *pkt_batch,
             write_ct_md(packet, zone, NULL, NULL, NULL);
             continue;
         }
-        process_one(ct, packet, &ctx, zone, force, commit,
-                    now, setmark, setlabel, nat_action_info, helper);
+        process_one(ct, packet, &ctx, zone, force, commit, now, setmark,
+                    setlabel, nat_action_info, helper);
     }
 
     return 0;
@@ -3099,8 +3142,11 @@ handle_ftp_ctl(struct conntrack *ct, const struct conn_lookup_ctx *ctx,
 
 static void
 handle_tftp_ctl(struct conntrack *ct,
+                const struct conn_lookup_ctx *ctx OVS_UNUSED,
+                struct dp_packet *pkt OVS_UNUSED,
                 const struct conn *conn_for_expectation,
-                long long now)
+                long long now, enum ftp_ctl_pkt ftp_ctl OVS_UNUSED,
+                bool nat OVS_UNUSED)
 {
     expectation_create(ct, conn_for_expectation->key.src.port, now,
                        CT_TFTP_MODE, conn_for_expectation);
