aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulian Anastasov <ja@ssi.bg>2005-09-15 00:08:51 -0400
committerDavid S. Miller <davem@davemloft.net>2005-09-15 00:08:51 -0400
commit87375ab47cd0ba04124c6d3fd80db5c368f5dcb6 (patch)
treec95f160e2b033b5f8d2fad58348400bab5f989f2
parentf5e229db9cdb27f83594712ca4bb98d9377eb6ed (diff)
[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: Julian Anastasov <ja@ssi.bg> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/ip_vs.h3
-rw-r--r--net/ipv4/ipvs/ip_vs_conn.c41
-rw-r--r--net/ipv4/ipvs/ip_vs_core.c16
-rw-r--r--net/ipv4/ipvs/ip_vs_sync.c20
4 files changed, 63 insertions, 17 deletions
diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
index e426641c519f..06b4235aa016 100644
--- a/include/net/ip_vs.h
+++ b/include/net/ip_vs.h
@@ -84,6 +84,7 @@
84#define IP_VS_CONN_F_IN_SEQ 0x0400 /* must do input seq adjust */ 84#define IP_VS_CONN_F_IN_SEQ 0x0400 /* must do input seq adjust */
85#define IP_VS_CONN_F_SEQ_MASK 0x0600 /* in/out sequence mask */ 85#define IP_VS_CONN_F_SEQ_MASK 0x0600 /* in/out sequence mask */
86#define IP_VS_CONN_F_NO_CPORT 0x0800 /* no client port set yet */ 86#define IP_VS_CONN_F_NO_CPORT 0x0800 /* no client port set yet */
87#define IP_VS_CONN_F_TEMPLATE 0x1000 /* template, not connection */
87 88
88/* Move it to better place one day, for now keep it unique */ 89/* Move it to better place one day, for now keep it unique */
89#define NFC_IPVS_PROPERTY 0x10000 90#define NFC_IPVS_PROPERTY 0x10000
@@ -739,6 +740,8 @@ enum {
739 740
740extern struct ip_vs_conn *ip_vs_conn_in_get 741extern struct ip_vs_conn *ip_vs_conn_in_get
741(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port); 742(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port);
743extern struct ip_vs_conn *ip_vs_ct_in_get
744(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port);
742extern struct ip_vs_conn *ip_vs_conn_out_get 745extern struct ip_vs_conn *ip_vs_conn_out_get
743(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port); 746(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port);
744 747
diff --git a/net/ipv4/ipvs/ip_vs_conn.c b/net/ipv4/ipvs/ip_vs_conn.c
index 5994521fddc5..f828fa2eb7de 100644
--- a/net/ipv4/ipvs/ip_vs_conn.c
+++ b/net/ipv4/ipvs/ip_vs_conn.c
@@ -196,6 +196,7 @@ static inline struct ip_vs_conn *__ip_vs_conn_in_get
196 list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { 196 list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) {
197 if (s_addr==cp->caddr && s_port==cp->cport && 197 if (s_addr==cp->caddr && s_port==cp->cport &&
198 d_port==cp->vport && d_addr==cp->vaddr && 198 d_port==cp->vport && d_addr==cp->vaddr &&
199 ((!s_port) ^ (!(cp->flags & IP_VS_CONN_F_NO_CPORT))) &&
199 protocol==cp->protocol) { 200 protocol==cp->protocol) {
200 /* HIT */ 201 /* HIT */
201 atomic_inc(&cp->refcnt); 202 atomic_inc(&cp->refcnt);
@@ -227,6 +228,40 @@ struct ip_vs_conn *ip_vs_conn_in_get
227 return cp; 228 return cp;
228} 229}
229 230
231/* Get reference to connection template */
232struct ip_vs_conn *ip_vs_ct_in_get
233(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port)
234{
235 unsigned hash;
236 struct ip_vs_conn *cp;
237
238 hash = ip_vs_conn_hashkey(protocol, s_addr, s_port);
239
240 ct_read_lock(hash);
241
242 list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) {
243 if (s_addr==cp->caddr && s_port==cp->cport &&
244 d_port==cp->vport && d_addr==cp->vaddr &&
245 cp->flags & IP_VS_CONN_F_TEMPLATE &&
246 protocol==cp->protocol) {
247 /* HIT */
248 atomic_inc(&cp->refcnt);
249 goto out;
250 }
251 }
252 cp = NULL;
253
254 out:
255 ct_read_unlock(hash);
256
257 IP_VS_DBG(7, "template lookup/in %s %u.%u.%u.%u:%d->%u.%u.%u.%u:%d %s\n",
258 ip_vs_proto_name(protocol),
259 NIPQUAD(s_addr), ntohs(s_port),
260 NIPQUAD(d_addr), ntohs(d_port),
261 cp?"hit":"not hit");
262
263 return cp;
264}
230 265
231/* 266/*
232 * Gets ip_vs_conn associated with supplied parameters in the ip_vs_conn_tab. 267 * 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 atomic_read(&dest->refcnt)); 402 atomic_read(&dest->refcnt));
368 403
369 /* Update the connection counters */ 404 /* Update the connection counters */
370 if (cp->cport || (cp->flags & IP_VS_CONN_F_NO_CPORT)) { 405 if (!(cp->flags & IP_VS_CONN_F_TEMPLATE)) {
371 /* It is a normal connection, so increase the inactive 406 /* It is a normal connection, so increase the inactive
372 connection counter because it is in TCP SYNRECV 407 connection counter because it is in TCP SYNRECV
373 state (inactive) or other protocol inacive state */ 408 state (inactive) or other protocol inacive state */
@@ -406,7 +441,7 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp)
406 atomic_read(&dest->refcnt)); 441 atomic_read(&dest->refcnt));
407 442
408 /* Update the connection counters */ 443 /* Update the connection counters */
409 if (cp->cport || (cp->flags & IP_VS_CONN_F_NO_CPORT)) { 444 if (!(cp->flags & IP_VS_CONN_F_TEMPLATE)) {
410 /* It is a normal connection, so decrease the inactconns 445 /* It is a normal connection, so decrease the inactconns
411 or activeconns counter */ 446 or activeconns counter */
412 if (cp->flags & IP_VS_CONN_F_INACTIVE) { 447 if (cp->flags & IP_VS_CONN_F_INACTIVE) {
@@ -776,7 +811,7 @@ void ip_vs_random_dropentry(void)
776 ct_write_lock_bh(hash); 811 ct_write_lock_bh(hash);
777 812
778 list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { 813 list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) {
779 if (!cp->cport && !(cp->flags & IP_VS_CONN_F_NO_CPORT)) 814 if (cp->flags & IP_VS_CONN_F_TEMPLATE)
780 /* connection template */ 815 /* connection template */
781 continue; 816 continue;
782 817
diff --git a/net/ipv4/ipvs/ip_vs_core.c b/net/ipv4/ipvs/ip_vs_core.c
index 3ac7eeca04ac..981cc3244ef2 100644
--- a/net/ipv4/ipvs/ip_vs_core.c
+++ b/net/ipv4/ipvs/ip_vs_core.c
@@ -243,10 +243,10 @@ ip_vs_sched_persist(struct ip_vs_service *svc,
243 if (ports[1] == svc->port) { 243 if (ports[1] == svc->port) {
244 /* Check if a template already exists */ 244 /* Check if a template already exists */
245 if (svc->port != FTPPORT) 245 if (svc->port != FTPPORT)
246 ct = ip_vs_conn_in_get(iph->protocol, snet, 0, 246 ct = ip_vs_ct_in_get(iph->protocol, snet, 0,
247 iph->daddr, ports[1]); 247 iph->daddr, ports[1]);
248 else 248 else
249 ct = ip_vs_conn_in_get(iph->protocol, snet, 0, 249 ct = ip_vs_ct_in_get(iph->protocol, snet, 0,
250 iph->daddr, 0); 250 iph->daddr, 0);
251 251
252 if (!ct || !ip_vs_check_template(ct)) { 252 if (!ct || !ip_vs_check_template(ct)) {
@@ -272,14 +272,14 @@ ip_vs_sched_persist(struct ip_vs_service *svc,
272 iph->daddr, 272 iph->daddr,
273 ports[1], 273 ports[1],
274 dest->addr, dest->port, 274 dest->addr, dest->port,
275 0, 275 IP_VS_CONN_F_TEMPLATE,
276 dest); 276 dest);
277 else 277 else
278 ct = ip_vs_conn_new(iph->protocol, 278 ct = ip_vs_conn_new(iph->protocol,
279 snet, 0, 279 snet, 0,
280 iph->daddr, 0, 280 iph->daddr, 0,
281 dest->addr, 0, 281 dest->addr, 0,
282 0, 282 IP_VS_CONN_F_TEMPLATE,
283 dest); 283 dest);
284 if (ct == NULL) 284 if (ct == NULL)
285 return NULL; 285 return NULL;
@@ -298,10 +298,10 @@ ip_vs_sched_persist(struct ip_vs_service *svc,
298 * port zero template: <protocol,caddr,0,vaddr,0,daddr,0> 298 * port zero template: <protocol,caddr,0,vaddr,0,daddr,0>
299 */ 299 */
300 if (svc->fwmark) 300 if (svc->fwmark)
301 ct = ip_vs_conn_in_get(IPPROTO_IP, snet, 0, 301 ct = ip_vs_ct_in_get(IPPROTO_IP, snet, 0,
302 htonl(svc->fwmark), 0); 302 htonl(svc->fwmark), 0);
303 else 303 else
304 ct = ip_vs_conn_in_get(iph->protocol, snet, 0, 304 ct = ip_vs_ct_in_get(iph->protocol, snet, 0,
305 iph->daddr, 0); 305 iph->daddr, 0);
306 306
307 if (!ct || !ip_vs_check_template(ct)) { 307 if (!ct || !ip_vs_check_template(ct)) {
@@ -326,14 +326,14 @@ ip_vs_sched_persist(struct ip_vs_service *svc,
326 snet, 0, 326 snet, 0,
327 htonl(svc->fwmark), 0, 327 htonl(svc->fwmark), 0,
328 dest->addr, 0, 328 dest->addr, 0,
329 0, 329 IP_VS_CONN_F_TEMPLATE,
330 dest); 330 dest);
331 else 331 else
332 ct = ip_vs_conn_new(iph->protocol, 332 ct = ip_vs_conn_new(iph->protocol,
333 snet, 0, 333 snet, 0,
334 iph->daddr, 0, 334 iph->daddr, 0,
335 dest->addr, 0, 335 dest->addr, 0,
336 0, 336 IP_VS_CONN_F_TEMPLATE,
337 dest); 337 dest);
338 if (ct == NULL) 338 if (ct == NULL)
339 return NULL; 339 return NULL;
diff --git a/net/ipv4/ipvs/ip_vs_sync.c b/net/ipv4/ipvs/ip_vs_sync.c
index 574d1f509b46..2e5ced3d8062 100644
--- a/net/ipv4/ipvs/ip_vs_sync.c
+++ b/net/ipv4/ipvs/ip_vs_sync.c
@@ -297,16 +297,24 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen)
297 297
298 p = (char *)buffer + sizeof(struct ip_vs_sync_mesg); 298 p = (char *)buffer + sizeof(struct ip_vs_sync_mesg);
299 for (i=0; i<m->nr_conns; i++) { 299 for (i=0; i<m->nr_conns; i++) {
300 unsigned flags;
301
300 s = (struct ip_vs_sync_conn *)p; 302 s = (struct ip_vs_sync_conn *)p;
301 cp = ip_vs_conn_in_get(s->protocol, 303 flags = ntohs(s->flags);
302 s->caddr, s->cport, 304 if (!(flags & IP_VS_CONN_F_TEMPLATE))
303 s->vaddr, s->vport); 305 cp = ip_vs_conn_in_get(s->protocol,
306 s->caddr, s->cport,
307 s->vaddr, s->vport);
308 else
309 cp = ip_vs_ct_in_get(s->protocol,
310 s->caddr, s->cport,
311 s->vaddr, s->vport);
304 if (!cp) { 312 if (!cp) {
305 cp = ip_vs_conn_new(s->protocol, 313 cp = ip_vs_conn_new(s->protocol,
306 s->caddr, s->cport, 314 s->caddr, s->cport,
307 s->vaddr, s->vport, 315 s->vaddr, s->vport,
308 s->daddr, s->dport, 316 s->daddr, s->dport,
309 ntohs(s->flags), NULL); 317 flags, NULL);
310 if (!cp) { 318 if (!cp) {
311 IP_VS_ERR("ip_vs_conn_new failed\n"); 319 IP_VS_ERR("ip_vs_conn_new failed\n");
312 return; 320 return;
@@ -315,11 +323,11 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen)
315 } else if (!cp->dest) { 323 } else if (!cp->dest) {
316 /* it is an entry created by the synchronization */ 324 /* it is an entry created by the synchronization */
317 cp->state = ntohs(s->state); 325 cp->state = ntohs(s->state);
318 cp->flags = ntohs(s->flags) | IP_VS_CONN_F_HASHED; 326 cp->flags = flags | IP_VS_CONN_F_HASHED;
319 } /* Note that we don't touch its state and flags 327 } /* Note that we don't touch its state and flags
320 if it is a normal entry. */ 328 if it is a normal entry. */
321 329
322 if (ntohs(s->flags) & IP_VS_CONN_F_SEQ_MASK) { 330 if (flags & IP_VS_CONN_F_SEQ_MASK) {
323 opt = (struct ip_vs_sync_conn_options *)&s[1]; 331 opt = (struct ip_vs_sync_conn_options *)&s[1];
324 memcpy(&cp->in_seq, opt, sizeof(*opt)); 332 memcpy(&cp->in_seq, opt, sizeof(*opt));
325 p += FULL_CONN_SIZE; 333 p += FULL_CONN_SIZE;