Configuring Multiple IP Addresses
This material is originally from John Ioannidis (ji@polaris.ctr.columbia.edu)
I have condensed it some and applied some corrections for SunOS 4.1.x
courtesy of Chuck Smoko (csmoko@relay.nswc.navy.mil).
Bob Baggerman (bob@bizweb.com)
12 Jan 94
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
John Ionnidis writes:
This is a topic that comes up once in a while on comp.protocols.tcp-ip
and other newsgroups. The question is, how to get a machine with one
network interface to respond to more than one IP addresses.
I have a solution than might suit you. For my doctoral work (there's
a paper about it in this year's ('91) SIGCOMM, also available for
anonymous FTP from cs.columbia.edu:/pub/ji/sigcomm*.ps.Z), I've
developed what I call the "Virtual Interface" (VIF). To the networking
code, it looks like an interface. It gets ifattach()ed when you open
the /dev/vif* device, and then you can ifconfig it as you like. It
does not have an if_input procedure; it only has an if_output. Packets
that it receives (from higher-level protocols) which have its
IP address, it simply loops back (like any well-behaved if driver).
Packets that it receives that are destined for some other address, it
encapsulates in an encapsulation protocol I call IPIP (IP-within-IP,
protocol number IPPROTO_IPIP == 94), and sends it to another machine
that groks that encapsulation protocol. This feature you won't need,
but here's how to have multiple IP addresses on a machine with a
single real interface:
Let's say your primary interface's IP address is 198.3.2.1, and you
also want it to respond to addresses 198.4.3.2 and 198.5.4.3 (note
that these are three distinct class C addresses in three distinct
class C nets). Here are the ifconfigs:
ifconfig le0 198.3.2.1 up -trailers # config primary interface
ifconfig vif0 198.4.3.2 up # config first virtual interface
route delete net 198.4.3 198.4.3.2 # delete spurious route
route add host 198.4.3.2 198.4.3.2 0 # add route for this i/f
ifconfig vif1 198.5.4.3 up # config second virtual interface
route delete net 198.5.4 198.5.4.3 # delete spurious route
route add host 198.5.4.3 198.5.4.3 0 # add route for this i/f
The route deletes are needed because the ifconfig creates a default
route to the interface's network, which can cause problems; all that's
needed is the (host) route to the interface's address.
Now, get le0's ethernet address (say, 8:0:20:3:2:1), and add the
following static ARP entries:
arp -s 198.4.3.2 8:0:20:3:2:1 pub
arp -s 198.5.4.3 8:0:20:3:2:1 pub
This will cause any ARP requests for the VIF addresses to be replied
with your machine's ethernet address.
Now, make sure your default route is to your segment's gateway,
throught the real interface. FInally, make sure your routers and/or
hosts on the same segment as yours know that 198.4.3.2 and 198.5.4.3
are on that cable.
Here's what you've accomplished.
ARP requests for any of your host's addresses will be replied to with
the host's ethernet address (the real one, because that's what it is,
the virtual ones because of the public static arp entries). Packets
reaching your host with any of these addresses will be accepted by the
ip_input routine because they match the address of one of the host's
interfaces. Packets leaving your host can have any of its addresses
(real and virtual).
The code for vif follows. To use it, put the stuff in netinet/if_vif.c
and netinet/if_vif.h, configure your kernel with the number of
virtual interfaces you want using a line like:
pseudo-device vif4 # Virtual IP interface
in your configuration file, and the line
netinet/if_vif.c optional vif device-driver
in the "files" file. Also, add the appropriate entries in conf.c, so
that you can access the if_attach() routine when you open the device:
-------------------------- conf.c------------------------------------------
add this in the appropriate place in the headers of conf.c:
--------------------
#include "vif.h"
#if NVIF > 0
int vifopen(), vifclose(), vifread(), vifwrite(), vifselect(), vifioctl();
#else
#define vifopen nodev
#define vifclose nodev
#define vifread nodev
#define vifwrite nodev
#define vifselect nodev
#define vifioctl nodev
#endif
--------------------
then, way down in the definition for cdevsw[]:
--------------------
vifopen, vifclose, vifread, vifwrite, /*14*/
vifioctl, nodev, nodev, 0,
0, nodev,
--------------------
Make sure you remember the correct major device number, 14 in this case!
---------------------------------------------------------------------------
Finally, here's the code. It has the tunneling pieces removed (you
need more code to use that anyway), and it comes from a Mach 2.6
kernel; it should compile on any berkeley-derived unix with minor
changes (most likely only in the includes).
---------------------netinet/if_vif.h--------------------------------------
typedef struct
{
struct ifnet vif_if;
struct ifnet *vif_sif; /* slave interface */
int vif_flags;
} vif_softc_t;
#define VIFMTU (1024+512)
---------------------------------------------------------------------------
and
---------------------netinet/if_vif.c--------------------------------------
/*
* Virtual IP interface module.
*/
#include "param.h"
#include "../sys/systm.h"
#include "../sys/mbuf.h"
#include "../sys/socket.h"
#include "../sys/errno.h"
#include "../sys/ioctl.h"
#include "../net/if.h"
#include "../net/netisr.h"
#include "../net/route.h"
#ifdef INET
#include "../netinet/in.h"
#include "../netinet/in_systm.h"
#include "../netinet/in_var.h"
#include "../netinet/ip.h"
#endif
#include "in_pcb.h"
#include "vif.h"
typedef struct
{
struct ifnet vif_if;
struct ifnet *vif_sif; /* slave interface */
int vif_flags;
} vif_softc_t;
#define VIFMTU (1024+512)
vif_softc_t vif_softc[NVIF];
int vifs_inited = 0;
vifattach()
{
register int i;
register struct ifnet *ifp;
int vifoutput(), vififioctl();
for (i=0; i<NVIF; i++)
{
ifp = &vif_softc[i].vif_if;
ifp->if_name = "vif";
ifp->if_unit = i;
ifp->if_mtu = VIFMTU;
ifp->if_flags = IFF_LOOPBACK | IFF_NOARP;
ifp->if_ioctl = vififioctl;
ifp->if_output = vifoutput;
if_attach(ifp);
}
}
vifopen(dev, flag)
int dev, flag;
{
int unit;
if (!vifs_inited)
{
vifattach();
vifs_inited = 1;
printf("vif initialized\n");
}
unit = minor(dev);
if ((unit < 0) || (unit >= NVIF))
{
return ENXIO;
}
return 0;
}
vifclose(dev, flag)
int dev, flag;
{
return 0;
}
vifread()
{
return ENXIO;
}
vifwrite()
{
return ENXIO;
}
vifselect()
{
return ENXIO;
}
vifoutput(ifp, m0, dst)
struct ifnet *ifp;
register struct mbuf *m0;
struct sockaddr *dst;
{
int s;
register struct ifqueue *ifq;
struct mbuf *m;
struct sockaddr_in *din;
if (dst->sa_family != AF_INET)
{
printf("%s%d: can't handle af%d\n",
ifp->if_name, ifp->if_unit,
dst->sa_family);
m_freem(m0);
return (EAFNOSUPPORT);
}
din = (struct sockaddr_in *)dst;
if (din->sin_addr.s_addr == IA_SIN(ifp->if_addrlist)->sin_addr.s_addr)
{
/* printf("%s%d: looping\n", ifp->if_name, ifp->if_unit); */
/*
* Place interface pointer before the data
* for the receiving protocol.
*/
if (m0->m_off <= MMAXOFF &&
m0->m_off >= MMINOFF + sizeof(struct ifnet *)) {
m0->m_off -= sizeof(struct ifnet *);
m0->m_len += sizeof(struct ifnet *);
} else {
MGET(m, M_DONTWAIT, MT_HEADER);
if (m == (struct mbuf *)0)
return (ENOBUFS);
m->m_off = MMINOFF;
m->m_len = sizeof(struct ifnet *);
m->m_next = m0;
m0 = m;
}
*(mtod(m0, struct ifnet **)) = ifp;
s = splimp();
ifp->if_opackets++;
ifq = &ipintrq;
if (IF_QFULL(ifq)) {
IF_DROP(ifq);
m_freem(m0);
splx(s);
return (ENOBUFS);
}
IF_ENQUEUE(ifq, m0);
schednetisr(NETISR_IP);
ifp->if_ipackets++;
splx(s);
return (0);
}
return EHOSTUNREACH;
}
/*
* Process an ioctl request.
*/
/* ARGSUSED */
vififioctl(ifp, cmd, data)
register struct ifnet *ifp;
int cmd;
caddr_t data;
{
int error = 0;
switch (cmd) {
case SIOCSIFADDR:
ifp->if_flags |= IFF_UP;
/*
* Everything else is done at a higher level.
*/
break;
default:
error = EINVAL;
}
return (error);
}
vifioctl(dev, cmd, arg, mode)
dev_t dev;
int cmd;
caddr_t arg;
int mode;
{
int unit;
unit = minor(dev);
if ((unit < 0) || (unit >= NVIF))
return ENXIO;
return EINVAL;
}
----------------------------------------------------------------------------
To use it, compile your kernel, and reboot. Then create the vif
device:
# mknod /dev/vif c 14 0
(or whatever major number it ended up being), and echo something into
it:
# echo > /dev/vif
This will cause the device to be opened, which will if_attach the
interfaces. If you feel like playing with the code, you may want to
kmem_alloc() the vif_softc structrure at open time, and use the minor
number of the device to tell it how many interfaces to create.
Now you can go ahead and ifconfig etc.
I'll be happy to answer minor questions, and hear about success and
failure stories, but I cannot help you if you don't already know how
to hack kernels.
Good luck!
/ji
In-Real-Life: John "Heldenprogrammer" Ioannidis
E-Mail-To: ji@cs.columbia.edu
V-Mail-To: +1 212 854 8120
P-Mail-To: 450 Computer Science \n Columbia University \n New York, NY 10027