Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 30f33e6d authored by Patrick McHardy's avatar Patrick McHardy Committed by David S. Miller
Browse files

[NETFILTER]: nf_conntrack_sip: support method specific request/response handling



Add support for per-method request/response handlers and perform SDP
parsing for INVITE/UPDATE requests and for all informational and
successful responses.

Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7d3dd043
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -5,6 +5,25 @@
#define SIP_PORT	5060
#define SIP_TIMEOUT	3600

struct sip_handler {
	const char	*method;
	unsigned int	len;
	int		(*request)(struct sk_buff *skb,
				   const char **dptr, unsigned int *datalen,
				   unsigned int cseq);
	int		(*response)(struct sk_buff *skb,
				    const char **dptr, unsigned int *datalen,
				    unsigned int cseq, unsigned int code);
};

#define SIP_HANDLER(__method, __request, __response)			\
{									\
	.method		= (__method),					\
	.len		= sizeof(__method) - 1,				\
	.request	= (__request),					\
	.response	= (__response),					\
}

struct sip_header {
	const char	*name;
	const char	*cname;
@@ -35,6 +54,7 @@ struct sip_header {
	__SIP_HDR(__name, NULL, __search, __match)

enum sip_header_types {
	SIP_HDR_CSEQ,
	SIP_HDR_FROM,
	SIP_HDR_TO,
	SIP_HDR_CONTACT,
+97 −10
Original line number Diff line number Diff line
@@ -212,6 +212,7 @@ EXPORT_SYMBOL_GPL(ct_sip_parse_request);
 * equivalent to multiple headers.
 */
static const struct sip_header ct_sip_hdrs[] = {
	[SIP_HDR_CSEQ]			= SIP_HDR("CSeq", NULL, NULL, digits_len),
	[SIP_HDR_FROM]			= SIP_HDR("From", "f", "sip:", skp_epaddr_len),
	[SIP_HDR_TO]			= SIP_HDR("To", "t", "sip:", skp_epaddr_len),
	[SIP_HDR_CONTACT]		= SIP_HDR("Contact", "m", "sip:", skp_epaddr_len),
@@ -566,7 +567,8 @@ static int set_expected_rtp(struct sk_buff *skb,
}

static int process_sdp(struct sk_buff *skb,
		       const char **dptr, unsigned int *datalen)
		       const char **dptr, unsigned int *datalen,
		       unsigned int cseq)
{
	enum ip_conntrack_info ctinfo;
	struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
@@ -600,6 +602,96 @@ static int process_sdp(struct sk_buff *skb,

	return set_expected_rtp(skb, dptr, datalen, &addr, htons(port));
}
static int process_invite_response(struct sk_buff *skb,
				   const char **dptr, unsigned int *datalen,
				   unsigned int cseq, unsigned int code)
{
	if ((code >= 100 && code <= 199) ||
	    (code >= 200 && code <= 299))
		return process_sdp(skb, dptr, datalen, cseq);

	return NF_ACCEPT;
}

static int process_update_response(struct sk_buff *skb,
				   const char **dptr, unsigned int *datalen,
				   unsigned int cseq, unsigned int code)
{
	if ((code >= 100 && code <= 199) ||
	    (code >= 200 && code <= 299))
		return process_sdp(skb, dptr, datalen, cseq);

	return NF_ACCEPT;
}

static const struct sip_handler sip_handlers[] = {
	SIP_HANDLER("INVITE", process_sdp, process_invite_response),
	SIP_HANDLER("UPDATE", process_sdp, process_update_response),
};

static int process_sip_response(struct sk_buff *skb,
				const char **dptr, unsigned int *datalen)
{
	static const struct sip_handler *handler;
	enum ip_conntrack_info ctinfo;
	struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
	unsigned int matchoff, matchlen;
	unsigned int code, cseq, dataoff, i;

	if (*datalen < strlen("SIP/2.0 200"))
		return NF_ACCEPT;
	code = simple_strtoul(*dptr + strlen("SIP/2.0 "), NULL, 10);
	if (!code)
		return NF_DROP;

	if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ,
			      &matchoff, &matchlen) <= 0)
		return NF_DROP;
	cseq = simple_strtoul(*dptr + matchoff, NULL, 10);
	if (!cseq)
		return NF_DROP;
	dataoff = matchoff + matchlen + 1;

	for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) {
		handler = &sip_handlers[i];
		if (handler->response == NULL)
			continue;
		if (*datalen < dataoff + handler->len ||
		    strnicmp(*dptr + dataoff, handler->method, handler->len))
			continue;
		return handler->response(skb, dptr, datalen, cseq, code);
	}
	return NF_ACCEPT;
}

static int process_sip_request(struct sk_buff *skb,
			       const char **dptr, unsigned int *datalen)
{
	static const struct sip_handler *handler;
	enum ip_conntrack_info ctinfo;
	struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
	unsigned int matchoff, matchlen;
	unsigned int cseq, i;

	for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) {
		handler = &sip_handlers[i];
		if (handler->request == NULL)
			continue;
		if (*datalen < handler->len ||
		    strnicmp(*dptr, handler->method, handler->len))
			continue;

		if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ,
				      &matchoff, &matchlen) <= 0)
			return NF_DROP;
		cseq = simple_strtoul(*dptr + matchoff, NULL, 10);
		if (!cseq)
			return NF_DROP;

		return handler->request(skb, dptr, datalen, cseq);
	}
	return NF_ACCEPT;
}

static int sip_help(struct sk_buff *skb,
		    unsigned int protoff,
@@ -634,15 +726,10 @@ static int sip_help(struct sk_buff *skb,
	if (datalen < strlen("SIP/2.0 200"))
		return NF_ACCEPT;

	/* RTP info only in some SDP pkts */
	if (strnicmp(dptr, "INVITE", strlen("INVITE")) != 0 &&
	    strnicmp(dptr, "UPDATE", strlen("UPDATE")) != 0 &&
	    strnicmp(dptr, "SIP/2.0 180", strlen("SIP/2.0 180")) != 0 &&
	    strnicmp(dptr, "SIP/2.0 183", strlen("SIP/2.0 183")) != 0 &&
	    strnicmp(dptr, "SIP/2.0 200", strlen("SIP/2.0 200")) != 0)
		return NF_ACCEPT;

	return process_sdp(skb, &dptr, &datalen);
	if (strnicmp(dptr, "SIP/2.0 ", strlen("SIP/2.0 ")) != 0)
		return process_sip_request(skb, &dptr, &datalen);
	else
		return process_sip_response(skb, &dptr, &datalen);
}

static struct nf_conntrack_helper sip[MAX_PORTS][2] __read_mostly;