Configuring Multiple IP Addresses

### Apache HTTP Server Version 1.3

# 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,
    through 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 structure 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

Note: there is also a commercial-product-turned-freeware called "Col. Patch" which does this as a loadable kernel module for SunOS 4.1.3_U1.

### Apache HTTP Server Version 1.3