#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <sys/kern_event.h>
#include <sys/mcache.h>
#include <net/bpf.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/if_dl.h>
#include <net/if_ether.h>
#include <net/if_types.h>
#include <net/if_6lowpan_var.h>
#include <net/frame802154.h>
#include <net/sixxlowpan.h>
#include <libkern/OSAtomic.h>
#include <net/dlil.h>
#include <net/kpi_interface.h>
#include <net/kpi_protocol.h>
#include <kern/locks.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/if_ether.h>
#endif
#include <net/if_media.h>
#include <net/multicast_list.h>
#include <net/ether_if_module.h>
#define SIXLOWPANNAME "6lowpan"
struct ifnet *p_6lowpan_ifnet = NULL;
extern errno_t nd6_lookup_ipv6(ifnet_t interface,
const struct sockaddr_in6 *ip6_dest, struct sockaddr_dl *ll_dest,
size_t ll_dest_len, route_t hint, mbuf_t packet);
typedef int (bpf_callback_func)(struct ifnet *, struct mbuf *);
typedef int (if_set_bpf_tap_func)(struct ifnet *ifp, int mode, bpf_callback_func * func);
static __inline__ lck_grp_t *
my_lck_grp_alloc_init(const char * grp_name)
{
lck_grp_t * grp;
lck_grp_attr_t * grp_attrs;
grp_attrs = lck_grp_attr_alloc_init();
grp = lck_grp_alloc_init(grp_name, grp_attrs);
lck_grp_attr_free(grp_attrs);
return grp;
}
static __inline__ lck_mtx_t *
my_lck_mtx_alloc_init(lck_grp_t * lck_grp)
{
lck_attr_t * lck_attrs;
lck_mtx_t * lck_mtx;
lck_attrs = lck_attr_alloc_init();
lck_mtx = lck_mtx_alloc_init(lck_grp, lck_attrs);
lck_attr_free(lck_attrs);
return lck_mtx;
}
static lck_mtx_t *sixlowpan_lck_mtx;
static __inline__ void
sixlowpan_lock_init(void)
{
lck_grp_t *lck_grp;
lck_grp = my_lck_grp_alloc_init("if_6lowpan");
sixlowpan_lck_mtx = my_lck_mtx_alloc_init(lck_grp);
}
static __inline__ void
sixlowpan_assert_lock_held(void)
{
lck_mtx_assert(sixlowpan_lck_mtx, LCK_MTX_ASSERT_OWNED);
return;
}
#ifdef __UNUSED__
static __inline__ void
sixlowpan_assert_lock_not_held(void)
{
lck_mtx_assert(sixlowpan_lck_mtx, LCK_MTX_ASSERT_NOTOWNED);
return;
}
#endif
static __inline__ void
sixlowpan_lock(void)
{
lck_mtx_lock(sixlowpan_lck_mtx);
return;
}
static __inline__ void
sixlowpan_unlock(void)
{
lck_mtx_unlock(sixlowpan_lck_mtx);
return;
}
struct if6lpan;
LIST_HEAD(if6lpan_list, if6lpan);
typedef LIST_ENTRY(if6lpan)
if6lpan_entry;
#define IF6LPAN_SIGNATURE 0x6666face
struct if6lpan {
if6lpan_entry if6lpan_list;
char if6lpan_name[IFNAMSIZ];
char if6lpan_addr[IEEE802154_ADDR_LEN];
struct ifnet * if6lpan_ifp;
struct ifnet * if6lpan_pifp;
#define IF6LPANF_DETACHING 0x1
#define IF6LPANF_READY 0x2
u_int32_t if6lpan_flags;
bpf_packet_func if6lpan_bpf_input;
bpf_packet_func if6lpan_bpf_output;
int32_t if6lpan_retain_count;
u_int32_t if6lpan_signature;
u_int8_t if6lpan_ieee802154_seq;
};
typedef struct if6lpan * if6lpan_ref;
static __inline__ int
if6lpan_flags_ready(if6lpan_ref ifl)
{
return (ifl->if6lpan_flags & IF6LPANF_READY) != 0;
}
static __inline__ void
if6lpan_flags_set_ready(if6lpan_ref ifl)
{
ifl->if6lpan_flags |= IF6LPANF_READY;
return;
}
static __inline__ void
if6lpan_set_addr(if6lpan_ref ifl, caddr_t ether_addr)
{
ifl->if6lpan_addr[0] = 0x66;
ifl->if6lpan_addr[1] = 0x66;
bcopy(ether_addr, &ifl->if6lpan_addr[2], ETHER_ADDR_LEN);
return;
}
#ifdef __UNUSED__
static __inline__ u_int8_t*
if6lpan_get_addr(if6lpan_ref ifl)
{
return ifl->ifl6lpan_addr;
}
#endif
static __inline__ int
if6lpan_flags_detaching(if6lpan_ref ifl)
{
return (ifl->if6lpan_flags & IF6LPANF_DETACHING) != 0;
}
static __inline__ void
if6lpan_flags_set_detaching(if6lpan_ref ifl)
{
ifl->if6lpan_flags |= IF6LPANF_DETACHING;
return;
}
static int sixlowpan_clone_create(struct if_clone *, u_int32_t, void *);
static int sixlowpan_clone_destroy(struct ifnet *);
static int sixlowpan_input(ifnet_t ifp, protocol_family_t protocol,
mbuf_t m, char *frame_header);
static int sixlowpan_output(struct ifnet *ifp, struct mbuf *m);
static int sixlowpan_ioctl(ifnet_t ifp, u_long cmd, void *addr);
static int sixlowpan_set_bpf_tap(ifnet_t ifp, bpf_tap_mode mode,
bpf_packet_func func);
static int sixlowpan_attach_protocol(struct ifnet *ifp);
static int sixlowpan_detach_protocol(struct ifnet *ifp);
static int sixlowpan_unconfig(if6lpan_ref ifl);
static int sixlowpan_config(struct ifnet *ifp, struct ifnet *p);
static void sixlowpan_if_free(struct ifnet *ifp);
static int sixlowpan_remove(if6lpan_ref ifl);
static int sixlowpan_framer_extended(struct ifnet *ifp, struct mbuf **m,
const struct sockaddr *ndest, const char *edst,
const char *ether_type, u_int32_t *prepend_len, u_int32_t *postpend_len);
#define SIXLOWPAN_MAXUNIT IF_MAXUNIT
#define SIXLOWPAN_ZONE_MAX_ELEM MIN(IFNETS_MAX, SIXLOWPAN_MAXUNIT)
static struct if_clone sixlowpan_cloner = IF_CLONE_INITIALIZER(SIXLOWPANNAME,
sixlowpan_clone_create,
sixlowpan_clone_destroy,
0,
SIXLOWPAN_MAXUNIT,
SIXLOWPAN_ZONE_MAX_ELEM,
sizeof(struct if6lpan));
static void
if6lpan_retain(if6lpan_ref ifl)
{
if (ifl->if6lpan_signature != IF6LPAN_SIGNATURE) {
panic("if6lpan_retain: bad signature\n");
}
if (ifl->if6lpan_retain_count == 0) {
panic("if6lpan_retain: retain count is 0\n");
}
OSIncrementAtomic(&ifl->if6lpan_retain_count);
}
static void
if6lpan_release(if6lpan_ref ifl)
{
u_int32_t old_retain_count;
if (ifl->if6lpan_signature != IF6LPAN_SIGNATURE) {
panic("if6lpan_release: bad signature\n");
}
old_retain_count = OSDecrementAtomic(&ifl->if6lpan_retain_count);
switch (old_retain_count) {
case 0:
panic("if6lpan_release: retain count is 0\n");
break;
case 1:
ifl->if6lpan_signature = 0;
if_clone_softc_deallocate(&sixlowpan_cloner, ifl);
break;
default:
break;
}
return;
}
static if6lpan_ref
ifnet_get_if6lpan(struct ifnet * ifp)
{
if6lpan_ref ifl;
ifl = (if6lpan_ref)ifnet_softc(ifp);
return ifl;
}
static if6lpan_ref
ifnet_get_if6lpan_retained(struct ifnet * ifp)
{
if6lpan_ref ifl;
ifl = ifnet_get_if6lpan(ifp);
if (ifl == NULL) {
return NULL;
}
if (if6lpan_flags_detaching(ifl)) {
return NULL;
}
if6lpan_retain(ifl);
return ifl;
}
static int
sixlowpan_clone_attach(void)
{
int error;
error = if_clone_attach(&sixlowpan_cloner);
if (error != 0) {
return error;
}
sixlowpan_lock_init();
return 0;
}
static int
sixlowpan_demux(
__unused ifnet_t ifp,
__unused mbuf_t m,
__unused char *frame_header,
protocol_family_t *protocol_family)
{
*protocol_family = PF_INET6;
return 0;
}
static errno_t
sixlowpan_add_proto(__unused ifnet_t interface, protocol_family_t protocol,
__unused const struct ifnet_demux_desc *demux_array,
__unused u_int32_t demux_count)
{
if (protocol == PF_INET6) {
return 0;
}
return ENOPROTOOPT;
}
static errno_t
sixlowpan_del_proto(__unused ifnet_t interface, __unused protocol_family_t protocol)
{
return 0;
}
static int
sixlowpan_clone_create(struct if_clone *ifc, u_int32_t unit, __unused void *params)
{
int error;
if6lpan_ref ifl;
ifnet_t ifp;
struct ifnet_init_eparams if_epraram;
ifl = if_clone_softc_allocate(&sixlowpan_cloner);
if (ifl == NULL) {
return ENOBUFS;
}
ifl->if6lpan_retain_count = 1;
ifl->if6lpan_signature = IF6LPAN_SIGNATURE;
if ((unsigned int)
snprintf(ifl->if6lpan_name, sizeof(ifl->if6lpan_name), "%s%d",
ifc->ifc_name, unit) >= sizeof(ifl->if6lpan_name)) {
if6lpan_release(ifl);
return EINVAL;
}
bzero(&if_epraram, sizeof(if_epraram));
if_epraram.ver = IFNET_INIT_CURRENT_VERSION;
if_epraram.len = sizeof(if_epraram);
if_epraram.flags = IFNET_INIT_LEGACY;
if_epraram.uniqueid = ifl->if6lpan_name;
if_epraram.uniqueid_len = (uint32_t)strlen(ifl->if6lpan_name);
if_epraram.name = ifc->ifc_name;
if_epraram.unit = unit;
if_epraram.family = IFNET_FAMILY_6LOWPAN;
if_epraram.type = IFT_6LOWPAN;
if_epraram.output = sixlowpan_output;
if_epraram.demux = sixlowpan_demux;
if_epraram.add_proto = sixlowpan_add_proto;
if_epraram.del_proto = sixlowpan_del_proto;
if_epraram.framer_extended = sixlowpan_framer_extended;
if_epraram.softc = ifl;
if_epraram.ioctl = sixlowpan_ioctl;
if_epraram.set_bpf_tap = sixlowpan_set_bpf_tap;
if_epraram.detach = sixlowpan_if_free;
error = ifnet_allocate_extended(&if_epraram, &ifp);
if (error) {
if6lpan_release(ifl);
return error;
}
ifnet_set_offload(ifp, 0);
ifnet_set_addrlen(ifp, IEEE802154_ADDR_LEN);
ifnet_set_baudrate(ifp, 0);
error = ifnet_attach(ifp, NULL);
if (error) {
ifnet_release(ifp);
if6lpan_release(ifl);
return error;
}
ifl->if6lpan_ifp = ifp;
p_6lowpan_ifnet = ifp;
bpfattach(ifp, DLT_IEEE802_15_4_NOFCS, IEEE802154_ENCAP_LEN);
return 0;
}
static int
sixlowpan_remove(if6lpan_ref ifl)
{
sixlowpan_assert_lock_held();
if (if6lpan_flags_detaching(ifl)) {
return 0;
}
if6lpan_flags_set_detaching(ifl);
sixlowpan_unconfig(ifl);
return 1;
}
static int
sixlowpan_clone_destroy(struct ifnet *ifp)
{
if6lpan_ref ifl;
sixlowpan_lock();
ifl = ifnet_get_if6lpan_retained(ifp);
if (ifl == NULL) {
sixlowpan_unlock();
return 0;
}
if (sixlowpan_remove(ifl) == 0) {
sixlowpan_unlock();
if6lpan_release(ifl);
return 0;
}
sixlowpan_unlock();
if6lpan_release(ifl);
ifnet_detach(ifp);
p_6lowpan_ifnet = NULL;
return 0;
}
static int
sixlowpan_set_bpf_tap(ifnet_t ifp, bpf_tap_mode mode, bpf_packet_func func)
{
if6lpan_ref ifl;
sixlowpan_lock();
ifl = ifnet_get_if6lpan_retained(ifp);
if (ifl == NULL) {
sixlowpan_unlock();
return ENODEV;
}
switch (mode) {
case BPF_TAP_DISABLE:
ifl->if6lpan_bpf_input = ifl->if6lpan_bpf_output = NULL;
break;
case BPF_TAP_INPUT:
ifl->if6lpan_bpf_input = func;
break;
case BPF_TAP_OUTPUT:
ifl->if6lpan_bpf_output = func;
break;
case BPF_TAP_INPUT_OUTPUT:
ifl->if6lpan_bpf_input = ifl->if6lpan_bpf_output = func;
break;
default:
break;
}
sixlowpan_unlock();
if6lpan_release(ifl);
return 0;
}
static int
sixlowpan_output(struct ifnet * ifp, struct mbuf * m)
{
struct ifnet *p_intf = NULL;
if6lpan_ref ifl = NULL;
struct flowadv adv = { .code = FADV_SUCCESS };
int err = 0;
char link_layer_dest[ETHER_ADDR_LEN];
bpf_packet_func bpf_func;
u_int16_t ethertype = htons(ETHERTYPE_IEEE802154);
memset(link_layer_dest, 0xff, ETHER_ADDR_LEN);
if (m == 0) {
return 0;
}
if ((m->m_flags & M_PKTHDR) == 0) {
m_freem_list(m);
return 0;
}
sixlowpan_lock();
ifl = ifnet_get_if6lpan_retained(ifp);
if (ifl == NULL || if6lpan_flags_ready(ifl) == 0) {
goto unlock_done;
}
p_intf = ifl->if6lpan_pifp;
bpf_func = ifl->if6lpan_bpf_output;
sixlowpan_unlock();
if6lpan_release(ifl);
(void)ifnet_stat_increment_out(ifp, 1, m->m_pkthdr.len, 0);
if (bpf_func && (mbuf_setdata(m, m->m_data + 2, m->m_len - 2) == 0)) {
bpf_func(ifp, m);
mbuf_setdata(m, m->m_data - 2, m->m_len + 2);
}
if ((err = ether_frameout_extended(p_intf, &m, NULL,
link_layer_dest, (const char *)ðertype,
NULL, NULL))) {
return err;
}
err = dlil_output(p_intf, PF_802154, m, NULL, NULL, 1, &adv);
if (err == 0) {
if (adv.code == FADV_FLOW_CONTROLLED) {
err = EQFULL;
} else if (adv.code == FADV_SUSPENDED) {
err = EQSUSPENDED;
}
}
return err;
unlock_done:
sixlowpan_unlock();
if (ifl != NULL) {
if6lpan_release(ifl);
}
m_freem(m);
return err;
}
static int
sixlowpan_input(ifnet_t p, __unused protocol_family_t protocol,
mbuf_t m, __unused char *frame_header)
{
frame802154_t ieee02154hdr;
u_int8_t *payload = NULL;
if6lpan_ref ifl = NULL;
bpf_packet_func bpf_func;
mbuf_t mc, m_temp;
int off, err = 0;
u_int16_t len;
mc = m_getcl(M_WAITOK, MT_DATA, M_PKTHDR);
if (mc == NULL) {
err = -1;
goto err_out;
}
memcpy(&len, mtod(m, u_int8_t *), sizeof(u_int16_t));
len = ntohs(len);
m_adj(m, sizeof(u_int16_t));
for (m_temp = m, off = 0; m_temp != NULL; m_temp = m_temp->m_next) {
if (m_temp->m_len > 0) {
m_copyback(mc, off, m_temp->m_len, mtod(m_temp, void *));
off += m_temp->m_len;
}
}
p = p_6lowpan_ifnet;
mc->m_pkthdr.rcvif = p;
if (len > mc->m_pkthdr.len || len > MCLBYTES) {
err = -1;
goto err_out;
}
sixlowpan_lock();
ifl = ifnet_get_if6lpan_retained(p);
if (ifl == NULL) {
sixlowpan_unlock();
err = -1;
goto err_out;
}
if (if6lpan_flags_ready(ifl) == 0) {
if6lpan_release(ifl);
sixlowpan_unlock();
err = -1;
goto err_out;
}
bpf_func = ifl->if6lpan_bpf_input;
sixlowpan_unlock();
if6lpan_release(ifl);
if (bpf_func) {
bpf_func(p, mc);
}
bzero(&ieee02154hdr, sizeof(ieee02154hdr));
(void)frame802154_parse(mtod(mc, uint8_t *), len, &ieee02154hdr, &payload);
if (payload == NULL) {
err = -1;
goto err_out;
}
if (sixxlowpan_input(&ieee02154hdr, payload) != 0) {
err = -1;
goto err_out;
}
if (mbuf_setdata(mc, payload, ieee02154hdr.payload_len)) {
err = -1;
goto err_out;
}
mbuf_pkthdr_setlen(mc, ieee02154hdr.payload_len);
if (proto_input(PF_INET6, mc) != 0) {
ifnet_stat_increment_in(p, 0, 0, 1);
err = -1;
goto err_out;
} else {
ifnet_stat_increment_in(p, 1, mc->m_pkthdr.len, 0);
}
err_out:
if (err && mc) {
m_freem(mc);
}
if (!err) {
m_freem(m);
}
return err;
}
#define SIXLOWPAN_IFMTU 1280
static int
sixlowpan_config(struct ifnet *ifp, struct ifnet *p)
{
if6lpan_ref ifl;
u_int16_t parent_flags;
sixlowpan_lock();
ifl = ifnet_get_if6lpan_retained(ifp);
if (ifl == NULL || ifl->if6lpan_pifp != NULL) {
sixlowpan_unlock();
if (ifl != NULL) {
if6lpan_release(ifl);
}
return EBUSY;
}
sixlowpan_attach_protocol(p);
if6lpan_set_addr(ifl, IF_LLADDR(p));
ifnet_set_lladdr_and_type(ifp, ifl->if6lpan_addr, IEEE802154_ADDR_LEN, IFT_6LOWPAN);
ifl->if6lpan_pifp = p;
ifl->if6lpan_flags = 0;
ifnet_set_mtu(ifp, SIXLOWPAN_IFMTU);
parent_flags = ifnet_flags(p) & (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX);
ifnet_set_flags(ifp, parent_flags, IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX);
ifnet_set_flags(ifp, IFF_RUNNING, IFF_RUNNING);
ifnet_set_eflags(ifp, IFEF_NOAUTOIPV6LL, IFEF_NOAUTOIPV6LL);
if6lpan_flags_set_ready(ifl);
if6lpan_release(ifl);
sixlowpan_unlock();
return 0;
}
static int
sixlowpan_unconfig(if6lpan_ref ifl)
{
struct ifnet *ifp = ifl->if6lpan_ifp;
sixlowpan_assert_lock_held();
ifnet_set_lladdr_and_type(ifp, NULL, 0, IFT_6LOWPAN);
sixlowpan_detach_protocol(ifl->if6lpan_pifp);
ifnet_set_mtu(ifp, 0);
ifnet_set_flags(ifp, 0,
IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | IFF_RUNNING);
ifnet_set_eflags(ifp, 0, IFEF_NOAUTOIPV6LL);
ifl->if6lpan_flags = 0;
return 0;
}
static int
sixlowpan_ioctl(ifnet_t ifp, u_long cmd, void * data)
{
int error = 0;
struct ifreq * ifr = NULL;
struct ifnet * p = NULL;
struct sixlowpanreq req = {};
user_addr_t user_addr = 0;
if6lpan_ref ifl = NULL;
if (ifnet_type(ifp) != IFT_6LOWPAN) {
return EOPNOTSUPP;
}
ifr = (struct ifreq *)data;
switch (cmd) {
case SIOCSIFADDR:
ifnet_set_flags(ifp, IFF_UP, IFF_UP);
break;
case SIOCSIF6LOWPAN:
user_addr = proc_is64bit(current_proc()) ?
CAST_USER_ADDR_T(ifr->ifr_data64) :
CAST_USER_ADDR_T(ifr->ifr_data);
error = copyin(user_addr, &req, sizeof(req));
req.parent[IFNAMSIZ - 1] = '\0';
if (error) {
break;
}
if (req.parent[0] != '\0') {
p = ifunit(req.parent);
if (p == NULL) {
error = ENXIO;
break;
}
if (ifnet_type(p) != IFT_ETHER
&& ifnet_type(p) != IFT_IEEE8023ADLAG) {
error = EPROTONOSUPPORT;
break;
}
error = sixlowpan_config(ifp, p);
if (error) {
break;
}
}
break;
case SIOCGIF6LOWPAN:
bzero(&req, sizeof req);
sixlowpan_lock();
ifl = (if6lpan_ref)ifnet_softc(ifp);
if (ifl == NULL || if6lpan_flags_detaching(ifl)) {
sixlowpan_unlock();
return ifl == NULL ? EOPNOTSUPP : EBUSY;
}
p = ifl->if6lpan_pifp;
sixlowpan_unlock();
if (p != NULL) {
snprintf(req.parent, sizeof(req.parent),
"%s%d", ifnet_name(p), ifnet_unit(p));
}
user_addr = proc_is64bit(current_proc()) ?
CAST_USER_ADDR_T(ifr->ifr_data64) :
CAST_USER_ADDR_T(ifr->ifr_data);
error = copyout(&req, user_addr, sizeof(req));
break;
#ifdef SIOCSIFMTU
case SIOCGIFMTU:
break;
case SIOCSIFMTU:
ifnet_set_mtu(ifp, ifr->ifr_mtu);
break;
#endif
default:
error = EOPNOTSUPP;
}
return error;
}
static void
sixlowpan_if_free(struct ifnet * ifp)
{
if6lpan_ref ifl;
if (ifp == NULL) {
return;
}
ifl = (if6lpan_ref)ifnet_softc(ifp);
if (ifl == NULL) {
return;
}
if6lpan_release(ifl);
ifnet_release(ifp);
return;
}
static errno_t
sixlowpan_detached(ifnet_t p, __unused protocol_family_t protocol)
{
if (ifnet_is_attached(p, 0) == 0) {
}
return 0;
}
static int
sixlowpan_attach_protocol(struct ifnet *ifp)
{
int error;
struct ifnet_attach_proto_param reg;
bzero(®, sizeof(reg));
reg.input = sixlowpan_input;
reg.detached = sixlowpan_detached;
error = ifnet_attach_protocol(ifp, PF_802154, ®);
if (error) {
printf("%s(%s%d) ifnet_attach_protocol failed, %d\n",
__func__, ifnet_name(ifp), ifnet_unit(ifp), error);
}
return error;
}
static int
sixlowpan_detach_protocol(struct ifnet *ifp)
{
int error;
error = ifnet_detach_protocol(ifp, PF_802154);
if (error) {
printf("(%s%d) ifnet_detach_protocol failed, %d\n",
ifnet_name(ifp), ifnet_unit(ifp), error);
}
return error;
}
static errno_t
sixlowpan_proto_pre_output(ifnet_t ifp,
__unused protocol_family_t protocol_family,
mbuf_t *m0,
const struct sockaddr *dest,
void *route,
char *type,
char *ll_dest)
{
#pragma unused(protocol_family)
errno_t result = 0;
struct sockaddr_dl sdl = {};
struct sockaddr_in6 *dest6 = (struct sockaddr_in6 *)(uintptr_t)(size_t)dest;
if (!IN6_IS_ADDR_MULTICAST(&dest6->sin6_addr)) {
result = nd6_lookup_ipv6(ifp, dest6, &sdl, sizeof(sdl), route, *m0);
if (result == 0) {
bcopy(LLADDR(&sdl), ll_dest, sdl.sdl_alen);
}
} else {
ll_dest[0] = (dest6->sin6_addr.s6_addr8[14] & 0x1f) | 0x80;
ll_dest[1] = dest6->sin6_addr.s6_addr8[15];
}
if (result == 0) {
u_int16_t ethertype = htons(ETHERTYPE_IEEE802154);
bcopy(ðertype, type, sizeof(ethertype));
}
return result;
}
static int
sixlowpan_framer_extended(struct ifnet *ifp, struct mbuf **m,
const struct sockaddr *ndest, const char *edst,
const char *ether_type, u_int32_t *prepend_len, u_int32_t *postpend_len)
{
#pragma unused(ndest)
#pragma unused(ether_type)
char buf[IEEE802154_ENCAP_LEN] = {0};
int buflen = 0, err = 0;
frame802154_t ieee02154hdr;
if6lpan_ref ifl = NULL;
uint8_t *payload = NULL;
struct mbuf *mc = NULL;
uint16_t len;
struct sockaddr_in6 *dest6 = (struct sockaddr_in6 *)(uintptr_t)(size_t)ndest;
bzero(&ieee02154hdr, sizeof(ieee02154hdr));
if (!IN6_IS_ADDR_MULTICAST(&dest6->sin6_addr)) {
bcopy(edst, ieee02154hdr.dest_addr, sizeof(ieee02154hdr.dest_addr));
ieee02154hdr.fcf.dest_addr_mode = FRAME802154_LONGADDRMODE;
} else {
bcopy(edst, ieee02154hdr.dest_addr, 2);
ieee02154hdr.fcf.dest_addr_mode = FRAME802154_SHORTADDRMODE;
}
payload = _MALLOC(m_pktlen(*m) + 1, M_TEMP, M_WAITOK | M_ZERO);
if (payload == NULL) {
err = -1;
goto err_out;
}
if (mbuf_copydata(*m, 0, m_pktlen(*m), payload)) {
err = -1;
goto err_out;
}
mc = m_getcl(M_WAITOK, MT_DATA, M_PKTHDR);
if (mc == NULL) {
err = -1;
goto err_out;
}
sixlowpan_lock();
ifl = ifnet_get_if6lpan_retained(ifp);
if (ifl == NULL || if6lpan_flags_ready(ifl) == 0) {
if (ifl != NULL) {
if6lpan_release(ifl);
}
sixlowpan_unlock();
err = -1;
goto err_out;
}
bcopy(ifl->if6lpan_addr, ieee02154hdr.src_addr, sizeof(ieee02154hdr.src_addr));
ieee02154hdr.seq = ifl->if6lpan_ieee802154_seq++;
if6lpan_release(ifl);
sixlowpan_unlock();
ieee02154hdr.fcf.frame_type = FRAME802154_DATAFRAME;
ieee02154hdr.fcf.security_enabled = 0;
ieee02154hdr.fcf.frame_pending = 0;
ieee02154hdr.fcf.ack_required = 0;
ieee02154hdr.fcf.panid_compression = 0;
ieee02154hdr.fcf.frame_version = FRAME802154_IEEE802154_2006;
ieee02154hdr.fcf.src_addr_mode = FRAME802154_LONGADDRMODE;
ieee02154hdr.dest_pid = IEEE802154_PANID;
ieee02154hdr.src_pid = IEEE802154_PANID;
ieee02154hdr.payload_len = m_pktlen(*m);
buflen = frame802154_create(&ieee02154hdr, (uint8_t *)buf);
sixxlowpan_output(&ieee02154hdr, payload);
len = htons((uint16_t)(buflen + ieee02154hdr.payload_len));
m_copyback(mc, 0, sizeof(len), &len);
m_copyback(mc, sizeof(len), buflen, buf);
m_copyback(mc, buflen + sizeof(len), ieee02154hdr.payload_len, payload);
if (prepend_len != NULL) {
*prepend_len = buflen;
}
if (postpend_len != NULL) {
*postpend_len = 0;
}
err_out:
if (payload != NULL) {
_FREE(payload, M_TEMP);
}
m_freem(*m);
*m = mc;
return err;
}
static errno_t
sixlowpan_attach_inet6(struct ifnet *ifp, protocol_family_t protocol_family)
{
struct ifnet_attach_proto_param proto;
errno_t error;
bzero(&proto, sizeof(proto));
proto.pre_output = sixlowpan_proto_pre_output;
error = ifnet_attach_protocol(ifp, protocol_family, &proto);
if (error && error != EEXIST) {
printf("WARNING: %s can't attach ipv6 to %s\n", __func__,
if_name(ifp));
}
return error;
}
static void
sixlowpan_detach_inet6(struct ifnet *ifp, protocol_family_t protocol_family)
{
(void) ifnet_detach_protocol(ifp, protocol_family);
}
__private_extern__ int
sixlowpan_family_init(void)
{
int error = 0;
error = proto_register_plumber(PF_INET6, IFNET_FAMILY_6LOWPAN,
sixlowpan_attach_inet6, sixlowpan_detach_inet6);
if (error != 0) {
printf("6lowpan: proto_register_plumber failed for AF_INET6 error=%d\n",
error);
goto done;
}
error = sixlowpan_clone_attach();
if (error != 0) {
printf("6lowpan: proto_register_plumber failed sixlowpan_clone_attach error=%d\n",
error);
goto done;
}
done:
return error;
}