Add pair(4), a vether-based virtual Ethernet driver to interconnect
authorreyk <reyk@openbsd.org>
Sat, 24 Oct 2015 10:52:05 +0000 (10:52 +0000)
committerreyk <reyk@openbsd.org>
Sat, 24 Oct 2015 10:52:05 +0000 (10:52 +0000)
rdomains and bridges on the local system.  This can be used to route
through local rdomains, to create L2 devices (like trunks) between
them, and many other things.

Discussed with many, with input from mpi@
OK sthen@ phessler@ yasuoka@ mikeb@

sbin/ifconfig/ifconfig.8
sbin/ifconfig/ifconfig.c
share/man/man4/Makefile
share/man/man4/pair.4 [new file with mode: 0644]
sys/conf/GENERIC
sys/conf/files
sys/net/if.c
sys/net/if.h
sys/net/if_pair.c [new file with mode: 0644]
sys/net/if_var.h
sys/sys/sockio.h

index 2e4897d..c2cab16 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: ifconfig.8,v 1.259 2015/10/24 08:42:57 jmc Exp $
+.\"    $OpenBSD: ifconfig.8,v 1.260 2015/10/24 10:52:05 reyk Exp $
 .\"    $NetBSD: ifconfig.8,v 1.11 1996/01/04 21:27:29 pk Exp $
 .\"     $FreeBSD: ifconfig.8,v 1.16 1998/02/01 07:03:29 steve Exp $
 .\"
@@ -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 6c681e3..4fa5b3e 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ifconfig.c,v 1.303 2015/10/23 01:19:04 dlg Exp $      */
+/*     $OpenBSD: ifconfig.c,v 1.304 2015/10/24 10:52:05 reyk Exp $     */
 /*     $NetBSD: ifconfig.c,v 1.40 1997/10/01 02:19:43 enami Exp $      */
 
 /*
@@ -276,6 +276,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
@@ -492,6 +494,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 },
@@ -2919,6 +2923,7 @@ status(int link, struct sockaddr_dl *sdl, int ls)
        struct ifreq ifrdesc;
        struct ifkalivereq ikardesc;
        char ifdescr[IFDESCRSIZE];
+       char ifname[IF_NAMESIZE];
 #endif
        uint64_t *media_list;
        int i;
@@ -2957,6 +2962,9 @@ status(int link, struct sockaddr_dl *sdl, int ls)
            (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
@@ -5212,6 +5220,29 @@ setinstance(const char *id, int param)
 }
 #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
+
 #ifdef SMALL
 void
 setignore(const char *id, int param)
index 96e901f..938b99f 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: Makefile,v 1.601 2015/10/23 16:44:25 claudio Exp $
+#      $OpenBSD: Makefile,v 1.602 2015/10/24 10:52:05 reyk Exp $
 
 MAN=   aac.4 ac97.4 acphy.4 \
        acpi.4 acpiac.4 acpiasus.4 acpibat.4 acpibtn.4 acpicpu.4 acpidock.4 \
@@ -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 \
diff --git a/share/man/man4/pair.4 b/share/man/man4/pair.4
new file mode 100644 (file)
index 0000000..123b57f
--- /dev/null
@@ -0,0 +1,78 @@
+.\"    $OpenBSD: pair.4,v 1.1 2015/10/24 10:52:05 reyk Exp $
+.\"
+.\" Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
+.\" Copyright (c) 2009 Theo de Raadt <deraadt@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: October 24 2015 $
+.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 deraadt@openbsd.org .
+It has been extended and turned into
+.Xr pair 4
+by
+.An Reyk Floeter Aq Mt reyk@openbsd.org .
+.Sh BUGS
+Like
+.Xr tun 4 ,
+the Ethernet address chosen will be partially random, and may
+occasionally collide with another address.
index a174d9e..22f9826 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: GENERIC,v 1.221 2015/10/23 15:10:53 claudio Exp $
+#      $OpenBSD: GENERIC,v 1.222 2015/10/24 10:52:05 reyk Exp $
 #
 #      Machine-independent option; used by all architectures for their
 #      GENERIC kernel
@@ -96,6 +96,7 @@ pseudo-device gre             # GRE encapsulation interface
 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 04c3ff7..7e56c2e 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: files,v 1.604 2015/10/09 01:17:21 deraadt Exp $
+#      $OpenBSD: files,v 1.605 2015/10/24 10:52:05 reyk Exp $
 #      $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $
 
 #      @(#)files.newconf       7.5 (Berkeley) 5/10/93
@@ -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 91e41f7..2e24c4f 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if.c,v 1.393 2015/10/22 17:48:34 mpi Exp $    */
+/*     $OpenBSD: if.c,v 1.394 2015/10/24 10:52:05 reyk Exp $   */
 /*     $NetBSD: if.c,v 1.35 1996/05/07 05:26:04 thorpej Exp $  */
 
 /*
@@ -1761,6 +1761,7 @@ ifioctl(struct socket *so, u_long cmd, caddr_t data, struct proc *p)
        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, caddr_t data, struct proc *p)
        case SIOCGLIFPHYTTL:
        case SIOCGIFMEDIA:
        case SIOCGVNETID:
+       case SIOCGIFPAIR:
                if (ifp->if_ioctl == 0)
                        return (EOPNOTSUPP);
                error = (*ifp->if_ioctl)(ifp, cmd, data);
index 7608929..7830725 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if.h,v 1.171 2015/10/23 10:22:29 claudio Exp $        */
+/*     $OpenBSD: if.h,v 1.172 2015/10/24 10:52:05 reyk Exp $   */
 /*     $NetBSD: if.h,v 1.23 1996/05/07 02:40:27 thorpej Exp $  */
 
 /*
@@ -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 {
diff --git a/sys/net/if_pair.c b/sys/net/if_pair.c
new file mode 100644 (file)
index 0000000..66f9914
--- /dev/null
@@ -0,0 +1,293 @@
+/*     $OpenBSD: if_pair.c,v 1.1 2015/10/24 10:52:05 reyk Exp $        */
+
+/*
+ * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2009 Theo de Raadt <deraadt@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 b2e542f..af1c8a5 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if_var.h,v 1.49 2015/10/22 17:48:34 mpi Exp $ */
+/*     $OpenBSD: if_var.h,v 1.50 2015/10/24 10:52:05 reyk Exp $        */
 /*     $NetBSD: if.h,v 1.23 1996/05/07 02:40:27 thorpej Exp $  */
 
 /*
@@ -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 09f9dd1..1e9739c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: sockio.h,v 1.61 2015/10/23 01:19:04 dlg Exp $ */
+/*     $OpenBSD: sockio.h,v 1.62 2015/10/24 10:52:05 reyk Exp $        */
 /*     $NetBSD: sockio.h,v 1.5 1995/08/23 00:40:47 thorpej Exp $       */
 
 /*-
 
 #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 */