Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 7 Jun 1999 13:37:08 GMT
From:      fanf@demon.net
To:        FreeBSD-gnats-submit@freebsd.org
Subject:   kern/12071: [PATCH] large scale IP aliasing
Message-ID:  <199906071337.NAA32673@shirt.www.demon.net>

next in thread | raw e-mail | index | archive | help

>Number:         12071
>Category:       kern
>Synopsis:       [PATCH] large scale IP aliasing
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Mon Jun  7 06:40:00 PDT 1999
>Closed-Date:
>Last-Modified:
>Originator:     Tony Finch
>Release:        FreeBSD 3.2-STABLE-19990601 i386
>Organization:
Demon Internet Ltd.
>Environment:

patch applies to versions of FreeBSD since 3.1

>Description:

The NETALIAS patch makes it feasible to have a very large number of IP
addresses on one box by making it possible to configure a CIDR network
block onto an interface in one go. The additional NETBIND feature
allows a program to bind a socket to one of these network aliases.

The patch should be applied to a FreeBSD system with the command
	patch -p0 < netalias-netbind.patch
You need to add ``options NETALIAS'' and ``options NETBIND'' to the
kernel configuration file and recompile the kernel. You also need to
recompile any userland utilities that depend on struct inpcb, in
particular systat, netstat, and fstat (and also ipfilter). This can be
done with (for example)
	cd /usr/src/usr.bin/systat
	make all install clean
This must be done even if you only use the NETALIAS functionality
because struct inpcb is changed unconditionally. You don't need to
do any special compile-time definitions.

After rebooting, network aliases can be configured as follows:

	ifconfig lo0 inet 192.168.0.0 netmask 255.255.0.0 alias


Commentary:

The NETALIAS patch has four components:

(1) A change to ifa_ifwithaddr() in net/if.c that allows an address to
match an alias on an interface if the alias is a network address (i.e.
zero outside the netmask) and the address is within the network (i.e.
equal to the alias when masked by the netmask). 

(2) A change to netinet/ip_input.c that allows destination addresses
on input packets to match a netaliased interface. (This is simpler
than the change to if.c because it does not nead to deal with the
general form of struct sockaddr.) 

(3) A similar change to icmp_reflect() in netinet/ip_icmp.c that makes
ping work with netalias. 

(4) A couple of changes to netinet/in.c that makes the automatic
addition and deletion of routes work by loosening the assumption that
all aliases on the loopback interface only need host routes. (We
usually bring up the netaliases on the loopback interface to avoid
problems with ARP. XXX: Does ARP work at all with a netaliased
physical interface?)

The first three parts were originally implemented by Ronald Khoo
<ronald@demon.net> for NetBSD 1.0. The latter change was added by me
for cosmetic reasons when I ported the changes to FreeBSD 3.0.


The NETBIND patch has three components:

(1) A change to struct inpcb in netinet/in_pcb.h so that it includes a
netmask. It is initialised to INADDR_BROADCAST (i.e. 0xFFFFFFFF) in
in_pcballoc() in netinet/in_pcb.c. [Note that /usr/src/sys/netinet/in_pcb.h
must be installed into /usr/include/netinet/in_pcb.h, and that netstat,
systat, and fstat must all be recompiled so that they know of the change
to the struct inpcb.]

(2) When an address is bound to a socket, in_pcbbind() in
netinet/in_pcb.c checks if the interface address is a netalias, and if
so it copies the netmask into the protocol control block.

(3) When a protocol control block corresponding to an incoming packet
is looked up, in_pcblookup_hash() only checks for a match within the
netmask. (This is normally the same as complete equality because of
the default netmask, but matches any address in the network for a
netbound socket.)

The NETBIND functionality was implemented by me.


>How-To-Repeat:


>Fix:
	

--- /usr/src/sys/conf/options.orig	Mon Feb  8 19:05:55 1999
+++ /usr/src/sys/conf/options	Thu Feb 18 01:40:13 1999
@@ -334,3 +334,7 @@
 
 # Include LKM compatability module
 LKM
+
+# Demon virtual hosting hacks
+NETALIAS
+NETBIND
--- /usr/src/sys/net/if.c.orig	Wed Dec 16 18:30:42 1998
+++ /usr/src/sys/net/if.c	Thu Feb 18 01:39:34 1999
@@ -35,6 +35,7 @@
  */
 
 #include "opt_compat.h"
+#include "opt_netalias.h"
 
 #include <sys/param.h>
 #include <sys/malloc.h>
