1f325335cSAndrey V. Elsukov /*- 2f325335cSAndrey V. Elsukov * Copyright (c) 2014 Andrey V. Elsukov <ae@FreeBSD.org> 3f325335cSAndrey V. Elsukov * All rights reserved. 4f325335cSAndrey V. Elsukov * 5f325335cSAndrey V. Elsukov * Redistribution and use in source and binary forms, with or without 6f325335cSAndrey V. Elsukov * modification, are permitted provided that the following conditions 7f325335cSAndrey V. Elsukov * are met: 8f325335cSAndrey V. Elsukov * 9f325335cSAndrey V. Elsukov * 1. Redistributions of source code must retain the above copyright 10f325335cSAndrey V. Elsukov * notice, this list of conditions and the following disclaimer. 11f325335cSAndrey V. Elsukov * 2. Redistributions in binary form must reproduce the above copyright 12f325335cSAndrey V. Elsukov * notice, this list of conditions and the following disclaimer in the 13f325335cSAndrey V. Elsukov * documentation and/or other materials provided with the distribution. 14f325335cSAndrey V. Elsukov * 15f325335cSAndrey V. Elsukov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16f325335cSAndrey V. Elsukov * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17f325335cSAndrey V. Elsukov * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18f325335cSAndrey V. Elsukov * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19f325335cSAndrey V. Elsukov * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20f325335cSAndrey V. Elsukov * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21f325335cSAndrey V. Elsukov * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22f325335cSAndrey V. Elsukov * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23f325335cSAndrey V. Elsukov * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24f325335cSAndrey V. Elsukov * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25f325335cSAndrey V. Elsukov */ 26f325335cSAndrey V. Elsukov 27f325335cSAndrey V. Elsukov #include <sys/cdefs.h> 28f325335cSAndrey V. Elsukov __FBSDID("$FreeBSD$"); 29f325335cSAndrey V. Elsukov 30f325335cSAndrey V. Elsukov #include <sys/param.h> 31f325335cSAndrey V. Elsukov #include <sys/jail.h> 32f325335cSAndrey V. Elsukov #include <sys/kernel.h> 33f325335cSAndrey V. Elsukov #include <sys/lock.h> 34f325335cSAndrey V. Elsukov #include <sys/libkern.h> 35f325335cSAndrey V. Elsukov #include <sys/malloc.h> 36f325335cSAndrey V. Elsukov #include <sys/module.h> 37f325335cSAndrey V. Elsukov #include <sys/mbuf.h> 38f325335cSAndrey V. Elsukov #include <sys/priv.h> 39f325335cSAndrey V. Elsukov #include <sys/proc.h> 40f325335cSAndrey V. Elsukov #include <sys/protosw.h> 41f325335cSAndrey V. Elsukov #include <sys/rmlock.h> 42f325335cSAndrey V. Elsukov #include <sys/socket.h> 43f325335cSAndrey V. Elsukov #include <sys/sockio.h> 44f325335cSAndrey V. Elsukov #include <sys/sx.h> 45f325335cSAndrey V. Elsukov #include <sys/sysctl.h> 46f325335cSAndrey V. Elsukov #include <sys/syslog.h> 47f325335cSAndrey V. Elsukov #include <sys/systm.h> 48f325335cSAndrey V. Elsukov 49f325335cSAndrey V. Elsukov #include <net/bpf.h> 50f325335cSAndrey V. Elsukov #include <net/ethernet.h> 51f325335cSAndrey V. Elsukov #include <net/if.h> 52f325335cSAndrey V. Elsukov #include <net/if_var.h> 53f325335cSAndrey V. Elsukov #include <net/if_clone.h> 54f325335cSAndrey V. Elsukov #include <net/if_types.h> 55f325335cSAndrey V. Elsukov #include <net/netisr.h> 56f325335cSAndrey V. Elsukov #include <net/vnet.h> 57eccfe69aSAndrey V. Elsukov #include <net/route.h> 58f325335cSAndrey V. Elsukov 59f325335cSAndrey V. Elsukov #include <netinet/in.h> 60f325335cSAndrey V. Elsukov #include <netinet/in_systm.h> 61f325335cSAndrey V. Elsukov #include <netinet/in_var.h> 62f325335cSAndrey V. Elsukov #include <netinet/ip.h> 63f325335cSAndrey V. Elsukov #include <netinet/ip_var.h> 64f325335cSAndrey V. Elsukov #include <netinet/ip_encap.h> 65f325335cSAndrey V. Elsukov 66f325335cSAndrey V. Elsukov #include <machine/in_cksum.h> 67f325335cSAndrey V. Elsukov #include <security/mac/mac_framework.h> 68f325335cSAndrey V. Elsukov 69*c00bf730SAndrey V. Elsukov #define MEMTU (1500 - sizeof(struct mobhdr)) 70f325335cSAndrey V. Elsukov static const char mename[] = "me"; 71f325335cSAndrey V. Elsukov static MALLOC_DEFINE(M_IFME, mename, "Minimal Encapsulation for IP"); 72f325335cSAndrey V. Elsukov static VNET_DEFINE(struct mtx, me_mtx); 73f325335cSAndrey V. Elsukov #define V_me_mtx VNET(me_mtx) 74f325335cSAndrey V. Elsukov /* Minimal forwarding header RFC 2004 */ 75f325335cSAndrey V. Elsukov struct mobhdr { 76f325335cSAndrey V. Elsukov uint8_t mob_proto; /* protocol */ 77f325335cSAndrey V. Elsukov uint8_t mob_flags; /* flags */ 78f325335cSAndrey V. Elsukov #define MOB_FLAGS_SP 0x80 /* source present */ 79f325335cSAndrey V. Elsukov uint16_t mob_csum; /* header checksum */ 80f325335cSAndrey V. Elsukov struct in_addr mob_dst; /* original destination address */ 81f325335cSAndrey V. Elsukov struct in_addr mob_src; /* original source addr (optional) */ 82f325335cSAndrey V. Elsukov } __packed; 83f325335cSAndrey V. Elsukov 84f325335cSAndrey V. Elsukov struct me_softc { 85f325335cSAndrey V. Elsukov struct ifnet *me_ifp; 86f325335cSAndrey V. Elsukov LIST_ENTRY(me_softc) me_list; 87f325335cSAndrey V. Elsukov struct rmlock me_lock; 88f325335cSAndrey V. Elsukov u_int me_fibnum; 89f325335cSAndrey V. Elsukov const struct encaptab *me_ecookie; 90f325335cSAndrey V. Elsukov struct in_addr me_src; 91f325335cSAndrey V. Elsukov struct in_addr me_dst; 92f325335cSAndrey V. Elsukov }; 93f325335cSAndrey V. Elsukov #define ME2IFP(sc) ((sc)->me_ifp) 94f325335cSAndrey V. Elsukov #define ME_READY(sc) ((sc)->me_src.s_addr != 0) 95f325335cSAndrey V. Elsukov #define ME_LOCK_INIT(sc) rm_init(&(sc)->me_lock, "me softc") 96f325335cSAndrey V. Elsukov #define ME_LOCK_DESTROY(sc) rm_destroy(&(sc)->me_lock) 97f325335cSAndrey V. Elsukov #define ME_RLOCK_TRACKER struct rm_priotracker me_tracker 98f325335cSAndrey V. Elsukov #define ME_RLOCK(sc) rm_rlock(&(sc)->me_lock, &me_tracker) 99f325335cSAndrey V. Elsukov #define ME_RUNLOCK(sc) rm_runlock(&(sc)->me_lock, &me_tracker) 100f325335cSAndrey V. Elsukov #define ME_RLOCK_ASSERT(sc) rm_assert(&(sc)->me_lock, RA_RLOCKED) 101f325335cSAndrey V. Elsukov #define ME_WLOCK(sc) rm_wlock(&(sc)->me_lock) 102f325335cSAndrey V. Elsukov #define ME_WUNLOCK(sc) rm_wunlock(&(sc)->me_lock) 103f325335cSAndrey V. Elsukov #define ME_WLOCK_ASSERT(sc) rm_assert(&(sc)->me_lock, RA_WLOCKED) 104f325335cSAndrey V. Elsukov 105f325335cSAndrey V. Elsukov #define ME_LIST_LOCK_INIT(x) mtx_init(&V_me_mtx, "me_mtx", NULL, MTX_DEF) 106f325335cSAndrey V. Elsukov #define ME_LIST_LOCK_DESTROY(x) mtx_destroy(&V_me_mtx) 107f325335cSAndrey V. Elsukov #define ME_LIST_LOCK(x) mtx_lock(&V_me_mtx) 108f325335cSAndrey V. Elsukov #define ME_LIST_UNLOCK(x) mtx_unlock(&V_me_mtx) 109f325335cSAndrey V. Elsukov 110f325335cSAndrey V. Elsukov static VNET_DEFINE(LIST_HEAD(, me_softc), me_softc_list); 111f325335cSAndrey V. Elsukov #define V_me_softc_list VNET(me_softc_list) 112f325335cSAndrey V. Elsukov static struct sx me_ioctl_sx; 113f325335cSAndrey V. Elsukov SX_SYSINIT(me_ioctl_sx, &me_ioctl_sx, "me_ioctl"); 114f325335cSAndrey V. Elsukov 115f325335cSAndrey V. Elsukov static int me_clone_create(struct if_clone *, int, caddr_t); 116f325335cSAndrey V. Elsukov static void me_clone_destroy(struct ifnet *); 117f325335cSAndrey V. Elsukov static VNET_DEFINE(struct if_clone *, me_cloner); 118f325335cSAndrey V. Elsukov #define V_me_cloner VNET(me_cloner) 119f325335cSAndrey V. Elsukov 120f325335cSAndrey V. Elsukov static void me_qflush(struct ifnet *); 121f325335cSAndrey V. Elsukov static int me_transmit(struct ifnet *, struct mbuf *); 122f325335cSAndrey V. Elsukov static int me_ioctl(struct ifnet *, u_long, caddr_t); 123f325335cSAndrey V. Elsukov static int me_output(struct ifnet *, struct mbuf *, 124f325335cSAndrey V. Elsukov const struct sockaddr *, struct route *); 125f325335cSAndrey V. Elsukov static int me_input(struct mbuf **, int *, int); 126f325335cSAndrey V. Elsukov 127f325335cSAndrey V. Elsukov static int me_set_tunnel(struct ifnet *, struct sockaddr_in *, 128f325335cSAndrey V. Elsukov struct sockaddr_in *); 129f325335cSAndrey V. Elsukov static void me_delete_tunnel(struct ifnet *); 130f325335cSAndrey V. Elsukov 131f325335cSAndrey V. Elsukov SYSCTL_DECL(_net_link); 132f325335cSAndrey V. Elsukov static SYSCTL_NODE(_net_link, IFT_TUNNEL, me, CTLFLAG_RW, 0, 133f325335cSAndrey V. Elsukov "Minimal Encapsulation for IP (RFC 2004)"); 134f325335cSAndrey V. Elsukov #ifndef MAX_ME_NEST 135f325335cSAndrey V. Elsukov #define MAX_ME_NEST 1 136f325335cSAndrey V. Elsukov #endif 137f325335cSAndrey V. Elsukov 138f325335cSAndrey V. Elsukov static VNET_DEFINE(int, max_me_nesting) = MAX_ME_NEST; 139f325335cSAndrey V. Elsukov #define V_max_me_nesting VNET(max_me_nesting) 140f325335cSAndrey V. Elsukov SYSCTL_INT(_net_link_me, OID_AUTO, max_nesting, CTLFLAG_RW | CTLFLAG_VNET, 141f325335cSAndrey V. Elsukov &VNET_NAME(max_me_nesting), 0, "Max nested tunnels"); 142f325335cSAndrey V. Elsukov 143f325335cSAndrey V. Elsukov extern struct domain inetdomain; 144f325335cSAndrey V. Elsukov static const struct protosw in_mobile_protosw = { 145f325335cSAndrey V. Elsukov .pr_type = SOCK_RAW, 146f325335cSAndrey V. Elsukov .pr_domain = &inetdomain, 147f325335cSAndrey V. Elsukov .pr_protocol = IPPROTO_MOBILE, 148f325335cSAndrey V. Elsukov .pr_flags = PR_ATOMIC|PR_ADDR, 149f325335cSAndrey V. Elsukov .pr_input = me_input, 150f325335cSAndrey V. Elsukov .pr_output = rip_output, 151f325335cSAndrey V. Elsukov .pr_ctlinput = rip_ctlinput, 152f325335cSAndrey V. Elsukov .pr_ctloutput = rip_ctloutput, 153f325335cSAndrey V. Elsukov .pr_usrreqs = &rip_usrreqs 154f325335cSAndrey V. Elsukov }; 155f325335cSAndrey V. Elsukov 156f325335cSAndrey V. Elsukov static void 157f325335cSAndrey V. Elsukov vnet_me_init(const void *unused __unused) 158f325335cSAndrey V. Elsukov { 159f325335cSAndrey V. Elsukov LIST_INIT(&V_me_softc_list); 160f325335cSAndrey V. Elsukov ME_LIST_LOCK_INIT(); 161f325335cSAndrey V. Elsukov V_me_cloner = if_clone_simple(mename, me_clone_create, 162f325335cSAndrey V. Elsukov me_clone_destroy, 0); 163f325335cSAndrey V. Elsukov } 164f325335cSAndrey V. Elsukov VNET_SYSINIT(vnet_me_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, 165f325335cSAndrey V. Elsukov vnet_me_init, NULL); 166f325335cSAndrey V. Elsukov 167f325335cSAndrey V. Elsukov static void 168f325335cSAndrey V. Elsukov vnet_me_uninit(const void *unused __unused) 169f325335cSAndrey V. Elsukov { 170f325335cSAndrey V. Elsukov 171f325335cSAndrey V. Elsukov if_clone_detach(V_me_cloner); 172f325335cSAndrey V. Elsukov ME_LIST_LOCK_DESTROY(); 173f325335cSAndrey V. Elsukov } 174f325335cSAndrey V. Elsukov VNET_SYSUNINIT(vnet_me_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, 175f325335cSAndrey V. Elsukov vnet_me_uninit, NULL); 176f325335cSAndrey V. Elsukov 177f325335cSAndrey V. Elsukov static int 178f325335cSAndrey V. Elsukov me_clone_create(struct if_clone *ifc, int unit, caddr_t params) 179f325335cSAndrey V. Elsukov { 180f325335cSAndrey V. Elsukov struct me_softc *sc; 181f325335cSAndrey V. Elsukov 182f325335cSAndrey V. Elsukov sc = malloc(sizeof(struct me_softc), M_IFME, M_WAITOK | M_ZERO); 183f325335cSAndrey V. Elsukov sc->me_fibnum = curthread->td_proc->p_fibnum; 184f325335cSAndrey V. Elsukov ME2IFP(sc) = if_alloc(IFT_TUNNEL); 185f325335cSAndrey V. Elsukov ME_LOCK_INIT(sc); 186f325335cSAndrey V. Elsukov ME2IFP(sc)->if_softc = sc; 187f325335cSAndrey V. Elsukov if_initname(ME2IFP(sc), mename, unit); 188f325335cSAndrey V. Elsukov 189*c00bf730SAndrey V. Elsukov ME2IFP(sc)->if_mtu = MEMTU;; 190f325335cSAndrey V. Elsukov ME2IFP(sc)->if_flags = IFF_POINTOPOINT|IFF_MULTICAST; 191f325335cSAndrey V. Elsukov ME2IFP(sc)->if_output = me_output; 192f325335cSAndrey V. Elsukov ME2IFP(sc)->if_ioctl = me_ioctl; 193f325335cSAndrey V. Elsukov ME2IFP(sc)->if_transmit = me_transmit; 194f325335cSAndrey V. Elsukov ME2IFP(sc)->if_qflush = me_qflush; 195f1aaad0cSHiroki Sato ME2IFP(sc)->if_capabilities |= IFCAP_LINKSTATE; 196f1aaad0cSHiroki Sato ME2IFP(sc)->if_capenable |= IFCAP_LINKSTATE; 197f325335cSAndrey V. Elsukov if_attach(ME2IFP(sc)); 198f325335cSAndrey V. Elsukov bpfattach(ME2IFP(sc), DLT_NULL, sizeof(u_int32_t)); 199f325335cSAndrey V. Elsukov ME_LIST_LOCK(); 200f325335cSAndrey V. Elsukov LIST_INSERT_HEAD(&V_me_softc_list, sc, me_list); 201f325335cSAndrey V. Elsukov ME_LIST_UNLOCK(); 202f325335cSAndrey V. Elsukov return (0); 203f325335cSAndrey V. Elsukov } 204f325335cSAndrey V. Elsukov 205f325335cSAndrey V. Elsukov static void 206f325335cSAndrey V. Elsukov me_clone_destroy(struct ifnet *ifp) 207f325335cSAndrey V. Elsukov { 208f325335cSAndrey V. Elsukov struct me_softc *sc; 209f325335cSAndrey V. Elsukov 210f325335cSAndrey V. Elsukov sx_xlock(&me_ioctl_sx); 211f325335cSAndrey V. Elsukov sc = ifp->if_softc; 212f325335cSAndrey V. Elsukov me_delete_tunnel(ifp); 213f325335cSAndrey V. Elsukov ME_LIST_LOCK(); 214f325335cSAndrey V. Elsukov LIST_REMOVE(sc, me_list); 215f325335cSAndrey V. Elsukov ME_LIST_UNLOCK(); 216f325335cSAndrey V. Elsukov bpfdetach(ifp); 217f325335cSAndrey V. Elsukov if_detach(ifp); 218f325335cSAndrey V. Elsukov ifp->if_softc = NULL; 219f325335cSAndrey V. Elsukov sx_xunlock(&me_ioctl_sx); 220f325335cSAndrey V. Elsukov 221f325335cSAndrey V. Elsukov if_free(ifp); 222f325335cSAndrey V. Elsukov ME_LOCK_DESTROY(sc); 223f325335cSAndrey V. Elsukov free(sc, M_IFME); 224f325335cSAndrey V. Elsukov } 225f325335cSAndrey V. Elsukov 226f325335cSAndrey V. Elsukov static int 227f325335cSAndrey V. Elsukov me_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 228f325335cSAndrey V. Elsukov { 229f325335cSAndrey V. Elsukov ME_RLOCK_TRACKER; 230f325335cSAndrey V. Elsukov struct ifreq *ifr = (struct ifreq *)data; 231f325335cSAndrey V. Elsukov struct sockaddr_in *src, *dst; 232f325335cSAndrey V. Elsukov struct me_softc *sc; 233f325335cSAndrey V. Elsukov int error; 234f325335cSAndrey V. Elsukov 235f325335cSAndrey V. Elsukov switch (cmd) { 236f325335cSAndrey V. Elsukov case SIOCSIFMTU: 237f325335cSAndrey V. Elsukov if (ifr->ifr_mtu < 576) 238f325335cSAndrey V. Elsukov return (EINVAL); 239*c00bf730SAndrey V. Elsukov ifp->if_mtu = ifr->ifr_mtu; 240f325335cSAndrey V. Elsukov return (0); 241f325335cSAndrey V. Elsukov case SIOCSIFADDR: 242f325335cSAndrey V. Elsukov ifp->if_flags |= IFF_UP; 243f325335cSAndrey V. Elsukov case SIOCSIFFLAGS: 244f325335cSAndrey V. Elsukov case SIOCADDMULTI: 245f325335cSAndrey V. Elsukov case SIOCDELMULTI: 246f325335cSAndrey V. Elsukov return (0); 247f325335cSAndrey V. Elsukov } 248f325335cSAndrey V. Elsukov sx_xlock(&me_ioctl_sx); 249f325335cSAndrey V. Elsukov sc = ifp->if_softc; 250f325335cSAndrey V. Elsukov if (sc == NULL) { 251f325335cSAndrey V. Elsukov error = ENXIO; 252f325335cSAndrey V. Elsukov goto end; 253f325335cSAndrey V. Elsukov } 254f325335cSAndrey V. Elsukov error = 0; 255f325335cSAndrey V. Elsukov switch (cmd) { 256f325335cSAndrey V. Elsukov case SIOCSIFPHYADDR: 257f325335cSAndrey V. Elsukov src = (struct sockaddr_in *) 258f325335cSAndrey V. Elsukov &(((struct in_aliasreq *)data)->ifra_addr); 259f325335cSAndrey V. Elsukov dst = (struct sockaddr_in *) 260f325335cSAndrey V. Elsukov &(((struct in_aliasreq *)data)->ifra_dstaddr); 261f325335cSAndrey V. Elsukov if (src->sin_family != dst->sin_family || 262f325335cSAndrey V. Elsukov src->sin_family != AF_INET || 263f325335cSAndrey V. Elsukov src->sin_len != dst->sin_len || 264f325335cSAndrey V. Elsukov src->sin_len != sizeof(struct sockaddr_in)) { 265f325335cSAndrey V. Elsukov error = EINVAL; 266f325335cSAndrey V. Elsukov break; 267f325335cSAndrey V. Elsukov } 268f325335cSAndrey V. Elsukov if (src->sin_addr.s_addr == INADDR_ANY || 269f325335cSAndrey V. Elsukov dst->sin_addr.s_addr == INADDR_ANY) { 270f325335cSAndrey V. Elsukov error = EADDRNOTAVAIL; 271f325335cSAndrey V. Elsukov break; 272f325335cSAndrey V. Elsukov } 273f325335cSAndrey V. Elsukov error = me_set_tunnel(ifp, src, dst); 274f325335cSAndrey V. Elsukov break; 275f325335cSAndrey V. Elsukov case SIOCDIFPHYADDR: 276f325335cSAndrey V. Elsukov me_delete_tunnel(ifp); 277f325335cSAndrey V. Elsukov break; 278f325335cSAndrey V. Elsukov case SIOCGIFPSRCADDR: 279f325335cSAndrey V. Elsukov case SIOCGIFPDSTADDR: 280f325335cSAndrey V. Elsukov ME_RLOCK(sc); 281f325335cSAndrey V. Elsukov if (!ME_READY(sc)) { 282f325335cSAndrey V. Elsukov error = EADDRNOTAVAIL; 283f325335cSAndrey V. Elsukov ME_RUNLOCK(sc); 284f325335cSAndrey V. Elsukov break; 285f325335cSAndrey V. Elsukov } 286f325335cSAndrey V. Elsukov src = (struct sockaddr_in *)&ifr->ifr_addr; 287f325335cSAndrey V. Elsukov memset(src, 0, sizeof(*src)); 288f325335cSAndrey V. Elsukov src->sin_family = AF_INET; 289f325335cSAndrey V. Elsukov src->sin_len = sizeof(*src); 290f325335cSAndrey V. Elsukov switch (cmd) { 291f325335cSAndrey V. Elsukov case SIOCGIFPSRCADDR: 292f325335cSAndrey V. Elsukov src->sin_addr = sc->me_src; 293f325335cSAndrey V. Elsukov break; 294f325335cSAndrey V. Elsukov case SIOCGIFPDSTADDR: 295f325335cSAndrey V. Elsukov src->sin_addr = sc->me_dst; 296f325335cSAndrey V. Elsukov break; 297f325335cSAndrey V. Elsukov } 298f325335cSAndrey V. Elsukov ME_RUNLOCK(sc); 299f325335cSAndrey V. Elsukov error = prison_if(curthread->td_ucred, sintosa(src)); 300f325335cSAndrey V. Elsukov if (error != 0) 301f325335cSAndrey V. Elsukov memset(src, 0, sizeof(*src)); 302f325335cSAndrey V. Elsukov break; 303eccfe69aSAndrey V. Elsukov case SIOCGTUNFIB: 304eccfe69aSAndrey V. Elsukov ifr->ifr_fib = sc->me_fibnum; 305eccfe69aSAndrey V. Elsukov break; 306eccfe69aSAndrey V. Elsukov case SIOCSTUNFIB: 307eccfe69aSAndrey V. Elsukov if ((error = priv_check(curthread, PRIV_NET_GRE)) != 0) 308eccfe69aSAndrey V. Elsukov break; 309eccfe69aSAndrey V. Elsukov if (ifr->ifr_fib >= rt_numfibs) 310eccfe69aSAndrey V. Elsukov error = EINVAL; 311eccfe69aSAndrey V. Elsukov else 312eccfe69aSAndrey V. Elsukov sc->me_fibnum = ifr->ifr_fib; 313eccfe69aSAndrey V. Elsukov break; 314f325335cSAndrey V. Elsukov default: 315f325335cSAndrey V. Elsukov error = EINVAL; 316f325335cSAndrey V. Elsukov break; 317f325335cSAndrey V. Elsukov } 318f325335cSAndrey V. Elsukov end: 319f325335cSAndrey V. Elsukov sx_xunlock(&me_ioctl_sx); 320f325335cSAndrey V. Elsukov return (error); 321f325335cSAndrey V. Elsukov } 322f325335cSAndrey V. Elsukov 323f325335cSAndrey V. Elsukov static int 324f325335cSAndrey V. Elsukov me_encapcheck(const struct mbuf *m, int off, int proto, void *arg) 325f325335cSAndrey V. Elsukov { 326f325335cSAndrey V. Elsukov ME_RLOCK_TRACKER; 327f325335cSAndrey V. Elsukov struct me_softc *sc; 328f325335cSAndrey V. Elsukov struct ip *ip; 329f325335cSAndrey V. Elsukov int ret; 330f325335cSAndrey V. Elsukov 331f325335cSAndrey V. Elsukov sc = (struct me_softc *)arg; 332f325335cSAndrey V. Elsukov if ((ME2IFP(sc)->if_flags & IFF_UP) == 0) 333f325335cSAndrey V. Elsukov return (0); 334f325335cSAndrey V. Elsukov 335f325335cSAndrey V. Elsukov M_ASSERTPKTHDR(m); 336f325335cSAndrey V. Elsukov 337f325335cSAndrey V. Elsukov if (m->m_pkthdr.len < sizeof(struct ip) + sizeof(struct mobhdr) - 338f325335cSAndrey V. Elsukov sizeof(struct in_addr)) 339f325335cSAndrey V. Elsukov return (0); 340f325335cSAndrey V. Elsukov 341f325335cSAndrey V. Elsukov ret = 0; 342f325335cSAndrey V. Elsukov ME_RLOCK(sc); 343f325335cSAndrey V. Elsukov if (ME_READY(sc)) { 344f325335cSAndrey V. Elsukov ip = mtod(m, struct ip *); 345f325335cSAndrey V. Elsukov if (sc->me_src.s_addr == ip->ip_dst.s_addr && 346f325335cSAndrey V. Elsukov sc->me_dst.s_addr == ip->ip_src.s_addr) 347f325335cSAndrey V. Elsukov ret = 32 * 2; 348f325335cSAndrey V. Elsukov } 349f325335cSAndrey V. Elsukov ME_RUNLOCK(sc); 350f325335cSAndrey V. Elsukov return (ret); 351f325335cSAndrey V. Elsukov } 352f325335cSAndrey V. Elsukov 353f325335cSAndrey V. Elsukov static int 354f325335cSAndrey V. Elsukov me_set_tunnel(struct ifnet *ifp, struct sockaddr_in *src, 355f325335cSAndrey V. Elsukov struct sockaddr_in *dst) 356f325335cSAndrey V. Elsukov { 357f325335cSAndrey V. Elsukov struct me_softc *sc, *tsc; 358f325335cSAndrey V. Elsukov 359f325335cSAndrey V. Elsukov sx_assert(&me_ioctl_sx, SA_XLOCKED); 360f325335cSAndrey V. Elsukov ME_LIST_LOCK(); 361f325335cSAndrey V. Elsukov sc = ifp->if_softc; 362f325335cSAndrey V. Elsukov LIST_FOREACH(tsc, &V_me_softc_list, me_list) { 363f325335cSAndrey V. Elsukov if (tsc == sc || !ME_READY(tsc)) 364f325335cSAndrey V. Elsukov continue; 365f325335cSAndrey V. Elsukov if (tsc->me_src.s_addr == src->sin_addr.s_addr && 366f325335cSAndrey V. Elsukov tsc->me_dst.s_addr == dst->sin_addr.s_addr) { 367f325335cSAndrey V. Elsukov ME_LIST_UNLOCK(); 368f325335cSAndrey V. Elsukov return (EADDRNOTAVAIL); 369f325335cSAndrey V. Elsukov } 370f325335cSAndrey V. Elsukov } 371f325335cSAndrey V. Elsukov ME_LIST_UNLOCK(); 372f325335cSAndrey V. Elsukov 373f325335cSAndrey V. Elsukov ME_WLOCK(sc); 374f325335cSAndrey V. Elsukov sc->me_dst = dst->sin_addr; 375f325335cSAndrey V. Elsukov sc->me_src = src->sin_addr; 376f325335cSAndrey V. Elsukov ME_WUNLOCK(sc); 377f325335cSAndrey V. Elsukov 378f325335cSAndrey V. Elsukov if (sc->me_ecookie == NULL) 379f325335cSAndrey V. Elsukov sc->me_ecookie = encap_attach_func(AF_INET, IPPROTO_MOBILE, 380f325335cSAndrey V. Elsukov me_encapcheck, &in_mobile_protosw, sc); 381f1aaad0cSHiroki Sato if (sc->me_ecookie != NULL) { 382f325335cSAndrey V. Elsukov ifp->if_drv_flags |= IFF_DRV_RUNNING; 383f1aaad0cSHiroki Sato if_link_state_change(ifp, LINK_STATE_UP); 384f1aaad0cSHiroki Sato } 385f325335cSAndrey V. Elsukov return (0); 386f325335cSAndrey V. Elsukov } 387f325335cSAndrey V. Elsukov 388f325335cSAndrey V. Elsukov static void 389f325335cSAndrey V. Elsukov me_delete_tunnel(struct ifnet *ifp) 390f325335cSAndrey V. Elsukov { 391f325335cSAndrey V. Elsukov struct me_softc *sc = ifp->if_softc; 392f325335cSAndrey V. Elsukov 393f325335cSAndrey V. Elsukov sx_assert(&me_ioctl_sx, SA_XLOCKED); 394f325335cSAndrey V. Elsukov if (sc->me_ecookie != NULL) 395f325335cSAndrey V. Elsukov encap_detach(sc->me_ecookie); 396f325335cSAndrey V. Elsukov sc->me_ecookie = NULL; 397f325335cSAndrey V. Elsukov ME_WLOCK(sc); 398f325335cSAndrey V. Elsukov sc->me_src.s_addr = 0; 399f325335cSAndrey V. Elsukov sc->me_dst.s_addr = 0; 400f325335cSAndrey V. Elsukov ME_WUNLOCK(sc); 401f325335cSAndrey V. Elsukov ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 402f1aaad0cSHiroki Sato if_link_state_change(ifp, LINK_STATE_DOWN); 403f325335cSAndrey V. Elsukov } 404f325335cSAndrey V. Elsukov 405f325335cSAndrey V. Elsukov static uint16_t 406f325335cSAndrey V. Elsukov me_in_cksum(uint16_t *p, int nwords) 407f325335cSAndrey V. Elsukov { 408f325335cSAndrey V. Elsukov uint32_t sum = 0; 409f325335cSAndrey V. Elsukov 410f325335cSAndrey V. Elsukov while (nwords-- > 0) 411f325335cSAndrey V. Elsukov sum += *p++; 412f325335cSAndrey V. Elsukov sum = (sum >> 16) + (sum & 0xffff); 413f325335cSAndrey V. Elsukov sum += (sum >> 16); 414f325335cSAndrey V. Elsukov return (~sum); 415f325335cSAndrey V. Elsukov } 416f325335cSAndrey V. Elsukov 417f325335cSAndrey V. Elsukov int 418f325335cSAndrey V. Elsukov me_input(struct mbuf **mp, int *offp, int proto) 419f325335cSAndrey V. Elsukov { 420f325335cSAndrey V. Elsukov struct me_softc *sc; 421f325335cSAndrey V. Elsukov struct mobhdr *mh; 422f325335cSAndrey V. Elsukov struct ifnet *ifp; 423f325335cSAndrey V. Elsukov struct mbuf *m; 424f325335cSAndrey V. Elsukov struct ip *ip; 425f325335cSAndrey V. Elsukov int hlen; 426f325335cSAndrey V. Elsukov 427f325335cSAndrey V. Elsukov m = *mp; 428f325335cSAndrey V. Elsukov sc = encap_getarg(m); 429f325335cSAndrey V. Elsukov KASSERT(sc != NULL, ("encap_getarg returned NULL")); 430f325335cSAndrey V. Elsukov 431f325335cSAndrey V. Elsukov ifp = ME2IFP(sc); 432f325335cSAndrey V. Elsukov /* checks for short packets */ 433f325335cSAndrey V. Elsukov hlen = sizeof(struct mobhdr); 434f325335cSAndrey V. Elsukov if (m->m_pkthdr.len < sizeof(struct ip) + hlen) 435f325335cSAndrey V. Elsukov hlen -= sizeof(struct in_addr); 436f325335cSAndrey V. Elsukov if (m->m_len < sizeof(struct ip) + hlen) 437f325335cSAndrey V. Elsukov m = m_pullup(m, sizeof(struct ip) + hlen); 438f325335cSAndrey V. Elsukov if (m == NULL) 439f325335cSAndrey V. Elsukov goto drop; 440f325335cSAndrey V. Elsukov mh = (struct mobhdr *)mtodo(m, sizeof(struct ip)); 441f325335cSAndrey V. Elsukov /* check for wrong flags */ 442f325335cSAndrey V. Elsukov if (mh->mob_flags & (~MOB_FLAGS_SP)) { 443f325335cSAndrey V. Elsukov m_freem(m); 444f325335cSAndrey V. Elsukov goto drop; 445f325335cSAndrey V. Elsukov } 446f325335cSAndrey V. Elsukov if (mh->mob_flags) { 447f325335cSAndrey V. Elsukov if (hlen != sizeof(struct mobhdr)) { 448f325335cSAndrey V. Elsukov m_freem(m); 449f325335cSAndrey V. Elsukov goto drop; 450f325335cSAndrey V. Elsukov } 451f325335cSAndrey V. Elsukov } else 452f325335cSAndrey V. Elsukov hlen = sizeof(struct mobhdr) - sizeof(struct in_addr); 453f325335cSAndrey V. Elsukov /* check mobile header checksum */ 454f325335cSAndrey V. Elsukov if (me_in_cksum((uint16_t *)mh, hlen / sizeof(uint16_t)) != 0) { 455f325335cSAndrey V. Elsukov m_freem(m); 456f325335cSAndrey V. Elsukov goto drop; 457f325335cSAndrey V. Elsukov } 458f325335cSAndrey V. Elsukov #ifdef MAC 459f325335cSAndrey V. Elsukov mac_ifnet_create_mbuf(ifp, m); 460f325335cSAndrey V. Elsukov #endif 461f325335cSAndrey V. Elsukov ip = mtod(m, struct ip *); 462f325335cSAndrey V. Elsukov ip->ip_dst = mh->mob_dst; 463f325335cSAndrey V. Elsukov ip->ip_p = mh->mob_proto; 464f325335cSAndrey V. Elsukov ip->ip_sum = 0; 465f325335cSAndrey V. Elsukov ip->ip_len = htons(m->m_pkthdr.len - hlen); 466f325335cSAndrey V. Elsukov if (mh->mob_flags) 467f325335cSAndrey V. Elsukov ip->ip_src = mh->mob_src; 468f325335cSAndrey V. Elsukov memmove(mtodo(m, hlen), ip, sizeof(struct ip)); 469f325335cSAndrey V. Elsukov m_adj(m, hlen); 470f325335cSAndrey V. Elsukov m_clrprotoflags(m); 471f325335cSAndrey V. Elsukov m->m_pkthdr.rcvif = ifp; 472f325335cSAndrey V. Elsukov m->m_pkthdr.csum_flags |= (CSUM_IP_CHECKED | CSUM_IP_VALID); 473eccfe69aSAndrey V. Elsukov M_SETFIB(m, ifp->if_fib); 474f325335cSAndrey V. Elsukov hlen = AF_INET; 475f325335cSAndrey V. Elsukov BPF_MTAP2(ifp, &hlen, sizeof(hlen), m); 476f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 477f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); 478f325335cSAndrey V. Elsukov if ((ifp->if_flags & IFF_MONITOR) != 0) 479f325335cSAndrey V. Elsukov m_freem(m); 480f325335cSAndrey V. Elsukov else 481f325335cSAndrey V. Elsukov netisr_dispatch(NETISR_IP, m); 482f325335cSAndrey V. Elsukov return (IPPROTO_DONE); 483f325335cSAndrey V. Elsukov drop: 484f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 485f325335cSAndrey V. Elsukov return (IPPROTO_DONE); 486f325335cSAndrey V. Elsukov } 487f325335cSAndrey V. Elsukov 488f325335cSAndrey V. Elsukov #define MTAG_ME 1414491977 489f325335cSAndrey V. Elsukov static int 490f325335cSAndrey V. Elsukov me_check_nesting(struct ifnet *ifp, struct mbuf *m) 491f325335cSAndrey V. Elsukov { 492f325335cSAndrey V. Elsukov struct m_tag *mtag; 493f325335cSAndrey V. Elsukov int count; 494f325335cSAndrey V. Elsukov 495f325335cSAndrey V. Elsukov count = 1; 496f325335cSAndrey V. Elsukov mtag = NULL; 497b347bc3bSAndrey V. Elsukov while ((mtag = m_tag_locate(m, MTAG_ME, 0, mtag)) != NULL) { 498f325335cSAndrey V. Elsukov if (*(struct ifnet **)(mtag + 1) == ifp) { 499f325335cSAndrey V. Elsukov log(LOG_NOTICE, "%s: loop detected\n", ifp->if_xname); 500f325335cSAndrey V. Elsukov return (EIO); 501f325335cSAndrey V. Elsukov } 502f325335cSAndrey V. Elsukov count++; 503f325335cSAndrey V. Elsukov } 504f325335cSAndrey V. Elsukov if (count > V_max_me_nesting) { 505f325335cSAndrey V. Elsukov log(LOG_NOTICE, 506f325335cSAndrey V. Elsukov "%s: if_output recursively called too many times(%d)\n", 507f325335cSAndrey V. Elsukov ifp->if_xname, count); 508f325335cSAndrey V. Elsukov return (EIO); 509f325335cSAndrey V. Elsukov } 510f325335cSAndrey V. Elsukov mtag = m_tag_alloc(MTAG_ME, 0, sizeof(struct ifnet *), M_NOWAIT); 511f325335cSAndrey V. Elsukov if (mtag == NULL) 512f325335cSAndrey V. Elsukov return (ENOMEM); 513f325335cSAndrey V. Elsukov *(struct ifnet **)(mtag + 1) = ifp; 514f325335cSAndrey V. Elsukov m_tag_prepend(m, mtag); 515f325335cSAndrey V. Elsukov return (0); 516f325335cSAndrey V. Elsukov } 517f325335cSAndrey V. Elsukov 518f325335cSAndrey V. Elsukov static int 519f325335cSAndrey V. Elsukov me_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, 520f325335cSAndrey V. Elsukov struct route *ro) 521f325335cSAndrey V. Elsukov { 522f325335cSAndrey V. Elsukov uint32_t af; 523f325335cSAndrey V. Elsukov int error; 524f325335cSAndrey V. Elsukov 525f325335cSAndrey V. Elsukov #ifdef MAC 526f325335cSAndrey V. Elsukov error = mac_ifnet_check_transmit(ifp, m); 527f325335cSAndrey V. Elsukov if (error != 0) 528f325335cSAndrey V. Elsukov goto drop; 529f325335cSAndrey V. Elsukov #endif 530f325335cSAndrey V. Elsukov if ((ifp->if_flags & IFF_MONITOR) != 0 || 531f325335cSAndrey V. Elsukov (ifp->if_flags & IFF_UP) == 0) { 532f325335cSAndrey V. Elsukov error = ENETDOWN; 533f325335cSAndrey V. Elsukov goto drop; 534f325335cSAndrey V. Elsukov } 535f325335cSAndrey V. Elsukov 536f325335cSAndrey V. Elsukov error = me_check_nesting(ifp, m); 537f325335cSAndrey V. Elsukov if (error != 0) 538f325335cSAndrey V. Elsukov goto drop; 539f325335cSAndrey V. Elsukov 540f325335cSAndrey V. Elsukov m->m_flags &= ~(M_BCAST|M_MCAST); 541f325335cSAndrey V. Elsukov if (dst->sa_family == AF_UNSPEC) 542f325335cSAndrey V. Elsukov bcopy(dst->sa_data, &af, sizeof(af)); 543f325335cSAndrey V. Elsukov else 544f325335cSAndrey V. Elsukov af = dst->sa_family; 545f325335cSAndrey V. Elsukov if (af != AF_INET) { 546f325335cSAndrey V. Elsukov error = EAFNOSUPPORT; 547f325335cSAndrey V. Elsukov goto drop; 548f325335cSAndrey V. Elsukov } 549f325335cSAndrey V. Elsukov BPF_MTAP2(ifp, &af, sizeof(af), m); 550f325335cSAndrey V. Elsukov return (ifp->if_transmit(ifp, m)); 551f325335cSAndrey V. Elsukov drop: 552f325335cSAndrey V. Elsukov m_freem(m); 553f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 554f325335cSAndrey V. Elsukov return (error); 555f325335cSAndrey V. Elsukov } 556f325335cSAndrey V. Elsukov 557f325335cSAndrey V. Elsukov static int 558f325335cSAndrey V. Elsukov me_transmit(struct ifnet *ifp, struct mbuf *m) 559f325335cSAndrey V. Elsukov { 560f325335cSAndrey V. Elsukov ME_RLOCK_TRACKER; 561f325335cSAndrey V. Elsukov struct mobhdr mh; 562f325335cSAndrey V. Elsukov struct me_softc *sc; 563f325335cSAndrey V. Elsukov struct ip *ip; 564f325335cSAndrey V. Elsukov int error, hlen, plen; 565f325335cSAndrey V. Elsukov 566f325335cSAndrey V. Elsukov sc = ifp->if_softc; 567f325335cSAndrey V. Elsukov if (sc == NULL) { 568f325335cSAndrey V. Elsukov error = ENETDOWN; 569f325335cSAndrey V. Elsukov m_freem(m); 570f325335cSAndrey V. Elsukov goto drop; 571f325335cSAndrey V. Elsukov } 572f325335cSAndrey V. Elsukov if (m->m_len < sizeof(struct ip)) 573f325335cSAndrey V. Elsukov m = m_pullup(m, sizeof(struct ip)); 574f325335cSAndrey V. Elsukov if (m == NULL) { 575f325335cSAndrey V. Elsukov error = ENOBUFS; 576f325335cSAndrey V. Elsukov goto drop; 577f325335cSAndrey V. Elsukov } 578f325335cSAndrey V. Elsukov ip = mtod(m, struct ip *); 579f325335cSAndrey V. Elsukov /* Fragmented datagramms shouldn't be encapsulated */ 580f325335cSAndrey V. Elsukov if (ip->ip_off & htons(IP_MF | IP_OFFMASK)) { 581f325335cSAndrey V. Elsukov error = EINVAL; 582f325335cSAndrey V. Elsukov m_freem(m); 583f325335cSAndrey V. Elsukov goto drop; 584f325335cSAndrey V. Elsukov } 585f325335cSAndrey V. Elsukov mh.mob_proto = ip->ip_p; 586f325335cSAndrey V. Elsukov mh.mob_src = ip->ip_src; 587f325335cSAndrey V. Elsukov mh.mob_dst = ip->ip_dst; 588f325335cSAndrey V. Elsukov ME_RLOCK(sc); 589f325335cSAndrey V. Elsukov if (!ME_READY(sc)) { 590f325335cSAndrey V. Elsukov ME_RUNLOCK(sc); 591f325335cSAndrey V. Elsukov error = ENETDOWN; 592f325335cSAndrey V. Elsukov m_freem(m); 593f325335cSAndrey V. Elsukov goto drop; 594f325335cSAndrey V. Elsukov } 595f325335cSAndrey V. Elsukov if (in_hosteq(sc->me_src, ip->ip_src)) { 596f325335cSAndrey V. Elsukov hlen = sizeof(struct mobhdr) - sizeof(struct in_addr); 597f325335cSAndrey V. Elsukov mh.mob_flags = 0; 598f325335cSAndrey V. Elsukov } else { 599f325335cSAndrey V. Elsukov hlen = sizeof(struct mobhdr); 600f325335cSAndrey V. Elsukov mh.mob_flags = MOB_FLAGS_SP; 601f325335cSAndrey V. Elsukov } 602f325335cSAndrey V. Elsukov plen = m->m_pkthdr.len; 603f325335cSAndrey V. Elsukov ip->ip_src = sc->me_src; 604f325335cSAndrey V. Elsukov ip->ip_dst = sc->me_dst; 605f325335cSAndrey V. Elsukov M_SETFIB(m, sc->me_fibnum); 606f325335cSAndrey V. Elsukov ME_RUNLOCK(sc); 607f325335cSAndrey V. Elsukov M_PREPEND(m, hlen, M_NOWAIT); 608f325335cSAndrey V. Elsukov if (m == NULL) { 609f325335cSAndrey V. Elsukov error = ENOBUFS; 610f325335cSAndrey V. Elsukov goto drop; 611f325335cSAndrey V. Elsukov } 612f325335cSAndrey V. Elsukov if (m->m_len < sizeof(struct ip) + hlen) 613f325335cSAndrey V. Elsukov m = m_pullup(m, sizeof(struct ip) + hlen); 614f325335cSAndrey V. Elsukov if (m == NULL) { 615f325335cSAndrey V. Elsukov error = ENOBUFS; 616f325335cSAndrey V. Elsukov goto drop; 617f325335cSAndrey V. Elsukov } 618f325335cSAndrey V. Elsukov memmove(mtod(m, void *), mtodo(m, hlen), sizeof(struct ip)); 619f325335cSAndrey V. Elsukov ip = mtod(m, struct ip *); 620f325335cSAndrey V. Elsukov ip->ip_len = htons(m->m_pkthdr.len); 621f325335cSAndrey V. Elsukov ip->ip_p = IPPROTO_MOBILE; 622f325335cSAndrey V. Elsukov ip->ip_sum = 0; 623f325335cSAndrey V. Elsukov mh.mob_csum = 0; 624f325335cSAndrey V. Elsukov mh.mob_csum = me_in_cksum((uint16_t *)&mh, hlen / sizeof(uint16_t)); 625f325335cSAndrey V. Elsukov bcopy(&mh, mtodo(m, sizeof(struct ip)), hlen); 626f325335cSAndrey V. Elsukov error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL); 627f325335cSAndrey V. Elsukov drop: 628f325335cSAndrey V. Elsukov if (error) 629f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 630f325335cSAndrey V. Elsukov else { 631f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 632f325335cSAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_OBYTES, plen); 633f325335cSAndrey V. Elsukov } 634f325335cSAndrey V. Elsukov return (error); 635f325335cSAndrey V. Elsukov } 636f325335cSAndrey V. Elsukov 637f325335cSAndrey V. Elsukov static void 638f325335cSAndrey V. Elsukov me_qflush(struct ifnet *ifp __unused) 639f325335cSAndrey V. Elsukov { 640f325335cSAndrey V. Elsukov 641f325335cSAndrey V. Elsukov } 642f325335cSAndrey V. Elsukov 643f325335cSAndrey V. Elsukov static int 644f325335cSAndrey V. Elsukov memodevent(module_t mod, int type, void *data) 645f325335cSAndrey V. Elsukov { 646f325335cSAndrey V. Elsukov 647f325335cSAndrey V. Elsukov switch (type) { 648f325335cSAndrey V. Elsukov case MOD_LOAD: 649f325335cSAndrey V. Elsukov case MOD_UNLOAD: 650f325335cSAndrey V. Elsukov break; 651f325335cSAndrey V. Elsukov default: 652f325335cSAndrey V. Elsukov return (EOPNOTSUPP); 653f325335cSAndrey V. Elsukov } 654f325335cSAndrey V. Elsukov return (0); 655f325335cSAndrey V. Elsukov } 656f325335cSAndrey V. Elsukov 657f325335cSAndrey V. Elsukov static moduledata_t me_mod = { 658f325335cSAndrey V. Elsukov "if_me", 659f325335cSAndrey V. Elsukov memodevent, 660f325335cSAndrey V. Elsukov 0 661f325335cSAndrey V. Elsukov }; 662f325335cSAndrey V. Elsukov 663f325335cSAndrey V. Elsukov DECLARE_MODULE(if_me, me_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 664f325335cSAndrey V. Elsukov MODULE_VERSION(if_me, 1); 665