libnetfilter_conntrack SNAT: How to speed up this??

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello all,

In order to try to do SNAT at user-space, I have the following
'testing' code(based on a example). The problem is that the connection
get slow when handled by this code. On the first testing I've found
it's slow to connect. But looking anymore I get it's slow after the
connection is established too.

I've tested it using: iptables -t nat -A POSTROUTING -p tcp --dport 80
-j NFQUEUE

I also pasted here the output generated by tcpdump.

It works as expected, but the performance makes a 500kbps download get 10kbps.

What's my mistake??

Thank you a lot!!

------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <linux/types.h>
#include <linux/netfilter.h>            /* for NF_ACCEPT */
#include <netinet/ip.h>
#include <netinet/tcp.h>

#include <libnetfilter_queue/libnetfilter_queue.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack_tcp.h>

#include <errno.h>

char **iplist = NULL;
int ips = 0;
int curr_ip = 0;
struct nfct_handle *cth = NULL;
struct nf_conntrack *ct = NULL;

void print_ipaddr(int addr, char *addrs) {
        char ret[20];
        char **ptr = malloc(sizeof(char*)*10);
        int i;

        ptr[0] = (char*) &addr;
        for (i = 1; i < 4; i++) {
                ptr[i] = ptr[i-1]+1;
        }
        sprintf(addrs, "%hhu.%hhu.%hhu.%hhu", ptr[0][0], ptr[1][0],
ptr[2][0], ptr[3][0]);
        free(ptr);
}

void do_snat(struct iphdr *ipnat, struct tcphdr *tcpnat, char *saddr,
int port) {
        int ret;

#ifdef DISABLE_NAT
        return;
#endif
        printf("Destroying old conntracks...\n");
        nfct_set_attr_u8(ct, ATTR_L3PROTO, AF_INET);
        nfct_set_attr_u32(ct, ATTR_IPV4_SRC, ipnat->saddr);
        nfct_set_attr_u32(ct, ATTR_IPV4_DST, ipnat->daddr);

        nfct_set_attr_u8(ct, ATTR_L4PROTO, IPPROTO_TCP);
        nfct_set_attr_u16(ct, ATTR_PORT_SRC, tcpnat->source);
        nfct_set_attr_u16(ct, ATTR_PORT_DST, tcpnat->dest);

        ret = nfct_query(cth, NFCT_Q_DESTROY, ct);
        if (ret == -1) {
                perror("Deu caca"); //ignoring...
        }

        printf("Setting attributes...\n");
        nfct_set_attr_u8(ct, ATTR_L3PROTO, AF_INET);
        nfct_set_attr_u32(ct, ATTR_IPV4_SRC, ipnat->saddr);
        nfct_set_attr_u32(ct, ATTR_IPV4_DST, ipnat->daddr);

        nfct_set_attr_u8(ct, ATTR_L4PROTO, IPPROTO_TCP);
        nfct_set_attr_u16(ct, ATTR_PORT_SRC, tcpnat->source);
        nfct_set_attr_u16(ct, ATTR_PORT_DST, tcpnat->dest);

        nfct_setobjopt(ct, NFCT_SOPT_SETUP_REPLY);
        nfct_set_attr_u8(ct, ATTR_TCP_STATE, TCP_CONNTRACK_SYN_SENT);
        nfct_set_attr_u32(ct, ATTR_TIMEOUT, 100);

        printf("Setting NAT attributes...\n");
        if (saddr != NULL) {
                nfct_set_attr_u32(ct, ATTR_SNAT_IPV4, inet_addr(saddr));
        }
        if (port > 0) {
                nfct_set_attr_u32(ct, ATTR_SNAT_IPV4, htons(port));

        }


        printf("Creating...\n");
        ret = nfct_query(cth, NFCT_Q_CREATE_UPDATE, ct);
        printf("Done...\n");

        printf("TEST: create conntrack ");
        if (ret == -1)
                printf("(%d)(%s)\n", ret, strerror(errno));
        else
                printf("(OK)\n");

        /*nfct_destroy(ct);
        ct = nfct_new();
        printf("ct=%p\n",ct);
        if (!ct)
                perror("nfct_new");*/

}

/* returns packet id */
static u_int32_t print_pkt (struct nfq_data *tb)
{
        int id = 0;
        struct nfqnl_msg_packet_hdr *ph;
        u_int32_t mark,ifi;
        int ret;
        char *data;
        struct iphdr *iph;
        struct tcphdr *tcph;
        char ssaddr[20], sdaddr[20];

        ph = nfq_get_msg_packet_hdr(tb);
        if (ph){
                id = ntohl(ph->packet_id);
                printf("hw_protocol=0x%04x hook=%u id=%u ",
                        ntohs(ph->hw_protocol), ph->hook, id);
        }

        mark = nfq_get_nfmark(tb);
        if (mark)
                printf("mark=%u ", mark);

        ifi = nfq_get_indev(tb);
        if (ifi)
                printf("indev=%u ", ifi);

        ifi = nfq_get_outdev(tb);
        if (ifi)
                printf("outdev=%u ", ifi);

        ret = nfq_get_payload(tb, &data);
        if (ret >= 0)
                printf("payload_len=%d ", ret);

        iph = (struct iphdr *) data;
        tcph = (struct tcphdr *) ((struct iphdr *) iph + 1);
        printf("sizeof(struct iphdr)=%u; sizeof(struct tcphdr)=%u\n",
sizeof(struct iphdr), sizeof(struct tcphdr));
        printf("data=%p, iph=%p, tcph=%p\n", data, iph, tcph);

        print_ipaddr(iph->saddr, ssaddr);
        print_ipaddr(iph->daddr, sdaddr);

        printf("Go!");
        printf("SNAT: src=%s dst=%s sport=%hhu dport=%hhu\n",ssaddr,
sdaddr, tcph->source, tcph->dest);
        do_snat(iph, tcph, iplist[curr_ip++ % ips], 0);
        printf("Injetando pacote...\n");

        fputc('\n', stdout);

        return id;
}


