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