/* This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include "6lowpan_i.h" static int lowpan_give_skb_to_devices(struct sk_buff *skb, struct net_device *dev) { struct lowpan_dev_record *entry; struct sk_buff *skb_cp; int stat = NET_RX_SUCCESS; skb->protocol = htons(ETH_P_IPV6); skb->pkt_type = PACKET_HOST; rcu_read_lock(); list_for_each_entry_rcu(entry, &lowpan_devices, list) if (lowpan_dev_info(entry->ldev)->real_dev == skb->dev) { skb_cp = skb_copy(skb, GFP_ATOMIC); if (!skb_cp) { kfree_skb(skb); rcu_read_unlock(); return NET_RX_DROP; } skb_cp->dev = entry->ldev; stat = netif_rx(skb_cp); if (stat == NET_RX_DROP) break; } rcu_read_unlock(); consume_skb(skb); return stat; } static int iphc_decompress(struct sk_buff *skb, const struct ieee802154_hdr *hdr) { u8 iphc0, iphc1; struct ieee802154_addr_sa sa, da; void *sap, *dap; raw_dump_table(__func__, "raw skb data dump", skb->data, skb->len); /* at least two bytes will be used for the encoding */ if (skb->len < 2) return -EINVAL; if (lowpan_fetch_skb_u8(skb, &iphc0)) return -EINVAL; if (lowpan_fetch_skb_u8(skb, &iphc1)) return -EINVAL; ieee802154_addr_to_sa(&sa, &hdr->source); ieee802154_addr_to_sa(&da, &hdr->dest); if (sa.addr_type == IEEE802154_ADDR_SHORT) sap = &sa.short_addr; else sap = &sa.hwaddr; if (da.addr_type == IEEE802154_ADDR_SHORT) dap = &da.short_addr; else dap = &da.hwaddr; return lowpan_header_decompress(skb, skb->dev, sap, sa.addr_type, IEEE802154_ADDR_LEN, dap, da.addr_type, IEEE802154_ADDR_LEN, iphc0, iphc1); } static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { struct ieee802154_hdr hdr; int ret; skb = skb_share_check(skb, GFP_ATOMIC); if (!skb) goto drop; if (!netif_running(dev)) goto drop_skb; if (skb->pkt_type == PACKET_OTHERHOST) goto drop_skb; if (dev->type != ARPHRD_IEEE802154) goto drop_skb; if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) goto drop_skb; /* check that it's our buffer */ if (skb->data[0] == LOWPAN_DISPATCH_IPV6) { /* Pull off the 1-byte of 6lowpan header. */ skb_pull(skb, 1); return lowpan_give_skb_to_devices(skb, NULL); } else { switch (skb->data[0] & 0xe0) { case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */ ret = iphc_decompress(skb, &hdr); if (ret < 0) goto drop_skb; return lowpan_give_skb_to_devices(skb, NULL); case LOWPAN_DISPATCH_FRAG1: /* first fragment header */ ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAG1); if (ret == 1) { ret = iphc_decompress(skb, &hdr); if (ret < 0) goto drop_skb; return lowpan_give_skb_to_devices(skb, NULL); } else if (ret == -1) { return NET_RX_DROP; } else { return NET_RX_SUCCESS; } case LOWPAN_DISPATCH_FRAGN: /* next fragments headers */ ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAGN); if (ret == 1) { ret = iphc_decompress(skb, &hdr); if (ret < 0) goto drop_skb; return lowpan_give_skb_to_devices(skb, NULL); } else if (ret == -1) { return NET_RX_DROP; } else { return NET_RX_SUCCESS; } default: break; } } drop_skb: kfree_skb(skb); drop: return NET_RX_DROP; } static struct packet_type lowpan_packet_type = { .type = htons(ETH_P_IEEE802154), .func = lowpan_rcv, }; void lowpan_rx_init(void) { dev_add_pack(&lowpan_packet_type); } void lowpan_rx_exit(void) { dev_remove_pack(&lowpan_packet_type); }