static int cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,
              struct nfq_data *nfa, void *data)
{
        int ret;
        printf("Doing SNAT...\n");
        u_int32_t id = print_pkt(nfa);
        printf("Setting verdict...\n");
        ret = nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL);
        printf("Verdict is set!\n");
        return ret;
}

int main(int argc, char **argv)
{
        struct nfq_handle *h;
        struct nfq_q_handle *qh;
        struct nfnl_handle *nh;
        int fd;
        int rv;
        char buf[4096] __attribute__ ((aligned));
        ips = argc - 1;
        iplist = argv+1;

        printf("ct=%p\n", ct);

        ct = nfct_new();
        if (!ct) {
                perror("nfct_new");
        }
        printf("ct=%p\n", ct);

        cth = nfct_open(CONNTRACK, 0);
        if (!cth) {
                perror("nfct_open");
                return -1;
        }

        printf("opening library handle\n");
        h = nfq_open();
        if (!h) {
                fprintf(stderr, "error during nfq_open()\n");
                exit(1);
        }

        printf("unbinding existing nf_queue handler for AF_INET (if any)\n");
        if (nfq_unbind_pf(h, AF_INET) < 0) {
                fprintf(stderr, "error during nfq_unbind_pf()\n");
                exit(1);
        }

        printf("binding nfnetlink_queue as nf_queue handler for AF_INET\n");
        if (nfq_bind_pf(h, AF_INET) < 0) {
                fprintf(stderr, "error during nfq_bind_pf()\n");
                exit(1);
        }

        printf("binding this socket to queue '0'\n");
        qh = nfq_create_queue(h,  0, &cb, NULL);
        if (!qh) {
                fprintf(stderr, "error during nfq_create_queue()\n");
                exit(1);
        }

        printf("setting copy_packet mode\n");
        if (nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff) < 0) {
                fprintf(stderr, "can't set packet_copy mode\n");
                exit(1);
        }

        nh = nfq_nfnlh(h);
        fd = nfnl_fd(nh);

        while ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0) {
                printf("pkt received\n");
                nfq_handle_packet(h, buf, rv);
        }

        nfct_close(cth);
        printf("unbinding from queue 0\n");
        nfq_destroy_queue(qh);

#ifdef INSANE
        /* normally, applications SHOULD NOT issue this command, since
         * it detaches other programs/sockets from AF_INET, too ! */
        printf("unbinding from AF_INET\n");
        nfq_unbind_pf(h, AF_INET);
#endif

        printf("closing library handle\n");
        nfq_close(h);

        exit(0);
}

------------

tcpdump shows the following:
10:54:13.167534 IP 201.20.14.228.48038 > 174.132.237.34.http: S
1345420508:1345420508(0) win 5840 <mss 1460,sackOK,timestamp 264734000
0,nop,wscale 7>
10:54:13.324514 IP 174.132.237.34.http > 201.20.14.228.48038: S
1997922810:1997922810(0) ack 1345420509 win 5792 <mss
1460,sackOK,timestamp 1088523895 264734000,nop,wscale 7>
10:54:13.324555 IP 201.20.14.228.48038 > 174.132.237.34.http: . ack 1
win 46 <nop,nop,timestamp 264734039 1088523895>
10:54:13.324776 IP 201.20.14.228.48038 > 174.132.237.34.http: P
1:137(136) ack 1 win 46 <nop,nop,timestamp 264734039 1088523895>
10:54:13.479983 IP 174.132.237.34.http > 201.20.14.228.48038: . ack
137 win 54 <nop,nop,timestamp 1088524052 264734039>
10:54:13.492976 IP 174.132.237.34.http > 201.20.14.228.48038: P
1:136(135) ack 137 win 54 <nop,nop,timestamp 1088524065 264734039>
10:54:13.492995 IP 201.20.14.228.48038 > 174.132.237.34.http: . ack
136 win 54 <nop,nop,timestamp 264734081 1088524065>
10:54:13.493005 IP 174.132.237.34.http > 201.20.14.228.48038: P
136:509(373) ack 137 win 54 <nop,nop,timestamp 1088524065 264734039>
10:54:13.493013 IP 201.20.14.228.48038 > 174.132.237.34.http: . ack
509 win 63 <nop,nop,timestamp 264734081 1088524065>
10:54:13.494129 IP 174.132.237.34.http > 201.20.14.228.48038: FP
509:1136(627) ack 137 win 54 <nop,nop,timestamp 1088524066 264734039>
10:54:13.494149 IP 201.20.14.228.48038 > 174.132.237.34.http: . ack
1137 win 73 <nop,nop,timestamp 264734081 1088524066>
10:54:13.494256 IP 201.20.14.228.48038 > 174.132.237.34.http: F
137:137(0) ack 1137 win 73 <nop,nop,timestamp 264734081 1088524066>
10:54:13.649399 IP 174.132.237.34.http > 201.20.14.228.48038: . ack
138 win 54 <nop,nop,timestamp 1088524222 264734081>
--
To unsubscribe from this list: send the line "unsubscribe netfilter" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Netfilter Development]     [Linux Kernel Networking Development]     [Netem]     [Berkeley Packet Filter]     [Linux Kernel Development]     [Advanced Routing & Traffice Control]     [Bugtraq]

  Powered by Linux