⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nf_conntrack_sip.c

📁 linux 内核源代码
💻 C
字号:
/* SIP extension for IP connection tracking. * * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar> * based on RR's ip_conntrack_ftp.c and other modules. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */#include <linux/module.h>#include <linux/ctype.h>#include <linux/skbuff.h>#include <linux/inet.h>#include <linux/in.h>#include <linux/udp.h>#include <linux/netfilter.h>#include <net/netfilter/nf_conntrack.h>#include <net/netfilter/nf_conntrack_expect.h>#include <net/netfilter/nf_conntrack_helper.h>#include <linux/netfilter/nf_conntrack_sip.h>MODULE_LICENSE("GPL");MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>");MODULE_DESCRIPTION("SIP connection tracking helper");MODULE_ALIAS("ip_conntrack_sip");#define MAX_PORTS	8static unsigned short ports[MAX_PORTS];static int ports_c;module_param_array(ports, ushort, &ports_c, 0400);MODULE_PARM_DESC(ports, "port numbers of SIP servers");static unsigned int sip_timeout __read_mostly = SIP_TIMEOUT;module_param(sip_timeout, uint, 0600);MODULE_PARM_DESC(sip_timeout, "timeout for the master SIP session");unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb,				enum ip_conntrack_info ctinfo,				struct nf_conn *ct,				const char **dptr) __read_mostly;EXPORT_SYMBOL_GPL(nf_nat_sip_hook);unsigned int (*nf_nat_sdp_hook)(struct sk_buff *skb,				enum ip_conntrack_info ctinfo,				struct nf_conntrack_expect *exp,				const char *dptr) __read_mostly;EXPORT_SYMBOL_GPL(nf_nat_sdp_hook);static int digits_len(struct nf_conn *, const char *, const char *, int *);static int epaddr_len(struct nf_conn *, const char *, const char *, int *);static int skp_digits_len(struct nf_conn *, const char *, const char *, int *);static int skp_epaddr_len(struct nf_conn *, const char *, const char *, int *);struct sip_header_nfo {	const char	*lname;	const char	*sname;	const char	*ln_str;	size_t		lnlen;	size_t		snlen;	size_t		ln_strlen;	int		case_sensitive;	int		(*match_len)(struct nf_conn *, const char *,				     const char *, int *);};static const struct sip_header_nfo ct_sip_hdrs[] = {	[POS_REG_REQ_URI] = { 	/* SIP REGISTER request URI */		.lname		= "sip:",		.lnlen		= sizeof("sip:") - 1,		.ln_str		= ":",		.ln_strlen	= sizeof(":") - 1,		.match_len	= epaddr_len,	},	[POS_REQ_URI] = { 	/* SIP request URI */		.lname		= "sip:",		.lnlen		= sizeof("sip:") - 1,		.ln_str		= "@",		.ln_strlen	= sizeof("@") - 1,		.match_len	= epaddr_len,	},	[POS_FROM] = {		/* SIP From header */		.lname		= "From:",		.lnlen		= sizeof("From:") - 1,		.sname		= "\r\nf:",		.snlen		= sizeof("\r\nf:") - 1,		.ln_str		= "sip:",		.ln_strlen	= sizeof("sip:") - 1,		.match_len	= skp_epaddr_len,	},	[POS_TO] = {		/* SIP To header */		.lname		= "To:",		.lnlen		= sizeof("To:") - 1,		.sname		= "\r\nt:",		.snlen		= sizeof("\r\nt:") - 1,		.ln_str		= "sip:",		.ln_strlen	= sizeof("sip:") - 1,		.match_len	= skp_epaddr_len	},	[POS_VIA] = { 		/* SIP Via header */		.lname		= "Via:",		.lnlen		= sizeof("Via:") - 1,		.sname		= "\r\nv:",		.snlen		= sizeof("\r\nv:") - 1, /* rfc3261 "\r\n" */		.ln_str		= "UDP ",		.ln_strlen	= sizeof("UDP ") - 1,		.match_len	= epaddr_len,	},	[POS_CONTACT] = { 	/* SIP Contact header */		.lname		= "Contact:",		.lnlen		= sizeof("Contact:") - 1,		.sname		= "\r\nm:",		.snlen		= sizeof("\r\nm:") - 1,		.ln_str		= "sip:",		.ln_strlen	= sizeof("sip:") - 1,		.match_len	= skp_epaddr_len	},	[POS_CONTENT] = { 	/* SIP Content length header */		.lname		= "Content-Length:",		.lnlen		= sizeof("Content-Length:") - 1,		.sname		= "\r\nl:",		.snlen		= sizeof("\r\nl:") - 1,		.ln_str		= ":",		.ln_strlen	= sizeof(":") - 1,		.match_len	= skp_digits_len	},	[POS_MEDIA] = {		/* SDP media info */		.case_sensitive	= 1,		.lname		= "\nm=",		.lnlen		= sizeof("\nm=") - 1,		.sname		= "\rm=",		.snlen		= sizeof("\rm=") - 1,		.ln_str		= "audio ",		.ln_strlen	= sizeof("audio ") - 1,		.match_len	= digits_len	},	[POS_OWNER_IP4] = {	/* SDP owner address*/		.case_sensitive	= 1,		.lname		= "\no=",		.lnlen		= sizeof("\no=") - 1,		.sname		= "\ro=",		.snlen		= sizeof("\ro=") - 1,		.ln_str		= "IN IP4 ",		.ln_strlen	= sizeof("IN IP4 ") - 1,		.match_len	= epaddr_len	},	[POS_CONNECTION_IP4] = {/* SDP connection info */		.case_sensitive	= 1,		.lname		= "\nc=",		.lnlen		= sizeof("\nc=") - 1,		.sname		= "\rc=",		.snlen		= sizeof("\rc=") - 1,		.ln_str		= "IN IP4 ",		.ln_strlen	= sizeof("IN IP4 ") - 1,		.match_len	= epaddr_len	},	[POS_OWNER_IP6] = {	/* SDP owner address*/		.case_sensitive	= 1,		.lname		= "\no=",		.lnlen		= sizeof("\no=") - 1,		.sname		= "\ro=",		.snlen		= sizeof("\ro=") - 1,		.ln_str		= "IN IP6 ",		.ln_strlen	= sizeof("IN IP6 ") - 1,		.match_len	= epaddr_len	},	[POS_CONNECTION_IP6] = {/* SDP connection info */		.case_sensitive	= 1,		.lname		= "\nc=",		.lnlen		= sizeof("\nc=") - 1,		.sname		= "\rc=",		.snlen		= sizeof("\rc=") - 1,		.ln_str		= "IN IP6 ",		.ln_strlen	= sizeof("IN IP6 ") - 1,		.match_len	= epaddr_len	},	[POS_SDP_HEADER] = { 	/* SDP version header */		.case_sensitive	= 1,		.lname		= "\nv=",		.lnlen		= sizeof("\nv=") - 1,		.sname		= "\rv=",		.snlen		= sizeof("\rv=") - 1,		.ln_str		= "=",		.ln_strlen	= sizeof("=") - 1,		.match_len	= digits_len	}};/* get line length until first CR or LF seen. */int ct_sip_lnlen(const char *line, const char *limit){	const char *k = line;	while ((line <= limit) && (*line == '\r' || *line == '\n'))		line++;	while (line <= limit) {		if (*line == '\r' || *line == '\n')			break;		line++;	}	return line - k;}EXPORT_SYMBOL_GPL(ct_sip_lnlen);/* Linear string search, case sensitive. */const char *ct_sip_search(const char *needle, const char *haystack,			  size_t needle_len, size_t haystack_len,			  int case_sensitive){	const char *limit = haystack + (haystack_len - needle_len);	while (haystack <= limit) {		if (case_sensitive) {			if (strncmp(haystack, needle, needle_len) == 0)				return haystack;		} else {			if (strnicmp(haystack, needle, needle_len) == 0)				return haystack;		}		haystack++;	}	return NULL;}EXPORT_SYMBOL_GPL(ct_sip_search);static int digits_len(struct nf_conn *ct, const char *dptr,		      const char *limit, int *shift){	int len = 0;	while (dptr <= limit && isdigit(*dptr)) {		dptr++;		len++;	}	return len;}/* get digits length, skipping blank spaces. */static int skp_digits_len(struct nf_conn *ct, const char *dptr,			  const char *limit, int *shift){	for (; dptr <= limit && *dptr == ' '; dptr++)		(*shift)++;	return digits_len(ct, dptr, limit, shift);}static int parse_addr(struct nf_conn *ct, const char *cp, const char **endp,		      union nf_conntrack_address *addr, const char *limit){	const char *end;	int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;	int ret = 0;	switch (family) {	case AF_INET:		ret = in4_pton(cp, limit - cp, (u8 *)&addr->ip, -1, &end);		break;	case AF_INET6:		ret = in6_pton(cp, limit - cp, (u8 *)&addr->ip6, -1, &end);		break;	default:		BUG();	}	if (ret == 0 || end == cp)		return 0;	if (endp)		*endp = end;	return 1;}/* skip ip address. returns its length. */static int epaddr_len(struct nf_conn *ct, const char *dptr,		      const char *limit, int *shift){	union nf_conntrack_address addr;	const char *aux = dptr;	if (!parse_addr(ct, dptr, &dptr, &addr, limit)) {		pr_debug("ip: %s parse failed.!\n", dptr);		return 0;	}	/* Port number */	if (*dptr == ':') {		dptr++;		dptr += digits_len(ct, dptr, limit, shift);	}	return dptr - aux;}/* get address length, skiping user info. */static int skp_epaddr_len(struct nf_conn *ct, const char *dptr,			  const char *limit, int *shift){	const char *start = dptr;	int s = *shift;	/* Search for @, but stop at the end of the line.	 * We are inside a sip: URI, so we don't need to worry about	 * continuation lines. */	while (dptr <= limit &&	       *dptr != '@' && *dptr != '\r' && *dptr != '\n') {		(*shift)++;		dptr++;	}	if (dptr <= limit && *dptr == '@') {		dptr++;		(*shift)++;	} else {		dptr = start;		*shift = s;	}	return epaddr_len(ct, dptr, limit, shift);}/* Returns 0 if not found, -1 error parsing. */int ct_sip_get_info(struct nf_conn *ct,		    const char *dptr, size_t dlen,		    unsigned int *matchoff,		    unsigned int *matchlen,		    enum sip_header_pos pos){	const struct sip_header_nfo *hnfo = &ct_sip_hdrs[pos];	const char *limit, *aux, *k = dptr;	int shift = 0;	limit = dptr + (dlen - hnfo->lnlen);	while (dptr <= limit) {		if ((strncmp(dptr, hnfo->lname, hnfo->lnlen) != 0) &&		    (hnfo->sname == NULL ||		     strncmp(dptr, hnfo->sname, hnfo->snlen) != 0)) {			dptr++;			continue;		}		aux = ct_sip_search(hnfo->ln_str, dptr, hnfo->ln_strlen,				    ct_sip_lnlen(dptr, limit),				    hnfo->case_sensitive);		if (!aux) {			pr_debug("'%s' not found in '%s'.\n", hnfo->ln_str,				 hnfo->lname);			return -1;		}		aux += hnfo->ln_strlen;		*matchlen = hnfo->match_len(ct, aux, limit, &shift);		if (!*matchlen)			return -1;		*matchoff = (aux - k) + shift;		pr_debug("%s match succeeded! - len: %u\n", hnfo->lname,			 *matchlen);		return 1;	}	pr_debug("%s header not found.\n", hnfo->lname);	return 0;}EXPORT_SYMBOL_GPL(ct_sip_get_info);static int set_expected_rtp(struct sk_buff *skb,			    struct nf_conn *ct,			    enum ip_conntrack_info ctinfo,			    union nf_conntrack_address *addr,			    __be16 port,			    const char *dptr){	struct nf_conntrack_expect *exp;	enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);	int family = ct->tuplehash[!dir].tuple.src.l3num;	int ret;	typeof(nf_nat_sdp_hook) nf_nat_sdp;	exp = nf_ct_expect_alloc(ct);	if (exp == NULL)		return NF_DROP;	nf_ct_expect_init(exp, family,			  &ct->tuplehash[!dir].tuple.src.u3, addr,			  IPPROTO_UDP, NULL, &port);	nf_nat_sdp = rcu_dereference(nf_nat_sdp_hook);	if (nf_nat_sdp && ct->status & IPS_NAT_MASK)		ret = nf_nat_sdp(skb, ctinfo, exp, dptr);	else {		if (nf_ct_expect_related(exp) != 0)			ret = NF_DROP;		else			ret = NF_ACCEPT;	}	nf_ct_expect_put(exp);	return ret;}static int sip_help(struct sk_buff *skb,		    unsigned int protoff,		    struct nf_conn *ct,		    enum ip_conntrack_info ctinfo){	int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;	union nf_conntrack_address addr;	unsigned int dataoff, datalen;	const char *dptr;	int ret = NF_ACCEPT;	int matchoff, matchlen;	u_int16_t port;	enum sip_header_pos pos;	typeof(nf_nat_sip_hook) nf_nat_sip;	/* No Data ? */	dataoff = protoff + sizeof(struct udphdr);	if (dataoff >= skb->len)		return NF_ACCEPT;	nf_ct_refresh(ct, skb, sip_timeout * HZ);	if (!skb_is_nonlinear(skb))		dptr = skb->data + dataoff;	else {		pr_debug("Copy of skbuff not supported yet.\n");		goto out;	}	nf_nat_sip = rcu_dereference(nf_nat_sip_hook);	if (nf_nat_sip && ct->status & IPS_NAT_MASK) {		if (!nf_nat_sip(skb, ctinfo, ct, &dptr)) {			ret = NF_DROP;			goto out;		}	}	datalen = skb->len - dataoff;	if (datalen < sizeof("SIP/2.0 200") - 1)		goto out;	/* RTP info only in some SDP pkts */	if (memcmp(dptr, "INVITE", sizeof("INVITE") - 1) != 0 &&	    memcmp(dptr, "UPDATE", sizeof("UPDATE") - 1) != 0 &&	    memcmp(dptr, "SIP/2.0 180", sizeof("SIP/2.0 180") - 1) != 0 &&	    memcmp(dptr, "SIP/2.0 183", sizeof("SIP/2.0 183") - 1) != 0 &&	    memcmp(dptr, "SIP/2.0 200", sizeof("SIP/2.0 200") - 1) != 0) {		goto out;	}	/* Get address and port from SDP packet. */	pos = family == AF_INET ? POS_CONNECTION_IP4 : POS_CONNECTION_IP6;	if (ct_sip_get_info(ct, dptr, datalen, &matchoff, &matchlen, pos) > 0) {		/* We'll drop only if there are parse problems. */		if (!parse_addr(ct, dptr + matchoff, NULL, &addr,				dptr + datalen)) {			ret = NF_DROP;			goto out;		}		if (ct_sip_get_info(ct, dptr, datalen, &matchoff, &matchlen,				    POS_MEDIA) > 0) {			port = simple_strtoul(dptr + matchoff, NULL, 10);			if (port < 1024) {				ret = NF_DROP;				goto out;			}			ret = set_expected_rtp(skb, ct, ctinfo, &addr,					       htons(port), dptr);		}	}out:	return ret;}static struct nf_conntrack_helper sip[MAX_PORTS][2] __read_mostly;static char sip_names[MAX_PORTS][2][sizeof("sip-65535")] __read_mostly;static void nf_conntrack_sip_fini(void){	int i, j;	for (i = 0; i < ports_c; i++) {		for (j = 0; j < 2; j++) {			if (sip[i][j].me == NULL)				continue;			nf_conntrack_helper_unregister(&sip[i][j]);		}	}}static int __init nf_conntrack_sip_init(void){	int i, j, ret;	char *tmpname;	if (ports_c == 0)		ports[ports_c++] = SIP_PORT;	for (i = 0; i < ports_c; i++) {		memset(&sip[i], 0, sizeof(sip[i]));		sip[i][0].tuple.src.l3num = AF_INET;		sip[i][1].tuple.src.l3num = AF_INET6;		for (j = 0; j < 2; j++) {			sip[i][j].tuple.dst.protonum = IPPROTO_UDP;			sip[i][j].tuple.src.u.udp.port = htons(ports[i]);			sip[i][j].max_expected = 2;			sip[i][j].timeout = 3 * 60; /* 3 minutes */			sip[i][j].me = THIS_MODULE;			sip[i][j].help = sip_help;			tmpname = &sip_names[i][j][0];			if (ports[i] == SIP_PORT)				sprintf(tmpname, "sip");			else				sprintf(tmpname, "sip-%u", i);			sip[i][j].name = tmpname;			pr_debug("port #%u: %u\n", i, ports[i]);			ret = nf_conntrack_helper_register(&sip[i][j]);			if (ret) {				printk("nf_ct_sip: failed to register helper "				       "for pf: %u port: %u\n",				       sip[i][j].tuple.src.l3num, ports[i]);				nf_conntrack_sip_fini();				return ret;			}		}	}	return 0;}module_init(nf_conntrack_sip_init);module_exit(nf_conntrack_sip_fini);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -