nf_conntrack_ftp.c

来自「linux 内核源代码」· C语言 代码 · 共 589 行 · 第 1/2 页

C
589
字号
	i++;	pr_debug("Skipped up to `%c'!\n", skip);	*numoff = i;	*numlen = getnum(data + i, dlen - i, cmd, term);	if (!*numlen)		return -1;	pr_debug("Match succeeded!\n");	return 1;}/* Look up to see if we're just after a \n. */static int find_nl_seq(u32 seq, const struct nf_ct_ftp_master *info, int dir){	unsigned int i;	for (i = 0; i < info->seq_aft_nl_num[dir]; i++)		if (info->seq_aft_nl[dir][i] == seq)			return 1;	return 0;}/* We don't update if it's older than what we have. */static void update_nl_seq(u32 nl_seq, struct nf_ct_ftp_master *info, int dir,			  struct sk_buff *skb){	unsigned int i, oldest = NUM_SEQ_TO_REMEMBER;	/* Look for oldest: if we find exact match, we're done. */	for (i = 0; i < info->seq_aft_nl_num[dir]; i++) {		if (info->seq_aft_nl[dir][i] == nl_seq)			return;		if (oldest == info->seq_aft_nl_num[dir] ||		    before(info->seq_aft_nl[dir][i],			   info->seq_aft_nl[dir][oldest]))			oldest = i;	}	if (info->seq_aft_nl_num[dir] < NUM_SEQ_TO_REMEMBER) {		info->seq_aft_nl[dir][info->seq_aft_nl_num[dir]++] = nl_seq;		nf_conntrack_event_cache(IPCT_HELPINFO_VOLATILE, skb);	} else if (oldest != NUM_SEQ_TO_REMEMBER &&		   after(nl_seq, info->seq_aft_nl[dir][oldest])) {		info->seq_aft_nl[dir][oldest] = nl_seq;		nf_conntrack_event_cache(IPCT_HELPINFO_VOLATILE, skb);	}}static int help(struct sk_buff *skb,		unsigned int protoff,		struct nf_conn *ct,		enum ip_conntrack_info ctinfo){	unsigned int dataoff, datalen;	struct tcphdr _tcph, *th;	char *fb_ptr;	int ret;	u32 seq;	int dir = CTINFO2DIR(ctinfo);	unsigned int matchlen, matchoff;	struct nf_ct_ftp_master *ct_ftp_info = &nfct_help(ct)->help.ct_ftp_info;	struct nf_conntrack_expect *exp;	union nf_conntrack_address *daddr;	struct nf_conntrack_man cmd = {};	unsigned int i;	int found = 0, ends_in_nl;	typeof(nf_nat_ftp_hook) nf_nat_ftp;	/* Until there's been traffic both ways, don't look in packets. */	if (ctinfo != IP_CT_ESTABLISHED	    && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) {		pr_debug("ftp: Conntrackinfo = %u\n", ctinfo);		return NF_ACCEPT;	}	th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph);	if (th == NULL)		return NF_ACCEPT;	dataoff = protoff + th->doff * 4;	/* No data? */	if (dataoff >= skb->len) {		pr_debug("ftp: dataoff(%u) >= skblen(%u)\n", dataoff,			 skb->len);		return NF_ACCEPT;	}	datalen = skb->len - dataoff;	spin_lock_bh(&nf_ftp_lock);	fb_ptr = skb_header_pointer(skb, dataoff, datalen, ftp_buffer);	BUG_ON(fb_ptr == NULL);	ends_in_nl = (fb_ptr[datalen - 1] == '\n');	seq = ntohl(th->seq) + datalen;	/* Look up to see if we're just after a \n. */	if (!find_nl_seq(ntohl(th->seq), ct_ftp_info, dir)) {		/* Now if this ends in \n, update ftp info. */		pr_debug("nf_conntrack_ftp: wrong seq pos %s(%u) or %s(%u)\n",			 ct_ftp_info->seq_aft_nl_num[dir] > 0 ? "" : "(UNSET)",			 ct_ftp_info->seq_aft_nl[dir][0],			 ct_ftp_info->seq_aft_nl_num[dir] > 1 ? "" : "(UNSET)",			 ct_ftp_info->seq_aft_nl[dir][1]);		ret = NF_ACCEPT;		goto out_update_nl;	}	/* Initialize IP/IPv6 addr to expected address (it's not mentioned	   in EPSV responses) */	cmd.l3num = ct->tuplehash[dir].tuple.src.l3num;	memcpy(cmd.u3.all, &ct->tuplehash[dir].tuple.src.u3.all,	       sizeof(cmd.u3.all));	for (i = 0; i < ARRAY_SIZE(search[dir]); i++) {		found = find_pattern(fb_ptr, datalen,				     search[dir][i].pattern,				     search[dir][i].plen,				     search[dir][i].skip,				     search[dir][i].term,				     &matchoff, &matchlen,				     &cmd,				     search[dir][i].getnum);		if (found) break;	}	if (found == -1) {		/* We don't usually drop packets.  After all, this is		   connection tracking, not packet filtering.		   However, it is necessary for accurate tracking in		   this case. */		if (net_ratelimit())			printk("conntrack_ftp: partial %s %u+%u\n",			       search[dir][i].pattern,			       ntohl(th->seq), datalen);		ret = NF_DROP;		goto out;	} else if (found == 0) { /* No match */		ret = NF_ACCEPT;		goto out_update_nl;	}	pr_debug("conntrack_ftp: match `%.*s' (%u bytes at %u)\n",		 matchlen, fb_ptr + matchoff,		 matchlen, ntohl(th->seq) + matchoff);	exp = nf_ct_expect_alloc(ct);	if (exp == NULL) {		ret = NF_DROP;		goto out;	}	/* We refer to the reverse direction ("!dir") tuples here,	 * because we're expecting something in the other direction.	 * Doesn't matter unless NAT is happening.  */	daddr = &ct->tuplehash[!dir].tuple.dst.u3;	/* Update the ftp info */	if ((cmd.l3num == ct->tuplehash[dir].tuple.src.l3num) &&	    memcmp(&cmd.u3.all, &ct->tuplehash[dir].tuple.src.u3.all,		     sizeof(cmd.u3.all))) {		/* Enrico Scholz's passive FTP to partially RNAT'd ftp		   server: it really wants us to connect to a		   different IP address.  Simply don't record it for		   NAT. */		if (cmd.l3num == PF_INET) {			pr_debug("conntrack_ftp: NOT RECORDING: " NIPQUAD_FMT				 " != " NIPQUAD_FMT "\n",				 NIPQUAD(cmd.u3.ip),				 NIPQUAD(ct->tuplehash[dir].tuple.src.u3.ip));		} else {			pr_debug("conntrack_ftp: NOT RECORDING: " NIP6_FMT				 " != " NIP6_FMT "\n",				 NIP6(*((struct in6_addr *)cmd.u3.ip6)),				 NIP6(*((struct in6_addr *)					ct->tuplehash[dir].tuple.src.u3.ip6)));		}		/* Thanks to Cristiano Lincoln Mattos		   <lincoln@cesar.org.br> for reporting this potential		   problem (DMZ machines opening holes to internal		   networks, or the packet filter itself). */		if (!loose) {			ret = NF_ACCEPT;			goto out_put_expect;		}		daddr = &cmd.u3;	}	nf_ct_expect_init(exp, cmd.l3num,			  &ct->tuplehash[!dir].tuple.src.u3, daddr,			  IPPROTO_TCP, NULL, &cmd.u.tcp.port);	/* Now, NAT might want to mangle the packet, and register the	 * (possibly changed) expectation itself. */	nf_nat_ftp = rcu_dereference(nf_nat_ftp_hook);	if (nf_nat_ftp && ct->status & IPS_NAT_MASK)		ret = nf_nat_ftp(skb, ctinfo, search[dir][i].ftptype,				 matchoff, matchlen, exp);	else {		/* Can't expect this?  Best to drop packet now. */		if (nf_ct_expect_related(exp) != 0)			ret = NF_DROP;		else			ret = NF_ACCEPT;	}out_put_expect:	nf_ct_expect_put(exp);out_update_nl:	/* Now if this ends in \n, update ftp info.  Seq may have been	 * adjusted by NAT code. */	if (ends_in_nl)		update_nl_seq(seq, ct_ftp_info, dir, skb); out:	spin_unlock_bh(&nf_ftp_lock);	return ret;}static struct nf_conntrack_helper ftp[MAX_PORTS][2] __read_mostly;static char ftp_names[MAX_PORTS][2][sizeof("ftp-65535")] __read_mostly;/* don't make this __exit, since it's called from __init ! */static void nf_conntrack_ftp_fini(void){	int i, j;	for (i = 0; i < ports_c; i++) {		for (j = 0; j < 2; j++) {			if (ftp[i][j].me == NULL)				continue;			pr_debug("nf_ct_ftp: unregistering helper for pf: %d "				 "port: %d\n",				 ftp[i][j].tuple.src.l3num, ports[i]);			nf_conntrack_helper_unregister(&ftp[i][j]);		}	}	kfree(ftp_buffer);}static int __init nf_conntrack_ftp_init(void){	int i, j = -1, ret = 0;	char *tmpname;	ftp_buffer = kmalloc(65536, GFP_KERNEL);	if (!ftp_buffer)		return -ENOMEM;	if (ports_c == 0)		ports[ports_c++] = FTP_PORT;	/* FIXME should be configurable whether IPv4 and IPv6 FTP connections		 are tracked or not - YK */	for (i = 0; i < ports_c; i++) {		ftp[i][0].tuple.src.l3num = PF_INET;		ftp[i][1].tuple.src.l3num = PF_INET6;		for (j = 0; j < 2; j++) {			ftp[i][j].tuple.src.u.tcp.port = htons(ports[i]);			ftp[i][j].tuple.dst.protonum = IPPROTO_TCP;			ftp[i][j].max_expected = 1;			ftp[i][j].timeout = 5 * 60;	/* 5 Minutes */			ftp[i][j].me = THIS_MODULE;			ftp[i][j].help = help;			tmpname = &ftp_names[i][j][0];			if (ports[i] == FTP_PORT)				sprintf(tmpname, "ftp");			else				sprintf(tmpname, "ftp-%d", ports[i]);			ftp[i][j].name = tmpname;			pr_debug("nf_ct_ftp: registering helper for pf: %d "				 "port: %d\n",				 ftp[i][j].tuple.src.l3num, ports[i]);			ret = nf_conntrack_helper_register(&ftp[i][j]);			if (ret) {				printk("nf_ct_ftp: failed to register helper "				       " for pf: %d port: %d\n",					ftp[i][j].tuple.src.l3num, ports[i]);				nf_conntrack_ftp_fini();				return ret;			}		}	}	return 0;}module_init(nf_conntrack_ftp_init);module_exit(nf_conntrack_ftp_fini);

⌨️ 快捷键说明

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