Hi,

as requested by Theo and discussed with many, the following diff moves
it into a new driver.  This also allowed to improve the logic of link
states related to the connection (as discussed with Claudio).

The new driver is called pair(4).

        # ifconfig pair1 rdomain 1 10.1.1.1/24 up
        # ifconfig pair2 rdomain 2 10.1.1.2/24 up
        # ifconfig pair1 patch pair2
        # route -T 1 exec ping 10.1.1.2
        # ifconfig pair1 -patch

manpages and documentation can be improved, but I'd like to continue
in the tree if there are no other serious concerns.

OK?

Reyk

Index: sbin/ifconfig/ifconfig.8
===================================================================
RCS file: /cvs/src/sbin/ifconfig/ifconfig.8,v
retrieving revision 1.257
diff -u -p -u -p -r1.257 ifconfig.8
--- sbin/ifconfig/ifconfig.8    6 Oct 2015 17:23:21 -0000       1.257
+++ sbin/ifconfig/ifconfig.8    23 Oct 2015 15:44:28 -0000
@@ -1270,6 +1270,33 @@ The
 is an IPv4 address that will be used to find the nexthop in the MPLS
 network.
 .El
+.\" PAIR
+.Sh PAIR
+.nr nS 1
+.Bk -words
+.Nm ifconfig
+.Ar pair-interface
+.Op Oo Fl Oc Ns Cm patch Ar interface
+.Ek
+.nr nS 0
+.Pp
+The following options are available for a
+.Xr pair 4
+interface:
+.Bl -tag -width Ds
+.It Cm patch Ar interface
+Connect the interface with a second
+.Xr pair 4
+interface.
+Any outgoing packets from the first
+.Ar pair-interface
+will be received by the second
+.Ar interface
+and vice versa.
+This link allows to interconnect two routing domains locally.
+.It Fl patch
+If configured, disconnect the interface pair.
+.El
 .\" PFLOW
 .Sh PFLOW
 .nr nS 1
Index: sbin/ifconfig/ifconfig.c
===================================================================
RCS file: /cvs/src/sbin/ifconfig/ifconfig.c,v
retrieving revision 1.302
diff -u -p -u -p -r1.302 ifconfig.c
--- sbin/ifconfig/ifconfig.c    3 Oct 2015 10:44:23 -0000       1.302
+++ sbin/ifconfig/ifconfig.c    23 Oct 2015 15:44:29 -0000
@@ -275,6 +275,8 @@ void        setifipdst(const char *, int);
 void   setifdesc(const char *, int);
 void   unsetifdesc(const char *, int);
 void   printifhwfeatures(const char *, int);
+void   setpair(const char *, int);
+void   unsetpair(const char *, int);
 #else
 void   setignore(const char *, int);
 #endif
