diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index 72e6b666c3406ffd74ec8d63e5408b00cb3d357b..92f070be2bb28653e105b1545429416f81ec94ea 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -2039,15 +2039,17 @@ static int addr_range_delme_cb(void *obj, void *arg, int flags)
 static int addr_range_hash_cb(const void *obj, const int flags)
 {
 	const struct addr_range *lim = obj;
-	return abs((int) lim->ha.netaddr.s_addr);
+	struct sockaddr_in sin;
+	ast_sockaddr_to_sin(&lim->ha.addr, &sin);
+	return abs((int) sin.sin_addr.s_addr);
 }
 
 static int addr_range_cmp_cb(void *obj, void *arg, int flags)
 {
 	struct addr_range *lim1 = obj, *lim2 = arg;
-	return ((lim1->ha.netaddr.s_addr == lim2->ha.netaddr.s_addr) &&
-		(lim1->ha.netmask.s_addr == lim2->ha.netmask.s_addr)) ?
-		CMP_MATCH | CMP_STOP : 0;
+	return (!(ast_sockaddr_cmp_addr(&lim1->ha.addr, &lim2->ha.addr)) &&
+			!(ast_sockaddr_cmp_addr(&lim1->ha.netmask, &lim2->ha.netmask))) ?
+			CMP_MATCH | CMP_STOP : 0;
 }
 
 static int peercnt_hash_cb(const void *obj, const int flags)
