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.