cregit-Linux how code gets into the kernel

Release 4.8 net/netfilter/nf_conntrack_h323_main.c

Directory: net/netfilter
/*
 * H.323 connection tracking helper
 *
 * Copyright (c) 2006 Jing Min Zhao <zhaojingmin@users.sourceforge.net>
 * Copyright (c) 2006-2012 Patrick McHardy <kaber@trash.net>
 *
 * This source code is licensed under General Public License version 2.
 *
 * Based on the 'brute force' H.323 connection tracking module by
 * Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
 *
 * For more information, please see http://nath323.sourceforge.net/
 */

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/ctype.h>
#include <linux/inet.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/slab.h>
#include <linux/udp.h>
#include <linux/tcp.h>
#include <linux/skbuff.h>
#include <net/route.h>
#include <net/ip6_route.h>

#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_core.h>
#include <net/netfilter/nf_conntrack_tuple.h>
#include <net/netfilter/nf_conntrack_expect.h>
#include <net/netfilter/nf_conntrack_ecache.h>
#include <net/netfilter/nf_conntrack_helper.h>
#include <net/netfilter/nf_conntrack_zones.h>
#include <linux/netfilter/nf_conntrack_h323.h>

/* Parameters */

static unsigned int default_rrq_ttl __read_mostly = 300;
module_param(default_rrq_ttl, uint, 0600);
MODULE_PARM_DESC(default_rrq_ttl, "use this TTL if it's missing in RRQ");


static int gkrouted_only __read_mostly = 1;
module_param(gkrouted_only, int, 0600);
MODULE_PARM_DESC(gkrouted_only, "only accept calls from gatekeeper");


static bool callforward_filter __read_mostly = true;
module_param(callforward_filter, bool, 0600);
MODULE_PARM_DESC(callforward_filter, "only create call forwarding expectations "
				     "if both endpoints are on different sides "
				     "(determined by routing information)");

/* Hooks for NAT */
int (*set_h245_addr_hook) (struct sk_buff *skb, unsigned int protoff,
			   unsigned char **data, int dataoff,
			   H245_TransportAddress *taddr,
			   union nf_inet_addr *addr, __be16 port)
			   __read_mostly;
int (*set_h225_addr_hook) (struct sk_buff *skb, unsigned int protoff,
			   unsigned char **data, int dataoff,
			   TransportAddress *taddr,
			   union nf_inet_addr *addr, __be16 port)
			   __read_mostly;
int (*set_sig_addr_hook) (struct sk_buff *skb,
			  struct nf_conn *ct,
			  enum ip_conntrack_info ctinfo,
			  unsigned int protoff, unsigned char **data,
			  TransportAddress *taddr, int count) __read_mostly;
int (*set_ras_addr_hook) (struct sk_buff *skb,
			  struct nf_conn *ct,
			  enum ip_conntrack_info ctinfo,
			  unsigned int protoff, unsigned char **data,
			  TransportAddress *taddr, int count) __read_mostly;
int (*nat_rtp_rtcp_hook) (struct sk_buff *skb,
			  struct nf_conn *ct,
			  enum ip_conntrack_info ctinfo,
			  unsigned int protoff,
			  unsigned char **data, int dataoff,
			  H245_TransportAddress *taddr,
			  __be16 port, __be16 rtp_port,
			  struct nf_conntrack_expect *rtp_exp,
			  struct nf_conntrack_expect *rtcp_exp) __read_mostly;
int (*nat_t120_hook) (struct sk_buff *skb,
		      struct nf_conn *ct,
		      enum ip_conntrack_info ctinfo,
		      unsigned int protoff,
		      unsigned char **data, int dataoff,
		      H245_TransportAddress *taddr, __be16 port,
		      struct nf_conntrack_expect *exp) __read_mostly;
int (*nat_h245_hook) (struct sk_buff *skb,
		      struct nf_conn *ct,
		      enum ip_conntrack_info ctinfo,
		      unsigned int protoff,
		      unsigned char **data, int dataoff,
		      TransportAddress *taddr, __be16 port,
		      struct nf_conntrack_expect *exp) __read_mostly;
