/* * (C) 2013 by Pablo Neira Ayuso * * Adapted from: * * Amanda extension for IP connection tracking * * (C) 2002 by Brian J. Murrell * based on HW's ip_conntrack_irc.c as well as other modules * (C) 2006 Patrick McHardy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #include "conntrackd.h" #include "helper.h" #include "myct.h" #include "log.h" #include #include #include #include #include #include #include #include #include #include static int nat_amanda(struct pkt_buff *pkt, uint32_t ctinfo, unsigned int matchoff, unsigned int matchlen, struct nf_expect *exp) { char buffer[sizeof("65535")]; uint16_t port, initial_port; unsigned int ret; const struct nf_conntrack *expected; struct nf_conntrack *nat_tuple; nat_tuple = nfct_new(); if (nat_tuple == NULL) return NF_ACCEPT; expected = nfexp_get_attr(exp, ATTR_EXP_EXPECTED); /* Connection comes from client. */ initial_port = nfct_get_attr_u16(expected, ATTR_PORT_DST); nfexp_set_attr_u32(exp, ATTR_EXP_NAT_DIR, IP_CT_DIR_ORIGINAL); /* libnetfilter_conntrack needs this */ nfct_set_attr_u8(nat_tuple, ATTR_L3PROTO, AF_INET); nfct_set_attr_u32(nat_tuple, ATTR_IPV4_SRC, 0); nfct_set_attr_u32(nat_tuple, ATTR_IPV4_DST, 0); nfct_set_attr_u8(nat_tuple, ATTR_L4PROTO, IPPROTO_TCP); nfct_set_attr_u16(nat_tuple, ATTR_PORT_DST, 0); /* When you see the packet, we need to NAT it the same as the * this one (ie. same IP: it will be TCP and master is UDP). */ nfexp_set_attr(exp, ATTR_EXP_FN, "nat-follow-master"); /* Try to get same port: if not, try to change it. */ for (port = ntohs(initial_port); port != 0; port++) { int res; nfct_set_attr_u16(nat_tuple, ATTR_PORT_SRC, htons(port)); nfexp_set_attr(exp, ATTR_EXP_NAT_TUPLE, nat_tuple); res = cthelper_add_expect(exp); if (res == 0) break; else if (res != -EBUSY) { port = 0; break; } } if (port == 0) { pr_debug("all ports in use\n"); return NF_DROP; } sprintf(buffer, "%u", port); ret = nfq_udp_mangle_ipv4(pkt, matchoff, matchlen, buffer, strlen(buffer)); if (ret != NF_ACCEPT) { pr_debug("cannot mangle packet\n"); cthelper_del_expect(exp); } return ret; } static char amanda_buffer[65536]; static unsigned int master_timeout = 300; enum amanda_strings { SEARCH_CONNECT, SEARCH_NEWLINE, SEARCH_DATA, SEARCH_MESG, SEARCH_INDEX, }; static const char *conns[] = { "DATA ", "MESG ", "INDEX " }; static int amanda_helper_cb(struct pkt_buff *pkt, uint32_t protoff, struct myct *myct, uint32_t ctinfo) { struct nf_expect *exp; char *data, *data_limit, *tmp; unsigned int dataoff, i; uint16_t port, len; int ret = NF_ACCEPT; struct iphdr *iph; union nfct_attr_grp_addr saddr, daddr; /* Only look at packets from the Amanda server */ if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) return NF_ACCEPT; /* increase the UDP timeout of the master connection as replies from * Amanda clients to the server can be quite delayed */ nfct_set_attr_u32(myct->ct, ATTR_TIMEOUT, master_timeout); /* No data? */ iph = (struct iphdr *)pktb_network_header(pkt); dataoff = iph->ihl*4 + sizeof(struct udphdr); if (dataoff >= pktb_len(pkt)) { pr_debug("amanda_help: pktlen = %u\n", pktb_len(pkt)); return NF_ACCEPT; } memcpy(amanda_buffer, pktb_network_header(pkt) + dataoff, pktb_len(pkt) - dataoff); data = amanda_buffer; data_limit = amanda_buffer + pktb_len(pkt) - dataoff; *data_limit = '\0'; /* Search for the CONNECT string */ data = strstr(data, "CONNECT "); if (!data) goto out; data += strlen("CONNECT "); /* Only search first line. */ if ((tmp = strchr(data, '\n'))) *tmp = '\0'; for (i = 0; i < ARRAY_SIZE(conns); i++) { char *match = strstr(data, conns[i]); if (!match) continue; tmp = data = match + strlen(conns[i]); port = strtoul(data, &data, 10); len = data - tmp; if (port == 0 || len > 5) break; exp = nfexp_new(); if (exp == NULL) return NF_DROP; cthelper_get_addr_src(myct->ct, MYCT_DIR_ORIG, &saddr); cthelper_get_addr_dst(myct->ct, MYCT_DIR_ORIG, &daddr); cthelper_get_port_src(myct->ct, MYCT_DIR_ORIG, &port); if (cthelper_expect_init(exp, myct->ct, 0, &saddr, &daddr, IPPROTO_TCP, NULL, &port, 0)) { nfexp_destroy(exp); return NF_DROP; } if (nfct_get_attr_u32(myct->ct, ATTR_STATUS) & IPS_NAT_MASK) { ret = nat_amanda(pkt, ctinfo, tmp - amanda_buffer, len, exp); } else myct->exp = exp; } out: return ret; } static struct ctd_helper amanda_helper = { .name = "amanda", .l4proto = IPPROTO_UDP, .cb = amanda_helper_cb, .policy = { [0] = { .name = "amanda", .expect_max = ARRAY_SIZE(conns), .expect_timeout = 180, }, }, }; void __attribute__ ((constructor)) amanda_init(void); void amanda_init(void) { helper_register(&amanda_helper); }