@@ -2066,8 +2068,13 @@ static int addr_range_match_address_cb(void *obj, void *arg, int flags)
 {
 	struct addr_range *addr_range = obj;
 	struct sockaddr_in *sin = arg;
+	struct sockaddr_in ha_netmask_sin;
+	struct sockaddr_in ha_addr_sin;
+
+	ast_sockaddr_to_sin(&addr_range->ha.netmask, &ha_netmask_sin);
+	ast_sockaddr_to_sin(&addr_range->ha.addr, &ha_addr_sin);
 
-	if ((sin->sin_addr.s_addr & addr_range->ha.netmask.s_addr) == addr_range->ha.netaddr.s_addr) {
+	if ((sin->sin_addr.s_addr & ha_netmask_sin.sin_addr.s_addr) == ha_addr_sin.sin_addr.s_addr) {
 		return CMP_MATCH | CMP_STOP;
 	}
 	return 0;
@@ -7385,6 +7392,7 @@ static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies
 	int gotcapability = 0;
 	struct ast_variable *v = NULL, *tmpvar = NULL;
 	struct ao2_iterator i;
+	struct ast_sockaddr addr;
 
 	if (!iaxs[callno])
 		return res;
@@ -7442,10 +7450,11 @@ static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies
 	}
 	/* Search the userlist for a compatible entry, and fill in the rest */
 	i = ao2_iterator_init(users, 0);
+	ast_sockaddr_from_sin(&addr, sin);
 	while ((user = ao2_iterator_next(&i))) {
 		if ((ast_strlen_zero(iaxs[callno]->username) ||				/* No username specified */
 			!strcmp(iaxs[callno]->username, user->name))	/* Or this username specified */
-			&& ast_apply_ha(user->ha, sin) 	/* Access is permitted from this IP */
+			&& ast_apply_ha(user->ha, &addr) 	/* Access is permitted from this IP */
 			&& (ast_strlen_zero(iaxs[callno]->context) ||			/* No context specified */
 			     apply_context(user->contexts, iaxs[callno]->context))) {			/* Context is permitted */
 			if (!ast_strlen_zero(iaxs[callno]->username)) {
@@ -7787,6 +7796,7 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
 	int x;
 	int expire = 0;
 	int res = -1;
+	struct ast_sockaddr addr;
 
 	ast_clear_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED);
 	/* iaxs[callno]->peer[0] = '\0'; not necc. any more-- stringfield is pre-inited to null string */
@@ -7841,7 +7851,8 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
 		goto return_unref;
 	}
 
-	if (!ast_apply_ha(p->ha, sin)) {
+	ast_sockaddr_from_sin(&addr, sin);
+	if (!ast_apply_ha(p->ha, &addr)) {
 		if (authdebug)
 			ast_log(LOG_NOTICE, "Host %s denied access to register peer '%s'\n", ast_inet_ntoa(sin->sin_addr), p->name);
 		goto return_unref;
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 792ca698076d8a77ff061ac1e09cba99f2c22e2b..b6e167a791921ffb8324d804b591a248af8b55ce 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -3084,7 +3084,7 @@ static void build_via(struct sip_pvt *p)
 static void ast_sip_ouraddrfor(const struct ast_sockaddr *them, struct ast_sockaddr *us, struct sip_pvt *p)
 {
 	struct ast_sockaddr theirs;
-	struct sockaddr_in theirs_sin, externip_sin, us_sin;
+	struct sockaddr_in externip_sin;
 
 	/* Set want_remap to non-zero if we want to remap 'us' to an externally
 	 * reachable IP address and port. This is done if:
@@ -3112,16 +3112,13 @@ static void ast_sip_ouraddrfor(const struct ast_sockaddr *them, struct ast_socka
 				"remove \"localnet\" and/or \"externip\" settings.\n");
 		}
 	} else {
-		ast_sockaddr_to_sin(&theirs, &theirs_sin);
-		ast_sockaddr_to_sin(us, &us_sin);
-
 		want_remap = localaddr &&
 			!(ast_sockaddr_isnull(&externip) && stunaddr.sin_addr.s_addr) &&
-			ast_apply_ha(localaddr, &theirs_sin) == AST_SENSE_ALLOW ;
+			ast_apply_ha(localaddr, &theirs) == AST_SENSE_ALLOW ;
 	}
 
 	if (want_remap &&
-	    (!sip_cfg.matchexterniplocally || !ast_apply_ha(localaddr, &us_sin)) ) {
+	    (!sip_cfg.matchexterniplocally || !ast_apply_ha(localaddr, us)) ) {
 		/* if we used externhost or stun, see if it is time to refresh the info */
 		if (externexpire && time(NULL) >= externexpire) {
 			if (stunaddr.sin_addr.s_addr) {
@@ -12643,7 +12640,6 @@ static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, st
 	int transport_type;
 	const char *useragent;
 	struct ast_sockaddr oldsin, testsa;
-	struct sockaddr_in testsin;
 
 	ast_copy_string(contact, get_header(req, "Contact"), sizeof(contact));
 
@@ -12763,16 +12759,13 @@ static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, st
 	}
 
 	/* Check that they're allowed to register at this IP */
-	if (!ast_sockaddr_is_ipv6(&peer->addr)) {
-		ast_sockaddr_to_sin(&peer->addr, &testsin);
-		if (ast_apply_ha(sip_cfg.contact_ha, &testsin) != AST_SENSE_ALLOW ||
-				ast_apply_ha(peer->contactha, &testsin) != AST_SENSE_ALLOW) {
-			ast_log(LOG_WARNING, "Domain '%s' disallowed by contact ACL (violating IP %s)\n", domain,
-				ast_sockaddr_stringify_addr(&testsa));
-			ast_string_field_set(peer, fullcontact, "");
-			ast_string_field_set(pvt, our_contact, "");
-			return PARSE_REGISTER_DENIED;
-		}
+	if (ast_apply_ha(sip_cfg.contact_ha, &peer->addr) != AST_SENSE_ALLOW ||
+			ast_apply_ha(peer->contactha, &peer->addr) != AST_SENSE_ALLOW) {
+		ast_log(LOG_WARNING, "Domain '%s' disallowed by contact ACL (violating IP %s)\n", domain,
+			ast_sockaddr_stringify_addr(&testsa));
+		ast_string_field_set(peer, fullcontact, "");
+		ast_string_field_set(pvt, our_contact, "");
+		return PARSE_REGISTER_DENIED;
 	}
 
 	/* if the Contact header information copied into peer->addr matches the
@@ -13418,19 +13411,14 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock
 	}
 	peer = find_peer(name, NULL, TRUE, FINDPEERS, FALSE, 0);
 
-	if (!ast_sockaddr_is_ipv6(addr)) {
-		struct sockaddr_in sin_tmp;
-
-		ast_sockaddr_to_sin(addr, &sin_tmp);
-		if (!(peer && ast_apply_ha(peer->ha, &sin_tmp))) {
-			/* Peer fails ACL check */
-			if (peer) {
-				unref_peer(peer, "register_verify: unref_peer: from find_peer operation");
-				peer = NULL;
-				res = AUTH_ACL_FAILED;
-			} else {
-				res = AUTH_NOT_FOUND;
-			}
+	if (!(peer && ast_apply_ha(peer->ha, addr))) {
+		/* Peer fails ACL check */
+		if (peer) {
+			unref_peer(peer, "register_verify: unref_peer: from find_peer operation");
+			peer = NULL;
+			res = AUTH_ACL_FAILED;
+		} else {
+			res = AUTH_NOT_FOUND;
 		}
 	}
 
@@ -14533,15 +14521,11 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
 		}
 		return AUTH_DONT_KNOW;
 	}
-	if (!ast_sockaddr_is_ipv6(addr)) {
-		struct sockaddr_in sin_tmp;
 
-		ast_sockaddr_to_sin(addr, &sin_tmp);
-		if (!ast_apply_ha(peer->ha, &sin_tmp)) {
-			ast_debug(2, "Found peer '%s' for '%s', but fails host access\n", peer->name, of);
-			unref_peer(peer, "unref_peer: check_peer_ok: from find_peer call, early return of AUTH_ACL_FAILED");
-			return AUTH_ACL_FAILED;
-		}
+	if (!ast_apply_ha(peer->ha, addr)) {
+		ast_debug(2, "Found peer '%s' for '%s', but fails host access\n", peer->name, of);
+		unref_peer(peer, "unref_peer: check_peer_ok: from find_peer call, early return of AUTH_ACL_FAILED");
+		return AUTH_ACL_FAILED;
 	}
 	if (debug)
 		ast_verbose("Found peer '%s' for '%s' from %s\n",
@@ -16618,12 +16602,11 @@ static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_
 	{
 		struct ast_ha *d;
 		const char *prefix = "Localnet:";
-		char buf[INET_ADDRSTRLEN]; /* need to print two addresses */
 
 		for (d = localaddr; d ; prefix = "", d = d->next) {
 			ast_cli(a->fd, "  %-24s%s/%s\n",
-			    prefix, ast_inet_ntoa(d->netaddr),
-			    inet_ntop(AF_INET, &d->netmask, buf, sizeof(buf)) );
+			    prefix, ast_strdupa(ast_sockaddr_stringify_addr(&d->addr)),
+			    ast_strdupa(ast_sockaddr_stringify_addr(&d->netmask)));
 		}
 	}
 	ast_cli(a->fd, "  STUN server:            %s:%d\n", ast_inet_ntoa(stunaddr.sin_addr), ntohs(stunaddr.sin_port));
@@ -27195,20 +27178,12 @@ static int reload_config(enum channelreloadreason reason)
 static int apply_directmedia_ha(struct sip_pvt *p, const char *op)
 {
 	struct ast_sockaddr us = { { 0, }, }, them = { { 0, }, };
-	struct sockaddr_in them_sin;
 	int res = AST_SENSE_ALLOW;
 
 	ast_rtp_instance_get_remote_address(p->rtp, &them);
 	ast_rtp_instance_get_local_address(p->rtp, &us);
 
-	/* Currently ast_apply_ha doesn't support IPv6 */
-	if (ast_sockaddr_is_ipv6(&them)) {
-		return res;
-	}
-
-	ast_sockaddr_to_sin(&them, &them_sin);
-
-	if ((res = ast_apply_ha(p->directmediaha, &them_sin)) == AST_SENSE_DENY) {
+	if ((res = ast_apply_ha(p->directmediaha, &them)) == AST_SENSE_DENY) {
 		ast_debug(3, "Reinvite %s to %s denied by directmedia ACL on %s\n",
 			op, ast_strdupa(ast_sockaddr_stringify(&them)), ast_strdupa(ast_sockaddr_stringify(&us)));
 	}
diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c
index 31f70ec46aa458397bd100b408b78307c0fefa42..6d4968a7b73e803ab0cd900b4c9775cb240a9e15 100644
--- a/channels/chan_skinny.c
+++ b/channels/chan_skinny.c
@@ -1877,8 +1877,10 @@ static int skinny_register(struct skinny_req *req, struct skinnysession *s)
 
 	AST_LIST_LOCK(&devices);
 	AST_LIST_TRAVERSE(&devices, d, list){
+		struct ast_sockaddr addr;
+		ast_sockaddr_from_sin(&addr, &s->sin);
 		if (!strcasecmp(req->data.reg.name, d->id)
-				&& ast_apply_ha(d->ha, &(s->sin))) {
+				&& ast_apply_ha(d->ha, &addr)) {
 			s->device = d;
 			d->type = letohl(req->data.reg.type);
 			if (ast_strlen_zero(d->version_id)) {
diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample
index ea83d483eb8030069df5a7588c2c770b6ccbd21b..1ec5ae3314eebf5bc782e88338ff537b18d40c1c 100644
--- a/configs/sip.conf.sample
+++ b/configs/sip.conf.sample
@@ -1238,6 +1238,9 @@ srvlookup=yes                   ; Enable DNS SRV lookups on outbound calls
 ;deny=0.0.0.0/0.0.0.0            ; ACL: Control access to this account based on IP address
 ;permit=192.168.0.60/255.255.255.0
 ;permit=192.168.0.60/24          ; we can also use CIDR notation for subnet masks
+;permit=2001:db8::/32            ; IPv6 ACLs can be specified if desired. IPv6 ACLs
+                                 ; apply only to IPv6 addresses, and IPv4 ACLs apply
+                                 ; only to IPv4 addresses.
 
 ;[cisco1]
 ;type=friend
diff --git a/include/asterisk/acl.h b/include/asterisk/acl.h
index 2c4f6205189ee296a401cd25b3f9957155a51e86..a8c311cb25f316b6f35cf3baf135843c917940b6 100644
--- a/include/asterisk/acl.h
+++ b/include/asterisk/acl.h
@@ -46,11 +46,11 @@ extern "C" {
  * thing public and let users play with them.
  */
 struct ast_ha {
-        /* Host access rule */
-        struct in_addr netaddr;  
-        struct in_addr netmask;
-        int sense;
-        struct ast_ha *next;
+	/* Host access rule */
+	struct ast_sockaddr addr;
+	struct ast_sockaddr netmask;
+	int sense;
+	struct ast_ha *next;
 };
 
 /*!
@@ -111,11 +111,11 @@ struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha
  * the one whose sense will be returned.
  *
  * \param ha The head of the list of host access rules to follow
- * \param sin A sockaddr_in whose address is considered when matching rules
+ * \param addr An ast_sockaddr whose address is considered when matching rules
  * \retval AST_SENSE_ALLOW The IP address passes our ACL
  * \retval AST_SENSE_DENY The IP address fails our ACL
  */
-int ast_apply_ha(struct ast_ha *ha, struct sockaddr_in *sin);
+int ast_apply_ha(const struct ast_ha *ha, const struct ast_sockaddr *addr);
 
 /*!
  * \brief Get the IP address given a hostname
@@ -186,7 +186,7 @@ int ast_ouraddrfor(const struct ast_sockaddr *them, struct ast_sockaddr *us);
  * \retval -1 Failure. address is filled with 0s
  * \retval 0 Success
  */
-int ast_lookup_iface(char *iface, struct in_addr *address);
+int ast_lookup_iface(char *iface, struct ast_sockaddr *address);
 
 /*!
  * \brief Duplicate the contents of a list of host access rules
diff --git a/include/asterisk/netsock2.h b/include/asterisk/netsock2.h
index e7121cb949592d72a691b946ce998e0889430838..b73d848a0d898991593896b6d358e47b2f2dfc0e 100644
--- a/include/asterisk/netsock2.h
+++ b/include/asterisk/netsock2.h
@@ -55,6 +55,20 @@ struct ast_sockaddr {
 	socklen_t len;
 };
 
+/*!
+ * \brief
+ * Convert an IPv4-mapped IPv6 address into an IPv4 address.
+ *
+ * \warning You should rarely need this function. Only call this
+ * if you know what you're doing.
+ *
+ * \param addr The IPv4-mapped address to convert
+ * \param mapped_addr The resulting IPv4 address
+ * \retval 0 Unable to make the conversion
+ * \retval 1 Successful conversion
+ */
+int ast_sockaddr_ipv4_mapped(const struct ast_sockaddr *addr, struct ast_sockaddr *ast_mapped);
+
 /*!
  * \since 1.8
  *
diff --git a/main/acl.c b/main/acl.c
index 663c0b22596d3704f180a415e1cc4ca59c72d5a8..4ab102736c6c71a91b87d3e724f0bf011b10a1c1 100644
--- a/main/acl.c
+++ b/main/acl.c
@@ -229,8 +229,8 @@ void ast_free_ha(struct ast_ha *ha)
 /* Copy HA structure */
 void ast_copy_ha(const struct ast_ha *from, struct ast_ha *to)
 {
-	memcpy(&to->netaddr, &from->netaddr, sizeof(from->netaddr));
-	memcpy(&to->netmask, &from->netmask, sizeof(from->netmask));
+	ast_sockaddr_copy(&to->addr, &from->addr);
+	ast_sockaddr_copy(&to->netmask, &from->netmask);
 	to->sense = from->sense;
 }
 
@@ -271,14 +271,135 @@ struct ast_ha *ast_duplicate_ha_list(struct ast_ha *original)
 	return ret;                             /* Return start of list */
 }
 
+/*!
+ * \brief
+ * Isolate a 32-bit section of an IPv6 address
+ *
+ * An IPv6 address can be divided into 4 32-bit chunks. This gives
+ * easy access to one of these chunks.
+ *
+ * \param sin6 A pointer to a struct sockaddr_in6
+ * \param index Which 32-bit chunk to operate on. Must be in the range 0-3.
+ */
+#define V6_WORD(sin6, index) ((uint32_t *)&((sin6)->sin6_addr))[(index)]
+
+/*!
+ * \brief
+ * Apply a netmask to an address and store the result in a separate structure.
+ *
+ * When dealing with IPv6 addresses, one cannot apply a netmask with a simple
+ * logical and operation. Furthermore, the incoming address may be an IPv4 address
+ * and need to be mapped properly before attempting to apply a rule.
+ *
+ * \param addr The IP address to apply the mask to.
+ * \param netmask The netmask configured in the host access rule.
+ * \param result The resultant address after applying the netmask to the given address
+ * \retval 0 Successfully applied netmask
+ * \reval -1 Failed to apply netmask
+ */
+static int apply_netmask(const struct ast_sockaddr *addr, const struct ast_sockaddr *netmask,
+		struct ast_sockaddr *result)
+{
+	int res = 0;
+
+	if (ast_sockaddr_is_ipv4(addr)) {
+		struct sockaddr_in result4 = { 0, };
+		struct sockaddr_in *addr4 = (struct sockaddr_in *) &addr->ss;
+		struct sockaddr_in *mask4 = (struct sockaddr_in *) &netmask->ss;
+		result4.sin_family = AF_INET;
+		result4.sin_addr.s_addr = addr4->sin_addr.s_addr & mask4->sin_addr.s_addr;
+		ast_sockaddr_from_sin(result, &result4);
+	} else if (ast_sockaddr_is_ipv6(addr)) {
+		struct sockaddr_in6 result6 = { 0, };
+		struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) &addr->ss;
+		struct sockaddr_in6 *mask6 = (struct sockaddr_in6 *) &netmask->ss;
+		int i;
+		result6.sin6_family = AF_INET6;
+		for (i = 0; i < 4; ++i) {
+			V6_WORD(&result6, i) = V6_WORD(addr6, i) & V6_WORD(mask6, i);
+		}
+		memcpy(&result->ss, &result6, sizeof(result6));
+		result->len = sizeof(result6);
+	} else {
+		/* Unsupported address scheme */
+		res = -1;
+	}
+
+	return res;
+}
+
+/*!
+ * \brief
+ * Parse a netmask in CIDR notation
+ *
+ * \details
+ * For a mask of an IPv4 address, this should be a number between 0 and 32. For
+ * a mask of an IPv6 address, this should be a number between 0 and 128. This
+ * function creates an IPv6 ast_sockaddr from the given netmask. For masks of
+ * IPv4 addresses, this is accomplished by adding 96 to the original netmask.
+ *
+ * \param[out] addr The ast_sockaddr produced from the CIDR netmask
+ * \param is_v4 Tells if the address we are masking is IPv4.
+ * \param mask_str The CIDR mask to convert
+ * \retval -1 Failure
+ * \retval 0 Success
+ */
+static int parse_cidr_mask(struct ast_sockaddr *addr, int is_v4, const char *mask_str)
+{
+	int mask;
+
+	if (sscanf(mask_str, "%30d", &mask) != 1) {
+		return -1;
+	}
+
+	if (is_v4) {
+		struct sockaddr_in sin;
+		if (mask < 0 || mask > 32) {
+			return -1;
+		}
+		memset(&sin, 0, sizeof(sin));
+		sin.sin_family = AF_INET;
+		/* If mask is 0, then we already have the
+		 * appropriate all 0s address in sin from
+		 * the above memset.
+		 */
+		if (mask != 0) {
+			sin.sin_addr.s_addr = htonl(0xFFFFFFFF << (32 - mask));
+		}
+		ast_sockaddr_from_sin(addr, &sin);
+	} else {
+		struct sockaddr_in6 sin6;
+		int i;
+		if (mask < 0 || mask > 128) {
+			return -1;
+		}
+		memset(&sin6, 0, sizeof(sin6));
+		sin6.sin6_family = AF_INET6;
+		for (i = 0; i < 4; ++i) {
+			/* Once mask reaches 0, we don't have
+			 * to explicitly set anything anymore
+			 * since sin6 was zeroed out already
+			 */
+			if (mask > 0) {
+				V6_WORD(&sin6, i) = htonl(0xFFFFFFFF << (mask < 32 ? (32 - mask) : 0));
+				mask -= mask < 32 ? mask : 32;
+			}
+		}
+		memcpy(&addr->ss, &sin6, sizeof(sin6));
+		addr->len = sizeof(sin6);
+	}
+
+	return 0;
+}
+
 struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha *path, int *error)
 {
 	struct ast_ha *ha;
-	char *nm;
 	struct ast_ha *prev = NULL;
 	struct ast_ha *ret;
-	int x;
 	char *tmp = ast_strdupa(stuff);
+	char *address = NULL, *mask = NULL;
+	int addr_is_v4;
 
 	ret = path;
 	while (path) {
@@ -290,52 +411,73 @@ struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha
 		return ret;
 	}
 
-	if (!(nm = strchr(tmp, '/'))) {
-		/* assume /32. Yes, htonl does not do anything for this particular mask
-		   but we better use it to show we remember about byte order */
-		ha->netmask.s_addr = htonl(0xFFFFFFFF);
+	address = strsep(&tmp, "/");
+	if (!address) {
+		address = tmp;
 	} else {
-		*nm = '\0';
-		nm++;
-
-		if (!strchr(nm, '.')) {
-			if ((sscanf(nm, "%30d", &x) == 1) && (x >= 0) && (x <= 32)) {
-				if (x == 0) {
-					/* This is special-cased to prevent unpredictable
-					 * behavior of shifting left 32 bits
-					 */
-					ha->netmask.s_addr = 0;
-				} else {
-					ha->netmask.s_addr = htonl(0xFFFFFFFF << (32 - x));
-				}
-			} else {
-				ast_log(LOG_WARNING, "Invalid CIDR in %s\n", stuff);
-				ast_free(ha);
-				if (error) {
-					*error = 1;
-				}
-				return ret;
-			}
-		} else if (!inet_aton(nm, &ha->netmask)) {
-			ast_log(LOG_WARNING, "Invalid mask in %s\n", stuff);
-			ast_free(ha);
-			if (error) {
-				*error = 1;
-			}
-			return ret;
-		}
+		mask = tmp;
+	}
+
+	if (!ast_sockaddr_parse(&ha->addr, address, PARSE_PORT_FORBID)) {
+		ast_log(LOG_WARNING, "Invalid IP address: %s\n", address);
+		ast_free_ha(ha);
+		*error = 1;
+		return ret;
 	}
 
-	if (!inet_aton(tmp, &ha->netaddr)) {
-		ast_log(LOG_WARNING, "Invalid IP address in %s\n", stuff);
-		ast_free(ha);
-		if (error) {
+	/* If someone specifies an IPv4-mapped IPv6 address,
+	 * we just convert this to an IPv4 ACL
+	 */
+	if (ast_sockaddr_ipv4_mapped(&ha->addr, &ha->addr)) {
+		ast_log(LOG_NOTICE, "IPv4-mapped ACL network address specified. "
+				"Converting to an IPv4 ACL network address.\n");
+	}
+
+	addr_is_v4 = ast_sockaddr_is_ipv4(&ha->addr);
+
+	if (!mask) {
+		parse_cidr_mask(&ha->netmask, addr_is_v4, addr_is_v4 ? "32" : "128");
+	} else if (strchr(mask, ':') || strchr(mask, '.')) {
+		int mask_is_v4;
+		/* Mask is of x.x.x.x or x:x:x:x:x:x:x:x variety */
+		if (!ast_sockaddr_parse(&ha->netmask, mask, PARSE_PORT_FORBID)) {
+			ast_log(LOG_WARNING, "Invalid netmask: %s\n", mask);
+			ast_free_ha(ha);
+			*error = 1;
+			return ret;
+		}
+		/* If someone specifies an IPv4-mapped IPv6 netmask,
+		 * we just convert this to an IPv4 ACL
+		 */
+		if (ast_sockaddr_ipv4_mapped(&ha->netmask, &ha->netmask)) {
+			ast_log(LOG_NOTICE, "IPv4-mapped ACL netmask specified. "
+					"Converting to an IPv4 ACL netmask.\n");
+		}
+		mask_is_v4 = ast_sockaddr_is_ipv4(&ha->netmask);
+		if (addr_is_v4 ^ mask_is_v4) {
+			ast_log(LOG_WARNING, "Address and mask are not using same address scheme.\n");
+			ast_free_ha(ha);
 			*error = 1;
+			return ret;
 		}
+	} else if (parse_cidr_mask(&ha->netmask, addr_is_v4, mask)) {
+		ast_log(LOG_WARNING, "Invalid CIDR netmask: %s\n", mask);
+		ast_free_ha(ha);
+		*error = 1;
 		return ret;
 	}
 
-	ha->netaddr.s_addr &= ha->netmask.s_addr;
+	if (apply_netmask(&ha->addr, &ha->netmask, &ha->addr)) {
+		/* This shouldn't happen because ast_sockaddr_parse would
+		 * have failed much earlier on an unsupported address scheme
+		 */
+		char *failmask = ast_strdupa(ast_sockaddr_stringify(&ha->netmask));
+		char *failaddr = ast_strdupa(ast_sockaddr_stringify(&ha->addr));
+		ast_log(LOG_WARNING, "Unable to apply netmask %s to address %s\n", failmask, failaddr);
+		ast_free_ha(ha);
+		*error = 1;
+		return ret;
+	}
 
 	ha->sense = strncasecmp(sense, "p", 1) ? AST_SENSE_DENY : AST_SENSE_ALLOW;
 
@@ -346,16 +488,21 @@ struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha
 		ret = ha;
 	}
 
-	ast_debug(1, "%s/%s sense %d appended to acl for peer\n", ast_strdupa(ast_inet_ntoa(ha->netaddr)), ast_strdupa(ast_inet_ntoa(ha->netmask)), ha->sense);
+	ast_debug(1, "%s/%s sense %d appended to acl for peer\n", ast_strdupa(ast_sockaddr_stringify(&ha->addr)), ast_strdupa(ast_sockaddr_stringify(&ha->netmask)), ha->sense);
 
 	return ret;
 }
 
-int ast_apply_ha(struct ast_ha *ha, struct sockaddr_in *sin)
+int ast_apply_ha(const struct ast_ha *ha, const struct ast_sockaddr *addr)
 {
 	/* Start optimistic */
 	int res = AST_SENSE_ALLOW;
-	while (ha) {
+	const struct ast_ha *current_ha;
+
+	for (current_ha = ha; current_ha; current_ha = current_ha->next) {
+		struct ast_sockaddr result;
+		struct ast_sockaddr mapped_addr;
+		const struct ast_sockaddr *addr_to_use;
 #if 0	/* debugging code */
 		char iabuf[INET_ADDRSTRLEN];
 		char iabuf2[INET_ADDRSTRLEN];
@@ -364,12 +511,38 @@ int ast_apply_ha(struct ast_ha *ha, struct sockaddr_in *sin)
 		ast_copy_string(iabuf2, ast_inet_ntoa(ha->netaddr), sizeof(iabuf2));
 		ast_debug(1, "##### Testing %s with %s\n", iabuf, iabuf2);
 #endif
+		if (ast_sockaddr_is_ipv4(&ha->addr)) {
+			if (ast_sockaddr_is_ipv6(addr)) {
+				if (ast_sockaddr_is_ipv4_mapped(addr)) {
+					/* IPv4 ACLs apply to IPv4-mapped addresses */
+					ast_sockaddr_ipv4_mapped(addr, &mapped_addr);
+					addr_to_use = &mapped_addr;
+				} else {
+					/* An IPv4 ACL does not apply to an IPv6 address */
+					continue;
+				}
+			} else {
+				/* Address is IPv4 and ACL is IPv4. No biggie */
+				addr_to_use = addr;
+			}
+		} else {
+			if (ast_sockaddr_is_ipv6(addr) && !ast_sockaddr_is_ipv4_mapped(addr)) {
+				addr_to_use = addr;
+			} else {
+				/* Address is IPv4 or IPv4 mapped but ACL is IPv6. Skip */
+				continue;
+			}
+		}
+
 		/* For each rule, if this address and the netmask = the net address
 		   apply the current rule */
-		if ((sin->sin_addr.s_addr & ha->netmask.s_addr) == ha->netaddr.s_addr) {
-			res = ha->sense;
+		if (apply_netmask(addr_to_use, &current_ha->netmask, &result)) {
+			/* Unlikely to happen since we know the address to be IPv4 or IPv6 */
+			continue;
+		}
+		if (!ast_sockaddr_cmp_addr(&result, &current_ha->addr)) {
+			res = current_ha->sense;
 		}
-		ha = ha->next;
 	}
 	return res;
 }
diff --git a/main/manager.c b/main/manager.c
index e4d436903ef1ebdded28832e619457b71b102b86..42ad0919cf4639a1384ab21113cbaab74b99e296 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -2226,6 +2226,7 @@ static int authenticate(struct mansession *s, const struct message *m)
 	struct ast_manager_user *user = NULL;
 	regex_t *regex_filter;
 	struct ao2_iterator filter_iter;
+	struct ast_sockaddr addr;
 
 	if (ast_strlen_zero(username)) {	/* missing username */
 		return -1;
@@ -2234,10 +2235,12 @@ static int authenticate(struct mansession *s, const struct message *m)
 	/* locate user in locked state */
 	AST_RWLIST_WRLOCK(&users);
 
+	ast_sockaddr_from_sin(&addr, &s->session->sin);
+
 	if (!(user = get_manager_by_name_locked(username))) {
 		report_invalid_user(s, username);
 		ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
-	} else if (user->ha && !ast_apply_ha(user->ha, &(s->session->sin))) {
+	} else if (user->ha && !ast_apply_ha(user->ha, &addr)) {
 		report_failed_acl(s, username);
 		ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
 	} else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
@@ -5625,6 +5628,7 @@ static int auth_http_callback(struct ast_tcptls_session_instance *ser,
 	int u_writeperm;
 	int u_writetimeout;
 	int u_displayconnects;
+	struct ast_sockaddr addr;
 
 	if (method != AST_HTTP_GET && method != AST_HTTP_HEAD && method != AST_HTTP_POST) {
 		ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
@@ -5668,8 +5672,9 @@ static int auth_http_callback(struct ast_tcptls_session_instance *ser,
 		goto out_401;
 	}
 
+	ast_sockaddr_from_sin(&addr, remote_address);
 	/* --- We have User for this auth, now check ACL */
-	if (user->ha && !ast_apply_ha(user->ha, remote_address)) {
+	if (user->ha && !ast_apply_ha(user->ha, &addr)) {
 		AST_RWLIST_UNLOCK(&users);
 		ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(remote_address->sin_addr), d.username);
 		ast_http_error(ser, 403, "Permission denied", "Permission denied\n");
diff --git a/main/netsock2.c b/main/netsock2.c
index 8e4f55ebf7e90111f3143cdfa41cea9fa500b550..52b8a8d391dcc784469f2de95e71382055c9014c 100644
--- a/main/netsock2.c
+++ b/main/netsock2.c
@@ -32,7 +32,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/utils.h"
 #include "asterisk/threadstorage.h"
 
-static int ast_sockaddr_ipv4_mapped(const struct ast_sockaddr *addr, struct ast_sockaddr *ast_mapped)
+int ast_sockaddr_ipv4_mapped(const struct ast_sockaddr *addr, struct ast_sockaddr *ast_mapped)
 {
 	const struct sockaddr_in6 *sin6;
 	struct sockaddr_in sin4;
diff --git a/tests/test_acl.c b/tests/test_acl.c
index a0f005bc2fc662e3471e45d7afefb2a7228016c8..88180aa1b2bb8bebfd19f1a807b9c9ae7d412a54 100644
--- a/tests/test_acl.c
+++ b/tests/test_acl.c
@@ -35,21 +35,46 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/test.h"
 #include "asterisk/acl.h"
 #include "asterisk/module.h"
+#include "asterisk/netsock2.h"
+#include "asterisk/config.h"
 
 AST_TEST_DEFINE(invalid_acl)
 {
 	const char * invalid_acls[] = {
+		/* Negative netmask */
 		"1.3.3.7/-1",
+		/* Netmask too large */
 		"1.3.3.7/33",
+		/* Netmask waaaay too large */
 		"1.3.3.7/92342348927389492307420",
+		/* Netmask non-numeric */
 		"1.3.3.7/California",
+		/* Too many octets in Netmask */
 		"1.3.3.7/255.255.255.255.255",
+		/* Octets in IP address exceed 255 */
 		"57.60.278.900/31",
+		/* Octets in IP address exceed 255 and are negative */
 		"400.32.201029.-6/24",
+		/* Invalidly formatted IP address */
 		"EGGSOFDEATH/4000",
+		/* Too many octets in IP address */
 		"33.4.7.8.3/300030",
+		/* Too many octets in Netmask */
 		"1.2.3.4/6.7.8.9.0",
+		/* Too many octets in IP address */
 		"3.1.4.1.5.9/3",
+		/* IPv6 address has multiple double colons */
+		"ff::ff::ff/3",
+		/* IPv6 address is too long */
+		"1234:5678:90ab:cdef:1234:5678:90ab:cdef:1234/56",
+		/* IPv6 netmask is too large */
+		"::ffff/129",
+		/* IPv4-mapped IPv6 address has too few octets */
+		"::ffff:255.255.255/128",
+		/* Leading and trailing colons for IPv6 address */
+		":1234:/15",
+		/* IPv6 address and IPv4 netmask */
+		"fe80::1234/255.255.255.0",
 	};
 
 	enum ast_test_result_state res = AST_TEST_PASS;
@@ -89,10 +114,19 @@ struct acl {
 	const char *access;
 };
 
+/* These constants are defined for the sole purpose of being shorter
+ * than their real names. It makes lines in this test quite a bit shorter
+ */
+
+#define TACL_A AST_SENSE_ALLOW
+#define TACL_D AST_SENSE_DENY
+
 AST_TEST_DEFINE(acl)
 {
-	struct acl permitall = { "0.0.0.0/0", "permit" };
-	struct acl denyall = { "0.0.0.0/0", "deny" };
+	struct acl permitallv4 = { "0.0.0.0/0", "permit" };
+	struct acl denyallv4 = { "0.0.0.0/0", "deny" };
+	struct acl permitallv6 = { "::/0", "permit" };
+	struct acl denyallv6 = { "::/0", "deny" };
 	struct acl acl1[] = {
 		{ "0.0.0.0/0.0.0.0", "deny" },
 		{ "10.0.0.0/255.0.0.0", "permit" },
@@ -105,23 +139,49 @@ AST_TEST_DEFINE(acl)
 		{ "10.0.0.0/24", "permit" },
 	};
 
+	struct acl acl3[] = {
+		{ "::/0", "deny" },
+		{ "fe80::/64", "permit" },
+	};
+
+	struct acl acl4[] = {
+		{ "::/0", "deny" },
+		{ "fe80::/64", "permit" },
+		{ "fe80::ffff:0:0:0/80", "deny" },
+		{ "fe80::ffff:0:ffff:0/112", "permit" },
+	};
+
 	struct {
 		const char *test_address;
+		int v4_permitall_result;
+		int v4_denyall_result;
+		int v6_permitall_result;
+		int v6_denyall_result;
 		int acl1_result;
 		int acl2_result;
+		int acl3_result;
+		int acl4_result;
 	} acl_tests[] = {
-		{ "10.1.1.5", AST_SENSE_ALLOW, AST_SENSE_ALLOW },
-		{ "192.168.0.5", AST_SENSE_ALLOW, AST_SENSE_ALLOW },
-		{ "192.168.1.5", AST_SENSE_DENY, AST_SENSE_ALLOW },
-		{ "10.0.0.1", AST_SENSE_ALLOW, AST_SENSE_ALLOW },
-		{ "10.0.10.10", AST_SENSE_ALLOW, AST_SENSE_DENY },
-		{ "172.16.0.1", AST_SENSE_DENY, AST_SENSE_ALLOW },
+		{ "10.1.1.5", TACL_A, TACL_D, TACL_A, TACL_A, TACL_A, TACL_A, TACL_A, TACL_A },
+		{ "192.168.0.5", TACL_A, TACL_D, TACL_A, TACL_A, TACL_A, TACL_A, TACL_A, TACL_A },
+		{ "192.168.1.5", TACL_A, TACL_D, TACL_A, TACL_A, TACL_D, TACL_A, TACL_A, TACL_A },
+		{ "10.0.0.1", TACL_A, TACL_D, TACL_A, TACL_A, TACL_A, TACL_A, TACL_A, TACL_A },
+		{ "10.0.10.10", TACL_A, TACL_D, TACL_A, TACL_A, TACL_A, TACL_D, TACL_A, TACL_A },
+		{ "172.16.0.1", TACL_A, TACL_D, TACL_A, TACL_A, TACL_D, TACL_A, TACL_A, TACL_A },
+		{ "fe80::1234", TACL_A, TACL_A, TACL_A, TACL_D, TACL_A, TACL_A, TACL_A, TACL_A },
+		{ "fe80:1234::1234", TACL_A, TACL_A, TACL_A, TACL_D, TACL_A, TACL_A, TACL_D, TACL_D, },
+		{ "fe80::ffff:1213:dead:beef", TACL_A, TACL_A, TACL_A, TACL_D, TACL_A, TACL_A, TACL_A, TACL_D },
+		{ "fe80::ffff:0:ffff:ABCD", TACL_A, TACL_A, TACL_A, TACL_D, TACL_A, TACL_A, TACL_A, TACL_A },
 	};
 
-	struct ast_ha *permit_ha = NULL;
-	struct ast_ha *deny_ha = NULL;
+	struct ast_ha *permit_hav4 = NULL;
+	struct ast_ha *deny_hav4 = NULL;
+	struct ast_ha *permit_hav6 = NULL;
+	struct ast_ha *deny_hav6 = NULL;
 	struct ast_ha *ha1 = NULL;
 	struct ast_ha *ha2 = NULL;
+	struct ast_ha *ha3 = NULL;
+	struct ast_ha *ha4 = NULL;
 	enum ast_test_result_state res = AST_TEST_PASS;
 	int err = 0;
 	int i;
@@ -138,13 +198,25 @@ AST_TEST_DEFINE(acl)
 		break;
 	}
 
-	if (!(permit_ha = ast_append_ha(permitall.access, permitall.host, permit_ha, &err))) {
+	if (!(permit_hav4 = ast_append_ha(permitallv4.access, permitallv4.host, permit_hav4, &err))) {
+		ast_test_status_update(test, "Failed to create permit_all ACL\n");
+		res = AST_TEST_FAIL;
+		goto acl_cleanup;
+	}
+
+	if (!(deny_hav4 = ast_append_ha(denyallv4.access, denyallv4.host, deny_hav4, &err))) {
+		ast_test_status_update(test, "Failed to create deny_all ACL\n");
+		res = AST_TEST_FAIL;
+		goto acl_cleanup;
+	}
+
+	if (!(permit_hav6 = ast_append_ha(permitallv6.access, permitallv6.host, permit_hav6, &err))) {
 		ast_test_status_update(test, "Failed to create permit_all ACL\n");
 		res = AST_TEST_FAIL;
 		goto acl_cleanup;
 	}
 
-	if (!(deny_ha = ast_append_ha(denyall.access, denyall.host, deny_ha, &err))) {
+	if (!(deny_hav6 = ast_append_ha(denyallv6.access, denyallv6.host, deny_hav6, &err))) {
 		ast_test_status_update(test, "Failed to create deny_all ACL\n");
 		res = AST_TEST_FAIL;
 		goto acl_cleanup;
@@ -168,62 +240,128 @@ AST_TEST_DEFINE(acl)
 		}
 	}
 
+	for (i = 0; i < ARRAY_LEN(acl3); ++i) {
+		if (!(ha3 = ast_append_ha(acl3[i].access, acl3[i].host, ha3, &err))) {
+			ast_test_status_update(test, "Failed to add rule %s with access %s to ha3\n",
+					acl3[i].host, acl3[i].access);
+			res = AST_TEST_FAIL;
+			goto acl_cleanup;
+		}
+	}
+
+	for (i = 0; i < ARRAY_LEN(acl4); ++i) {
+		if (!(ha4 = ast_append_ha(acl4[i].access, acl4[i].host, ha4, &err))) {
+			ast_test_status_update(test, "Failed to add rule %s with access %s to ha4\n",
+					acl4[i].host, acl4[i].access);
+			res = AST_TEST_FAIL;
+			goto acl_cleanup;
+		}
+	}
+
 	for (i = 0; i < ARRAY_LEN(acl_tests); ++i) {
-		struct sockaddr_in sin;
-		int permit_res;
-		int deny_res;
+		struct ast_sockaddr addr;
+		int permit_resv4;
+		int permit_resv6;
+		int deny_resv4;
+		int deny_resv6;
 		int acl1_res;
 		int acl2_res;
+		int acl3_res;
+		int acl4_res;
 
-		inet_aton(acl_tests[i].test_address, &sin.sin_addr);
+		ast_sockaddr_parse(&addr, acl_tests[i].test_address, PARSE_PORT_FORBID);
+
+		permit_resv4 = ast_apply_ha(permit_hav4, &addr);
+		deny_resv4 = ast_apply_ha(deny_hav4, &addr);
+		permit_resv6 = ast_apply_ha(permit_hav6, &addr);
+		deny_resv6 = ast_apply_ha(deny_hav6, &addr);
+		acl1_res = ast_apply_ha(ha1, &addr);
+		acl2_res = ast_apply_ha(ha2, &addr);
+		acl3_res = ast_apply_ha(ha3, &addr);
+		acl4_res = ast_apply_ha(ha4, &addr);
+
+		if (permit_resv4 != acl_tests[i].v4_permitall_result) {
+			ast_test_status_update(test, "Access not as expected to %s on permitallv4. Expected %d but "
+					"got %d instead\n", acl_tests[i].test_address, acl_tests[i].v4_permitall_result, permit_resv4);
+			res = AST_TEST_FAIL;
+			goto acl_cleanup;
+		}
 
-		permit_res = ast_apply_ha(permit_ha, &sin);
-		deny_res = ast_apply_ha(deny_ha, &sin);
-		acl1_res = ast_apply_ha(ha1, &sin);
-		acl2_res = ast_apply_ha(ha2, &sin);
+		if (deny_resv4 != acl_tests[i].v4_denyall_result) {
+			ast_test_status_update(test, "Access not as expected to %s on denyallv4. Expected %d but "
+					"got %d instead\n", acl_tests[i].test_address, acl_tests[i].v4_denyall_result, deny_resv4);
+			res = AST_TEST_FAIL;
+			goto acl_cleanup;
+		}
 
-		if (permit_res != AST_SENSE_ALLOW) {
-			ast_test_status_update(test, "Access denied to %s on permit_all ACL\n",
-					acl_tests[i].test_address);
+		if (permit_resv6 != acl_tests[i].v6_permitall_result) {
+			ast_test_status_update(test, "Access not as expected to %s on permitallv6. Expected %d but "
+					"got %d instead\n", acl_tests[i].test_address, acl_tests[i].v6_permitall_result, permit_resv6);
 			res = AST_TEST_FAIL;
 			goto acl_cleanup;
 		}
 
-		if (deny_res != AST_SENSE_DENY) {
-			ast_test_status_update(test, "Access allowed to %s on deny_all ACL\n",
-					acl_tests[i].test_address);
+		if (deny_resv6 != acl_tests[i].v6_denyall_result) {
+			ast_test_status_update(test, "Access not as expected to %s on denyallv6. Expected %d but "
+					"got %d instead\n", acl_tests[i].test_address, acl_tests[i].v6_denyall_result, deny_resv6);
 			res = AST_TEST_FAIL;
 			goto acl_cleanup;
 		}
 
 		if (acl1_res != acl_tests[i].acl1_result) {
-			ast_test_status_update(test, "Access not as expected to %s on acl1. Expected %d but"
+			ast_test_status_update(test, "Access not as expected to %s on acl1. Expected %d but "
 					"got %d instead\n", acl_tests[i].test_address, acl_tests[i].acl1_result, acl1_res);
 			res = AST_TEST_FAIL;
 			goto acl_cleanup;
 		}
 
 		if (acl2_res != acl_tests[i].acl2_result) {
-			ast_test_status_update(test, "Access not as expected to %s on acl2. Expected %d but"
+			ast_test_status_update(test, "Access not as expected to %s on acl2. Expected %d but "
 					"got %d instead\n", acl_tests[i].test_address, acl_tests[i].acl2_result, acl2_res);
 			res = AST_TEST_FAIL;
 			goto acl_cleanup;
 		}
+
+		if (acl3_res != acl_tests[i].acl3_result) {
+			ast_test_status_update(test, "Access not as expected to %s on acl3. Expected %d but "
+					"got %d instead\n", acl_tests[i].test_address, acl_tests[i].acl3_result, acl3_res);
+			res = AST_TEST_FAIL;
+			goto acl_cleanup;
+		}
+
+		if (acl4_res != acl_tests[i].acl4_result) {
+			ast_test_status_update(test, "Access not as expected to %s on acl4. Expected %d but "
+					"got %d instead\n", acl_tests[i].test_address, acl_tests[i].acl4_result, acl4_res);
+			res = AST_TEST_FAIL;
+			goto acl_cleanup;
+		}
 	}
 
 acl_cleanup:
-	if (permit_ha) {
-		ast_free_ha(permit_ha);
+	if (permit_hav4) {
+		ast_free_ha(permit_hav4);
+	}
+	if (deny_hav4) {
+		ast_free_ha(deny_hav4);
+	}
+	if (permit_hav6) {
+		ast_free_ha(permit_hav6);
 	}
-	if (deny_ha) {
-		ast_free_ha(deny_ha);
+	if (deny_hav6) {
+		ast_free_ha(deny_hav6);
 	}
 	if (ha1) {
 		ast_free_ha(ha1);
 	}
-	if (ha1) {
+	if (ha2) {
 		ast_free_ha(ha2);
 	}
+	if (ha3) {
+		ast_free_ha(ha3);
+	}
+	if (ha4) {
+		ast_free_ha(ha4);
+	}
 	return res;
 }