int (*nat_callforwarding_hook) (struct sk_buff *skb,
				struct nf_conn *ct,
				enum ip_conntrack_info ctinfo,
				unsigned int protoff,
				unsigned char **data, int dataoff,
				TransportAddress *taddr, __be16 port,
				struct nf_conntrack_expect *exp) __read_mostly;
int (*nat_q931_hook) (struct sk_buff *skb,
		      struct nf_conn *ct,
		      enum ip_conntrack_info ctinfo,
		      unsigned int protoff,
		      unsigned char **data, TransportAddress *taddr, int idx,
		      __be16 port, struct nf_conntrack_expect *exp)
		      __read_mostly;

static DEFINE_SPINLOCK(nf_h323_lock);

static char *h323_buffer;


static struct nf_conntrack_helper nf_conntrack_helper_h245;

static struct nf_conntrack_helper nf_conntrack_helper_q931[];

static struct nf_conntrack_helper nf_conntrack_helper_ras[];

/****************************************************************************/

static int get_tpkt_data(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, enum ip_conntrack_info ctinfo, unsigned char **data, int *datalen, int *dataoff) { struct nf_ct_h323_master *info = nfct_help_data(ct); int dir = CTINFO2DIR(ctinfo); const struct tcphdr *th; struct tcphdr _tcph; int tcpdatalen; int tcpdataoff; unsigned char *tpkt; int tpktlen; int tpktoff; /* Get TCP header */ th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph); if (th == NULL) return 0; /* Get TCP data offset */ tcpdataoff = protoff + th->doff * 4; /* Get TCP data length */ tcpdatalen = skb->len - tcpdataoff; if (tcpdatalen <= 0) /* No TCP data */ goto clear_out; if (*data == NULL) { /* first TPKT */ /* Get first TPKT pointer */ tpkt = skb_header_pointer(skb, tcpdataoff, tcpdatalen, h323_buffer); BUG_ON(tpkt == NULL); /* Validate TPKT identifier */ if (tcpdatalen < 4 || tpkt[0] != 0x03 || tpkt[1] != 0) { /* Netmeeting sends TPKT header and data separately */ if (info->tpkt_len[dir] > 0) { pr_debug("nf_ct_h323: previous packet " "indicated separate TPKT data of %hu " "bytes\n", info->tpkt_len[dir]); if (info->tpkt_len[dir] <= tcpdatalen) { /* Yes, there was a TPKT header * received */ *data = tpkt; *datalen = info->tpkt_len[dir]; *dataoff = 0; goto out; } /* Fragmented TPKT */ pr_debug("nf_ct_h323: fragmented TPKT\n"); goto clear_out; } /* It is not even a TPKT */ return 0; } tpktoff = 0; } else { /* Next TPKT */ tpktoff = *dataoff + *datalen; tcpdatalen -= tpktoff; if (tcpdatalen <= 4) /* No more TPKT */ goto clear_out; tpkt = *data + *datalen; /* Validate TPKT identifier */ if (tpkt[0] != 0x03 || tpkt[1] != 0) goto clear_out; } /* Validate TPKT length */ tpktlen = tpkt[2] * 256 + tpkt[3]; if (tpktlen < 4) goto clear_out; if (tpktlen > tcpdatalen) { if (tcpdatalen == 4) { /* Separate TPKT header */ /* Netmeeting sends TPKT header and data separately */ pr_debug("nf_ct_h323: separate TPKT header indicates " "there will be TPKT data of %hu bytes\n", tpktlen - 4); info->tpkt_len[dir] = tpktlen - 4; return 0; } pr_debug("nf_ct_h323: incomplete TPKT (fragmented?)\n"); goto clear_out; } /* This is the encapsulated data */ *data = tpkt + 4; *datalen = tpktlen - 4; *dataoff = tpktoff + 4; out: /* Clear TPKT length */ info->tpkt_len[dir] = 0; return 1; clear_out: info->tpkt_len[dir] = 0; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
patrick mchardypatrick mchardy44597.80%457.14%
jan engelhardtjan engelhardt51.10%114.29%
herbert xuherbert xu40.88%114.29%
pablo neira ayusopablo neira ayuso10.22%114.29%
Total455100.00%7100.00%

