Based on http://www.freebsd.org/cgi/query-pr.cgi?pr=kern/12071 Please email any questions or comments to . 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 (updates from PR 12071 marked with ***): The NETALIAS patch has four components: *** It compiles but is not yet tested. (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). *** This should also deal with the INET6 case. (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.) *** We have to re-introduce the old interface address matching loop *** because the hash table doesn't work with NETALIAS, which is a shame. *** The INET6 code uses the routing table to decide whether or not to *** accept a packet, so perhaps we need to do nothing at all here! (3) A similar change to icmp_reflect() in netinet/ip_icmp.c that makes ping work with netalias. *** Same as the comments for (2). (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?) *** This probably isn't necessary for INET6 since it doesn't restrict *** network routes in this way. The first three parts were originally implemented by Ronald Khoo 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: *** Not finished yet. (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.] *** Lots has changed here, since struct inpcb is shared by INET and INET6. *** I've used a C99 compound literal to initialize the netmask in such a *** way that it covers the whole in6_addr which overlaps the in_addr. (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. Index: conf/options =================================================================== RCS file: /home/ncvs/src/sys/conf/options,v retrieving revision 1.375 diff -u -r1.375 options --- conf/options 25 Feb 2003 00:42:40 -0000 1.375 +++ conf/options 18 Mar 2003 23:48:23 -0000 @@ -360,6 +360,9 @@ TCPDEBUG TCP_DROP_SYNFIN opt_tcp_input.h XBONEHACK +# for ifconfig(1)ing or bind(2)ing a network address +NETALIAS +NETBIND # Netgraph(4). Use option NETGRAPH to enable the base netgraph code. # Each netgraph node type can be either be compiled into the kernel Index: net/if.c =================================================================== RCS file: /home/ncvs/src/sys/net/if.c,v retrieving revision 1.158 diff -u -r1.158 if.c --- net/if.c 28 Feb 2003 18:04:42 -0000 1.158 +++ net/if.c 19 Mar 2003 00:28:27 -0000 @@ -38,6 +38,7 @@ #include "opt_inet6.h" #include "opt_inet.h" #include "opt_mac.h" +#include "opt_netalias.h" #include #include @@ -859,6 +860,30 @@ TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != addr->sa_family) continue; +#ifdef NETALIAS + if (ifa->ifa_netmask) { + 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)) goto done; /* IP6 doesn't have broadcast */ Index: netinet/in.c =================================================================== RCS file: /home/ncvs/src/sys/netinet/in.c,v retrieving revision 1.71 diff -u -r1.71 in.c --- netinet/in.c 19 Feb 2003 05:47:33 -0000 1.71 +++ netinet/in.c 19 Mar 2003 00:29:00 -0000 @@ -34,6 +34,8 @@ * $FreeBSD: src/sys/netinet/in.c,v 1.71 2003/02/19 05:47:33 imp Exp $ */ +#include "opt_netalias.h" + #include #include #include @@ -639,7 +641,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); Index: netinet/in_pcb.c =================================================================== RCS file: /home/ncvs/src/sys/netinet/in_pcb.c,v retrieving revision 1.120 diff -u -r1.120 in_pcb.c --- netinet/in_pcb.c 21 Feb 2003 05:28:27 -0000 1.120 +++ netinet/in_pcb.c 19 Mar 2003 03:26:44 -0000 @@ -174,6 +174,11 @@ inp->inp_gencnt = ++pcbinfo->ipi_gencnt; inp->inp_pcbinfo = pcbinfo; inp->inp_socket = so; +#ifdef NETBIND + /* This will cover IPv4 as well. */ + /* We're using a C99 compound literal. */ + inp->in6p_lmask = (struct in6_addr)IN6MASK128; +#endif #ifdef IPSEC error = ipsec_init_policy(so, &inp->inp_sp); if (error != 0) { Index: netinet/in_pcb.h =================================================================== RCS file: /home/ncvs/src/sys/netinet/in_pcb.h,v retrieving revision 1.58 diff -u -r1.58 in_pcb.h --- netinet/in_pcb.h 19 Feb 2003 22:32:42 -0000 1.58 +++ netinet/in_pcb.h 19 Mar 2003 03:01:04 -0000 @@ -86,10 +86,17 @@ struct in_addr_4in6 ie46_local; struct in6_addr ie6_local; } ie_dependladdr; + union { + /* mask for locally bound network address (NETBIND) */ + struct in_addr_4in6 ie46_mask; + struct in6_addr ie6_mask; + } ie_dependlmask; #define ie_faddr ie_dependfaddr.ie46_foreign.ia46_addr4 #define ie_laddr ie_dependladdr.ie46_local.ia46_addr4 +#define ie_lmask ie_dependlmask.ie46_mask.ia46_addr4 #define ie6_faddr ie_dependfaddr.ie6_foreign #define ie6_laddr ie_dependladdr.ie6_local +#define ie6_lmask ie_dependlmask.ie6_mask }; /* @@ -115,9 +122,11 @@ #define inc_lport inc_ie.ie_lport #define inc_faddr inc_ie.ie_faddr #define inc_laddr inc_ie.ie_laddr +#define inc_lmask inc_ie.ie_lmask #define inc_route inc_dependroute.inc4_route #define inc6_faddr inc_ie.ie6_faddr #define inc6_laddr inc_ie.ie6_laddr +#define inc6_lmask inc_ie.ie6_lmask #define inc6_route inc_dependroute.inc6_route struct icmp6_filter; @@ -155,6 +164,7 @@ #define inp_lport inp_inc.inc_lport #define inp_faddr inp_inc.inc_faddr #define inp_laddr inp_inc.inc_laddr +#define inp_lmask inp_inc.inc_lmask #define inp_route inp_inc.inc_route #define inp_ip_tos inp_depend4.inp4_ip_tos #define inp_options inp_depend4.inp4_options @@ -181,6 +191,7 @@ #define in6p_faddr inp_inc.inc6_faddr #define in6p_laddr inp_inc.inc6_laddr +#define in6p_lmask inp_inc.inc6_lmask #define in6p_route inp_inc.inc6_route #define in6p_ip6_hlim inp_depend6.inp6_hlim #define in6p_hops inp_depend6.inp6_hops /* default hop limit */ Index: netinet/ip_icmp.c =================================================================== RCS file: /home/ncvs/src/sys/netinet/ip_icmp.c,v retrieving revision 1.77 diff -u -r1.77 ip_icmp.c --- netinet/ip_icmp.c 19 Feb 2003 05:47:34 -0000 1.77 +++ netinet/ip_icmp.c 19 Mar 2003 02:12:46 -0000 @@ -36,6 +36,7 @@ #include "opt_ipsec.h" #include "opt_mac.h" +#include "opt_netalias.h" #include #include @@ -622,6 +623,23 @@ LIST_FOREACH(ia, INADDR_HASH(t.s_addr), ia_hash) if (t.s_addr == IA_SIN(ia)->sin_addr.s_addr) goto match; +#ifdef NETALIAS + /* + * We can't use the hash table to do a quick match of a network alias + * address, but if we are using them it'll be to reduce the number of + * aliases we have on the system so this loop won't be too bad. + */ + TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) { +#ifdef DIAGNOSTIC + printf("netalias icmp: iaddr %lx subnet %lx mask %lx dst %lx\n", + IA_SIN(ia)->sin_addr.s_addr, + ia->ia_subnet, ia->ia_subnetmask, t.s_addr); +#endif + if (ntohl(IA_SIN(ia)->sin_addr.s_addr) == ia->ia_subnet && + (ntohl(t.s_addr) & ia->ia_subnetmask) == ia->ia_subnet) + goto match; + } +#endif /* NETALIAS */ if (m->m_pkthdr.rcvif != NULL && m->m_pkthdr.rcvif->if_flags & IFF_BROADCAST) { TAILQ_FOREACH(ifa, &m->m_pkthdr.rcvif->if_addrhead, ifa_link) { @@ -630,7 +648,7 @@ ia = ifatoia(ifa); if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr == t.s_addr) - goto match; + goto broadmatch; } } ia = ip_rtaddr(ip->ip_dst, ro); @@ -640,8 +658,9 @@ icmpstat.icps_noroute++; goto done; } -match: +broadmatch: t = IA_SIN(ia)->sin_addr; +match: ip->ip_src = t; ip->ip_ttl = ip_defttl; Index: netinet/ip_input.c =================================================================== RCS file: /home/ncvs/src/sys/netinet/ip_input.c,v retrieving revision 1.229 diff -u -r1.229 ip_input.c --- netinet/ip_input.c 26 Feb 2003 07:28:35 -0000 1.229 +++ netinet/ip_input.c 19 Mar 2003 02:10:48 -0000 @@ -42,6 +42,7 @@ #include "opt_ipstealth.h" #include "opt_ipsec.h" #include "opt_mac.h" +#include "opt_netalias.h" #include "opt_pfil_hooks.h" #include "opt_random_ip_id.h" @@ -581,6 +582,23 @@ (!checkif || ia->ia_ifp == m->m_pkthdr.rcvif)) goto ours; } +#ifdef NETALIAS + /* + * We can't use the hash table to do a quick match of a network alias + * address, but if we are using them it'll be to reduce the number of + * aliases we have on the system so this loop won't be too bad. + */ + TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) { +#ifdef DIAGNOSTIC + printf("netalias input: iaddr %lx subnet %lx mask %lx dst %lx\n", + IA_SIN(ia)->sin_addr.s_addr, + ia->ia_subnet, ia->ia_subnetmask, pkt_dst.s_addr); +#endif + if (ntohl(IA_SIN(ia)->sin_addr.s_addr) == ia->ia_subnet && + (ntohl(pkt_dst.s_addr) & ia->ia_subnetmask) == ia->ia_subnet) + goto ours; + } +#endif /* NETALIAS */ /* * Check for broadcast addresses. *