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