/****************************************************************************/
static int get_h245_addr(struct nf_conn *ct, const unsigned char *data, H245_TransportAddress *taddr, union nf_inet_addr *addr, __be16 *port) { const unsigned char *p; int len; if (taddr->choice != eH245_TransportAddress_unicastAddress) return 0; switch (taddr->unicastAddress.choice) { case eUnicastAddress_iPAddress: if (nf_ct_l3num(ct) != AF_INET) return 0; p = data + taddr->unicastAddress.iPAddress.network; len = 4; break; case eUnicastAddress_iP6Address: if (nf_ct_l3num(ct) != AF_INET6) return 0; p = data + taddr->unicastAddress.iP6Address.network; len = 16; break; default: return 0; } memcpy(addr, p, len); memset((void *)addr + len, 0, sizeof(*addr) - len); memcpy(port, p + len, sizeof(__be16)); return 1; }

Contributors

PersonTokensPropCommitsCommitProp
patrick mchardypatrick mchardy17298.29%250.00%
jan engelhardtjan engelhardt31.71%250.00%
Total175100.00%4100.00%

/****************************************************************************/
static int expect_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, unsigned int protoff, unsigned char **data, int dataoff, H245_TransportAddress *taddr) { int dir = CTINFO2DIR(ctinfo); int ret = 0; __be16 port; __be16 rtp_port, rtcp_port; union nf_inet_addr addr; struct nf_conntrack_expect *rtp_exp; struct nf_conntrack_expect *rtcp_exp; typeof(nat_rtp_rtcp_hook) nat_rtp_rtcp; /* Read RTP or RTCP address */ if (!get_h245_addr(ct, *data, taddr, &addr, &port) || memcmp(&addr, &ct->tuplehash[dir].tuple.src.u3, sizeof(addr)) || port == 0) return 0; /* RTP port is even */ rtp_port = port & ~htons(1); rtcp_port = port | htons(1); /* Create expect for RTP */ if ((rtp_exp = nf_ct_expect_alloc(ct)) == NULL) return -1; nf_ct_expect_init(rtp_exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct), &ct->tuplehash[!dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3, IPPROTO_UDP, NULL, &rtp_port); /* Create expect for RTCP */ if ((rtcp_exp = nf_ct_expect_alloc(ct)) == NULL) { nf_ct_expect_put(rtp_exp); return -1; } nf_ct_expect_init(rtcp_exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct), &ct->tuplehash[!dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3, IPPROTO_UDP, NULL, &rtcp_port); if (memcmp(&ct->tuplehash[dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3, sizeof(ct->tuplehash[dir].tuple.src.u3)) && (nat_rtp_rtcp = rcu_dereference(nat_rtp_rtcp_hook)) && nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK) { /* NAT needed */ ret = nat_rtp_rtcp(skb, ct, ctinfo, protoff, data, dataoff, taddr, port, rtp_port, rtp_exp, rtcp_exp); } else { /* Conntrack only */ if (nf_ct_expect_related(rtp_exp) == 0) { if (nf_ct_expect_related(rtcp_exp) == 0) { pr_debug("nf_ct_h323: expect RTP "); nf_ct_dump_tuple(&rtp_exp->tuple); pr_debug("nf_ct_h323: expect RTCP "); nf_ct_dump_tuple(&rtcp_exp->tuple); } else { nf_ct_unexpect_related(rtp_exp); ret = -1; } } else ret = -1; } nf_ct_expect_put(rtp_exp); nf_ct_expect_put(rtcp_exp); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
patrick mchardypatrick mchardy46697.69%763.64%
pablo neira ayusopablo neira ayuso61.26%19.09%
jan engelhardtjan engelhardt30.63%218.18%
herbert xuherbert xu20.42%19.09%
Total477100.00%11100.00%

/****************************************************************************/
static int expect_t120(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, unsigned int protoff, unsigned char **data, int dataoff, H245_TransportAddress *taddr) { int dir = CTINFO2DIR(ctinfo); int ret = 0; __be16 port; union nf_inet_addr addr; struct nf_conntrack_expect *exp; typeof(nat_t120_hook) nat_t120; /* Read T.120 address */ if (!get_h245_addr(ct, *data, taddr, &addr, &port) || memcmp(&addr, &ct->tuplehash[dir].tuple.src.u3, sizeof(addr)) || port == 0) return 0; /* Create expect for T.120 connections */ if ((exp = nf_ct_expect_alloc(ct)) == NULL) return -1; nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct), &ct->tuplehash[!dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3, IPPROTO_TCP, NULL, &port); exp->flags = NF_CT_EXPECT_PERMANENT; /* Accept multiple channels */ if (memcmp(&ct->tuplehash[dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3, sizeof(ct->tuplehash[dir].tuple.src.u3)) && (nat_t120 = rcu_dereference(nat_t120_hook)) && nf_ct_l3num(ct) == NFPROTO_IPV4 && ct->status & IPS_NAT_MASK) { /* NAT needed */ ret = nat_t120(skb, ct, ctinfo, protoff, data, dataoff, taddr, port, exp); } else { /* Conntrack only */ if (nf_ct_expect_related(exp) == 0) { pr_debug("nf_ct_h323: expect T.120 "); nf_ct_dump_tuple(&exp->tuple); } else ret = -1; } nf_ct_expect_put(exp); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
patrick mchardypatrick mchardy33098.80%770.00%
jan engelhardtjan engelhardt20.60%220.00%
herbert xuherbert xu20.60%110.00%
Total334100.00%10100.00%

/****************************************************************************/
static int process_h245_channel(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, unsigned int protoff, unsigned char **data, int dataoff, H2250LogicalChannelParameters *channel) { int ret; if (channel->options & eH2250LogicalChannelParameters_mediaChannel) { /* RTP */ ret = expect_rtp_rtcp(skb, ct, ctinfo, protoff, data, dataoff, &channel->mediaChannel); if (ret < 0) return -1; } if (channel-> options & eH2250LogicalChannelParameters_mediaControlChannel) { /* RTCP */ ret = expect_rtp_rtcp(skb, ct, ctinfo, protoff, data, dataoff, &channel->mediaControlChannel); if (ret < 0) return -1; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
patrick mchardypatrick mchardy12697.67%266.67%
herbert xuherbert xu32.33%133.33%
Total129100.00%3100.00%

/****************************************************************************/
static int process_olc(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, unsigned int protoff, unsigned char **data, int dataoff, OpenLogicalChannel *olc) { int ret; pr_debug("nf_ct_h323: OpenLogicalChannel\n"); if (olc->forwardLogicalChannelParameters.multiplexParameters.choice == eOpenLogicalChannel_forwardLogicalChannelParameters_multiplexParameters_h2250LogicalChannelParameters) { ret = process_h245_channel(skb, ct, ctinfo, protoff, data, dataoff, &olc-> forwardLogicalChannelParameters. multiplexParameters. h2250LogicalChannelParameters); if (ret < 0) return -1; } if ((olc->options & eOpenLogicalChannel_reverseLogicalChannelParameters) && (olc->reverseLogicalChannelParameters.options & eOpenLogicalChannel_reverseLogicalChannelParameters_multiplexParameters) && (olc->reverseLogicalChannelParameters.multiplexParameters. choice == eOpenLogicalChannel_reverseLogicalChannelParameters_multiplexParameters_h2250LogicalChannelParameters)) { ret = process_h245_channel(skb, ct, ctinfo, protoff, data, dataoff, &olc-> reverseLogicalChannelParameters. multiplexParameters. h2250LogicalChannelParameters); if (ret < 0) return -1; } if ((olc->options & eOpenLogicalChannel_separateStack) && olc->forwardLogicalChannelParameters.dataType.choice == eDataType_data && olc->forwardLogicalChannelParameters.dataType.data.application. choice == eDataApplicationCapability_application_t120 && olc->forwardLogicalChannelParameters.dataType.data.application. t120.choice == eDataProtocolCapability_separateLANStack && olc->separateStack.networkAddress.choice == eNetworkAccessParameters_networkAddress_localAreaAddress) { ret = expect_t120(skb, ct, ctinfo, protoff, data, dataoff, &olc->separateStack.networkAddress. localAreaAddress); if (ret < 0) return -1; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
patrick mchardypatrick mchardy26298.50%375.00%
herbert xuherbert xu41.50%125.00%
Total266100.00%4100.00%

/****************************************************************************/
static int process_olca(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, unsigned int protoff, unsigned char **data, int dataoff, OpenLogicalChannelAck *olca) { H2250LogicalChannelAckParameters *ack; int ret; pr_debug("nf_ct_h323: OpenLogicalChannelAck\n"); if ((olca->options & eOpenLogicalChannelAck_reverseLogicalChannelParameters) && (olca->reverseLogicalChannelParameters.options & eOpenLogicalChannelAck_reverseLogicalChannelParameters_multiplexParameters) && (olca->reverseLogicalChannelParameters.multiplexParameters. choice == eOpenLogicalChannelAck_reverseLogicalChannelParameters_multiplexParameters_h2250LogicalChannelParameters)) { ret = process_h245_channel(skb, ct, ctinfo, protoff, data, dataoff, &olca-> reverseLogicalChannelParameters. multiplexParameters. h2250LogicalChannelParameters); if (ret < 0) return -1; } if ((olca->options & eOpenLogicalChannelAck_forwardMultiplexAckParameters) && (olca->forwardMultiplexAckParameters.choice == eOpenLogicalChannelAck_forwardMultiplexAckParameters_h2250LogicalChannelAckParameters)) { ack = &olca->forwardMultiplexAckParameters. h2250LogicalChannelAckParameters; if (ack->options & eH2250LogicalChannelAckParameters_mediaChannel) { /* RTP */ ret = expect_rtp_rtcp(skb, ct, ctinfo, protoff, data, dataoff, &ack->mediaChannel); if (ret < 0) return -1; } if (ack->options & eH2250LogicalChannelAckParameters_mediaControlChannel) { /* RTCP */ ret = expect_rtp_rtcp(skb, ct, ctinfo, protoff, data, dataoff, &ack->mediaControlChannel); if (ret < 0) return -1; } } if ((olca->options & eOpenLogicalChannelAck_separateStack) && olca->separateStack.networkAddress.choice == eNetworkAccessParameters_networkAddress_localAreaAddress) { ret = expect_t120(skb, ct, ctinfo, protoff, data, dataoff, &olca->separateStack.networkAddress. localAreaAddress); if (ret < 0) return -1; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
patrick mchardypatrick mchardy23779.80%360.00%
jing min zhaojing min zhao5518.52%120.00%
herbert xuherbert xu51.68%120.00%
Total297100.00%5100.00%

/****************************************************************************/
static int process_h245(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, unsigned int protoff, unsigned char **data, int dataoff, MultimediaSystemControlMessage *mscm) { switch (mscm->choice) { case eMultimediaSystemControlMessage_request: if (mscm->request.choice == eRequestMessage_openLogicalChannel) { return process_olc(skb, ct, ctinfo, protoff, data, dataoff, &mscm->request.openLogicalChannel); } pr_debug("nf_ct_h323: H.245 Request %d\n", mscm->request.choice); break; case eMultimediaSystemControlMessage_response: if (mscm->response.choice == eResponseMessage_openLogicalChannelAck) { return process_olca(skb, ct, ctinfo, protoff, data, dataoff, &mscm->response. openLogicalChannelAck); } pr_debug("nf_ct_h323: H.245 Response %d\n", mscm->response.choice); break; default: pr_debug("nf_ct_h323: H.245 signal %d\n", mscm->choice); break; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
patrick mchardypatrick mchardy15698.11%375.00%
herbert xuherbert xu31.89%125.00%
Total159100.00%4100.00%

/****************************************************************************/
static int h245_help(struct sk_buff *skb, unsigned int protoff, struct nf_conn *