/* * Look at outgoing ftp packets to catch the response to a PASV command * from the server (inside-to-outside). * When we see one, we build a connection entry with the client address, * client port 0 (unknown at the moment), the server address and the * server port. Mark the current connection entry as a control channel * of the new entry. All this work is just to make the data connection * can be scheduled to the right server later. * * The outgoing packet should be something like * "227 Entering Passive Mode (xxx,xxx,xxx,xxx,ppp,ppp)". * xxx,xxx,xxx,xxx is the server address, ppp,ppp is the server port number. */ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, struct sk_buff *skb, int *diff) { struct iphdr *iph; struct tcphdr *th; char *data, *data_limit; char *start, *end; union nf_inet_addr from; __be16 port; struct ip_vs_conn *n_cp; char buf[24]; /* xxx.xxx.xxx.xxx,ppp,ppp\000 */ unsigned buf_len; int ret; #ifdef CONFIG_IP_VS_IPV6 /* This application helper doesn't work with IPv6 yet, * so turn this into a no-op for IPv6 packets */ if (cp->af == AF_INET6) return 1; #endif *diff = 0; /* Only useful for established sessions */ if (cp->state != IP_VS_TCP_S_ESTABLISHED) return 1; /* Linear packets are much easier to deal with. */ if (!skb_make_writable(skb, skb->len)) return 0; if (cp->app_data == &ip_vs_ftp_pasv) { iph = ip_hdr(skb); th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); data = (char *)th + (th->doff << 2); data_limit = skb_tail_pointer(skb); if (ip_vs_ftp_get_addrport(data, data_limit, SERVER_STRING, sizeof(SERVER_STRING)-1, ')', &from.ip, &port, &start, &end) != 1) return 1; IP_VS_DBG(7, "PASV response (%u.%u.%u.%u:%d) -> " "%u.%u.%u.%u:%d detected\n", NIPQUAD(from.ip), ntohs(port), NIPQUAD(cp->caddr.ip), 0); /* * Now update or create an connection entry for it */ n_cp = ip_vs_conn_out_get(AF_INET, iph->protocol, &from, port, &cp->caddr, 0); if (!n_cp) { n_cp = ip_vs_conn_new(AF_INET, IPPROTO_TCP, &cp->caddr, 0, &cp->vaddr, port, &from, port, IP_VS_CONN_F_NO_CPORT, cp->dest); if (!n_cp) return 0; /* add its controller */ ip_vs_control_add(n_cp, cp); } /* * Replace the old passive address with the new one */ from.ip = n_cp->vaddr.ip; port = n_cp->vport; sprintf(buf, "%d,%d,%d,%d,%d,%d", NIPQUAD(from.ip), (ntohs(port)>>8)&255, ntohs(port)&255); buf_len = strlen(buf); /* * Calculate required delta-offset to keep TCP happy */ *diff = buf_len - (end-start); if (*diff == 0) { /* simply replace it with new passive address */ memcpy(start, buf, buf_len); ret = 1; } else { ret = !ip_vs_skb_replace(skb, GFP_ATOMIC, start, end-start, buf, buf_len); } cp->app_data = NULL; ip_vs_tcp_conn_listen(n_cp); ip_vs_conn_put(n_cp); return ret; }
/* * Look at outgoing ftp packets to catch the response to a PASV command * from the server (inside-to-outside). * When we see one, we build a connection entry with the client address, * client port 0 (unknown at the moment), the server address and the * server port. Mark the current connection entry as a control channel * of the new entry. All this work is just to make the data connection * can be scheduled to the right server later. * * The outgoing packet should be something like * "227 Entering Passive Mode (xxx,xxx,xxx,xxx,ppp,ppp)". * xxx,xxx,xxx,xxx is the server address, ppp,ppp is the server port number. */ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, struct sk_buff **pskb, int *diff) { struct iphdr *iph; struct tcphdr *th; char *data, *data_limit; char *start, *end; __u32 from; __u16 port; struct ip_vs_conn *n_cp; char buf[24]; /* xxx.xxx.xxx.xxx,ppp,ppp\000 */ unsigned buf_len; int ret; *diff = 0; /* Only useful for established sessions */ if (cp->state != IP_VS_TCP_S_ESTABLISHED) return 1; /* Linear packets are much easier to deal with. */ if (!ip_vs_make_skb_writable(pskb, (*pskb)->len)) return 0; if (cp->app_data == &ip_vs_ftp_pasv) { iph = (*pskb)->nh.iph; th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); data = (char *)th + (th->doff << 2); data_limit = (*pskb)->tail; if (ip_vs_ftp_get_addrport(data, data_limit, SERVER_STRING, sizeof(SERVER_STRING)-1, ')', &from, &port, &start, &end) != 1) return 1; IP_VS_DBG(1-debug, "PASV response (%u.%u.%u.%u:%d) -> " "%u.%u.%u.%u:%d detected\n", NIPQUAD(from), ntohs(port), NIPQUAD(cp->caddr), 0); /* * Now update or create an connection entry for it */ n_cp = ip_vs_conn_out_get(iph->protocol, from, port, cp->caddr, 0); if (!n_cp) { n_cp = ip_vs_conn_new(IPPROTO_TCP, cp->caddr, 0, cp->vaddr, port, from, port, IP_VS_CONN_F_NO_CPORT, cp->dest); if (!n_cp) return 0; /* add its controller */ ip_vs_control_add(n_cp, cp); } /* * Replace the old passive address with the new one */ from = n_cp->vaddr; port = n_cp->vport; sprintf(buf,"%d,%d,%d,%d,%d,%d", NIPQUAD(from), port&255, (port>>8)&255); buf_len = strlen(buf); /* * Calculate required delta-offset to keep TCP happy */ *diff = buf_len - (end-start); if (*diff == 0) { /* simply replace it with new passive address */ memcpy(start, buf, buf_len); ret = 1; } else { ret = !ip_vs_skb_replace(*pskb, GFP_ATOMIC, start, end-start, buf, buf_len); } cp->app_data = NULL; ip_vs_tcp_conn_listen(n_cp); ip_vs_conn_put(n_cp); return ret; }
/* * Look at outgoing ftp packets to catch the response to a PASV command * from the server (inside-to-outside). * When we see one, we build a connection entry with the client address, * client port 0 (unknown at the moment), the server address and the * server port. Mark the current connection entry as a control channel * of the new entry. All this work is just to make the data connection * can be scheduled to the right server later. * * The outgoing packet should be something like * "227 Entering Passive Mode (xxx,xxx,xxx,xxx,ppp,ppp)". * xxx,xxx,xxx,xxx is the server address, ppp,ppp is the server port number. */ static int ip_vs_ftp_out(struct ip_vs_app *vapp, struct ip_vs_conn *cp, struct sk_buff *skb) { struct iphdr *iph; struct tcphdr *th; char *data, *data_limit; char *start, *end; __u32 from; __u16 port; struct ip_vs_conn *n_cp; char buf[24]; /* xxx.xxx.xxx.xxx,ppp,ppp\000 */ unsigned buf_len; int diff; /* Only useful for established sessions */ if (cp->state != IP_VS_S_ESTABLISHED) return 0; if (cp->app_data == &ip_vs_ftp_pasv) { iph = skb->nh.iph; th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); data = (char *)th + (th->doff << 2); data_limit = skb->tail; if (ip_vs_ftp_get_addrport(data, data_limit, SERVER_STRING, sizeof(SERVER_STRING)-1, ')', &from, &port, &start, &end) != 1) return 0; IP_VS_DBG(1-debug, "PASV response (%u.%u.%u.%u:%d) -> " "%u.%u.%u.%u:%d detected\n", NIPQUAD(from), ntohs(port), NIPQUAD(cp->caddr), 0); /* * Now update or create an connection entry for it */ n_cp = ip_vs_conn_out_get(iph->protocol, from, port, cp->caddr, 0); if (!n_cp) { n_cp = ip_vs_conn_new(IPPROTO_TCP, cp->caddr, 0, cp->vaddr, port, from, port, IP_VS_CONN_F_NO_CPORT, cp->dest); if (!n_cp) return 0; /* add its controller */ ip_vs_control_add(n_cp, cp); /* increase dest's inactive connection counter */ if (cp->dest) atomic_inc(&cp->dest->inactconns); } /* * Replace the old passive address with the new one */ from = n_cp->vaddr; port = n_cp->vport; sprintf(buf,"%d,%d,%d,%d,%d,%d", NIPQUAD(from), port&255, port>>8&255); buf_len = strlen(buf); /* * Calculate required delta-offset to keep TCP happy */ diff = buf_len - (end-start); if (diff == 0) { /* simply replace it with new passive address */ memcpy(start, buf, buf_len); } else { /* fixme: return value isn't checked here */ ip_vs_skb_replace(skb, GFP_ATOMIC, start, end-start, buf, buf_len); } cp->app_data = NULL; ip_vs_conn_listen(n_cp); ip_vs_conn_put(n_cp); return diff; }