diff --git a/lib/automake.mk b/lib/automake.mk
index effe5b5c2..0dc068b9e 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -51,6 +51,8 @@ lib_libopenvswitch_la_SOURCES = \
 	lib/connectivity.h \
 	lib/conntrack-icmp.c \
 	lib/conntrack-private.h \
+	lib/conntrack-sip.c \
+	lib/conntrack-sip.h \
 	lib/conntrack-tcp.c \
 	lib/conntrack-other.c \
 	lib/conntrack.c \
diff --git a/lib/conntrack-sip.c b/lib/conntrack-sip.c
new file mode 100644
index 000000000..7239bdad6
--- /dev/null
+++ b/lib/conntrack-sip.c
@@ -0,0 +1,477 @@
+/*
+ * Copyright (c) 2017 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include "conntrack-sip.h"
+#include "dp-packet.h"
+#include "util.h"
+
+enum sip_mthd sip_mthd_to_enum(char *mthd) {
+    if (mthd == NULL) {
+        return INVALID;
+    }
+
+    if (!strcmp(mthd, "INVITE")) {
+        return INVITE;
+    } else if (!strcmp(mthd, "REGISTER")) {
+        return REGISTER;
+    } else if (!strcmp(mthd, "ACK")) {
+        return ACK;
+    } else if (!strcmp(mthd, "CANCEL")) {
+        return CANCEL;
+    } else if (!strcmp(mthd, "BYE")) {
+        return BYE;
+    } else if (!strcmp(mthd, "OPTIONS")) {
+        return OPTIONS;
+    }
+
+    return INVALID;
+}
+/* A valid SIP status falls in between the following ranges (rfc3261):
+ * - 1xx: Provisional;
+ * - 2xx: Success;
+ * - 3xx: Redirection;
+ * - 4xx: Client Error;
+ * - 5xx: Server Error;
+ * - 6xx: Global Failure.
+ *
+ * Returns false to anything that falls outside of the ranges above, and true
+ * otherwise. */
+bool is_valid_status(uint32_t status) {
+    if ((status >= 100 && status < 200) ||
+        (status >= 200 && status < 300) ||
+        (status >= 300 && status < 400) ||
+        (status >= 400 && status < 500) ||
+        (status >= 500 && status < 600) ||
+        (status >= 600 && status < 700)) {
+        return true;
+    }
+
+    return false;
+}
+
+/* Find and validate the beginning of the CRLF ending, returning a pointer to
+ * the found CR, or NULL otherwise. */
+char * sip_ln_find_crlf(char *sip, size_t len) {
+    if (sip == NULL) {
+        return NULL;
+    }
+
+    char *cr = memchr(sip, '\r', len);
+    if (cr == NULL) {
+        return NULL;
+    }
+    char *lf = memchr(sip, '\n', len);
+    if (lf == NULL) {
+        return NULL;
+    }
+
+    if (lf - cr != 1) {
+        return NULL;
+    }
+
+    return cr;
+}
+
+/* Returns the size of the line until the next CRLF ending (not inclusive). If
+ * CRLF is not found it returns -1. */
+size_t sip_ln_len(char *sip, size_t len) {
+    char *cr = sip_ln_find_crlf(sip, len);
+    if (cr == NULL) {
+        return -1;
+    }
+
+    return cr - sip;
+}
+
+/* Ignores the next CRLF ending and moves to CRLF + 1. After returning, 'sip'
+ * is modified to point to the next line after CRLF (CRLF + 1), and 'len' has
+ * been updated accordingly. If CRLF is not found, 'sip' and 'len' are
+ * untouched */
+void sip_ln_skip(char **sip, size_t *len) {
+    char *cr = sip_ln_find_crlf(*sip, *len);
+    if (cr == NULL) {
+        return;
+    }
+    size_t sz = cr - *sip;
+    *sip += sz + 2;
+    *len -= sz + 2;
+}
+
+/* Iterate through each word in the line at 'sip', where each word is divided
+ * by the space char ' ', and store the resulting word in 'dst', if not NULL.
+ *
+ * After returning 'sip' is pointing to the next word in the line, and 'len'
+ * has been updated accordingly. */
+bool sip_ln_cpy_nxt_word(char **sip, size_t *len, char **dst) {
+    char *s;
+
+    s = memchr(*sip, ' ', *len);
+    if (s == NULL) {
+        return false;
+    }
+
+    size_t wlen = s - *sip;
+    if (dst != NULL) {
+        /* Store found word int the pointed to 'dst' */
+        *dst = xmalloc(sizeof(char) * (wlen + 1));
+        ovs_strzcpy(*dst, *sip, wlen + 1);
+    }
+
+    /* Move 'sip' to the beginning of next word */
+    *sip += wlen + 1;
+    *len -= wlen + 1;
+
+    return true;
+}
+
+/* Parse an 'o' line from the SDP.
+ *
+ * 'conn' should point to the beginning of the line, after 'o='.
+ * 'len' is the size the 'o' line, excluding 'o=' and the CRLF ending.
+ *
+ * XXX Returns only the origin's IP address for now.
+ *
+ * Example: o=user1 53655765 2353687637 IN IP4 127.0.1.1\r\n */
+char * sdp_get_origin(char *orig, size_t len) {
+    char *o_ln = orig;
+    char *ip_addr;
+
+    for (int i = 0; i < 5; i ++) {
+        if (!sip_ln_cpy_nxt_word(&o_ln, &len, NULL)) {
+            return NULL;
+        }
+    }
+
+    ip_addr = xmalloc(sizeof(char) * (len + 1));
+    ovs_strzcpy(ip_addr, o_ln, len + 1);
+
+    return ip_addr;
+}
+
+/* Parse an 'c' line from the SDP.
+ *
+ * 'conn' should point to the beginning of the line, after 'c='.
+ * 'len' is the size the 'c' line, excluding 'c=' and the CRLF ending.
+ *
+ * XXX Returns only the connection's IP address for now.
+ *
+ * Example: c=IN IP4 10.0.2.128\r\n */
+char * sdp_get_conn(char *conn, size_t len) {
+    char *c_ln = conn;
+    char *ip_addr;
+
+    for (int i = 0; i < 2; i ++) {
+        if (!sip_ln_cpy_nxt_word(&c_ln, &len, NULL)) {
+            return NULL;
+        }
+    }
+
+    ip_addr = xmalloc(sizeof(char) * (len + 1));
+    ovs_strzcpy(ip_addr, c_ln, len + 1);
+
+    return ip_addr;
+}
+
+/* Parse an 'm' line from the SDP.
+ *
+ * 'media' should point to the beginning of the line, after 'm='.
+ * 'len' is the size the 'm' line, excluding 'm=' and the CRLF ending.
+ *
+ * XXX Returns only the media's port.
+ *
+ * Example: m=audio 8192 RTP/AVP 0 */
+char * sdp_get_media(char *media, size_t len) {
+    char *m_ln = media;
+    char *port;
+
+    if (!sip_ln_cpy_nxt_word(&m_ln, &len, NULL)) {
+        return NULL;
+    }
+
+    if (!sip_ln_cpy_nxt_word(&m_ln, &len, &port)) {
+        return NULL;
+    }
+
+    return port;
+}
+
+/* Given a pointer to the beginning of an SDP, parses it accordingly.
+ *
+ * 'sdp' should point to the beginning of the SDP.
+ * 'len' should be the size of the whole SIP message after start-line and
+ * headers have been parsed.
+ *
+ * Returns a 'sip_sdp' struct, which the caller is responsible for freeing.
+ * XXX No order is enforced and only supports one 'm' line for now */
+struct sip_sdp * sip_parse_sdp(char *sdp, size_t len) {
+    char *sip = sdp;
+    char *addr_s = NULL;
+    char *port_s = NULL;
+    uint32_t hport;
+    uint32_t haddr;
+    struct sip_sdp *sip_sdp;
+    size_t ln_len;
+
+    if (sip == NULL) {
+       return NULL;
+    }
+
+    sip_sdp = xmalloc(sizeof(struct sip_sdp));
+
+    for (int i = 0; i < len; i++) {
+        ln_len = sip_ln_len(sip, len);
+        if (ln_len == -1) {
+            /* Message is invalid, most likely no CRLF found */
+            goto error;
+        }
+
+        switch (sip[0]) {
+        case 'o':
+            addr_s = sdp_get_origin(sip + 2, ln_len - 2);
+            if (!ip_parse(addr_s, &haddr)) {
+                free(addr_s);
+                goto error;
+            }
+            sip_sdp->orig = ntohl(haddr);
+            free(addr_s);
+            break;
+        case 'c':
+            addr_s = sdp_get_conn(sip + 2, ln_len - 2);
+            if (!ip_parse(addr_s, &haddr)) {
+                free(addr_s);
+                goto error;
+            }
+            sip_sdp->conn = ntohl(haddr);
+            free(addr_s);
+            break;
+        case 'm':
+            port_s = sdp_get_media(sip + 2, ln_len - 2);
+            if (!str_to_uint(port_s, 10, &hport)) {
+                free(port_s);
+                goto error;
+            }
+            sip_sdp->port = hport;
+            free(port_s);
+            break;
+        }
+
+        sip_ln_skip(&sip, &len);
+    }
+
+    return sip_sdp;
+
+error:
+    free(sip_sdp);
+
+    return NULL;
+}
+
+/* generic-message  =  start-line
+ *                     *message-header
+ *                     CRLF
+ *                     [ message-body ]
+ * start-line       =  Request-Line / Status-Line */
+int sip_skip_to_body(char **sip_hdr, size_t *len) {
+    if (*sip_hdr == NULL) {
+        return -EINVAL;
+    }
+
+    char *cr;
+    int sz;
+    while (*len >= 2) {
+        /* Detect the lone CRLF which divides the SIP message header from the
+         * the SIP message body */
+        if ((*sip_hdr)[0] == '\r' && (*sip_hdr)[1] == '\n') {
+            /* Skip 2 bytes for CRLF */
+            *sip_hdr += 2;
+            *len -= 2;
+            return 0;
+        }
+
+        cr = sip_ln_find_crlf(*sip_hdr, *len);
+        if (cr == NULL) {
+            return -EINVAL;
+        }
+        sz = cr - *sip_hdr;
+        *sip_hdr += sz + 2;
+        *len -= sz + 2;
+    }
+
+    return -EINVAL;
+}
+
+void free_strt_ln(struct sip_strt_ln *strt_ln) {
+    if (strt_ln->type == STATUS_LINE) {
+        free_stat_ln(strt_ln->stat_ln);
+    } else {
+        free_reqs_ln(strt_ln->reqs_ln);
+    }
+
+    free(strt_ln);
+}
+
+/* Given a pointer to the beginning of the SIP message (start-line), detects
+ * if it is a request-line or a status-line and parses accordingly.
+ *
+ * 'start_ln' should point to the beginning of the SIP message.
+ * 'len' may be the size of the whole SIP message, but must be at least the
+ * size of the start-line, including with the CRLF ending.
+ *
+ * Returns a 'sip_strt_ln' struct, which the caller is responsible for
+ * freeing. */
+struct sip_strt_ln *
+sip_parse_strt_ln(char *strt_ln_str, size_t len) {
+    struct sip_strt_ln *strt_ln;
+    size_t ln_len;
+
+    /* Calculate the length of the SIP star-line */
+    ln_len = sip_ln_len(strt_ln_str, len);
+    if (ln_len == -1) {
+        /* Message is invalid, most likely no CRLF found */
+        return NULL;
+    }
+
+    strt_ln = xzalloc(sizeof(struct sip_strt_ln));
+
+    if (!strncmp(strt_ln_str, "SIP", 3)) {
+        struct sip_stat_ln *stat_ln = sip_parse_stat_ln(strt_ln_str, ln_len);
+        if (stat_ln == NULL) {
+            goto error;
+        }
+
+        strt_ln->stat_ln = stat_ln;
+        strt_ln->type = STATUS_LINE;
+    } else if (!strncmp(strt_ln_str, "INVITE", 6) ||
+               !strncmp(strt_ln_str, "ACK", 3) ||
+               !strncmp(strt_ln_str, "BYE", 3)) {
+        struct sip_reqs_ln *reqs_ln = sip_parse_reqs_ln(strt_ln_str, ln_len);
+        if (reqs_ln == NULL) {
+            goto error;
+        }
+
+        strt_ln->reqs_ln = reqs_ln;
+        strt_ln->type = REQUEST_LINE;
+    } else {
+        /* Message not yet supported */
+        goto error;
+    }
+
+    return strt_ln;
+
+error:
+    free(strt_ln);
+
+    return NULL;
+}
+
+void free_stat_ln(struct sip_stat_ln *stat_ln) {
+    free(stat_ln->ver);
+    free(stat_ln->reason);
+    free(stat_ln);
+}
+
+/* Parses the SIP status-line.
+ *
+ * 'start_ln' should point to the beginning of the SIP message.
+ * 'len' is the size of the status-line, not including the CRLF ending.
+ *
+ * Returns a 'sip_stat_ln' struct, which the caller is responsible for freeing.
+ *
+ * Example SIP status-line: SIP/2.0 200 OK\r\n */
+struct sip_stat_ln *
+sip_parse_stat_ln(char *start_ln, size_t len) {
+    struct sip_stat_ln *stat_ln;
+
+    stat_ln = xzalloc(sizeof(struct sip_stat_ln));
+
+    if (!sip_ln_cpy_nxt_word(&start_ln, &len, &stat_ln->ver)) {
+        goto error;
+    }
+
+    char *status = NULL;
+
+    if (!sip_ln_cpy_nxt_word(&start_ln, &len, &status)) {
+        free(status);
+        goto error;
+    }
+
+    if (!str_to_uint(status, 10, &stat_ln->status) ||
+        !is_valid_status(stat_ln->status)) {
+        free(status);
+        goto error;
+    }
+
+    stat_ln->reason = xmalloc(sizeof(char) * (len + 1));
+    ovs_strzcpy(stat_ln->reason, start_ln, len + 1);
+
+    return stat_ln;
+
+error:
+    free_stat_ln(stat_ln);
+
+    return NULL;
+}
+
+void free_reqs_ln(struct sip_reqs_ln *reqs_ln) {
+    free(reqs_ln->req_uri);
+    free(reqs_ln->ver);
+    free(reqs_ln);
+}
+
+/* Parses the SIP request-line.
+ *
+ * 'start_ln' should point to the beginning of the SIP message.
+ * 'len' is the size of the request-line, not including the CRLF ending.
+ *
+ * Returns a 'sip_reqs_ln' struct, which the caller is responsible for freeing.
+ *
+ * Example SIP request-line: INVITE sip:service@10.0.1.128:5060 SIP/2.0\r\n */
+struct sip_reqs_ln *
+sip_parse_reqs_ln(char *start_ln, size_t len) {
+    struct sip_reqs_ln *reqs_ln;
+    char *sip_mthd = NULL;
+
+    reqs_ln = xzalloc(sizeof(struct sip_reqs_ln));
+
+    if (!sip_ln_cpy_nxt_word(&start_ln, &len, &sip_mthd)) {
+        goto error;
+    }
+
+    reqs_ln->mthd = sip_mthd_to_enum(sip_mthd);
+
+    if (reqs_ln->mthd == INVALID) {
+        goto error;
+    }
+
+    if (!sip_ln_cpy_nxt_word(&start_ln, &len, &reqs_ln->req_uri)) {
+        goto error;
+    }
+
+    reqs_ln->ver = xmalloc(sizeof(char) * (len + 1));
+    ovs_strzcpy(reqs_ln->ver, start_ln, len + 1);
+
+    return reqs_ln;
+
+error:
+    free(sip_mthd);
+    free_reqs_ln(reqs_ln);
+
+    return NULL;
+}
diff --git a/lib/conntrack-sip.h b/lib/conntrack-sip.h
new file mode 100644
index 000000000..134f35f3a
--- /dev/null
+++ b/lib/conntrack-sip.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2017 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/types.h>
+
+#include "dp-packet.h"
+
+struct sip_sdp {
+    uint32_t orig;
+    uint32_t conn;
+    uint16_t port;
+};
+
+enum sip_mthd {
+    INVITE,
+    REGISTER,
+    ACK,
+    CANCEL,
+    BYE,
+    OPTIONS,
+    INVALID,
+};
+
+struct sip_reqs_ln {
+    enum sip_mthd mthd;
+    char *req_uri;
+    char *ver;
+};
+
+struct sip_stat_ln {
+    char *ver;
+    uint32_t status;
+    char *reason;
+};
+
+enum sip_strt_type {
+    REQUEST_LINE,
+    STATUS_LINE,
+};
+
+struct sip_strt_ln {
+    union {
+        struct sip_reqs_ln *reqs_ln;
+        struct sip_stat_ln *stat_ln;
+    };
+    enum sip_strt_type type;
+};
+
+enum sip_mthd sip_mthd_to_enum(char *sip_mthd);
+int sip_skip_to_body(char **sip_hdr, size_t *len);
+uint32_t sip_body_len(char *sip_hdr);
+char * sip_ln_find_crlf(char *sip, size_t len);
+size_t sip_ln_len(char *sip, size_t len);
+bool sip_ln_cpy_nxt_word(char **sip, size_t *len, char **dst);
+bool is_valid_status(uint32_t status);
+void sip_ln_skip(char **sdp, size_t *len);
+
+char * sdp_get_origin(char *orig, size_t len);
+char * sdp_get_conn(char *conn, size_t len);
+char * sdp_get_media(char *media, size_t len);
+struct sip_sdp *
+sip_parse_sdp(char *sdp, size_t len);
+void free_strt_ln(struct sip_strt_ln *strt_ln);
+struct sip_strt_ln *
+sip_parse_strt_ln(char *start_line, size_t len);
+void free_stat_ln(struct sip_stat_ln *stat_ln);
+struct sip_stat_ln *
+sip_parse_stat_ln(char *start_ln, size_t len);
+void free_reqs_ln(struct sip_reqs_ln *reqs_ln);
+struct sip_reqs_ln *
+sip_parse_reqs_ln(char *start_ln, size_t len);
+
+/* Given a pointer to the beginning of the SIP message, return a pointer to
+ * the beginning of its message-header.
+ * This modifies the passed 'len' to the right size now that the SIP start-line
+ * has been discarded */
+static inline void *
+sip_msg_hdr(char *sip, size_t *len) {
+
+    char *msg_hdr = sip;
+
+    sip_ln_skip(&msg_hdr, len);
+
+    return msg_hdr;
+}
+
+/* Given a pointer to the beginning of the SIP message, return a pointer to
+ * the beginning of its message-body.
+ * This modifies the passed 'len' to the right size now that both the SIP start
+ * -line and message-header have been discarded */
+static inline void *
+sip_msg_bdy(char *sip, size_t *len) {
+
+    char *msg_bdy = sip;
+
+    sip_skip_to_body(&msg_bdy, len);
+
+    return msg_bdy;
+}
