Re: libnetfilter_conntrack SNAT: How to speed up this??

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

 



2009/7/29 Bruno Moreira Guedes <thbmatrix@xxxxxxxxx>:
> 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>
>

Hello people...

Am I asking it in the right place?? Should I ask in the
netfilter-devel instead??

Thank you.
--
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