@@ -193,6 +194,26 @@
 		 ifa = ifa->ifa_link.tqe_next) {
 		if (ifa->ifa_addr->sa_family != addr->sa_family)
 			continue;
+#ifdef NETALIAS
+		if (ifa->ifa_netmask) {
+			register char *argaddr, *ifaddr, *mask, *maskend;
+			/* check to see that the part of the address outside the
+			 * netmask is zero (i.e. it's a network rather than a host)
+			 * and that the addresses are equal within the netmask...
+			 * (see also ifa_ifwithnet() but I prefer these variable names)
+			 */
+			argaddr = addr->sa_data;
+			ifaddr = ifa->ifa_addr->sa_data;
+			mask = ifa->ifa_netmask->sa_data;
+			maskend = (char *)ifa->ifa_netmask + ifa->ifa_netmask->sa_len;
+			for (; mask < maskend; argaddr++, ifaddr++, mask++)
+   				if ((*ifaddr & ~*mask) ||
+				    ((*argaddr ^ *ifaddr) & *mask))
+					goto breakout;
+			return (ifa);
+		}
+		breakout:
+#endif /* NETALIAS */
 		if (equal(addr, ifa->ifa_addr))
 			return (ifa);
 		if ((ifp->if_flags & IFF_BROADCAST) && ifa->ifa_broadaddr &&
--- /usr/src/sys/netinet/in.c.orig	Mon Dec  7 05:41:10 1998
+++ /usr/src/sys/netinet/in.c	Thu Feb 18 01:39:34 1999
@@ -34,6 +34,8 @@
  *	$Id: netalias-netbind.patch,v 1.3 1999/02/18 10:04:13 fanf Exp $
  */
 
+#include "opt_netalias.h"
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/sockio.h>
@@ -374,7 +376,13 @@
 
 	if ((ia->ia_flags & IFA_ROUTE) == 0)
 		return;
+#ifdef NETALIAS
+	if (ifp->if_flags & IFF_POINTOPOINT ||
+	    (ifp->if_flags & IFF_LOOPBACK &&
+	    ia->ia_addr.sin_addr.s_addr & ia->ia_sockmask.sin_addr.s_addr))
+#else
 	if (ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT))
+#endif
 		rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST);
 	else
 		rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0);
