version 1.47, 2008/08/26 11:06:59 |
version 1.47.2.3, 2009/04/28 07:37:16 |
|
|
/* $NetBSD$ */ |
/* $NetBSD$ */ |
|
|
/* |
/* |
* Copyright (c) 2003, 2004, 2008 The NetBSD Foundation. |
* Copyright (c) 2003, 2004, 2008, 2009 The NetBSD Foundation. |
* All rights reserved. |
* All rights reserved. |
* |
* |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
Line 37 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 37 __KERNEL_RCSID(0, "$NetBSD$"); |
|
|
|
#if defined(_KERNEL_OPT) |
#if defined(_KERNEL_OPT) |
#include "bpfilter.h" |
#include "bpfilter.h" |
|
#include "opt_modular.h" |
|
#include "opt_compat_netbsd.h" |
#endif |
#endif |
|
|
#include <sys/param.h> |
#include <sys/param.h> |
Line 49 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 51 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include <sys/filedesc.h> |
#include <sys/filedesc.h> |
#include <sys/ksyms.h> |
#include <sys/ksyms.h> |
#include <sys/poll.h> |
#include <sys/poll.h> |
|
#include <sys/proc.h> |
#include <sys/select.h> |
#include <sys/select.h> |
#include <sys/sockio.h> |
#include <sys/sockio.h> |
|
#if defined(COMPAT_40) || defined(MODULAR) |
#include <sys/sysctl.h> |
#include <sys/sysctl.h> |
|
#endif |
#include <sys/kauth.h> |
#include <sys/kauth.h> |
#include <sys/mutex.h> |
#include <sys/mutex.h> |
#include <sys/simplelock.h> |
#include <sys/simplelock.h> |
#include <sys/intr.h> |
#include <sys/intr.h> |
|
#include <sys/stat.h> |
|
|
#include <net/if.h> |
#include <net/if.h> |
#include <net/if_dl.h> |
#include <net/if_dl.h> |
Line 68 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 74 __KERNEL_RCSID(0, "$NetBSD$"); |
|
|
|
#include <compat/sys/sockio.h> |
#include <compat/sys/sockio.h> |
|
|
|
#if defined(COMPAT_40) || defined(MODULAR) |
/* |
/* |
* sysctl node management |
* sysctl node management |
* |
* |
* It's not really possible to use a SYSCTL_SETUP block with |
* It's not really possible to use a SYSCTL_SETUP block with |
* current LKM implementation, so it is easier to just define |
* current module implementation, so it is easier to just define |
* our own function. |
* our own function. |
* |
* |
* The handler function is a "helper" in Andrew Brown's sysctl |
* The handler function is a "helper" in Andrew Brown's sysctl |
Line 85 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 92 __KERNEL_RCSID(0, "$NetBSD$"); |
|
static int tap_node; |
static int tap_node; |
static int tap_sysctl_handler(SYSCTLFN_PROTO); |
static int tap_sysctl_handler(SYSCTLFN_PROTO); |
SYSCTL_SETUP_PROTO(sysctl_tap_setup); |
SYSCTL_SETUP_PROTO(sysctl_tap_setup); |
|
#endif |
|
|
/* |
/* |
* Since we're an Ethernet device, we need the 3 following |
* Since we're an Ethernet device, we need the 3 following |
Line 108 struct tap_softc { |
|
Line 116 struct tap_softc { |
|
kmutex_t sc_rdlock; |
kmutex_t sc_rdlock; |
struct simplelock sc_kqlock; |
struct simplelock sc_kqlock; |
void *sc_sih; |
void *sc_sih; |
|
struct timespec sc_atime; |
|
struct timespec sc_mtime; |
|
struct timespec sc_btime; |
}; |
}; |
|
|
/* autoconf(9) glue */ |
/* autoconf(9) glue */ |
Line 138 static int tap_fops_write(file_t *, off_ |
|
Line 149 static int tap_fops_write(file_t *, off_ |
|
kauth_cred_t, int); |
kauth_cred_t, int); |
static int tap_fops_ioctl(file_t *, u_long, void *); |
static int tap_fops_ioctl(file_t *, u_long, void *); |
static int tap_fops_poll(file_t *, int); |
static int tap_fops_poll(file_t *, int); |
|
static int tap_fops_stat(file_t *, struct stat *); |
static int tap_fops_kqfilter(file_t *, struct knote *); |
static int tap_fops_kqfilter(file_t *, struct knote *); |
|
|
static const struct fileops tap_fileops = { |
static const struct fileops tap_fileops = { |
tap_fops_read, |
.fo_read = tap_fops_read, |
tap_fops_write, |
.fo_write = tap_fops_write, |
tap_fops_ioctl, |
.fo_ioctl = tap_fops_ioctl, |
fnullop_fcntl, |
.fo_fcntl = fnullop_fcntl, |
tap_fops_poll, |
.fo_poll = tap_fops_poll, |
fbadop_stat, |
.fo_stat = tap_fops_stat, |
tap_fops_close, |
.fo_close = tap_fops_close, |
tap_fops_kqfilter, |
.fo_kqfilter = tap_fops_kqfilter, |
|
.fo_drain = fnullop_drain, |
}; |
}; |
|
|
/* Helper for cloning open() */ |
/* Helper for cloning open() */ |
Line 197 static int tap_init(struct ifnet *); |
|
Line 210 static int tap_init(struct ifnet *); |
|
static int tap_ioctl(struct ifnet *, u_long, void *); |
static int tap_ioctl(struct ifnet *, u_long, void *); |
|
|
/* Internal functions */ |
/* Internal functions */ |
|
#if defined(COMPAT_40) || defined(MODULAR) |
static int tap_lifaddr(struct ifnet *, u_long, struct ifaliasreq *); |
static int tap_lifaddr(struct ifnet *, u_long, struct ifaliasreq *); |
|
#endif |
static void tap_softintr(void *); |
static void tap_softintr(void *); |
|
|
/* |
/* |
Line 246 tap_attach(device_t parent, device_t sel |
|
Line 261 tap_attach(device_t parent, device_t sel |
|
{ |
{ |
struct tap_softc *sc = device_private(self); |
struct tap_softc *sc = device_private(self); |
struct ifnet *ifp; |
struct ifnet *ifp; |
|
#if defined(COMPAT_40) || defined(MODULAR) |
const struct sysctlnode *node; |
const struct sysctlnode *node; |
|
int error; |
|
#endif |
uint8_t enaddr[ETHER_ADDR_LEN] = |
uint8_t enaddr[ETHER_ADDR_LEN] = |
{ 0xf2, 0x0b, 0xa4, 0xff, 0xff, 0xff }; |
{ 0xf2, 0x0b, 0xa4, 0xff, 0xff, 0xff }; |
char enaddrstr[3 * ETHER_ADDR_LEN]; |
char enaddrstr[3 * ETHER_ADDR_LEN]; |
struct timeval tv; |
struct timeval tv; |
uint32_t ui; |
uint32_t ui; |
int error; |
|
|
|
sc->sc_dev = self; |
sc->sc_dev = self; |
sc->sc_sih = softint_establish(SOFTINT_CLOCK, tap_softintr, sc); |
sc->sc_sih = softint_establish(SOFTINT_CLOCK, tap_softintr, sc); |
|
getnanotime(&sc->sc_btime); |
|
sc->sc_atime = sc->sc_mtime = sc->sc_btime; |
|
|
|
if (!pmf_device_register(self, NULL, NULL)) |
|
aprint_error_dev(self, "couldn't establish power handler\n"); |
|
|
/* |
/* |
* In order to obtain unique initial Ethernet address on a host, |
* In order to obtain unique initial Ethernet address on a host, |
Line 309 tap_attach(device_t parent, device_t sel |
|
Line 331 tap_attach(device_t parent, device_t sel |
|
|
|
sc->sc_flags = 0; |
sc->sc_flags = 0; |
|
|
|
#if defined(COMPAT_40) || defined(MODULAR) |
/* |
/* |
* Add a sysctl node for that interface. |
* Add a sysctl node for that interface. |
* |
* |
Line 331 tap_attach(device_t parent, device_t sel |
|
Line 354 tap_attach(device_t parent, device_t sel |
|
CTL_EOL)) != 0) |
CTL_EOL)) != 0) |
aprint_error_dev(self, "sysctl_createv returned %d, ignoring\n", |
aprint_error_dev(self, "sysctl_createv returned %d, ignoring\n", |
error); |
error); |
|
#endif |
|
|
/* |
/* |
* Initialize the two locks for the device. |
* Initialize the two locks for the device. |
Line 362 tap_detach(device_t self, int flags) |
|
Line 386 tap_detach(device_t self, int flags) |
|
{ |
{ |
struct tap_softc *sc = device_private(self); |
struct tap_softc *sc = device_private(self); |
struct ifnet *ifp = &sc->sc_ec.ec_if; |
struct ifnet *ifp = &sc->sc_ec.ec_if; |
int error, s; |
#if defined(COMPAT_40) || defined(MODULAR) |
|
int error; |
|
#endif |
|
int s; |
|
|
sc->sc_flags |= TAP_GOING; |
sc->sc_flags |= TAP_GOING; |
s = splnet(); |
s = splnet(); |
Line 372 tap_detach(device_t self, int flags) |
|
Line 399 tap_detach(device_t self, int flags) |
|
|
|
softint_disestablish(sc->sc_sih); |
softint_disestablish(sc->sc_sih); |
|
|
|
#if defined(COMPAT_40) || defined(MODULAR) |
/* |
/* |
* Destroying a single leaf is a very straightforward operation using |
* Destroying a single leaf is a very straightforward operation using |
* sysctl_destroyv. One should be sure to always end the path with |
* sysctl_destroyv. One should be sure to always end the path with |
Line 381 tap_detach(device_t self, int flags) |
|
Line 409 tap_detach(device_t self, int flags) |
|
device_unit(sc->sc_dev), CTL_EOL)) != 0) |
device_unit(sc->sc_dev), CTL_EOL)) != 0) |
aprint_error_dev(self, |
aprint_error_dev(self, |
"sysctl_destroyv returned %d, ignoring\n", error); |
"sysctl_destroyv returned %d, ignoring\n", error); |
|
#endif |
ether_ifdetach(ifp); |
ether_ifdetach(ifp); |
if_detach(ifp); |
if_detach(ifp); |
ifmedia_delete_instance(&sc->sc_im, IFM_INST_ANY); |
ifmedia_delete_instance(&sc->sc_im, IFM_INST_ANY); |
seldestroy(&sc->sc_rsel); |
seldestroy(&sc->sc_rsel); |
mutex_destroy(&sc->sc_rdlock); |
mutex_destroy(&sc->sc_rdlock); |
|
|
|
pmf_device_deregister(self); |
|
|
return (0); |
return (0); |
} |
} |
|
|
Line 516 tap_ioctl(struct ifnet *ifp, u_long cmd, |
|
Line 547 tap_ioctl(struct ifnet *ifp, u_long cmd, |
|
case SIOCGIFMEDIA: |
case SIOCGIFMEDIA: |
error = ifmedia_ioctl(ifp, ifr, &sc->sc_im, cmd); |
error = ifmedia_ioctl(ifp, ifr, &sc->sc_im, cmd); |
break; |
break; |
|
#if defined(COMPAT_40) || defined(MODULAR) |
case SIOCSIFPHYADDR: |
case SIOCSIFPHYADDR: |
error = tap_lifaddr(ifp, cmd, (struct ifaliasreq *)data); |
error = tap_lifaddr(ifp, cmd, (struct ifaliasreq *)data); |
break; |
break; |
|
#endif |
default: |
default: |
error = ether_ioctl(ifp, cmd, data); |
error = ether_ioctl(ifp, cmd, data); |
if (error == ENETRESET) |
if (error == ENETRESET) |
Line 531 tap_ioctl(struct ifnet *ifp, u_long cmd, |
|
Line 564 tap_ioctl(struct ifnet *ifp, u_long cmd, |
|
return (error); |
return (error); |
} |
} |
|
|
|
#if defined(COMPAT_40) || defined(MODULAR) |
/* |
/* |
* Helper function to set Ethernet address. This shouldn't be done there, |
* Helper function to set Ethernet address. This has been replaced by |
* and should actually be available to all Ethernet drivers, real or not. |
* the generic SIOCALIFADDR ioctl on a PF_LINK socket. |
*/ |
*/ |
static int |
static int |
tap_lifaddr(struct ifnet *ifp, u_long cmd, struct ifaliasreq *ifra) |
tap_lifaddr(struct ifnet *ifp, u_long cmd, struct ifaliasreq *ifra) |
{ |
{ |
const struct sockaddr_dl *sdl = satosdl(&ifra->ifra_addr); |
const struct sockaddr *sa = &ifra->ifra_addr; |
|
|
if (sdl->sdl_family != AF_LINK) |
if (sa->sa_family != AF_LINK) |
return (EINVAL); |
return (EINVAL); |
|
|
if_set_sadl(ifp, CLLADDR(sdl), ETHER_ADDR_LEN); |
if_set_sadl(ifp, sa->sa_data, ETHER_ADDR_LEN, false); |
|
|
return (0); |
return (0); |
} |
} |
|
#endif |
|
|
/* |
/* |
* _init() would typically be called when an interface goes up, |
* _init() would typically be called when an interface goes up, |
Line 863 tap_dev_read(int unit, struct uio *uio, |
|
Line 898 tap_dev_read(int unit, struct uio *uio, |
|
if (sc == NULL) |
if (sc == NULL) |
return (ENXIO); |
return (ENXIO); |
|
|
|
getnanotime(&sc->sc_atime); |
|
|
ifp = &sc->sc_ec.ec_if; |
ifp = &sc->sc_ec.ec_if; |
if ((ifp->if_flags & IFF_UP) == 0) |
if ((ifp->if_flags & IFF_UP) == 0) |
return (EHOSTDOWN); |
return (EHOSTDOWN); |
Line 880 tap_dev_read(int unit, struct uio *uio, |
|
Line 917 tap_dev_read(int unit, struct uio *uio, |
|
s = splnet(); |
s = splnet(); |
if (IFQ_IS_EMPTY(&ifp->if_snd)) { |
if (IFQ_IS_EMPTY(&ifp->if_snd)) { |
ifp->if_flags &= ~IFF_OACTIVE; |
ifp->if_flags &= ~IFF_OACTIVE; |
splx(s); |
|
/* |
/* |
* We must release the lock before sleeping, and re-acquire it |
* We must release the lock before sleeping, and re-acquire it |
* after. |
* after. |
Line 890 tap_dev_read(int unit, struct uio *uio, |
|
Line 926 tap_dev_read(int unit, struct uio *uio, |
|
error = EWOULDBLOCK; |
error = EWOULDBLOCK; |
else |
else |
error = tsleep(sc, PSOCK|PCATCH, "tap", 0); |
error = tsleep(sc, PSOCK|PCATCH, "tap", 0); |
|
splx(s); |
|
|
if (error != 0) |
if (error != 0) |
return (error); |
return (error); |
/* The device might have been downed */ |
/* The device might have been downed */ |
|
|
} |
} |
|
|
static int |
static int |
|
tap_fops_stat(file_t *fp, struct stat *st) |
|
{ |
|
int error; |
|
struct tap_softc *sc; |
|
int unit = (uintptr_t)fp->f_data; |
|
|
|
(void)memset(st, 0, sizeof(*st)); |
|
|
|
KERNEL_LOCK(1, NULL); |
|
sc = device_lookup_private(&tap_cd, unit); |
|
if (sc == NULL) { |
|
error = ENXIO; |
|
goto out; |
|
} |
|
|
|
st->st_dev = makedev(cdevsw_lookup_major(&tap_cdevsw), unit); |
|
st->st_atimespec = sc->sc_atime; |
|
st->st_mtimespec = sc->sc_mtime; |
|
st->st_ctimespec = st->st_birthtimespec = sc->sc_btime; |
|
st->st_uid = kauth_cred_geteuid(fp->f_cred); |
|
st->st_gid = kauth_cred_getegid(fp->f_cred); |
|
out: |
|
KERNEL_UNLOCK_ONE(NULL); |
|
return error; |
|
} |
|
|
|
static int |
tap_cdev_write(dev_t dev, struct uio *uio, int flags) |
tap_cdev_write(dev_t dev, struct uio *uio, int flags) |
{ |
{ |
return tap_dev_write(minor(dev), uio, flags); |
return tap_dev_write(minor(dev), uio, flags); |
Line 967 tap_dev_write(int unit, struct uio *uio, |
|
Line 1032 tap_dev_write(int unit, struct uio *uio, |
|
if (sc == NULL) |
if (sc == NULL) |
return (ENXIO); |
return (ENXIO); |
|
|
|
getnanotime(&sc->sc_mtime); |
ifp = &sc->sc_ec.ec_if; |
ifp = &sc->sc_ec.ec_if; |
|
|
/* One write, one packet, that's the rule */ |
/* One write, one packet, that's the rule */ |
Line 1212 tap_kqread(struct knote *kn, long hint) |
|
Line 1278 tap_kqread(struct knote *kn, long hint) |
|
return rv; |
return rv; |
} |
} |
|
|
|
#if defined(COMPAT_40) || defined(MODULAR) |
/* |
/* |
* sysctl management routines |
* sysctl management routines |
* You can set the address of an interface through: |
* You can set the address of an interface through: |
Line 1225 tap_kqread(struct knote *kn, long hint) |
|
Line 1292 tap_kqread(struct knote *kn, long hint) |
|
* (called a link set) which is used at init_sysctl() time to cycle |
* (called a link set) which is used at init_sysctl() time to cycle |
* through all those functions to create the kernel's sysctl tree. |
* through all those functions to create the kernel's sysctl tree. |
* |
* |
* It is not (currently) possible to use link sets in a LKM, so the |
* It is not possible to use link sets in a module, so the |
* easiest is to simply call our own setup routine at load time. |
* easiest is to simply call our own setup routine at load time. |
* |
* |
* In the SYSCTL_SETUP blocks you find in the kernel, nodes have the |
* In the SYSCTL_SETUP blocks you find in the kernel, nodes have the |
Line 1339 tap_sysctl_handler(SYSCTLFN_ARGS) |
|
Line 1406 tap_sysctl_handler(SYSCTLFN_ARGS) |
|
/* Commit change */ |
/* Commit change */ |
if (ether_nonstatic_aton(enaddr, addr) != 0) |
if (ether_nonstatic_aton(enaddr, addr) != 0) |
return (EINVAL); |
return (EINVAL); |
if_set_sadl(ifp, enaddr, ETHER_ADDR_LEN); |
if_set_sadl(ifp, enaddr, ETHER_ADDR_LEN, false); |
return (error); |
return (error); |
} |
} |
|
#endif |