@@ -490,6 +492,8 @@ const struct        cmd {
        { "-descr",     1,              0,              unsetifdesc },
        { "wol",        IFXF_WOL,       0,              setifxflags },
        { "-wol",       -IFXF_WOL,      0,              setifxflags },
+       { "patch",      NEXTARG,        0,              setpair },
+       { "-patch",     1,              0,              unsetpair },
 #else /* SMALL */
        { "powersave",  NEXTARG0,       0,              setignore },
        { "priority",   NEXTARG,        0,              setignore },
@@ -2917,6 +2921,7 @@ status(int link, struct sockaddr_dl *sdl
        struct ifreq ifrdesc;
        struct ifkalivereq ikardesc;
        char ifdescr[IFDESCRSIZE];
+       char ifname[IF_NAMESIZE];
 #endif
        uint64_t *media_list;
        int i;
@@ -2955,6 +2960,9 @@ status(int link, struct sockaddr_dl *sdl
            (ikardesc.ikar_timeo != 0 || ikardesc.ikar_cnt != 0))
                printf("\tkeepalive: timeout %d count %d\n",
                    ikardesc.ikar_timeo, ikardesc.ikar_cnt);
+       if (ioctl(s, SIOCGIFPAIR, &ifrdesc) == 0 && ifrdesc.ifr_index != 0 &&
+           if_indextoname(ifrdesc.ifr_index, ifname) != NULL)
+               printf("\tpatch: %s\n", ifname);
 #endif
        vlan_status();
 #ifndef SMALL
@@ -5199,6 +5207,29 @@ setinstance(const char *id, int param)
        ifr.ifr_rdomainid = rdomainid;
        if (ioctl(s, SIOCSIFRDOMAIN, (caddr_t)&ifr) < 0)
                warn("SIOCSIFRDOMAIN");
+}
+#endif
+
+#ifndef SMALL
+void
+setpair(const char *val, int d)
+{
+       strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+       if ((ifr.ifr_index = if_nametoindex(val)) == 0) {
+               errno = ENOENT;
+               err(1, "patch %s", val);
+       }
+       if (ioctl(s, SIOCSIFPAIR, (caddr_t)&ifr) < 0)
+               warn("SIOCSIFPAIR");
+}
+
+void
+unsetpair(const char *val, int d)
+{
+       ifr.ifr_index = 0;
+       strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+       if (ioctl(s, SIOCSIFPAIR, (caddr_t)&ifr) < 0)
+               warn("SIOCSIFPAIR");
 }
 #endif
 
Index: sys/conf/GENERIC
===================================================================
RCS file: /cvs/src/sys/conf/GENERIC,v
retrieving revision 1.220
diff -u -p -u -p -r1.220 GENERIC
--- sys/conf/GENERIC    10 Aug 2015 20:35:36 -0000      1.220
+++ sys/conf/GENERIC    23 Oct 2015 15:44:31 -0000
@@ -96,6 +96,7 @@ pseudo-device gre             # GRE encapsulation i
 pseudo-device  loop            # network loopback
 pseudo-device  mpe             # MPLS PE interface
 pseudo-device  mpw             # MPLS pseudowire support
+pseudo-device  pair            # Virtual Ethernet interface pair
 pseudo-device  ppp             # PPP
 pseudo-device  pppoe           # PPP over Ethernet (RFC 2516)
 pseudo-device  pppx            # PPP multiplexer
Index: sys/conf/files
===================================================================
RCS file: /cvs/src/sys/conf/files,v
retrieving revision 1.604
diff -u -p -u -p -r1.604 files
--- sys/conf/files      9 Oct 2015 01:17:21 -0000       1.604
+++ sys/conf/files      23 Oct 2015 15:44:31 -0000
@@ -526,6 +526,7 @@ pseudo-device msts: tty
 pseudo-device endrun: tty
 
 pseudo-device loop: ifnet
+pseudo-device pair: ifnet, ether
 pseudo-device ppp: ifnet
 pseudo-device tun: ifnet
 pseudo-device bpfilter: ifnet
@@ -786,6 +787,7 @@ file net/trunklacp.c                        trunk
 file net/if_mpe.c                      mpe                     needs-count
 file net/if_mpw.c                      mpw & bridge            needs-count
 file net/if_vether.c                   vether                  needs-count
+file net/if_pair.c                     pair                    needs-count
 file net/if_pppx.c                     pppx                    needs-count
 file net/if_vxlan.c                    vxlan                   needs-count
 file net80211/ieee80211.c              wlan
Index: sys/net/if.c
===================================================================
RCS file: /cvs/src/sys/net/if.c,v
retrieving revision 1.393
diff -u -p -u -p -r1.393 if.c
--- sys/net/if.c        22 Oct 2015 17:48:34 -0000      1.393
+++ sys/net/if.c        23 Oct 2015 15:44:32 -0000
@@ -1761,6 +1761,7 @@ ifioctl(struct socket *so, u_long cmd, c
        case SIOCDELMULTI:
        case SIOCSIFMEDIA:
        case SIOCSVNETID:
+       case SIOCSIFPAIR:
                if ((error = suser(p, 0)) != 0)
                        return (error);
                /* FALLTHROUGH */
@@ -1771,6 +1772,7 @@ ifioctl(struct socket *so, u_long cmd, c
        case SIOCGLIFPHYTTL:
        case SIOCGIFMEDIA:
        case SIOCGVNETID:
+       case SIOCGIFPAIR:
                if (ifp->if_ioctl == 0)
                        return (EOPNOTSUPP);
                error = (*ifp->if_ioctl)(ifp, cmd, data);
Index: sys/net/if.h
===================================================================
RCS file: /cvs/src/sys/net/if.h,v
retrieving revision 1.171
diff -u -p -u -p -r1.171 if.h
--- sys/net/if.h        23 Oct 2015 10:22:29 -0000      1.171
+++ sys/net/if.h        23 Oct 2015 15:44:32 -0000
@@ -363,6 +363,7 @@ struct      ifreq {
                uint32_t                ifru_vnetid;
                uint64_t                ifru_media;
                caddr_t                 ifru_data;
+               unsigned int            ifru_index;
        } ifr_ifru;
 #define        ifr_addr        ifr_ifru.ifru_addr      /* address */
 #define        ifr_dstaddr     ifr_ifru.ifru_dstaddr   /* other end of p-to-p 
link */
@@ -376,6 +377,7 @@ struct      ifreq {
 #define ifr_vnetid     ifr_ifru.ifru_vnetid    /* Virtual Net Id */
 #define ifr_ttl                ifr_ifru.ifru_metric    /* tunnel TTL 
(overload) */
 #define        ifr_data        ifr_ifru.ifru_data      /* for use by interface 
*/
+#define ifr_index      ifr_ifru.ifru_index     /* interface index */
 };
 
 struct ifaliasreq {
Index: sys/net/if_pair.c
===================================================================
RCS file: sys/net/if_pair.c
diff -N sys/net/if_pair.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ sys/net/if_pair.c   23 Oct 2015 15:44:32 -0000
@@ -0,0 +1,293 @@
+/*     $OpenBSD$       */
+
+/*
+ * Copyright (c) 2015 Reyk Floeter <r...@openbsd.org>
+ * Copyright (c) 2009 Theo de Raadt <dera...@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include "bpfilter.h"
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+void   pairattach(int);
+int    pairioctl(struct ifnet *, u_long, caddr_t);
+void   pairstart(struct ifnet *);
+int    pair_clone_create(struct if_clone *, int);
+int    pair_clone_destroy(struct ifnet *);
+int    pair_media_change(struct ifnet *);
+void   pair_media_status(struct ifnet *, struct ifmediareq *);
+void   pair_link_state(struct ifnet *);
+
+struct pair_softc {
+       struct arpcom           sc_ac;
+       struct ifmedia          sc_media;
+       unsigned int            sc_pairedif;
+};
+
+struct if_clone        pair_cloner =
+    IF_CLONE_INITIALIZER("pair", pair_clone_create, pair_clone_destroy);
+
+int
+pair_media_change(struct ifnet *ifp)
+{
+       return (0);
+}
+
+void
+pair_media_status(struct ifnet *ifp, struct ifmediareq *imr)
+{
+       struct pair_softc       *sc = ifp->if_softc;
+       struct ifnet            *pairedifp;
+
+       imr->ifm_active = IFM_ETHER | IFM_AUTO;
+
+       if ((pairedifp = if_get(sc->sc_pairedif)) == NULL) {
+               imr->ifm_status = 0;
+               return;
+       }
+       if_put(pairedifp);
+
+       imr->ifm_status = IFM_AVALID | IFM_ACTIVE;
+}
+
+void
+pair_link_state(struct ifnet *ifp)
+{
+       struct pair_softc       *sc = ifp->if_softc;
+       struct ifnet            *pairedifp;
+       unsigned int             link_state;
+
+       /* The pair state is determined by the paired interface */
+       if ((pairedifp = if_get(sc->sc_pairedif)) != NULL) {
+               link_state = LINK_STATE_UP;
+               if_put(pairedifp);
+       } else
+               link_state = LINK_STATE_DOWN;
+
+       if (ifp->if_link_state != link_state) {
+               ifp->if_link_state = link_state;
+               if_link_state_change(ifp);
+       }
+}
+
+void
+pairattach(int npair)
+{
+       if_clone_attach(&pair_cloner);
+}
+
+int
+pair_clone_create(struct if_clone *ifc, int unit)
+{
+       struct ifnet            *ifp;
+       struct pair_softc       *sc;
+
+       if ((sc = malloc(sizeof(*sc),
+           M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
+               return (ENOMEM);
+
+       ifp = &sc->sc_ac.ac_if;
+       snprintf(ifp->if_xname, sizeof ifp->if_xname, "pair%d", unit);
+       ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+       ether_fakeaddr(ifp);
+
+       ifp->if_softc = sc;
+       ifp->if_ioctl = pairioctl;
+       ifp->if_start = pairstart;
+       IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+       IFQ_SET_READY(&ifp->if_snd);
+
+       ifp->if_hardmtu = 0xffff;
+       ifp->if_capabilities = IFCAP_VLAN_MTU;
+
+       ifmedia_init(&sc->sc_media, 0, pair_media_change,
+           pair_media_status);
+       ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL);
+       ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
+
+       if_attach(ifp);
+       ether_ifattach(ifp);
+
+       pair_link_state(ifp);
+
+       return (0);
+}
+
+int
+pair_clone_destroy(struct ifnet *ifp)
+{
+       struct pair_softc       *sc = ifp->if_softc;
+       struct ifnet            *pairedifp;
+       struct pair_softc       *dstsc = ifp->if_softc;
+
+       if ((pairedifp = if_get(sc->sc_pairedif)) != NULL) {
+               dstsc = pairedifp->if_softc;
+               dstsc->sc_pairedif = 0;
+               pair_link_state(pairedifp);
+               if_put(pairedifp);
+       }
+
+       ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY);
+       ether_ifdetach(ifp);
+       if_detach(ifp);
+       free(sc, M_DEVBUF, sizeof(*sc));
+
+       return (0);
+}
+
+void
+pairstart(struct ifnet *ifp)
+{
+       struct pair_softc       *sc = (struct pair_softc *)ifp->if_softc;
+       struct mbuf_list         ml = MBUF_LIST_INITIALIZER();
+       struct ifnet            *pairedifp;
+       struct mbuf             *m;
+
+       pairedifp = if_get(sc->sc_pairedif);
+
+       for (;;) {
+               IFQ_DEQUEUE(&ifp->if_snd, m);
+               if (m == NULL)
+                       break;
+
+#if NBPFILTER > 0
+               if (ifp->if_bpf)
+                       bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
+#endif /* NBPFILTER > 0 */
+
+               ifp->if_opackets++;
+               if (pairedifp != NULL)
+                       ml_enqueue(&ml, m);
+               else
+                       m_freem(m);
+       }
+
+       if (pairedifp != NULL) {
+               if_input(pairedifp, &ml);
+               if_put(pairedifp);
+       }
+}
+
+/* ARGSUSED */
+int
+pairioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+       struct pair_softc       *sc = (struct pair_softc *)ifp->if_softc;
+       struct ifaddr           *ifa = (struct ifaddr *)data;
+       struct ifreq            *ifr = (struct ifreq *)data;
+       struct if_clone         *ifc;
+       struct pair_softc       *pairedsc = ifp->if_softc;
+       struct ifnet            *oldifp = NULL, *newifp = NULL;
+       int                      error = 0, unit;
+
+       switch (cmd) {
+       case SIOCSIFADDR:
+               ifp->if_flags |= IFF_UP;
+               if (ifa->ifa_addr->sa_family == AF_INET)
+                       arp_ifinit(&sc->sc_ac, ifa);
+               /* FALLTHROUGH */
+
+       case SIOCSIFFLAGS:
+               if (ifp->if_flags & IFF_UP)
+                       ifp->if_flags |= IFF_RUNNING;
+               else
+                       ifp->if_flags &= ~IFF_RUNNING;
+               break;
+
+       case SIOCADDMULTI:
+       case SIOCDELMULTI:
+               break;
+
+       case SIOCGIFMEDIA:
+       case SIOCSIFMEDIA:
+               error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
+               break;
+
+       case SIOCSIFPAIR:
+               if (sc->sc_pairedif == ifr->ifr_index)
+                       break;
+
+               /* Cannot link to myself */
+               if (ifr->ifr_index == ifp->if_index) {
+                       error = EINVAL;
+                       break;
+               }
+
+               oldifp = if_get(sc->sc_pairedif);
+               newifp = if_get(ifr->ifr_index);
+
+               if (newifp != NULL) {
+                       pairedsc = newifp->if_softc;
+
+                       if (pairedsc->sc_pairedif != 0) {
+                               error = EBUSY;
+                               break;
+                       }
+
+                       /* Only allow pair(4) interfaces for the pair */
+                       if ((ifc = if_clone_lookup(newifp->if_xname,
+                           &unit)) == NULL || strcmp("pair",
+                           ifc->ifc_name) != 0) {
+                               error = ENODEV;
+                               break;
+                       }
+
+                       pairedsc = newifp->if_softc;
+                       pairedsc->sc_pairedif = ifp->if_index;
+                       sc->sc_pairedif = ifr->ifr_index;
+               } else
+                       sc->sc_pairedif = 0;
+
+               if (oldifp != NULL) {
+                       pairedsc = oldifp->if_softc;
+                       pairedsc->sc_pairedif = 0;
+               }
+               break;
+
+       case SIOCGIFPAIR:
+               ifr->ifr_index = sc->sc_pairedif;
+               break;
+
+       default:
+               error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
+       }
+
+       if (newifp != NULL || oldifp != NULL)
+               pair_link_state(ifp);
+       if (oldifp != NULL) {
+               pair_link_state(oldifp);
+               if_put(oldifp);
+       }
+       if (newifp != NULL) {
+               pair_link_state(newifp);
+               if_put(newifp);
+       }
+
+       return (error);
+}
Index: sys/net/if_var.h
===================================================================
RCS file: /cvs/src/sys/net/if_var.h,v
retrieving revision 1.49
diff -u -p -u -p -r1.49 if_var.h
--- sys/net/if_var.h    22 Oct 2015 17:48:34 -0000      1.49
+++ sys/net/if_var.h    23 Oct 2015 15:44:32 -0000
@@ -430,6 +430,9 @@ void        if_clone_detach(struct if_clone *);
 int    if_clone_create(const char *);
 int    if_clone_destroy(const char *);
 
+struct if_clone *
+       if_clone_lookup(const char *, int *);
+
 int     sysctl_mq(int *, u_int, void *, size_t *, void *, size_t,
            struct mbuf_queue *);
 
Index: sys/sys/sockio.h
===================================================================
RCS file: /cvs/src/sys/sys/sockio.h,v
retrieving revision 1.61
diff -u -p -u -p -r1.61 sockio.h
--- sys/sys/sockio.h    23 Oct 2015 01:19:04 -0000      1.61
+++ sys/sys/sockio.h    23 Oct 2015 15:44:32 -0000
@@ -196,6 +196,9 @@
 
 #define SIOCDVNETID    _IOW('i', 175, struct ifreq)    /* del virt net id */
 
+#define SIOCSIFPAIR    _IOW('i', 176, struct ifreq)    /* set paired if */ 
+#define SIOCGIFPAIR    _IOWR('i', 177, struct ifreq)   /* get paired if */
+
 #define        SIOCSVH         _IOWR('i', 245, struct ifreq)   /* set carp 
param */
 #define        SIOCGVH         _IOWR('i', 246, struct ifreq)   /* get carp 
param */
 
Index: share/man/man4/Makefile
===================================================================
RCS file: /cvs/src/share/man/man4/Makefile,v
retrieving revision 1.600
diff -u -p -u -p -r1.600 Makefile
--- share/man/man4/Makefile     30 Sep 2015 13:46:25 -0000      1.600
+++ share/man/man4/Makefile     23 Oct 2015 15:44:32 -0000
@@ -40,9 +40,9 @@ MAN=  aac.4 ac97.4 acphy.4 \
        nsclpcsio.4 nsgphy.4 nsphy.4 nsphyter.4 null.4 nviic.4 nvt.4 \
        oce.4 ohci.4 options.4 onewire.4 oosiop.4 osiop.4 otus.4 \
        owid.4 owctr.4 owsbm.4 \
-       owtemp.4 pcagpio.4 pcaled.4 pcdisplay.4 pchb.4 pci.4 pcib.4 pcfadc.4 \
-       pcfiic.4 pciide.4 pckbc.4 pckbd.4 pcmcia.4 pcn.4 pcppi.4 pcscp.4 \
-       pf.4 pflog.4 pflow.4 pfsync.4 pgt.4 piixpm.4 pim.4 pipex.4 \
+       owtemp.4 pair.4 pcagpio.4 pcaled.4 pcdisplay.4 pchb.4 pci.4 pcib.4 \
+       pcfadc.4 pcfiic.4 pciide.4 pckbc.4 pckbd.4 pcmcia.4 pcn.4 pcppi.4 \
+       pcscp.4 pf.4 pflog.4 pflow.4 pfsync.4 pgt.4 piixpm.4 pim.4 pipex.4 \
        pms.4 ppb.4 ppp.4 pppoe.4 pppx.4 pty.4 puc.4 pvbus.4 pwdog.4 \
        qla.4 qle.4 qlw.4 qsphy.4 radio.4 \
        ral.4 random.4 rdomain.4 rd.4 rdac.4 re.4 rdcphy.4 rgephy.4 ricohrtc.4 \
Index: share/man/man4/pair.4
===================================================================
RCS file: share/man/man4/pair.4
diff -N share/man/man4/pair.4
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ share/man/man4/pair.4       23 Oct 2015 15:44:32 -0000
@@ -0,0 +1,78 @@
+.\"    $OpenBSD$
+.\"
+.\" Copyright (c) 2015 Theo de Raadt <r...@openbsd.org>
+.\" Copyright (c) 2009 Theo de Raadt <dera...@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate$
+.Dt pair 4
+.Os
+.Sh NAME
+.Nm pair
+.Nd virtual Ethernet interface pair
+.Sh SYNOPSIS
+.Cd "pseudo-device pair"
+.Sh DESCRIPTION
+The
+.Nm
+interface simulates a normal Ethernet interface by encapsulating
+standard network frames with an Ethernet header, specifically for use
+in a pair of interfaces that are interconnected with each other.
+.Pp
+To use it, the administrator needs to create two
+.Nm
+interfaces and connect them;
+the interfaces are
+.Sq patched
+as it would be done with physical network ports.
+All packets that are sent on the first interface are received on the
+second interface.
+.Sh EXAMPLES
+Set up a pair of interfaces where each of them is a member of a different
+.Xr rdomain 4 :
+.Bd -literal -offset indent
+# ifconfig pair1 rdomain 1 10.1.1.1/24 up
+# ifconfig pair2 rdomain 2 10.1.1.2/24 up
+# ifconfig pair1 patch pair2
+# route -T 1 exec ping 10.1.1.2
+.Ed
+.Sh SEE ALSO
+.Xr bridge 4 ,
+.Xr inet 4 ,
+.Xr inet6 4 ,
+.Xr rdomain 4 ,
+.Xr hostname.if 5 ,
+.Xr ifconfig 8 ,
+.Xr netstart 8
+.Sh HISTORY
+The
+.Nm
+interface first appeared in
+.Ox 5.9 .
+.Sh AUTHORS
+The
+.Nm
+driver is based on
+.Xr vether 4
+by
+.An Theo de Raadt Aq Mt dera...@openbsd.org .
+It has been extended and turned into
+.Xr pair 4
+by
+.An Reyk Floeter Aq Mt r...@openbsd.org .
+.Sh BUGS
+Like
+.Xr tun 4 ,
+the Ethernet address chosen will be partially random, and may
+occasionally collide with another address.

Reply via email to