Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 19 Apr 2011 15:06:34 +0000 (UTC)
From:      Gleb Smirnoff <glebius@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r220837 - in head/sys/netinet: . ipfw
Message-ID:  <201104191506.p3JF6YFJ011509@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: glebius
Date: Tue Apr 19 15:06:33 2011
New Revision: 220837
URL: http://svn.freebsd.org/changeset/base/220837

Log:
  - Rewrite functions that copyin/out NAT configuration, so that they
    calculate required memory size dynamically.
  - Fix races on chain re-lock.
  - Introduce new field to ip_fw_chain - generation count. Now utilized
    only in the NAT configuration, but can be utilized wider in ipfw.
  - Get rid of NAT_BUF_LEN in ip_fw.h
  
  PR:		kern/143653

Modified:
  head/sys/netinet/ip_fw.h
  head/sys/netinet/ipfw/ip_fw_nat.c
  head/sys/netinet/ipfw/ip_fw_private.h

Modified: head/sys/netinet/ip_fw.h
==============================================================================
--- head/sys/netinet/ip_fw.h	Tue Apr 19 15:05:12 2011	(r220836)
+++ head/sys/netinet/ip_fw.h	Tue Apr 19 15:06:33 2011	(r220837)
@@ -383,8 +383,6 @@ struct cfg_redir {
 };
 #endif
 
-#define NAT_BUF_LEN     1024
-
 #ifdef IPFW_INTERNAL
 /* Nat configuration data struct. */
 struct cfg_nat {

Modified: head/sys/netinet/ipfw/ip_fw_nat.c
==============================================================================
--- head/sys/netinet/ipfw/ip_fw_nat.c	Tue Apr 19 15:05:12 2011	(r220836)
+++ head/sys/netinet/ipfw/ip_fw_nat.c	Tue Apr 19 15:06:33 2011	(r220837)
@@ -138,7 +138,7 @@ del_redir_spool_cfg(struct cfg_nat *n, s
 	}
 }
 
-static int
+static void
 add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
 {
 	struct cfg_redir *r, *ser_r;
@@ -199,7 +199,6 @@ add_redir_spool_cfg(char *buf, struct cf
 		/* And finally hook this redir entry. */
 		LIST_INSERT_HEAD(&ptr->redir_chain, r, _next);
 	}
-	return (1);
 }
 
 static int
@@ -344,20 +343,28 @@ lookup_nat(struct nat_list *l, int nat_i
 static int
 ipfw_nat_cfg(struct sockopt *sopt)
 {
-	struct cfg_nat *ptr, *ser_n;
+	struct cfg_nat *cfg, *ptr;
 	char *buf;
 	struct ip_fw_chain *chain = &V_layer3_chain;
+	int gencnt, len, error = 0;
 
-	buf = malloc(NAT_BUF_LEN, M_IPFW, M_WAITOK | M_ZERO);
-	sooptcopyin(sopt, buf, NAT_BUF_LEN, sizeof(struct cfg_nat));
-	ser_n = (struct cfg_nat *)buf;
+	len = sopt->sopt_valsize;
+	buf = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
+	if ((error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat))) != 0)
+		goto out;
+
+	cfg = (struct cfg_nat *)buf;
+	if (cfg->id < 0) {
+		error = EINVAL;
+		goto out;
+	}
 
-	/* check valid parameter ser_n->id > 0 ? */
 	/*
 	 * Find/create nat rule.
 	 */
 	IPFW_WLOCK(chain);
-	ptr = lookup_nat(&chain->nat, ser_n->id);
+	gencnt = chain->gencnt;
+	ptr = lookup_nat(&chain->nat, cfg->id);
 	if (ptr == NULL) {
 		IPFW_WUNLOCK(chain);
 		/* New rule: allocate and init new instance. */
@@ -365,27 +372,27 @@ ipfw_nat_cfg(struct sockopt *sopt)
 		ptr->lib = LibAliasInit(NULL);
 		LIST_INIT(&ptr->redir_chain);
 	} else {
-		/* Entry already present: temporarly unhook it. */
+		/* Entry already present: temporarily unhook it. */
 		LIST_REMOVE(ptr, _next);
-		flush_nat_ptrs(chain, ser_n->id);
+		flush_nat_ptrs(chain, cfg->id);
 		IPFW_WUNLOCK(chain);
 	}
 
 	/*
 	 * Basic nat configuration.
 	 */
-	ptr->id = ser_n->id;
+	ptr->id = cfg->id;
 	/*
 	 * XXX - what if this rule doesn't nat any ip and just
 	 * redirect?
 	 * do we set aliasaddress to 0.0.0.0?
 	 */
-	ptr->ip = ser_n->ip;
-	ptr->redir_cnt = ser_n->redir_cnt;
-	ptr->mode = ser_n->mode;
-	LibAliasSetMode(ptr->lib, ser_n->mode, ser_n->mode);
+	ptr->ip = cfg->ip;
+	ptr->redir_cnt = cfg->redir_cnt;
+	ptr->mode = cfg->mode;
+	LibAliasSetMode(ptr->lib, cfg->mode, cfg->mode);
 	LibAliasSetAddress(ptr->lib, ptr->ip);
