From patchwork Fri Dec 22 19:53:35 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tiago Lam X-Patchwork-Id: 852553 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="n+STfd/A"; dkim-atps=neutral Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3z3K3X5tmJz9s71 for ; Sat, 23 Dec 2017 06:54:48 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 92794C8D; Fri, 22 Dec 2017 19:54:22 +0000 (UTC) X-Original-To: ovs-dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id BCADBC27 for ; Fri, 22 Dec 2017 19:54:21 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.7.6 Received: from mail-wm0-f66.google.com (mail-wm0-f66.google.com [74.125.82.66]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 471A7527 for ; Fri, 22 Dec 2017 19:54:20 +0000 (UTC) Received: by mail-wm0-f66.google.com with SMTP id b199so23833809wme.1 for ; Fri, 22 Dec 2017 11:54:20 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=AIx3UXAPYw7KAoP590teXL3J5Tsu0qSZdwVz/T8Cet8=; b=n+STfd/Am8cv0KX+o7JJBp0Hsy1OcRvlmE0KY9cUsY/vLI1iSq94yPZBWEd0Y9S3Om 34RWSIvNWiFmvs4g12zCzxntKcTQQsyHRi2AxuHoNAXZtoUkGGlSJ2x2ihkEh0/alrkz FsK2fO3Su1ReHLxJlA7XYfSLV9HIZwbiXQ1bN0qNvAFSvg9mqE0OLkVOZlD9/FX+6KE6 vJ0w2srJ3LwYt8Lwg5D3AB26fSqGziF1bhcdu1+tFTuNeqOrBpvn0XtamD7Fu+5egycu pYZe+/Oi8uii/ZPPMjBqD6J5uWcwsi7f7dEqbpy44R/W91tcAW/67hhOfy3kcLpIxMFF hXcQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=AIx3UXAPYw7KAoP590teXL3J5Tsu0qSZdwVz/T8Cet8=; b=TNgY5CR952HWltnrITRt6yn7x3fuZ/WC+b8bYzO+3SuD8tXFyk5jaZjtlszujRO1Dz PmQGGEoXq/znz73q0Y0TJ1UzKi6uVC34xV3lOS4/Iiw8jLd7FCylUIOBAdjj/8iQRkwk ZIRTZWjngKQmG8zS1Eyz+Ht21GQpwPo3CX0ZlKpemjAsA6wcqtRjNhpDy+mPbl+4dxKz 3266dvSUhbQo5IUbgwDRkgrxUGgO/NzDwEjJwtaIP7Llh97L4+Y5DZNDG3X1j60iPRk0 K76uRtbQyx+iPF223udp3YwLV/Dp5LhEqrIldhncXjQ3buv72ny01DDOpwfiuM5GgFNK iD0w== X-Gm-Message-State: AKGB3mJxGUcENeYlFbqKJtOOtwRsjMt+cWknmv6mq3WqKi7zeKlQVVKo 7Ol5gW9pd/8OAMshv4s0OzY3qX9rfA== X-Google-Smtp-Source: ACJfBou775DEH3a3i34SaBSBIvrkS1Y8QZ2eo15uMwHMnHr7I5aJ32hemmatcoTAYK53j2bodeJl8Q== X-Received: by 10.28.48.4 with SMTP id w4mr13571037wmw.16.1513972458115; Fri, 22 Dec 2017 11:54:18 -0800 (PST) Received: from elche.lan (bl5-204-96.dsl.telepac.pt. [82.154.204.96]) by smtp.googlemail.com with ESMTPSA id 47sm4858351wru.27.2017.12.22.11.54.15 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Fri, 22 Dec 2017 11:54:17 -0800 (PST) From: Tiago Lam To: ovs-dev@openvswitch.org Date: Fri, 22 Dec 2017 19:53:35 +0000 Message-Id: <20171222195337.28983-2-tiagolam@gmail.com> X-Mailer: git-send-email 2.14.3 In-Reply-To: <20171222195337.28983-1-tiagolam@gmail.com> References: <20171222195337.28983-1-tiagolam@gmail.com> X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH 1/3] Conntrack: Add new API for future SIP Alg. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org The new API (in conntrack-sip.h and conntrack-sip.c) defines preliminary functions, helpers and strucures to move through and parse SIP messages, including SDP in the message bodies, if present. Note that this is still a preliminary version of the API, and future work is still needed to improve the SIP and SDP handling. When parsing an SDP, for example, the order of the 'm', 'c' and 'o' sections is not enforced, and only the ports and IP addresses are stored at the moment. This API will be used by a future commit in order to implement the new SIP Alg in userspace conntrack. Signed-off-by: Tiago Lam --- lib/automake.mk | 2 + lib/conntrack-sip.c | 477 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/conntrack-sip.h | 112 ++++++++++++ 3 files changed, 591 insertions(+) create mode 100644 lib/conntrack-sip.c create mode 100644 lib/conntrack-sip.h 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 +#include +#include + +#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 + +#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; +} From patchwork Fri Dec 22 19:53:36 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tiago Lam X-Patchwork-Id: 852554 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="i8dyjzFu"; dkim-atps=neutral Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3z3K4J75cTz9s71 for ; Sat, 23 Dec 2017 06:55:28 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id EAE90C97; Fri, 22 Dec 2017 19:54:26 +0000 (UTC) X-Original-To: ovs-dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 43E05C84 for ; Fri, 22 Dec 2017 19:54:25 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.7.6 Received: from mail-wr0-f180.google.com (mail-wr0-f180.google.com [209.85.128.180]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 2622F527 for ; Fri, 22 Dec 2017 19:54:23 +0000 (UTC) Received: by mail-wr0-f180.google.com with SMTP id l41so16586184wre.11 for ; Fri, 22 Dec 2017 11:54:23 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=qsYe4/5titY5fy/rUs3xP9GL1JxbT8r3ySGXWcLaILQ=; b=i8dyjzFuDgX/PPbcSRtZbjTCbLHMyYle/NPWVi8MWtvcQ+FA/eeF8MvH1ua9SmY9kh Aa60STU7bwI5l4kzz/oO6K7wp4hDt/aYnbU1oOC1RaIQCeuiIQX6cwjdkDP4ZdItxUgN JjwH/zzVTszdbIL7GMI0pF7V1FZfDmWzZjF7MuOx9Eqj6v8dyY/tvC2KhdbObdR8CLgH 9FuNX2CU5PRGMl4f1BpVn6i9gwEPf8r+N8DDoHj/DtN77+ldis0z+vfpnDm54mLg/GAO YGFNDIcT/b7k+D89/SvxoSKqzcQZTL/fJVeeioGnXjkHJ9iRnw6AaJHKXAEo+ciErFsn /2PQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=qsYe4/5titY5fy/rUs3xP9GL1JxbT8r3ySGXWcLaILQ=; b=SjP9QNwDzVi+ufyRVeWEu9040O7czeU0ctZXBDnMqb/3iDVrcBIrdIpXezUmtqRM2e qxaHfFy3ImqfnyKeCALc9JbpkL7Y/j+X9Ckxspg+nlLTp3mMjJh5So0Pk/Kgv+zMLBNP nJRn2mex0NG73ibLqDu7iKMcISF/iCMMvkIlhxH4jl42nTp6fxg139ugbn03hsQVo6SQ VQR4L86p9A6thDyUHmeDQ8lO/X0lTt+BweTcfiXzF8A3eOBpQ76AqOvBtKR17Z8mjWIb 7l98pOPX6mqTlKjj21nBeqjUIDRZ7gqDTjOGDStWo7+KIs8m9himu3OVtHLBIPYyWN/H chrg== X-Gm-Message-State: AKGB3mKlv3R5QBG0MIdLaw/LkyG7y+v2t12LAZb1LOgWwSKs3ubAdoI+ /pLC6i0e6e+eAZsJVwZp8UG4KTwxFQ== X-Google-Smtp-Source: ACJfBouEncYXwkcmgJL3uDf+67vnLHr9XswwtZyukkZ7CnhIAnS6Od9B48BadjB94KhDWahe2W78XA== X-Received: by 10.223.161.11 with SMTP id o11mr10985858wro.4.1513972461051; Fri, 22 Dec 2017 11:54:21 -0800 (PST) Received: from elche.lan (bl5-204-96.dsl.telepac.pt. [82.154.204.96]) by smtp.googlemail.com with ESMTPSA id 47sm4858351wru.27.2017.12.22.11.54.19 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Fri, 22 Dec 2017 11:54:20 -0800 (PST) From: Tiago Lam To: ovs-dev@openvswitch.org Date: Fri, 22 Dec 2017 19:53:36 +0000 Message-Id: <20171222195337.28983-3-tiagolam@gmail.com> X-Mailer: git-send-email 2.14.3 In-Reply-To: <20171222195337.28983-1-tiagolam@gmail.com> References: <20171222195337.28983-1-tiagolam@gmail.com> X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH 2/3] Conntrack: Add initial support for new SIP Alg. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org Handle_sip, in conntrack.c, is the new function handler that handles the SIP messages received in the userspace conntrack (detected in process_one). It parses the SIP messages (and SDPs) using the newly introduced SIP API (conntrack-sip.h) and accordingly creates the expectations necessary to let the RTP traffic through. To identify the new SIP Alg, a new IPPROTO_SIP has also been introduced, which maps to port 5060, the SIP assigned port. As of now, this preliminary support mainly handles the following happy case, exchanged between a UAC and UAS: - UAC ----> UAS | SIP INVITE (SDP offer); - UAC <---- UAS | SIP 200 (SDP answer); - UAC ----> UAS | SIP ACK; - UAC ----> UAS | SIP BYE; - UAC <---- UAS | SIP 200; It also assumes the SIP messages are coming over TCP and IPv4 (although SIP's main usage is over UDP). This integration has been tested manually by setting the following flows, which let's tcp traffic from port 1 to flow to port 0, but only +ESTABLISHED or +RELATED traffic on the opposite direction (from port 0 to port 1). It also allows UDP +RELATED to pass through in both directions (which is needed for the RTP packets): - table=0,priority=1,action=drop - table=0,priority=10,arp,action=normal - table=0,priority=10,icmp,action=normal - table=0,priority=100,in_port=1,tcp,action=ct(alg=sip,commit),0 - table=0,priority=100,in_port=0,tcp,action=ct(table=1) - table=0,priority=100,in_port=1,udp,action=ct(table=2) - table=0,priority=100,in_port=0,udp,action=ct(table=2) - table=1,in_port=0,tcp,ct_state=+trk+est,action=1 - table=1,in_port=0,tcp,ct_state=+trk+rel,action=1 - table=2,in_port=0,udp,ct_state=+rel,action=1 - table=2,in_port=1,udp,ct_state=+rel,action=0 Signed-off-by: Tiago Lam --- include/openvswitch/ofp-actions.h | 4 + lib/conntrack-private.h | 2 + lib/conntrack-sip.c | 14 +++ lib/conntrack-sip.h | 11 ++ lib/conntrack.c | 228 +++++++++++++++++++++++++++++++++++++- lib/ofp-parse.c | 5 + ofproto/ofproto-dpif-xlate.c | 3 + 7 files changed, 266 insertions(+), 1 deletion(-) diff --git a/include/openvswitch/ofp-actions.h b/include/openvswitch/ofp-actions.h index 25f61ef93..fccd3a61c 100644 --- a/include/openvswitch/ofp-actions.h +++ b/include/openvswitch/ofp-actions.h @@ -604,6 +604,10 @@ enum nx_conntrack_flags { #define IPPORT_TFTP 69 #endif +#if !defined(IPPORT_SIP) +#define IPPORT_SIP 5060 +#endif + /* OFPACT_CT. * * Used for NXAST_CT. */ diff --git a/lib/conntrack-private.h b/lib/conntrack-private.h index ac0198f34..5205252d0 100644 --- a/lib/conntrack-private.h +++ b/lib/conntrack-private.h @@ -105,6 +105,8 @@ struct conn { uint8_t conn_type; /* TCP sequence skew due to NATTing of FTP control messages. */ uint8_t seq_skew_dir; + /* XXX: Move SIP specific state to a more appropriate place. */ + struct sip_state *sip_state; /* True if alg data connection. */ uint8_t alg_related; }; diff --git a/lib/conntrack-sip.c b/lib/conntrack-sip.c index 7239bdad6..4c850b5c0 100644 --- a/lib/conntrack-sip.c +++ b/lib/conntrack-sip.c @@ -22,6 +22,20 @@ #include "dp-packet.h" #include "util.h" +void free_sip_state(struct sip_state *sip_state) { + free(sip_state->peer[0].sdp); + free(sip_state->peer[1].sdp); + free(sip_state); +} + +void sip_state_init(struct sip_state *sip_state) { + sip_state->peer[0].sdp = NULL; + sip_state->peer[0].was_bye = false; + + sip_state->peer[1].sdp = NULL; + sip_state->peer[1].was_bye = false; +} + enum sip_mthd sip_mthd_to_enum(char *mthd) { if (mthd == NULL) { return INVALID; diff --git a/lib/conntrack-sip.h b/lib/conntrack-sip.h index 134f35f3a..37abaad10 100644 --- a/lib/conntrack-sip.h +++ b/lib/conntrack-sip.h @@ -59,6 +59,15 @@ struct sip_strt_ln { enum sip_strt_type type; }; +struct sip_peer { + struct sip_sdp *sdp; + bool was_bye; +}; + +struct sip_state { + struct sip_peer peer[2]; +}; + 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); @@ -68,6 +77,8 @@ 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); +void free_sip_state(struct sip_state *sip_state); +void sip_state_init(struct sip_state *sip_state); 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); diff --git a/lib/conntrack.c b/lib/conntrack.c index 6d078f5a8..65d10dd08 100644 --- a/lib/conntrack.c +++ b/lib/conntrack.c @@ -25,6 +25,7 @@ #include "bitmap.h" #include "conntrack.h" #include "conntrack-private.h" +#include "conntrack-sip.h" #include "coverage.h" #include "csum.h" #include "ct-dpif.h" @@ -40,6 +41,7 @@ #include "random.h" #include "timeval.h" + VLOG_DEFINE_THIS_MODULE(conntrack); COVERAGE_DEFINE(conntrack_full); @@ -71,6 +73,7 @@ enum ct_alg_ctl_type { CT_ALG_CTL_NONE, CT_ALG_CTL_FTP, CT_ALG_CTL_TFTP, + CT_ALG_CTL_SIP, }; static bool conn_key_extract(struct conntrack *, struct dp_packet *, @@ -169,6 +172,14 @@ handle_tftp_ctl(struct conntrack *ct, long long now, enum ftp_ctl_pkt ftp_ctl OVS_UNUSED, bool nat OVS_UNUSED); +static void +handle_sip(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, 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, @@ -180,6 +191,7 @@ static alg_helper alg_helpers[] = { [CT_ALG_CTL_NONE] = NULL, [CT_ALG_CTL_FTP] = handle_ftp_ctl, [CT_ALG_CTL_TFTP] = handle_tftp_ctl, + [CT_ALG_CTL_SIP] = handle_sip, }; long long ct_timeout_val[] = { @@ -470,22 +482,29 @@ get_alg_ctl_type(const struct dp_packet *pkt, ovs_be16 tp_src, ovs_be16 tp_dst, * the external dependency. */ enum { CT_IPPORT_FTP = 21 }; enum { CT_IPPORT_TFTP = 69 }; + enum { CT_IPPORT_SIP = 5060 }; uint8_t ip_proto = get_ip_proto(pkt); struct udp_header *uh = dp_packet_l4(pkt); struct tcp_header *th = dp_packet_l4(pkt); ovs_be16 ftp_src_port = htons(CT_IPPORT_FTP); ovs_be16 ftp_dst_port = htons(CT_IPPORT_FTP); ovs_be16 tftp_dst_port = htons(CT_IPPORT_TFTP); + ovs_be16 sip_src_port = htons(CT_IPPORT_SIP); + ovs_be16 sip_dst_port = htons(CT_IPPORT_SIP); if (OVS_UNLIKELY(tp_dst)) { if (helper && !strncmp(helper, "ftp", strlen("ftp"))) { ftp_dst_port = tp_dst; } else if (helper && !strncmp(helper, "tftp", strlen("tftp"))) { tftp_dst_port = tp_dst; + } else if (helper && !strncmp(helper, "sip", strlen("sip"))) { + sip_dst_port = tp_dst; } } else if (OVS_UNLIKELY(tp_src)) { if (helper && !strncmp(helper, "ftp", strlen("ftp"))) { ftp_src_port = tp_src; + } else if (helper && !strncmp(helper, "sip", strlen("sip"))) { + sip_src_port = tp_src; } } @@ -494,7 +513,11 @@ get_alg_ctl_type(const struct dp_packet *pkt, ovs_be16 tp_src, ovs_be16 tp_dst, } else if (ip_proto == IPPROTO_TCP && (th->tcp_src == ftp_src_port || th->tcp_dst == ftp_dst_port)) { return CT_ALG_CTL_FTP; + } else if (ip_proto == IPPROTO_TCP && + (th->tcp_src == sip_src_port || th->tcp_dst == sip_dst_port)) { + return CT_ALG_CTL_SIP; } + return CT_ALG_CTL_NONE; } @@ -831,6 +854,9 @@ ct_verify_helper(const char *helper, enum ct_alg_ctl_type ct_alg_ctl) } else if ((ct_alg_ctl == CT_ALG_CTL_TFTP) && !strncmp(helper, "tftp", strlen("tftp"))) { return true; + } else if ((ct_alg_ctl == CT_ALG_CTL_SIP) && + !strncmp(helper, "sip", strlen("sip"))) { + return true; } else { return false; } @@ -1178,6 +1204,7 @@ process_one(struct conntrack *ct, struct dp_packet *pkt, conn = ctx->conn; /* Delete found entry if in wrong direction. 'force' implies commit. */ + if (conn && force && ctx->reply) { conn_clean(ct, conn, &ct->buckets[bucket]); conn = NULL; @@ -1218,7 +1245,6 @@ process_one(struct conntrack *ct, struct dp_packet *pkt, enum ct_alg_ctl_type ct_alg_ctl = get_alg_ctl_type(pkt, tp_src, tp_dst, helper); - if (OVS_LIKELY(conn)) { if (OVS_LIKELY(!conn_update_state_alg(ct, pkt, ctx, conn, nat_action_info, @@ -2582,6 +2608,62 @@ expectation_lookup(struct hmap *alg_expectations, return NULL; } +static void +sip_expectation_create(struct conntrack *ct, + const ovs_be32 offer_addr, + const ovs_be32 answer_addr, + const ovs_be16 offer_port, + const long long now, + const struct conn *master_conn) +{ + /* Set src address coming from answer SDP 'c' */ + struct ct_addr src_addr; + memset(&src_addr, 0, sizeof src_addr); + src_addr.ipv4_aligned = answer_addr; + /* Set dst address coming from offer SDP 'c' */ + struct ct_addr dst_addr; + memset(&dst_addr, 0, sizeof dst_addr); + dst_addr.ipv4_aligned = offer_addr; + + struct alg_exp_node *alg_exp_node = + xzalloc(sizeof *alg_exp_node); + alg_exp_node->key.dl_type = master_conn->key.dl_type; + /* nw_proto might won't be the same as SIP, since RTP is over UDP - hence + * set it to UDP explicitly */ + alg_exp_node->key.nw_proto = IPPROTO_UDP; + alg_exp_node->key.zone = master_conn->key.zone; + alg_exp_node->key.src.addr = src_addr; + alg_exp_node->key.dst.addr = dst_addr; + alg_exp_node->key.src.port = ALG_WC_SRC_PORT; + alg_exp_node->key.dst.port = offer_port; + alg_exp_node->master_mark = master_conn->mark; + alg_exp_node->master_label = master_conn->label; + alg_exp_node->master_key = master_conn->key; + /* Take the write lock here because it is almost 100% + * likely that the lookup will fail and + * expectation_create() will be called below. */ + ct_rwlock_wrlock(&ct->resources_lock); + struct alg_exp_node *alg_exp = expectation_lookup( + &ct->alg_expectations, &alg_exp_node->key, ct->hash_basis); + if (alg_exp) { + free(alg_exp_node); + ct_rwlock_unlock(&ct->resources_lock); + return; + } + + /* TODO(tlam): NAT not supported yet */ + + uint32_t alg_exp_conn_key_hash = + conn_key_hash(&alg_exp_node->key, + ct->hash_basis); + hmap_insert(&ct->alg_expectations, + &alg_exp_node->node, + alg_exp_conn_key_hash); + + alg_exp_init_expiration(ct, alg_exp_node, now); + ct_rwlock_unlock(&ct->resources_lock); +} + static void expectation_create(struct conntrack *ct, ovs_be16 dst_port, @@ -3186,3 +3268,147 @@ handle_tftp_ctl(struct conntrack *ct, CT_TFTP_MODE, conn_for_expectation); return; } + +static void +sip_delete_conn(struct conntrack *ct, const struct conn_key *key, + long long now) +{ + uint32_t hash = conn_key_hash(key, ct->hash_basis); + unsigned bucket = hash_to_bucket(hash); + ct_lock_lock(&ct->buckets[bucket].lock); + struct conn *conn = conn_lookup(ct, key, now); + /* Free sip_state associated with the connection */ + free_sip_state(conn->sip_state); + conn_clean(ct, conn, &ct->buckets[bucket]); + ct_lock_unlock(&ct->buckets[bucket].lock); +} + + +static void +sip_set_conn_state(struct conntrack *ct, const struct conn_key *key, + long long now, struct sip_sdp *sdp) +{ + uint32_t hash = conn_key_hash(key, ct->hash_basis); + unsigned bucket = hash_to_bucket(hash); + ct_lock_lock(&ct->buckets[bucket].lock); + struct conn *conn = conn_lookup(ct, key, now); + struct sip_state *sip_state = xmalloc(sizeof(struct sip_state)); + sip_state_init(sip_state); + conn->sip_state = sip_state; + conn->sip_state->peer[0].sdp = sdp; + ct_lock_unlock(&ct->buckets[bucket].lock); +} + +/* XXX Only handles IPv4 for now, must include IPv6 in the future */ +/* XXX Doesn't deal with NAT either */ +/* XXX No support for SIP over UDP either (not tested at least), which might + * be required in the future */ +static void +handle_sip(struct conntrack *ct, + const struct conn_lookup_ctx *ctx OVS_UNUSED, + struct dp_packet *pkt, + const struct conn *conn_for_expectation, + long long now, enum ftp_ctl_pkt ftp_ctl OVS_UNUSED, + bool nat OVS_UNUSED) +{ + struct ip_header *l3_hdr = dp_packet_l3(pkt); + struct tcp_header *th = dp_packet_l4(pkt); + size_t tcp_hdr_len = TCP_OFFSET(th->tcp_ctl) * 4; + size_t ip_hdr_len = (IP_IHL(l3_hdr->ip_ihl_ver) * 4); + size_t tcp_len = ntohs(l3_hdr->ip_tot_len) - ip_hdr_len - tcp_hdr_len; + if (!tcp_len) { + /* Packet of no interest, with no TCP payload */ + return; + } + + char *tcp_hdr = (char *) th; + /* Move to beginning of TCP payload, where the SIP payload is */ + char *sip = tcp_hdr + tcp_hdr_len; + + size_t sip_len = tcp_len; + + struct sip_strt_ln *strt_ln = sip_parse_strt_ln(sip, sip_len); + if (strt_ln == NULL) { + pkt->md.ct_state |= CS_TRACKED | CS_INVALID; + return; + } + + const struct conn *conn = conn_for_expectation; + + /* Pointer to the beginning of SIP message-header */ + char *msg_hdr = sip_msg_hdr(sip, &sip_len); + + /* XXX Validate "Content-Length" header to know exact size of body */ + + /* Pointer to the beginning of SIP message-body */ + char *msg_bdy = sip_msg_bdy(msg_hdr, &sip_len); + if (msg_bdy == NULL) { + /* Couldn't parse the SIP message-body */ + pkt->md.ct_state |= CS_TRACKED | CS_INVALID; + return; + } + + if (strt_ln->type == REQUEST_LINE) { + enum sip_mthd mthd = strt_ln->reqs_ln->mthd; + if (mthd == INVITE) { + struct sip_sdp *sdp = sip_parse_sdp(msg_bdy, sip_len); + /* We now have the SDP offer, et the connection state */ + sip_set_conn_state(ct, &conn_for_expectation->key, now, sdp); + } else if (mthd == ACK) { + /* A SIP ACK may carry an SDP answer, when the offer is carried in + * the 200 OK */ + + /* XXX Consider supporting the above with the SDP answer */ + } else if (mthd == BYE) { + /* Once a BYE is received the RTP connnections are torn down from + * the UAC side, and then from the UAS side when it sends either + * 481 or 2XX response. + * Record the state here, deleting the connection, including + * expectations, when the response is received */ + conn->sip_state->peer[0].was_bye = true; + } + } else { + /* XXX In the future we will also need to confirm if this SDP is + * an SDP offer or an SDP answer. */ + struct sip_stat_ln *stat_ln = strt_ln->stat_ln; + if (stat_ln->status != 200) { + /* Currently we don't support anything else than 200 OK */ + return; + } else { + if (conn->sip_state->peer[0].was_bye) { + /* Received a 200 OK for the previous BYE, delete connection */ + sip_delete_conn(ct, &conn_for_expectation->key, now); + return; + } + + /* Check if this SIP reply has an SDP. */ + struct sip_sdp *sdp = sip_parse_sdp(msg_bdy, sip_len); + ovs_be32 offer_addr; + ovs_be32 offer_port; + ovs_be32 answer_addr; + if (sdp == NULL) { + if (conn->sip_state->peer[0].sdp != NULL && + conn->sip_state->peer[1].sdp == NULL) { + /* No SDP is carried here, but we did expect one since the + * previous message was a SIP INVITE */ + pkt->md.ct_state |= CS_TRACKED | CS_INVALID; + } + return; + } else { + conn->sip_state->peer[1].sdp = sdp; + + /* Received a SIP 200 OK from peer, we are now able to set up + * the expectations for the data connections, based on the SDP + * offer and answer */ + offer_addr = htonl(conn->sip_state->peer[0].sdp->conn); + offer_port = htons(conn->sip_state->peer[0].sdp->port); + answer_addr = htonl(conn->sip_state->peer[1].sdp->conn); + + sip_expectation_create(ct, offer_addr, answer_addr, offer_port, + now, conn); + } + } + } + + free_strt_ln(strt_ln); +} diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index 0c68495bf..dc7ab1e49 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -185,6 +185,11 @@ str_to_connhelper(const char *str, uint16_t *alg) *alg = IPPORT_TFTP; return NULL; } + if (!strcmp(str, "sip")) { + *alg = IPPORT_SIP; + return NULL; + } + return xasprintf("invalid conntrack helper \"%s\"", str); } diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index a2b4fdb3b..3a3e1f0f5 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -5693,6 +5693,9 @@ put_ct_helper(struct xlate_ctx *ctx, case IPPORT_TFTP: nl_msg_put_string(odp_actions, OVS_CT_ATTR_HELPER, "tftp"); break; + case IPPORT_SIP: + nl_msg_put_string(odp_actions, OVS_CT_ATTR_HELPER, "sip"); + break; default: xlate_report_error(ctx, "cannot serialize ct_helper %d", ofc->alg); break; From patchwork Fri Dec 22 19:53:37 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tiago Lam X-Patchwork-Id: 852555 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="pCndCqll"; dkim-atps=neutral Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3z3K4x02sVz9s71 for ; Sat, 23 Dec 2017 06:56:01 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id E9A70C9B; Fri, 22 Dec 2017 19:54:28 +0000 (UTC) X-Original-To: ovs-dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 2CE4F910 for ; Fri, 22 Dec 2017 19:54:26 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.7.6 Received: from mail-wr0-f194.google.com (mail-wr0-f194.google.com [209.85.128.194]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 4E64F527 for ; Fri, 22 Dec 2017 19:54:25 +0000 (UTC) Received: by mail-wr0-f194.google.com with SMTP id l19so16409987wrc.2 for ; Fri, 22 Dec 2017 11:54:25 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=gLgDp0Jzz/Y4B2Ly6ymEEd4s5F+k/vgX7gTUw8hYOCg=; b=pCndCqll1ZZfR+SS9LmYd/aWniGFKYV2rX1FTfoRTvtkH6hGE5IhXuv1SvXlNNmR3V 8cC5Oli76R20/R4V0bQkTe3A1B3M56pla2watdtJ7ywzcgHPLkx0rEF5xOzcWs2nhhMD 1bUKFN1nF46wi7Q51R3RgYXv/hEHHgVVl1cQ2ZhFAGujQcafdoiYUjvPooEnksk916Od YK+oLPuhqsgbU0PYBacXMmTBp8MzlBQo5YmnQ14pqa3kXQJJ7pWJp00XfSj4Vu3FUP/9 pupd+QZheysAAQhAMTL9ogGEcAnGiicrpE74CH5Aluynlc5nUu62WNW5093CNocmcDyn s4Tw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=gLgDp0Jzz/Y4B2Ly6ymEEd4s5F+k/vgX7gTUw8hYOCg=; b=g4mleL92U5GJ8KOKIlJwbt2EFnVD8TO6YGXB0uKeooCQZm4bWGKNemJYajZ8lwISi6 eXUI/DMCs0zREfSdd8Mt4L8jgnojcECkgh1VDi6/oCab6cASIeCa4fK8tgoGivdX1zXB fzKemvwElb4p28ofSAie6DMdHhG3e/w9FAleq0nlANqXZuASNw4o4rltTXQEiyn5aoI9 wZII8yGTruhbHynMOxuVYPY/XfObnTnAaEV2HR1AlcYxTLRmBf67uhKXQ33Eoj8exfv8 dzbfPdLaXdkvG1SvEbdP8J6NviF9EwG9weK3S9BE0cgSGYTtOiNaMs75g/1RbvJl9fSm vwyg== X-Gm-Message-State: AKGB3mIlkCPdRsU9folKG9iyYB7asQIkpUssws2ZX5bBqSa4WQZFFFCt GsLw4neAv0RmZUtiiyDUvCINmfoQIQ== X-Google-Smtp-Source: ACJfBoudd/TYB4Uc1kw3CQBh+X+XHoMDq1cHI657DPDpg6T/LonppB3uNVdRpcUMyd4rpuxJ0wJGmw== X-Received: by 10.223.129.133 with SMTP id 5mr15195416wra.55.1513972463554; Fri, 22 Dec 2017 11:54:23 -0800 (PST) Received: from elche.lan (bl5-204-96.dsl.telepac.pt. [82.154.204.96]) by smtp.googlemail.com with ESMTPSA id 47sm4858351wru.27.2017.12.22.11.54.21 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Fri, 22 Dec 2017 11:54:22 -0800 (PST) From: Tiago Lam To: ovs-dev@openvswitch.org Date: Fri, 22 Dec 2017 19:53:37 +0000 Message-Id: <20171222195337.28983-4-tiagolam@gmail.com> X-Mailer: git-send-email 2.14.3 In-Reply-To: <20171222195337.28983-1-tiagolam@gmail.com> References: <20171222195337.28983-1-tiagolam@gmail.com> X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH 3/3] Conntrack: Support asymmetric RTP port for SIP. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org Previously, when creating the expecations, SIP Alg would assume the RTP packets are sent from the same IP address + port which had been announced in the SDP offer + answer. As specified in rfc4961, this might not be the case, as the RTP packets might be sent from different IP addresses and / or port. This commit adds extra logic, by creating two expectations, one in each direction, to support the "port case", i.e., the RTP packets are sent from a different port than the one the host is listenning on. Signed-off-by: Tiago Lam --- lib/conntrack.c | 76 ++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 25 deletions(-) diff --git a/lib/conntrack.c b/lib/conntrack.c index 65d10dd08..0db3447f2 100644 --- a/lib/conntrack.c +++ b/lib/conntrack.c @@ -2610,32 +2610,31 @@ expectation_lookup(struct hmap *alg_expectations, static void sip_expectation_create(struct conntrack *ct, - const ovs_be32 offer_addr, - const ovs_be32 answer_addr, - const ovs_be16 offer_port, + const ovs_be32 src_addr, + const ovs_be32 dst_addr, + const ovs_be16 dst_port, const long long now, const struct conn *master_conn) { - /* Set src address coming from answer SDP 'c' */ - struct ct_addr src_addr; - memset(&src_addr, 0, sizeof src_addr); - src_addr.ipv4_aligned = answer_addr; - /* Set dst address coming from offer SDP 'c' */ - struct ct_addr dst_addr; - memset(&dst_addr, 0, sizeof dst_addr); - dst_addr.ipv4_aligned = offer_addr; + /* Set src address (from SDP's 'c') */ + struct ct_addr ct_src_addr; + memset(&ct_src_addr, 0, sizeof ct_src_addr); + ct_src_addr.ipv4_aligned = src_addr; + /* Set dst address (from SDP's 'c') */ + struct ct_addr ct_dst_addr; + memset(&ct_dst_addr, 0, sizeof ct_dst_addr); + ct_dst_addr.ipv4_aligned = dst_addr; struct alg_exp_node *alg_exp_node = xzalloc(sizeof *alg_exp_node); alg_exp_node->key.dl_type = master_conn->key.dl_type; - /* nw_proto might won't be the same as SIP, since RTP is over UDP - hence - * set it to UDP explicitly */ + /* RTP is over UDP - hence set nw_proto to UDP explicitly */ alg_exp_node->key.nw_proto = IPPROTO_UDP; alg_exp_node->key.zone = master_conn->key.zone; - alg_exp_node->key.src.addr = src_addr; - alg_exp_node->key.dst.addr = dst_addr; + alg_exp_node->key.src.addr = ct_src_addr; + alg_exp_node->key.dst.addr = ct_dst_addr; alg_exp_node->key.src.port = ALG_WC_SRC_PORT; - alg_exp_node->key.dst.port = offer_port; + alg_exp_node->key.dst.port = dst_port; alg_exp_node->master_mark = master_conn->mark; alg_exp_node->master_label = master_conn->label; alg_exp_node->master_key = master_conn->key; @@ -2664,6 +2663,39 @@ sip_expectation_create(struct conntrack *ct, ct_rwlock_unlock(&ct->resources_lock); } +/* Setups two expectations for the RTP connections, one in each direction. + * Example: If an SDP answer announces it is listenning at IP address 10.0.1. + * 10:6000 and an the SDP offer at 10.0.2.10:6000, then: + * - Sets a UDP expectation going from 10.0.1.10:0 to 10.0.2.10:6000: + * - Sets another UDP expectation going from 10.0.2.10:0 to 10.0.1.10:6000; + * + * Thus, this allows traffic from a different port than the one the host is + * listenning to (the one it announced in the SDP). + * + * XXX According to rfc4961, hosts might send RTP packets from a different IP + * address and / or port (i.e. asymmetrically). Since this function supports + * the sending of packets from a different port, consider supporting the + * sending from a different IP address. + */ +static void +sip_expectations_setup(struct conntrack *ct, + const struct sip_sdp *offer_sdp, + const struct sip_sdp *answer_sdp, + const long long now, + const struct conn *conn) { + ovs_be32 offer_addr = htonl(offer_sdp->conn); + ovs_be16 offer_port = htons(offer_sdp->port); + ovs_be32 answer_addr = htonl(answer_sdp->conn); + ovs_be16 answer_port = htons(answer_sdp->port); + + /* Set expectation from SDP answer -> SDP offer */ + sip_expectation_create(ct, answer_addr, offer_addr, offer_port, now, + conn); + /* Set expectation from SDP offer -> SDP answer */ + sip_expectation_create(ct, offer_addr, answer_addr, answer_port, now, + conn); +} + static void expectation_create(struct conntrack *ct, ovs_be16 dst_port, @@ -3383,9 +3415,6 @@ handle_sip(struct conntrack *ct, /* Check if this SIP reply has an SDP. */ struct sip_sdp *sdp = sip_parse_sdp(msg_bdy, sip_len); - ovs_be32 offer_addr; - ovs_be32 offer_port; - ovs_be32 answer_addr; if (sdp == NULL) { if (conn->sip_state->peer[0].sdp != NULL && conn->sip_state->peer[1].sdp == NULL) { @@ -3400,12 +3429,9 @@ handle_sip(struct conntrack *ct, /* Received a SIP 200 OK from peer, we are now able to set up * the expectations for the data connections, based on the SDP * offer and answer */ - offer_addr = htonl(conn->sip_state->peer[0].sdp->conn); - offer_port = htons(conn->sip_state->peer[0].sdp->port); - answer_addr = htonl(conn->sip_state->peer[1].sdp->conn); - - sip_expectation_create(ct, offer_addr, answer_addr, offer_port, - now, conn); + sip_expectations_setup(ct, conn->sip_state->peer[0].sdp, + conn->sip_state->peer[1].sdp, now, + conn); } } }