- 论坛徽章:
- 0
|
本帖最后由 HereItIs 于 2010-02-26 16:00 编辑
How conntrack works in Netfilter(Part 2 - How to track FTP connections)
本文档基于Linux2.6.30内核,版权归hereitis所有,可以自由拷贝/转载,转载时请保持文档的完整性并且注明来源,禁止用于任何商业用途。
Blog:http://blog.chinaunix.net/u3/110948/
Email:hereitis.cu@gmail.com
http://blog.chinaunix.net/u3/110948/showart.php?id=2166655
1. Initialization
1. nf_conntrack_ftp_init(net/netfilter/nf_conntrack_ftp.c)- 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) // static unsigned int ports_c;
- ports[ports_c++] = FTP_PORT; //static u_int16_t ports[MAX_PORTS];
- /* 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;// Will be used when hash tuple
- 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]); // Will be used when hash tuple
- ftp[i][j].tuple.dst.protonum = IPPROTO_TCP; // Will be used when hash tuple
- ftp[i][j].expect_policy = &ftp_exp_policy;
- 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;
- }
复制代码 2. nf_conntrack_helper_register(net/netfilter/nf_conntrack_helper.c)- int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
- {
- unsigned int h = helper_hash(&me->tuple);
- BUG_ON(me->expect_policy == NULL);
- BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES);
- BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1);
- mutex_lock(&nf_ct_helper_mutex);
- hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]); // Global helper list
- nf_ct_helper_count++;
- mutex_unlock(&nf_ct_helper_mutex);
- return 0;
- }
复制代码 3. helper_hash(net/netfilter/nf_conntrack_helper.c)- /* Stupid hash, but collision free for the default registrations of the
- * helpers currently in the kernel. */
- static unsigned int helper_hash(const struct nf_conntrack_tuple *tuple)
- {
- return (((tuple->src.l3num << 8) | tuple->dst.protonum) ^
- (__force __u16)tuple->src.u.all) % nf_ct_helper_hsize;
- }
复制代码 4. How ftp helper registered
2. How ftp helper works
1. Big picture
2. Main helper function(net/netfilter/nf_conntrack_ftp.c)- static int help(struct sk_buff *skb,
- unsigned int protoff,
- struct nf_conn *ct,
- enum ip_conntrack_info ctinfo)
- {
- unsigned int dataoff, datalen;
- const struct tcphdr *th;
- struct tcphdr _tcph;
- const char *fb_ptr;
- int ret;
- u32 seq;
- int dir = CTINFO2DIR(ctinfo);
- unsigned int uninitialized_var(matchlen), uninitialized_var(matchoff);
- struct nf_ct_ftp_master *ct_ftp_info = &nfct_help(ct)->help.ct_ftp_info;
- struct nf_conntrack_expect *exp;
- union nf_inet_addr *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 = nf_ct_l3num(ct);
- 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, // Try to find packet which trigger a ftp connection tracking
- 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. */
- pr_debug("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 == nf_ct_l3num(ct)) &&
- 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: %pI4 != %pI4\n",
- &cmd.u3.ip,
- &ct->tuplehash[dir].tuple.src.u3.ip);
- } else {
- pr_debug("conntrack_ftp: NOT RECORDING: %pI6 != %pI6\n",
- cmd.u3.ip6,
- ct->tuplehash[dir].tuple.src.u3.ip6);
- }
- /* Thanks to Cristiano Lincoln Mattos
- <[email]lincoln@cesar.org.br[/email]> 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, NF_CT_EXPECT_CLASS_DEFAULT, cmd.l3num, // Setup expectation for this ftp connection
- &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(ct, seq, ct_ftp_info, dir, skb);
- out:
- spin_unlock_bh(&nf_ftp_lock);
- return ret;
- }
复制代码 3. Pattern search(net/netfilter/nf_conntrack_ftp.c)
1. Structure- static struct ftp_search {
- const char *pattern;
- size_t plen;
- char skip;
- char term;
- enum nf_ct_ftp_type ftptype;
- int (*getnum)(const char *, size_t, struct nf_conntrack_man *, char);
- } search[IP_CT_DIR_MAX][2] = { // 2 directions and 2 modes
- [IP_CT_DIR_ORIGINAL] = {
- {
- .pattern = "PORT",
- .plen = sizeof("PORT") - 1,
- .skip = ' ',
- .term = '\r',
- .ftptype = NF_CT_FTP_PORT,
- .getnum = try_rfc959,
- },
- {
- .pattern = "EPRT",
- .plen = sizeof("EPRT") - 1,
- .skip = ' ',
- .term = '\r',
- .ftptype = NF_CT_FTP_EPRT,
- .getnum = try_eprt,
- },
- },
- [IP_CT_DIR_REPLY] = {
- {
- .pattern = "227 ",
- .plen = sizeof("227 ") - 1,
- .skip = '(',
- .term = ')',
- .ftptype = NF_CT_FTP_PASV,
- .getnum = try_rfc959,
- },
- {
- .pattern = "229 ",
- .plen = sizeof("229 ") - 1,
- .skip = '(',
- .term = ')',
- .ftptype = NF_CT_FTP_EPSV,
- .getnum = try_epsv_response,
- },
- },
- };
复制代码 2. try_rfc959(net/netfilter/nf_conntrack_ftp.c)- /* Returns 0, or length of numbers: 192,168,1,1,5,6 */
- static int try_rfc959(const char *data, size_t dlen,
- struct nf_conntrack_man *cmd, char term)
- {
- int length;
- u_int32_t array[6];
- length = try_number(data, dlen, array, 6, ',', term);
- if (length == 0)
- return 0;
- cmd->u3.ip = htonl((array[0] << 24) | (array[1] << 16) |
- (array[2] << 8) | array[3]);
- cmd->u.tcp.port = htons((array[4] << 8) | array[5]);
- return length;
- }
复制代码 3. try_eprt(net/netfilter/nf_conntrack_ftp.c)- /* Returns 0, or length of numbers: |1|132.235.1.2|6275| or |2|3ffe::1|6275| */
- static int try_eprt(const char *data, size_t dlen, struct nf_conntrack_man *cmd,
- char term)
- {
- char delim;
- int length;
- /* First character is delimiter, then "1" for IPv4 or "2" for IPv6,
- then delimiter again. */
- if (dlen <= 3) {
- pr_debug("EPRT: too short\n");
- return 0;
- }
- delim = data[0];
- if (isdigit(delim) || delim < 33 || delim > 126 || data[2] != delim) {
- pr_debug("try_eprt: invalid delimitter.\n");
- return 0;
- }
- if ((cmd->l3num == PF_INET && data[1] != '1') ||
- (cmd->l3num == PF_INET6 && data[1] != '2')) {
- pr_debug("EPRT: invalid protocol number.\n");
- return 0;
- }
- pr_debug("EPRT: Got %c%c%c\n", delim, data[1], delim);
- if (data[1] == '1') {
- u_int32_t array[4];
- /* Now we have IP address. */
- length = try_number(data + 3, dlen - 3, array, 4, '.', delim);
- if (length != 0)
- cmd->u3.ip = htonl((array[0] << 24) | (array[1] << 16)
- | (array[2] << 8) | array[3]);
- } else {
- /* Now we have IPv6 address. */
- length = get_ipv6_addr(data + 3, dlen - 3,
- (struct in6_addr *)cmd->u3.ip6, delim);
- }
- if (length == 0)
- return 0;
- pr_debug("EPRT: Got IP address!\n");
- /* Start offset includes initial "|1|", and trailing delimiter */
- return get_port(data, 3 + length + 1, dlen, delim, &cmd->u.tcp.port);
- }
复制代码 4. try_epsv_response(net/netfilter/nf_conntrack_ftp.c)- /* Returns 0, or length of numbers: |||6446| */
- static int try_epsv_response(const char *data, size_t dlen,
- struct nf_conntrack_man *cmd, char term)
- {
- char delim;
- /* Three delimiters. */
- if (dlen <= 3) return 0;
- delim = data[0];
- if (isdigit(delim) || delim < 33 || delim > 126
- || data[1] != delim || data[2] != delim)
- return 0;
- return get_port(data, 3, dlen, delim, &cmd->u.tcp.port);
- }
复制代码 5. nf_ct_expect_init(net/netfilter/nf_conntrack_expect.c)- void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
- u_int8_t family,
- const union nf_inet_addr *saddr,
- const union nf_inet_addr *daddr,
- u_int8_t proto, const __be16 *src, const __be16 *dst)
- {
- int len;
- if (family == AF_INET)
- len = 4;
- else
- len = 16;
- exp->flags = 0;
- exp->class = class;
- exp->expectfn = NULL;
- exp->helper = NULL;
- exp->tuple.src.l3num = family;
- exp->tuple.dst.protonum = proto;
- if (saddr) {
- memcpy(&exp->tuple.src.u3, saddr, len);
- if (sizeof(exp->tuple.src.u3) > len)
- /* address needs to be cleared for nf_ct_tuple_equal */
- memset((void *)&exp->tuple.src.u3 + len, 0x00,
- sizeof(exp->tuple.src.u3) - len);
- memset(&exp->mask.src.u3, 0xFF, len);
- if (sizeof(exp->mask.src.u3) > len)
- memset((void *)&exp->mask.src.u3 + len, 0x00,
- sizeof(exp->mask.src.u3) - len);
- } else {
- memset(&exp->tuple.src.u3, 0x00, sizeof(exp->tuple.src.u3));
- memset(&exp->mask.src.u3, 0x00, sizeof(exp->mask.src.u3));
- }
- if (src) {
- exp->tuple.src.u.all = *src;
- exp->mask.src.u.all = htons(0xFFFF);
- } else {
- exp->tuple.src.u.all = 0;
- exp->mask.src.u.all = 0;
- }
- memcpy(&exp->tuple.dst.u3, daddr, len);
- if (sizeof(exp->tuple.dst.u3) > len)
- /* address needs to be cleared for nf_ct_tuple_equal */
- memset((void *)&exp->tuple.dst.u3 + len, 0x00,
- sizeof(exp->tuple.dst.u3) - len);
- exp->tuple.dst.u.all = *dst;
- }
- EXPORT_SYMBOL_GPL(nf_ct_expect_init);
复制代码 |
评分
-
查看全部评分
|