-	memcpy(ptr->if_name, ser_n->if_name, IF_NAMESIZE);
+	memcpy(ptr->if_name, cfg->if_name, IF_NAMESIZE);
 
 	/*
 	 * Redir and LSNAT configuration.
@@ -394,15 +401,19 @@ ipfw_nat_cfg(struct sockopt *sopt)
 	del_redir_spool_cfg(ptr, &ptr->redir_chain);
 	/* Add new entries. */
 	add_redir_spool_cfg(&buf[(sizeof(struct cfg_nat))], ptr);
-	free(buf, M_IPFW);
+
 	IPFW_WLOCK(chain);
-	/*
-	 * XXXGL race here: another ipfw_nat_cfg() may already inserted
-	 * entry with the same ser_n->id.
-	 */
+	/* Extra check to avoid race with another ipfw_nat_cfg() */
+	if (gencnt != chain->gencnt &&
+	    ((cfg = lookup_nat(&chain->nat, ptr->id)) != NULL))
+		LIST_REMOVE(cfg, _next);
 	LIST_INSERT_HEAD(&chain->nat, ptr, _next);
+	chain->gencnt++;
 	IPFW_WUNLOCK(chain);
-	return (0);
+
+out:
+	free(buf, M_TEMP);
+	return (error);
 }
 
 static int
@@ -432,52 +443,61 @@ ipfw_nat_del(struct sockopt *sopt)
 static int
 ipfw_nat_get_cfg(struct sockopt *sopt)
 {
-	uint8_t *data;
+	struct ip_fw_chain *chain = &V_layer3_chain;
 	struct cfg_nat *n;
 	struct cfg_redir *r;
 	struct cfg_spool *s;
-	int nat_cnt, off;
-	struct ip_fw_chain *chain;
-	int err = ENOSPC;
+	char *data;
+	int gencnt, nat_cnt, len, error;
 
-	chain = &V_layer3_chain;
 	nat_cnt = 0;
-	off = sizeof(nat_cnt);
+	len = sizeof(nat_cnt);
 
-	data = malloc(NAT_BUF_LEN, M_IPFW, M_WAITOK | M_ZERO);
 	IPFW_RLOCK(chain);
-	/* Serialize all the data. */
+retry:
+	gencnt = chain->gencnt;
+	/* Estimate memory amount */
 	LIST_FOREACH(n, &chain->nat, _next) {
 		nat_cnt++;
-		if (off + SOF_NAT >= NAT_BUF_LEN)
-			goto nospace;
-		bcopy(n, &data[off], SOF_NAT);
-		off += SOF_NAT;
+		len += sizeof(struct cfg_nat);
+		LIST_FOREACH(r, &n->redir_chain, _next) {
+			len += sizeof(struct cfg_redir);
+			LIST_FOREACH(s, &r->spool_chain, _next)
+				len += sizeof(struct cfg_spool);
+		}
+	}
+	IPFW_RUNLOCK(chain);
+
+	data = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
+	bcopy(&nat_cnt, data, sizeof(nat_cnt));
+
+	nat_cnt = 0;
+	len = sizeof(nat_cnt);
+
+	IPFW_RLOCK(chain);
+	if (gencnt != chain->gencnt) {
+		free(data, M_TEMP);
+		goto retry;
+	}
+	/* Serialize all the data. */
+	LIST_FOREACH(n, &chain->nat, _next) {
+		bcopy(n, &data[len], sizeof(struct cfg_nat));
+		len += sizeof(struct cfg_nat);
 		LIST_FOREACH(r, &n->redir_chain, _next) {
-			if (off + SOF_REDIR >= NAT_BUF_LEN)
-				goto nospace;
-			bcopy(r, &data[off], SOF_REDIR);
-			off += SOF_REDIR;
+			bcopy(r, &data[len], sizeof(struct cfg_redir));
+			len += sizeof(struct cfg_redir);
 			LIST_FOREACH(s, &r->spool_chain, _next) {
-				if (off + SOF_SPOOL >= NAT_BUF_LEN)
-					goto nospace;
-				bcopy(s, &data[off], SOF_SPOOL);
-				off += SOF_SPOOL;
+				bcopy(s, &data[len], sizeof(struct cfg_spool));
+				len += sizeof(struct cfg_spool);
 			}
 		}
 	}
-	err = 0; /* all good */
-nospace:
 	IPFW_RUNLOCK(chain);
-	if (err == 0) {
-		bcopy(&nat_cnt, data, sizeof(nat_cnt));
-		sooptcopyout(sopt, data, NAT_BUF_LEN);
-	} else {
-		printf("serialized data buffer not big enough:"
-		    "please increase NAT_BUF_LEN\n");
-	}
-	free(data, M_IPFW);
-	return (err);
+
+	error = sooptcopyout(sopt, data, len);
+	free(data, M_TEMP);
+
+	return (error);
 }
 
 static int

Modified: head/sys/netinet/ipfw/ip_fw_private.h
==============================================================================
--- head/sys/netinet/ipfw/ip_fw_private.h	Tue Apr 19 15:05:12 2011	(r220836)
+++ head/sys/netinet/ipfw/ip_fw_private.h	Tue Apr 19 15:06:33 2011	(r220837)
@@ -225,6 +225,7 @@ struct ip_fw_chain {
 	struct rwlock	uh_lock;	/* lock for upper half */
 #endif
 	uint32_t	id;		/* ruleset id */
+	uint32_t	gencnt;		/* generation count */
 };
 
 struct sockopt;	/* used by tcp_var.h */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201104191506.p3JF6YFJ011509>