> On 6 Nov 2015, at 15:42, David Gwynne <da...@gwynne.id.au> wrote:
> 
> the last assignment i set for the operating systems course i was
> helping with was to write a driver for a virtual network interface
> that implemented the client side of a protocol i made up.
> 
> the protocol was largely inspired by vxlan, but requires some
> negotiation for a client to get a working link with the concentrator
> (server).
> 
> the spec is up at http://www.uq.id.au/dlg/comp3301/assignment3.pdf.
> 
> i wrote a dodgy server for the students to run their code against,
> which is up at https://source.eait.uq.edu.au/viewvc/comp3301-pracs/2015/.

oops, the server is up at 
https://source.eait.uq.edu.au/viewvc/comp3301-eou-server/.

dlg

> 
> my implementation of the driver (so i could test the server) is
> below.
> 
> Index: conf/GENERIC
> ===================================================================
> RCS file: /cvs/src/sys/conf/GENERIC,v
> retrieving revision 1.220
> diff -u -p -r1.220 GENERIC
> --- conf/GENERIC      10 Aug 2015 20:35:36 -0000      1.220
> +++ conf/GENERIC      29 Oct 2015 23:14:30 -0000
> @@ -105,6 +105,7 @@ pseudo-device     tun             # network tunneling o
> pseudo-device vether          # Virtual ethernet
> pseudo-device vxlan           # Virtual extensible LAN
> pseudo-device vlan            # IEEE 802.1Q VLAN
> +pseudo-device        eou
> 
> pseudo-device bio     1       # ioctl multiplexing device
> 
> Index: conf/files
> ===================================================================
> RCS file: /cvs/src/sys/conf/files,v
> retrieving revision 1.603
> diff -u -p -r1.603 files
> --- conf/files        28 Sep 2015 08:32:04 -0000      1.603
> +++ conf/files        29 Oct 2015 23:14:30 -0000
> @@ -541,6 +541,7 @@ pseudo-device trunk: ifnet, ether, ifmed
> pseudo-device mpe: ifnet, ether
> pseudo-device mpw: ifnet, ether
> pseudo-device vether: ifnet, ether
> +pseudo-device eou: ifnet, ether
> pseudo-device pppx: ifnet
> pseudo-device vxlan: ifnet, ether, ifmedia
> 
> @@ -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_eou.c                    eou                     needs-count
> file net/if_pppx.c                    pppx                    needs-count
> file net/if_vxlan.c                   vxlan                   needs-count
> file net80211/ieee80211.c             wlan
> Index: net/if_eou.c
> ===================================================================
> RCS file: net/if_eou.c
> diff -N net/if_eou.c
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ net/if_eou.c      29 Oct 2015 23:14:32 -0000
> @@ -0,0 +1,781 @@
> +/* $OpenBSD$ */
> +
> +/*
> + * Copyright (c) 2009 Theo de Raadt
> + * Copyright (c) 2015 David Gwynne <d...@uq.edu.au>
> + *
> + * 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 <sys/protosw.h>
> +#include <sys/socketvar.h>
> +#include <sys/task.h>
> +#include <sys/timeout.h>
> +
> +#include <net/if.h>
> +#include <net/if_media.h>
> +
> +#include <netinet/in.h>
> +#include <netinet/if_ether.h>
> +
> +#include <crypto/siphash.h>
> +
> +#include "bpfilter.h"
> +#if NBPFILTER > 0
> +#include <net/bpf.h>
> +#endif
> +
> +/*
> + * protocol
> + */
> +struct eou_header {
> +     uint32_t                eou_network;
> +     uint16_t                eou_type;
> +} __packed;
> +
> +#define EOU_T_DATA      0x0000
> +#define EOU_T_PING      0x8000
> +#define EOU_T_PONG      0x8001
> +
> +struct eou_pingpong {
> +     struct eou_header       hdr;
> +     uint16_t                _pad;
> +     uint64_t                utime;
> +     uint8_t                 random[32];
> +     uint8_t                 mac[8];
> +} __packed;
> +
> +#define EOU_PORT     3301
> +
> +/*
> + * driver
> + */
> +struct eou_softc;
> +TAILQ_HEAD(eou_softcs, eou_softc);
> +
> +struct eou_socket {
> +     TAILQ_ENTRY(eou_socket)  eso_entry;
> +     struct eou_softcs        eso_softcs;
> +
> +     struct socket           *eso_so;
> +     struct sockaddr_storage  eso_src;
> +     struct sockaddr_storage  eso_dst;
> +};
> +
> +struct eou_softc {
> +     struct arpcom            sc_ac;
> +     struct ifmedia           sc_media;
> +
> +     uint32_t                 sc_vnetid; /* network byte order */
> +
> +     struct eou_socket       *sc_eso;
> +     TAILQ_ENTRY(eou_softc)   sc_entry;
> +
> +     struct task              sc_send;
> +     struct timeout           sc_tick;
> +     struct task              sc_ping;
> +     struct timeout           sc_dead;
> +};
> +
> +TAILQ_HEAD(eou_sockets, eou_socket) eou_sockets;
> +union {
> +     uint8_t         bytes[SIPHASH_KEY_LENGTH];
> +     SIPHASH_KEY     key;
> +} _eou_key = {
> +     .bytes = {
> +             'c', 'o', 'm', 'p', '3', '3', '0', '1',
> +             'c', 'o', 'm', 'p', '7', '3', '0', '8'
> +     }
> +};
> +#define eou_key _eou_key.key
> +
> +void eouattach(int);
> +
> +int  eou_create(struct if_clone *, int);
> +int  eou_destroy(struct ifnet *);
> +
> +int  eou_ioctl(struct ifnet *, u_long, caddr_t);
> +void eou_start(struct ifnet *);
> +int  eou_media_change(struct ifnet *);
> +void eou_media_status(struct ifnet *, struct ifmediareq *);
> +
> +int  eou_can_run(struct eou_softc *);
> +void eou_run(struct eou_softc *);
> +void eou_stop(struct eou_softc *);
> +void eou_bounce(struct eou_softc *);
> +
> +int  eou_set_tunnel(struct eou_softc *, struct if_laddrreq *);
> +int  eou_get_tunnel(struct eou_softc *, struct if_laddrreq *);
> +int  eou_del_tunnel(struct eou_softc *, struct if_laddrreq *);
> +
> +int  eou_vnetid_used(struct eou_socket *, uint32_t);
> +int  eou_socreate(struct eou_softc *, const struct sockaddr *,
> +         const struct sockaddr *, struct eou_socket **);
> +void eou_soclose(struct eou_softc *, struct eou_socket *);
> +
> +void eou_upcall(struct socket *, caddr_t, int);
> +void eou_input(struct eou_socket *, struct mbuf *);
> +
> +void eou_send(void *);
> +void eou_tick(void *);
> +void eou_ping(void *);
> +void eou_dead(void *);
> +
> +void eou_pong(struct eou_softc *, struct mbuf *);
> +
> +struct if_clone      eou_cloner =
> +    IF_CLONE_INITIALIZER("eou", eou_create, eou_destroy);
> +
> +void
> +eouattach(int n)
> +{
> +     TAILQ_INIT(&eou_sockets);
> +     if_clone_attach(&eou_cloner);
> +}
> +
> +int
> +eou_create(struct if_clone *ifc, int unit)
> +{
> +     struct eou_softc *sc;
> +     struct ifnet *ifp;
> +
> +     sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
> +
> +     task_set(&sc->sc_send, eou_send, sc);
> +     timeout_set(&sc->sc_tick, eou_tick, sc);
> +     task_set(&sc->sc_ping, eou_ping, sc);
> +     timeout_set(&sc->sc_dead, eou_dead, sc);
> +
> +     ifp = &sc->sc_ac.ac_if;
> +     snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d",
> +         ifc->ifc_name, unit);
> +     ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
> +     ether_fakeaddr(ifp);
> +
> +     ifp->if_softc = sc;
> +     ifp->if_ioctl = eou_ioctl;
> +     ifp->if_start = eou_start;
> +     IFQ_SET_MAXLEN(&ifp->if_snd, 1);
> +     IFQ_SET_READY(&ifp->if_snd);
> +
> +     ifp->if_capabilities = IFCAP_VLAN_MTU;
> +     ifp->if_link_state = LINK_STATE_DOWN;
> +
> +     ifmedia_init(&sc->sc_media, 0, eou_media_change, eou_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);
> +     return (0);
> +}
> +
> +int
> +eou_destroy(struct ifnet *ifp)
> +{
> +     struct eou_softc *sc = ifp->if_softc;
> +
> +     timeout_del(&sc->sc_tick);
> +     timeout_del(&sc->sc_dead);
> +
> +     if (sc->sc_eso != NULL)
> +             eou_soclose(sc, sc->sc_eso);
> +
> +     ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY);
> +     ether_ifdetach(ifp);
> +     if_detach(ifp);
> +     free(sc, M_DEVBUF, sizeof(*sc));
> +     return (0);
> +}
> +
> +void
> +eou_upcall(struct socket *so, caddr_t arg, int waitflag)
> +{
> +     struct eou_socket *eso = (struct eou_socket *)arg;
> +     struct mbuf *m;
> +     struct uio uio;
> +     int flags;
> +     int error;
> +
> +     KASSERT(so == eso->eso_so);
> +
> +     do {
> +             uio.uio_resid = 1000000000;
> +             flags = MSG_DONTWAIT;
> +             error = soreceive(so, NULL, &uio, &m, NULL, &flags, 0);
> +             if (m != NULL)
> +                     eou_input(eso, m);
> +
> +             if (error != EWOULDBLOCK &&
> +                 ISSET(so->so_proto->pr_flags, PR_CONNREQUIRED)) {
> +                     /* link down? */
> +                     break;
> +             }
> +     } while (m != NULL);
> +}
> +
> +void
> +eou_input(struct eou_socket *eso, struct mbuf *m)
> +{
> +     struct eou_softc *sc;
> +     struct ifnet *ifp;
> +     struct mbuf *mp;
> +     struct eou_header *eouh;
> +     int offp;
> +     struct mbuf_list ml;
> +
> +     mp = m_pulldown(m, 0, sizeof(*eouh), &offp);
> +     if (mp == NULL)
> +             return;
> +
> +     eouh = (struct eou_header *)(mp->m_data + offp);
> +     TAILQ_FOREACH(sc, &eso->eso_softcs, sc_entry) {
> +             if (eouh->eou_network == sc->sc_vnetid)
> +                     break;
> +     }
> +     if (sc == NULL)
> +             goto done;
> +
> +     switch (bemtoh16(&eouh->eou_type)) {
> +     case EOU_T_DATA:
> +             ifp = &sc->sc_ac.ac_if;
> +             if (!LINK_STATE_IS_UP(ifp->if_link_state))
> +                     goto done;
> +
> +             m_adj(m, sizeof(*eouh));
> +             ml_init(&ml);
> +             ml_enqueue(&ml, m);
> +             if_input(ifp, &ml);
> +             break;
> +
> +     case EOU_T_PONG:
> +             eou_pong(sc, m);
> +             break;
> +     default:
> +             m_freem(m);
> +             break;
> +     }
> +
> +     return;
> +done:
> +     m_freem(m);
> +}
> +
> +/* ARGSUSED */
> +int
> +eou_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
> +{
> +     struct eou_softc *sc = (struct eou_softc *)ifp->if_softc;
> +     struct ifaddr *ifa = (struct ifaddr *)data;
> +     struct ifreq *ifr = (struct ifreq *)data;
> +     uint32_t vnetid;
> +     int error = 0;
> +
> +     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 (eou_can_run(sc))
> +                     eou_run(sc);
> +             else
> +                     eou_stop(sc);
> +             break;
> +
> +     case SIOCADDMULTI:
> +     case SIOCDELMULTI:
> +             break;
> +
> +     case SIOCGIFMEDIA:
> +     case SIOCSIFMEDIA:
> +             error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
> +             break;
> +
> +     case SIOCSLIFPHYADDR:
> +             error = eou_set_tunnel(sc, (struct if_laddrreq *)data);
> +             break;
> +     case SIOCGLIFPHYADDR:
> +             error = eou_get_tunnel(sc, (struct if_laddrreq *)data);
> +             break;
> +     case SIOCDIFPHYADDR:
> +             error = eou_del_tunnel(sc, (struct if_laddrreq *)data);
> +             break;
> +
> +     case SIOCSVNETID:
> +             if ((error = suser(curproc, 0)) != 0)
> +                     break;
> +
> +             vnetid = htobe32(ifr->ifr_vnetid);
> +             if (sc->sc_vnetid == vnetid)
> +                     break;
> +
> +             if (sc->sc_eso != NULL &&
> +                 eou_vnetid_used(sc->sc_eso, vnetid)) {
> +                     error = EINVAL;
> +                     break;
> +             }
> +
> +             sc->sc_vnetid = vnetid;
> +             if (ISSET(ifp->if_flags, IFF_RUNNING))
> +                     eou_bounce(sc);
> +                break;
> +
> +     case SIOCGVNETID:
> +             ifr->ifr_vnetid = betoh32(sc->sc_vnetid);
> +             break;
> +
> +     default:
> +             error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
> +     }
> +
> +     return (error);
> +}
> +
> +int
> +eou_can_run(struct eou_softc *sc)
> +{
> +     struct ifnet *ifp = &sc->sc_ac.ac_if;
> +
> +     return (ISSET(ifp->if_flags, IFF_UP) && sc->sc_eso != NULL);
> +}
> +
> +void
> +eou_bounce(struct eou_softc *sc)
> +{
> +     eou_stop(sc);
> +     eou_run(sc);
> +}
> +
> +void
> +eou_run(struct eou_softc *sc)
> +{
> +     struct ifnet *ifp = &sc->sc_ac.ac_if;
> +
> +     if (ISSET(ifp->if_flags, IFF_RUNNING))
> +             return;
> +
> +     SET(ifp->if_flags, IFF_RUNNING);
> +
> +     eou_tick(sc);
> +}
> +
> +void
> +eou_stop(struct eou_softc *sc)
> +{
> +     struct ifnet *ifp = &sc->sc_ac.ac_if;
> +
> +     if (!ISSET(ifp->if_flags, IFF_RUNNING))
> +             return;
> +
> +     if (LINK_STATE_IS_UP(ifp->if_link_state)) {
> +             ifp->if_link_state = LINK_STATE_DOWN;
> +             if_link_state_change(ifp);
> +     }
> +
> +     timeout_del(&sc->sc_tick);
> +     timeout_del(&sc->sc_dead);
> +
> +     CLR(ifp->if_flags, IFF_RUNNING);
> +}
> +
> +int
> +eou_set_tunnel(struct eou_softc *sc, struct if_laddrreq *req)
> +{
> +     struct eou_socket *eso, *oeso;
> +     struct sockaddr *src = (struct sockaddr *)&req->addr;
> +     struct sockaddr *dst = (struct sockaddr *)&req->dstaddr;
> +     struct sockaddr_in *sin;
> +     struct sockaddr_in6 *sin6;
> +     int error;
> +
> +     /* sa_family and sa_len must be equal */
> +     if (src->sa_family != dst->sa_family || src->sa_len != dst->sa_len)
> +             return (EINVAL);
> +
> +     /* validate */
> +     switch (dst->sa_family) {
> +     case AF_INET:
> +             if (dst->sa_len != sizeof(*sin))
> +                     return (EINVAL);
> +
> +             sin = (struct sockaddr_in *)src;
> +             if (in_nullhost(sin->sin_addr))
> +                     return (EINVAL);
> +
> +             sin = (struct sockaddr_in *)dst;
> +             if (in_nullhost(sin->sin_addr))
> +                     return (EINVAL);
> +             if (sin->sin_port == htons(0))
> +                     sin->sin_port = htons(EOU_PORT);
> +             break;
> +#ifdef INET6
> +     case AF_INET6:
> +             if (dst->sa_len != sizeof(*sin6))
> +                     return (EINVAL);
> +
> +             sin6 = (struct sockaddr_in6 *)src;
> +             if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
> +                     return (EINVAL);
> +
> +             sin6 = (struct sockaddr_in6 *)dst;
> +             if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
> +                     return (EINVAL);
> +             if (sin6->sin6_port == htons(0))
> +                     sin6->sin6_port = htons(EOU_PORT);
> +             break;
> +#endif
> +     default:
> +             return (EAFNOSUPPORT);
> +     }
> +
> +     oeso = sc->sc_eso;
> +     if (oeso != NULL) {
> +             if (memcmp(&req->addr, &oeso->eso_src,
> +                 oeso->eso_src.ss_len) == 0 &&
> +                 memcmp(&req->dstaddr, &oeso->eso_dst,
> +                 oeso->eso_dst.ss_len) == 0)
> +                     return (0);
> +
> +             eou_soclose(sc, oeso);
> +     }
> +
> +     /* let's go */
> +     error = eou_socreate(sc, src, dst, &eso);
> +     if (error != 0)
> +             return (error);
> +
> +     /* commit */
> +     sc->sc_eso = eso;
> +
> +     if (ISSET(sc->sc_ac.ac_if.if_flags, IFF_RUNNING))
> +             eou_bounce(sc);
> +
> +     return (0);
> +}
> +
> +int
> +eou_vnetid_used(struct eou_socket *eso, uint32_t vnetid)
> +{
> +     struct eou_softc *sc;
> +
> +     TAILQ_FOREACH(sc, &eso->eso_softcs, sc_entry) {
> +             if (sc->sc_vnetid == vnetid)
> +                     return (1);
> +     }
> +
> +     return (0);
> +}
> +
> +int
> +eou_socreate(struct eou_softc *sc, const struct sockaddr *src,
> +    const struct sockaddr *dst, struct eou_socket **esop)
> +{
> +     struct eou_socket *eso;
> +     struct socket *so;
> +     struct sockaddr *nam;
> +     struct mbuf *m;
> +     int error;
> +     int s;
> +
> +     TAILQ_FOREACH(eso, &eou_sockets, eso_entry) {
> +             if (memcmp(src, &eso->eso_src, eso->eso_src.ss_len) == 0 &&
> +                 memcmp(dst, &eso->eso_dst, eso->eso_dst.ss_len) == 0) {
> +                     if (eou_vnetid_used(eso, sc->sc_vnetid))
> +                             return (EINVAL);
> +
> +                     goto done;
> +             }
> +     }
> +
> +     eso = malloc(sizeof(*eso), M_IFADDR, M_WAITOK|M_ZERO);
> +
> +     error = socreate(dst->sa_family, &so, SOCK_DGRAM, 0);
> +     if (error != 0)
> +             goto free;
> +
> +     MGET(m, M_WAIT, MT_SONAME);
> +     m->m_len = src->sa_len;
> +     nam = mtod(m, struct sockaddr *);
> +
> +     memcpy(nam, src, src->sa_len);
> +     error = sobind(so, m, curproc);
> +     if (error)
> +             goto close;
> +
> +     memcpy(nam, dst, dst->sa_len);
> +
> +     s = splsoftnet();
> +     error = soconnect(so, m);
> +     if (error != 0)
> +             goto splx;
> +
> +     while (ISSET(so->so_state, SS_ISCONNECTING) && so->so_error == 0) {
> +             error = tsleep(&so->so_timeo, PSOCK | PCATCH, "eoucon", 0);
> +             if (error != 0)
> +                     goto splx;
> +     }
> +     splx(s);
> +
> +     error = so->so_error;
> +     if (error != 0)
> +             goto close;
> +
> +     m_freem(m);
> +
> +     so->so_upcall = eou_upcall;
> +     so->so_upcallarg = (caddr_t)eso;
> +
> +     eso->eso_so = so;
> +     memcpy(&eso->eso_src, src, src->sa_len);
> +     memcpy(&eso->eso_dst, dst, dst->sa_len);
> +     TAILQ_INIT(&eso->eso_softcs);
> +
> +     TAILQ_INSERT_TAIL(&eou_sockets, eso, eso_entry);
> +
> +done:
> +     TAILQ_INSERT_TAIL(&eso->eso_softcs, sc, sc_entry);
> +     *esop = eso;
> +     return (0);
> +
> +splx:
> +     splx(s);
> +close:
> +     m_freem(m);
> +     soclose(so);
> +free:
> +     free(eso, M_IFADDR, sizeof(*eso));
> +     return (error);
> +}
> +
> +static inline int
> +eou_sosend(struct eou_softc *sc, struct mbuf *m)
> +{
> +     KASSERT(sc->sc_eso);
> +
> +     return (sosend(sc->sc_eso->eso_so, NULL, NULL, m, NULL, MSG_DONTWAIT));
> +}
> +
> +void
> +eou_soclose(struct eou_softc *sc, struct eou_socket *eso)
> +{
> +     TAILQ_REMOVE(&eso->eso_softcs, sc, sc_entry);
> +     if (!TAILQ_EMPTY(&eso->eso_softcs))
> +             return;
> +
> +     soclose(eso->eso_so);
> +     TAILQ_REMOVE(&eou_sockets, eso, eso_entry);
> +     free(eso, M_IFADDR, sizeof(*eso));
> +}
> +
> +int
> +eou_get_tunnel(struct eou_softc *sc, struct if_laddrreq *req)
> +{
> +     struct eou_socket *eso;
> +
> +     eso = sc->sc_eso;
> +     if (eso == NULL)
> +             return (EADDRNOTAVAIL);
> +
> +     KASSERT(sizeof(req->addr) == sizeof(eso->eso_src));
> +     KASSERT(sizeof(req->dstaddr) == sizeof(eso->eso_dst));
> +
> +     memcpy(&req->addr, &eso->eso_src, sizeof(req->addr));
> +     memcpy(&req->dstaddr, &eso->eso_dst, sizeof(req->dstaddr));
> +
> +     return (0);
> +}
> +
> +int
> +eou_del_tunnel(struct eou_softc *sc, struct if_laddrreq *req)
> +{
> +     struct ifnet *ifp = &sc->sc_ac.ac_if;
> +     struct eou_socket *eso;
> +
> +     eso = sc->sc_eso;
> +     if (eso == NULL)
> +             return (EADDRNOTAVAIL);
> +
> +     eou_soclose(sc, eso);
> +     sc->sc_eso = NULL;
> +
> +     if (ISSET(ifp->if_flags, IFF_UP)) {
> +             CLR(ifp->if_flags, IFF_RUNNING);
> +             ifp->if_link_state = LINK_STATE_DOWN;
> +             if_link_state_change(ifp);
> +     }
> +
> +     return (0);
> +}
> +
> +int
> +eou_media_change(struct ifnet *ifp)
> +{
> +     return (0);
> +}
> +
> +void
> +eou_media_status(struct ifnet *ifp, struct ifmediareq *imr)
> +{
> +     if (LINK_STATE_IS_UP(ifp->if_link_state)) {
> +             imr->ifm_status = IFM_AVALID | IFM_ACTIVE;
> +             imr->ifm_active = IFM_ETHER | IFM_AUTO | IFM_FDX;
> +     } else {
> +             imr->ifm_status = IFM_AVALID;
> +             imr->ifm_active = IFM_ETHER | IFM_AUTO;
> +     }
> +}
> +
> +void
> +eou_mac(const SIPHASH_KEY *key, const struct eou_pingpong *p, void *digest)
> +{
> +     SIPHASH_CTX ctx;
> +
> +     SipHash24_Init(&ctx, key);
> +     SipHash24_Update(&ctx, &p->hdr.eou_network, sizeof(p->hdr.eou_network));
> +     SipHash24_Update(&ctx, &p->utime, sizeof(p->utime));
> +     SipHash24_Update(&ctx, &p->random, sizeof(p->random));
> +     SipHash24_Final(digest, &ctx);
> +}
> +
> +void
> +eou_tick(void *x)
> +{
> +     struct eou_softc *sc = x;
> +     task_add(systq, &sc->sc_ping);
> +     timeout_add_sec(&sc->sc_tick, 30);
> +}
> +
> +void
> +eou_start(struct ifnet *ifp)
> +{
> +     struct eou_softc *sc = ifp->if_softc;
> +
> +     task_add(systq, &sc->sc_send);
> +}
> +
> +void
> +eou_send(void *scp)
> +{
> +     struct eou_softc *sc = scp;
> +     struct ifnet *ifp = &sc->sc_ac.ac_if;
> +     struct eou_header eouh;
> +     struct mbuf *m;
> +
> +     eouh.eou_network = sc->sc_vnetid;
> +     eouh.eou_type = htobe16(EOU_T_DATA);
> +
> +     for (;;) {
> +             IFQ_DEQUEUE(&ifp->if_snd, m);
> +             if (m == NULL)
> +                     return;
> +
> +#if NBPFILTER > 0
> +             if (ifp->if_bpf)
> +                     bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
> +#endif /* NBPFILTER > 0 */
> +
> +             m = m_prepend(m, sizeof(eouh), M_WAITOK);
> +             if (m == NULL) {
> +                     ifp->if_oerrors++;
> +                     continue;
> +             }
> +
> +             memcpy(mtod(m, void *), &eouh, sizeof(eouh));
> +
> +             if (eou_sosend(sc, m) != 0) {
> +                     ifp->if_oerrors++;
> +                     continue;
> +             }
> +
> +             ifp->if_opackets++;
> +     }
> +}
> +
> +void
> +eou_ping(void *x)
> +{
> +     struct eou_softc *sc = x;
> +     struct mbuf *m;
> +     struct eou_pingpong *ping;
> +
> +     MGETHDR(m, M_WAIT, MT_DATA);
> +     if (max_linkhdr + max_protohdr + sizeof(*ping) > MHLEN)
> +             MCLGET(m, M_WAIT);
> +
> +     m->m_data += max_linkhdr + max_protohdr;
> +     m->m_len = m->m_pkthdr.len = sizeof(*ping);
> +
> +     ping = mtod(m, struct eou_pingpong *);
> +     ping->hdr.eou_network = sc->sc_vnetid;
> +     htobem16(&ping->hdr.eou_type, EOU_T_PING);
> +     ping->_pad = 0;
> +     htobem64(&ping->utime, time_second);
> +     arc4random_buf(ping->random, sizeof(ping->random));
> +     eou_mac(&eou_key, ping, ping->mac);
> +
> +     eou_sosend(sc, m);
> +}
> +
> +void
> +eou_pong(struct eou_softc *sc, struct mbuf *m)
> +{
> +     struct ifnet *ifp = &sc->sc_ac.ac_if;
> +     struct mbuf *mp;
> +     struct eou_pingpong *pong;
> +     int offp;
> +     uint8_t mac[8];
> +     uint64_t utime;
> +
> +     mp = m_pulldown(m, 0, sizeof(*pong), &offp);
> +     if (mp == NULL)
> +             return;
> +
> +     pong = (struct eou_pingpong *)(mp->m_data + offp);
> +     eou_mac(&eou_key, pong, mac);
> +     if (memcmp(mac, pong->mac, sizeof(mac)) != 0)
> +             goto done;
> +
> +     utime = bemtoh64(&pong->utime);
> +     if (utime > time_second + 30 || utime < time_second - 30)
> +             goto done;
> +
> +     /* validation succeeded */
> +     if (!LINK_STATE_IS_UP(ifp->if_link_state)) {
> +             ifp->if_link_state = LINK_STATE_UP;
> +             if_link_state_change(ifp);
> +     }
> +
> +     timeout_add_sec(&sc->sc_dead, 100); 
> +
> +done:
> +     m_freem(m);
> +}
> +
> +void
> +eou_dead(void *x)
> +{
> +     struct eou_softc *sc = x;
> +     struct ifnet *ifp = &sc->sc_ac.ac_if;
> +
> +     ifp->if_link_state = LINK_STATE_DOWN;
> +     if_link_state_change(ifp);
> +}

Reply via email to