@@ -445,6 +453,9 @@
 			htonl(ia->ia_net | ~ ia->ia_netmask);
 	} else if (ifp->if_flags & IFF_LOOPBACK) {
 		ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr;
+#ifdef NETALIAS
+		if (i & ~ia->ia_subnetmask)
+#endif
 		flags |= RTF_HOST;
 	} else if (ifp->if_flags & IFF_POINTOPOINT) {
 		if (ia->ia_dstaddr.sin_family != AF_INET)
--- /usr/src/sys/netinet/ip_icmp.c.orig	Fri Dec  4 04:21:25 1998
+++ /usr/src/sys/netinet/ip_icmp.c	Thu Feb 18 01:39:34 1999
@@ -34,6 +34,8 @@
  *	$Id: netalias-netbind.patch,v 1.3 1999/02/18 10:04:13 fanf Exp $
  */
 
+#include "opt_netalias.h"
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/mbuf.h>
@@ -545,6 +547,11 @@
 	 * to the incoming interface.
 	 */
 	for (ia = in_ifaddrhead.tqh_first; ia; ia = ia->ia_link.tqe_next) {
+#ifdef NETALIAS
+		if (ntohl(IA_SIN(ia)->sin_addr.s_addr) == ia->ia_subnet &&
+		    (ntohl(t.s_addr) & ia->ia_subnetmask) == ia->ia_subnet)
+			goto t_ok;
+#endif /* NETALIAS */
 		if (t.s_addr == IA_SIN(ia)->sin_addr.s_addr)
 			break;
 		if (ia->ia_ifp && (ia->ia_ifp->if_flags & IFF_BROADCAST) &&
@@ -562,6 +569,9 @@
 	if (ia == (struct in_ifaddr *)0)
 		ia = in_ifaddrhead.tqh_first;
 	t = IA_SIN(ia)->sin_addr;
+#ifdef NETALIAS
+	t_ok:
+#endif /* NETALIAS */
 	ip->ip_src = t;
 	ip->ip_ttl = MAXTTL;
 
--- /usr/src/sys/netinet/ip_input.c.orig	Tue Jan 12 12:25:00 1999
+++ /usr/src/sys/netinet/ip_input.c	Thu Feb 18 01:39:34 1999
@@ -42,6 +42,7 @@
 #include "opt_ipdn.h"
 #include "opt_ipdivert.h"
 #include "opt_ipfilter.h"
+#include "opt_netalias.h"
 
 #include <stddef.h>
 
@@ -469,6 +470,17 @@
 	for (ia = TAILQ_FIRST(&in_ifaddrhead); ia;
 					ia = TAILQ_NEXT(ia, ia_link)) {
 #define	satosin(sa)	((struct sockaddr_in *)(sa))
+
+#ifdef NETALIAS
+#ifdef notdef
+	        printf("netalias check: iaddr %lx subnet %lx mask %lx dst %lx\n",
+		       IA_SIN(ia)->sin_addr.s_addr,
+		       ia->ia_subnet, ia->ia_subnetmask, ip->ip_dst.s_addr);
+#endif
+		if (ntohl(IA_SIN(ia)->sin_addr.s_addr) == ia->ia_subnet &&
+		    (ntohl(ip->ip_dst.s_addr) & ia->ia_subnetmask) == ia->ia_subnet)
+			goto ours;
+#endif /* NETALIAS */
 
 		if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr)
 			goto ours;
--- /usr/src/sys/netinet/in_pcb.c.orig	Mon Dec  7 21:58:00 1998
+++ /usr/src/sys/netinet/in_pcb.c	Thu Feb 18 01:42:27 1999
@@ -34,6 +34,8 @@
  *	$Id: netalias-netbind.patch,v 1.3 1999/02/18 10:04:13 fanf Exp $
  */
 
+#include "opt_netbind.h"
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/malloc.h>
@@ -135,6 +137,9 @@
 	bzero((caddr_t)inp, sizeof(*inp));
 	inp->inp_gencnt = ++pcbinfo->ipi_gencnt;
 	inp->inp_pcbinfo = pcbinfo;
+#ifdef NETBIND
+	inp->inp_lmask.s_addr = INADDR_BROADCAST;
+#endif
 	inp->inp_socket = so;
 	LIST_INSERT_HEAD(pcbinfo->listhead, inp, inp_list);
 	pcbinfo->ipi_count++;
@@ -152,6 +157,7 @@
 	unsigned short *lastport;
 	struct sockaddr_in *sin;
 	struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
+	struct in_ifaddr *ia;
 	u_short lport = 0;
 	int wild = 0, reuseport = (so->so_options & SO_REUSEPORT);
 	int error;
@@ -187,8 +193,19 @@
 				reuseport = SO_REUSEADDR|SO_REUSEPORT;
 		} else if (sin->sin_addr.s_addr != INADDR_ANY) {
 			sin->sin_port = 0;		/* yech... */
-			if (ifa_ifwithaddr((struct sockaddr *)sin) == 0)
+			ia = (struct in_ifaddr *)
+				ifa_ifwithaddr((struct sockaddr *)sin);
+			if (ia == 0)
 				return (EADDRNOTAVAIL);
+#ifdef NETBIND
+			/* if this socket is a network address then copy the netmask into the PCB */
+			if (ntohl(IA_SIN(ia)->sin_addr.s_addr) == ia->ia_subnet)
+				inp->inp_lmask = ia->ia_sockmask.sin_addr;
+#ifdef notdef
+			log(LOG_DEBUG, "netbind check: in_pcbbind addr %08x mask %08x\n",
+			    sin->sin_addr.s_addr, inp->inp_lmask.s_addr);
+#endif
+#endif
 		}
 		if (lport) {
 			struct inpcb *t;
@@ -797,8 +814,21 @@
 		for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) {
 			if (inp->inp_faddr.s_addr == INADDR_ANY &&
 			    inp->inp_lport == lport) {
+#ifdef NETBIND
+#ifdef notdef
+				if (inp->inp_lmask.s_addr != INADDR_BROADCAST)
+					log(LOG_DEBUG,
+					    "netbind check: in_pcblookup_hash addr %08x mask %08x options %x\n",
+					    inp->inp_laddr.s_addr, inp->inp_lmask.s_addr, inp->inp_socket->so_options);
+#endif
+				/* allow matches to any address within netmask */
+				if ((inp->inp_laddr.s_addr & inp->inp_lmask.s_addr)
+				    == (laddr.s_addr & inp->inp_lmask.s_addr))
+					return (inp);
+#else
 				if (inp->inp_laddr.s_addr == laddr.s_addr)
 					return (inp);
+#endif /* NETBIND */
 				else if (inp->inp_laddr.s_addr == INADDR_ANY)
 					local_wild = inp;
 			}
--- /usr/src/sys/netinet/in_pcb.h.orig	Fri May 15 00:00:00 1998
+++ /usr/src/sys/netinet/in_pcb.h	Thu Feb 18 01:42:41 1999
@@ -77,6 +77,7 @@
 	LIST_ENTRY(inpcb) inp_portlist;	/* list for this PCB's local port */
 	struct	inpcbport *inp_phd;	/* head of this list */
 	inp_gen_t inp_gencnt;		/* generation count of this instance */
+	struct  in_addr inp_lmask;	/* local netmask (NETBIND) */
 };
 /*
  * The range of the generation count, as used in this implementation,
--- /usr/include/netinet/in_pcb.h.orig	Fri May 15 00:00:00 1998
+++ /usr/include/netinet/in_pcb.h	Thu Feb 18 01:42:41 1999
@@ -77,6 +77,7 @@
 	LIST_ENTRY(inpcb) inp_portlist;	/* list for this PCB's local port */
 	struct	inpcbport *inp_phd;	/* head of this list */
 	inp_gen_t inp_gencnt;		/* generation count of this instance */
+	struct  in_addr inp_lmask;	/* local netmask (NETBIND) */
 };
 /*
  * The range of the generation count, as used in this implementation,

>Release-Note:
>Audit-Trail:
>Unformatted:


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




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