Commit 87375ab4 authored by Julian Anastasov's avatar Julian Anastasov Committed by David S. Miller

[IPVS]: ip_vs_ftp breaks connections using persistence

ip_vs_ftp when loaded can create NAT connections with unknown client
port for passive FTP. For such expectations we lookup with cport=0 on
incoming packet but it matches the format of the persistence templates
causing packets to other persistent virtual servers to be forwarded to
real server without creating connection. Later the reply packets are
treated as foreign and not SNAT-ed.

This patch changes the connection lookup for packets from clients:

* introduce IP_VS_CONN_F_TEMPLATE connection flag to mark the
  connection as template

* create new connection lookup function just for templates -
  ip_vs_ct_in_get

* make sure ip_vs_conn_in_get hits only connections with
  IP_VS_CONN_F_NO_CPORT flag set when s_port is 0. By this way
  we avoid returning template when looking for cport=0 (ftp)
Signed-off-by: default avatarJulian Anastasov <ja@ssi.bg>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent f5e229db
...@@ -84,6 +84,7 @@ ...@@ -84,6 +84,7 @@
#define IP_VS_CONN_F_IN_SEQ 0x0400 /* must do input seq adjust */ #define IP_VS_CONN_F_IN_SEQ 0x0400 /* must do input seq adjust */
#define IP_VS_CONN_F_SEQ_MASK 0x0600 /* in/out sequence mask */ #define IP_VS_CONN_F_SEQ_MASK 0x0600 /* in/out sequence mask */
#define IP_VS_CONN_F_NO_CPORT 0x0800 /* no client port set yet */ #define IP_VS_CONN_F_NO_CPORT 0x0800 /* no client port set yet */
#define IP_VS_CONN_F_TEMPLATE 0x1000 /* template, not connection */
/* Move it to better place one day, for now keep it unique */ /* Move it to better place one day, for now keep it unique */
#define NFC_IPVS_PROPERTY 0x10000 #define NFC_IPVS_PROPERTY 0x10000
...@@ -739,6 +740,8 @@ enum { ...@@ -739,6 +740,8 @@ enum {
extern struct ip_vs_conn *ip_vs_conn_in_get extern struct ip_vs_conn *ip_vs_conn_in_get
(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port); (int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port);
extern struct ip_vs_conn *ip_vs_ct_in_get
(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port);
extern struct ip_vs_conn *ip_vs_conn_out_get extern struct ip_vs_conn *ip_vs_conn_out_get
(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port); (int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port);
......
...@@ -196,6 +196,7 @@ static inline struct ip_vs_conn *__ip_vs_conn_in_get ...@@ -196,6 +196,7 @@ static inline struct ip_vs_conn *__ip_vs_conn_in_get
list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) {
if (s_addr==cp->caddr && s_port==cp->cport && if (s_addr==cp->caddr && s_port==cp->cport &&
d_port==cp->vport && d_addr==cp->vaddr && d_port==cp->vport && d_addr==cp->vaddr &&
((!s_port) ^ (!(cp->flags & IP_VS_CONN_F_NO_CPORT))) &&
protocol==cp->protocol) { protocol==cp->protocol) {
/* HIT */ /* HIT */
atomic_inc(&cp->refcnt); atomic_inc(&cp->refcnt);
...@@ -227,6 +228,40 @@ struct ip_vs_conn *ip_vs_conn_in_get ...@@ -227,6 +228,40 @@ struct ip_vs_conn *ip_vs_conn_in_get
return cp; return cp;
} }
/* Get reference to connection template */
struct ip_vs_conn *ip_vs_ct_in_get
(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port)
{
unsigned hash;
struct ip_vs_conn *cp;
hash = ip_vs_conn_hashkey(protocol, s_addr, s_port);
ct_read_lock(hash);
list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) {
if (s_addr==cp->caddr && s_port==cp->cport &&
d_port==cp->vport && d_addr==cp->vaddr &&
cp->flags & IP_VS_CONN_F_TEMPLATE &&
protocol==cp->protocol) {
/* HIT */
atomic_inc(&cp->refcnt);
goto out;
}
}
cp = NULL;
out:
ct_read_unlock(hash);
IP_VS_DBG(7, "template lookup/in %s %u.%u.%u.%u:%d->%u.%u.%u.%u:%d %s\n",
ip_vs_proto_name(protocol),
NIPQUAD(s_addr), ntohs(s_port),
NIPQUAD(d_addr), ntohs(d_port),
cp?"hit":"not hit");
return cp;
}
/* /*
* Gets ip_vs_conn associated with supplied parameters in the ip_vs_conn_tab. * Gets ip_vs_conn associated with supplied parameters in the ip_vs_conn_tab.
...@@ -367,7 +402,7 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest) ...@@ -367,7 +402,7 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)
atomic_read(&dest->refcnt)); atomic_read(&dest->refcnt));
/* Update the connection counters */ /* Update the connection counters */
if (cp->cport || (cp->flags & IP_VS_CONN_F_NO_CPORT)) { if (!(cp->flags & IP_VS_CONN_F_TEMPLATE)) {
/* It is a normal connection, so increase the inactive /* It is a normal connection, so increase the inactive
connection counter because it is in TCP SYNRECV connection counter because it is in TCP SYNRECV
state (inactive) or other protocol inacive state */ state (inactive) or other protocol inacive state */
...@@ -406,7 +441,7 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp) ...@@ -406,7 +441,7 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp)
atomic_read(&dest->refcnt)); atomic_read(&dest->refcnt));
/* Update the connection counters */ /* Update the connection counters */
if (cp->cport || (cp->flags & IP_VS_CONN_F_NO_CPORT)) { if (!(cp->flags & IP_VS_CONN_F_TEMPLATE)) {
/* It is a normal connection, so decrease the inactconns /* It is a normal connection, so decrease the inactconns
or activeconns counter */ or activeconns counter */
if (cp->flags & IP_VS_CONN_F_INACTIVE) { if (cp->flags & IP_VS_CONN_F_INACTIVE) {
...@@ -776,7 +811,7 @@ void ip_vs_random_dropentry(void) ...@@ -776,7 +811,7 @@ void ip_vs_random_dropentry(void)
ct_write_lock_bh(hash); ct_write_lock_bh(hash);
list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) {
if (!cp->cport && !(cp->flags & IP_VS_CONN_F_NO_CPORT)) if (cp->flags & IP_VS_CONN_F_TEMPLATE)
/* connection template */ /* connection template */
continue; continue;
......
...@@ -243,10 +243,10 @@ ip_vs_sched_persist(struct ip_vs_service *svc, ...@@ -243,10 +243,10 @@ ip_vs_sched_persist(struct ip_vs_service *svc,
if (ports[1] == svc->port) { if (ports[1] == svc->port) {
/* Check if a template already exists */ /* Check if a template already exists */
if (svc->port != FTPPORT) if (svc->port != FTPPORT)
ct = ip_vs_conn_in_get(iph->protocol, snet, 0, ct = ip_vs_ct_in_get(iph->protocol, snet, 0,
iph->daddr, ports[1]); iph->daddr, ports[1]);
else else
ct = ip_vs_conn_in_get(iph->protocol, snet, 0, ct = ip_vs_ct_in_get(iph->protocol, snet, 0,
iph->daddr, 0); iph->daddr, 0);
if (!ct || !ip_vs_check_template(ct)) { if (!ct || !ip_vs_check_template(ct)) {
...@@ -272,14 +272,14 @@ ip_vs_sched_persist(struct ip_vs_service *svc, ...@@ -272,14 +272,14 @@ ip_vs_sched_persist(struct ip_vs_service *svc,
iph->daddr, iph->daddr,
ports[1], ports[1],
dest->addr, dest->port, dest->addr, dest->port,
0, IP_VS_CONN_F_TEMPLATE,
dest); dest);
else else
ct = ip_vs_conn_new(iph->protocol, ct = ip_vs_conn_new(iph->protocol,
snet, 0, snet, 0,
iph->daddr, 0, iph->daddr, 0,
dest->addr, 0, dest->addr, 0,
0, IP_VS_CONN_F_TEMPLATE,
dest); dest);
if (ct == NULL) if (ct == NULL)
return NULL; return NULL;
...@@ -298,10 +298,10 @@ ip_vs_sched_persist(struct ip_vs_service *svc, ...@@ -298,10 +298,10 @@ ip_vs_sched_persist(struct ip_vs_service *svc,
* port zero template: <protocol,caddr,0,vaddr,0,daddr,0> * port zero template: <protocol,caddr,0,vaddr,0,daddr,0>
*/ */
if (svc->fwmark) if (svc->fwmark)
ct = ip_vs_conn_in_get(IPPROTO_IP, snet, 0, ct = ip_vs_ct_in_get(IPPROTO_IP, snet, 0,
htonl(svc->fwmark), 0); htonl(svc->fwmark), 0);
else else
ct = ip_vs_conn_in_get(iph->protocol, snet, 0, ct = ip_vs_ct_in_get(iph->protocol, snet, 0,
iph->daddr, 0); iph->daddr, 0);
if (!ct || !ip_vs_check_template(ct)) { if (!ct || !ip_vs_check_template(ct)) {
...@@ -326,14 +326,14 @@ ip_vs_sched_persist(struct ip_vs_service *svc, ...@@ -326,14 +326,14 @@ ip_vs_sched_persist(struct ip_vs_service *svc,
snet, 0, snet, 0,
htonl(svc->fwmark), 0, htonl(svc->fwmark), 0,
dest->addr, 0, dest->addr, 0,
0, IP_VS_CONN_F_TEMPLATE,
dest); dest);
else else
ct = ip_vs_conn_new(iph->protocol, ct = ip_vs_conn_new(iph->protocol,
snet, 0, snet, 0,
iph->daddr, 0, iph->daddr, 0,
dest->addr, 0, dest->addr, 0,
0, IP_VS_CONN_F_TEMPLATE,
dest); dest);
if (ct == NULL) if (ct == NULL)
return NULL; return NULL;
......
...@@ -297,16 +297,24 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen) ...@@ -297,16 +297,24 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen)
p = (char *)buffer + sizeof(struct ip_vs_sync_mesg); p = (char *)buffer + sizeof(struct ip_vs_sync_mesg);
for (i=0; i<m->nr_conns; i++) { for (i=0; i<m->nr_conns; i++) {
unsigned flags;
s = (struct ip_vs_sync_conn *)p; s = (struct ip_vs_sync_conn *)p;
flags = ntohs(s->flags);
if (!(flags & IP_VS_CONN_F_TEMPLATE))
cp = ip_vs_conn_in_get(s->protocol, cp = ip_vs_conn_in_get(s->protocol,
s->caddr, s->cport, s->caddr, s->cport,
s->vaddr, s->vport); s->vaddr, s->vport);
else
cp = ip_vs_ct_in_get(s->protocol,
s->caddr, s->cport,
s->vaddr, s->vport);
if (!cp) { if (!cp) {
cp = ip_vs_conn_new(s->protocol, cp = ip_vs_conn_new(s->protocol,
s->caddr, s->cport, s->caddr, s->cport,
s->vaddr, s->vport, s->vaddr, s->vport,
s->daddr, s->dport, s->daddr, s->dport,
ntohs(s->flags), NULL); flags, NULL);
if (!cp) { if (!cp) {
IP_VS_ERR("ip_vs_conn_new failed\n"); IP_VS_ERR("ip_vs_conn_new failed\n");
return; return;
...@@ -315,11 +323,11 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen) ...@@ -315,11 +323,11 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen)
} else if (!cp->dest) { } else if (!cp->dest) {
/* it is an entry created by the synchronization */ /* it is an entry created by the synchronization */
cp->state = ntohs(s->state); cp->state = ntohs(s->state);
cp->flags = ntohs(s->flags) | IP_VS_CONN_F_HASHED; cp->flags = flags | IP_VS_CONN_F_HASHED;
} /* Note that we don't touch its state and flags } /* Note that we don't touch its state and flags
if it is a normal entry. */ if it is a normal entry. */
if (ntohs(s->flags) & IP_VS_CONN_F_SEQ_MASK) { if (flags & IP_VS_CONN_F_SEQ_MASK) {
opt = (struct ip_vs_sync_conn_options *)&s[1]; opt = (struct ip_vs_sync_conn_options *)&s[1];
memcpy(&cp->in_seq, opt, sizeof(*opt)); memcpy(&cp->in_seq, opt, sizeof(*opt));
p += FULL_CONN_SIZE; p += FULL_CONN_SIZE;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment