static int udp_uncompress(struct sk_buff *skb, size_t needed) { u8 tmp = 0, val = 0; struct udphdr uh; bool fail; int err; fail = lowpan_fetch_skb(skb, &tmp, sizeof(tmp)); pr_debug("UDP header uncompression\n"); switch (tmp & LOWPAN_NHC_UDP_CS_P_11) { case LOWPAN_NHC_UDP_CS_P_00: fail |= lowpan_fetch_skb(skb, &uh.source, sizeof(uh.source)); fail |= lowpan_fetch_skb(skb, &uh.dest, sizeof(uh.dest)); break; case LOWPAN_NHC_UDP_CS_P_01: fail |= lowpan_fetch_skb(skb, &uh.source, sizeof(uh.source)); fail |= lowpan_fetch_skb(skb, &val, sizeof(val)); uh.dest = htons(val + LOWPAN_NHC_UDP_8BIT_PORT); break; case LOWPAN_NHC_UDP_CS_P_10: fail |= lowpan_fetch_skb(skb, &val, sizeof(val)); uh.source = htons(val + LOWPAN_NHC_UDP_8BIT_PORT); fail |= lowpan_fetch_skb(skb, &uh.dest, sizeof(uh.dest)); break; case LOWPAN_NHC_UDP_CS_P_11: fail |= lowpan_fetch_skb(skb, &val, sizeof(val)); uh.source = htons(LOWPAN_NHC_UDP_4BIT_PORT + (val >> 4)); uh.dest = htons(LOWPAN_NHC_UDP_4BIT_PORT + (val & 0x0f)); break; default: BUG(); } pr_debug("uncompressed UDP ports: src = %d, dst = %d\n", ntohs(uh.source), ntohs(uh.dest)); /* checksum */ if (tmp & LOWPAN_NHC_UDP_CS_C) { pr_debug_ratelimited("checksum elided currently not supported\n"); fail = true; } else { fail |= lowpan_fetch_skb(skb, &uh.check, sizeof(uh.check)); } if (fail) return -EINVAL; /* UDP length needs to be infered from the lower layers * here, we obtain the hint from the remaining size of the * frame */ switch (lowpan_priv(skb->dev)->lltype) { case LOWPAN_LLTYPE_IEEE802154: if (lowpan_802154_cb(skb)->d_size) uh.len = htons(lowpan_802154_cb(skb)->d_size - sizeof(struct ipv6hdr)); else uh.len = htons(skb->len + sizeof(struct udphdr)); break; default: uh.len = htons(skb->len + sizeof(struct udphdr)); break; } pr_debug("uncompressed UDP length: src = %d", ntohs(uh.len)); /* replace the compressed UDP head by the uncompressed UDP * header */ err = skb_cow(skb, needed); if (unlikely(err)) return err; skb_push(skb, sizeof(struct udphdr)); skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr)); return 0; }
void lowpan_netdev_setup(struct net_device *dev, enum lowpan_lltypes lltype) { lowpan_priv(dev)->lltype = lltype; }