> 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); > +}