0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/netdevice.h>
0010
0011 #include <net/ipv6.h>
0012
0013 #include "nhc.h"
0014
0015 static const struct lowpan_nhc *lowpan_nexthdr_nhcs[NEXTHDR_MAX + 1];
0016 static DEFINE_SPINLOCK(lowpan_nhc_lock);
0017
0018 static const struct lowpan_nhc *lowpan_nhc_by_nhcid(struct sk_buff *skb)
0019 {
0020 const struct lowpan_nhc *nhc;
0021 int i;
0022 u8 id;
0023
0024 if (!pskb_may_pull(skb, 1))
0025 return NULL;
0026
0027 id = *skb->data;
0028
0029 for (i = 0; i < NEXTHDR_MAX + 1; i++) {
0030 nhc = lowpan_nexthdr_nhcs[i];
0031 if (!nhc)
0032 continue;
0033
0034 if ((id & nhc->idmask) == nhc->id)
0035 return nhc;
0036 }
0037
0038 return NULL;
0039 }
0040
0041 int lowpan_nhc_check_compression(struct sk_buff *skb,
0042 const struct ipv6hdr *hdr, u8 **hc_ptr)
0043 {
0044 const struct lowpan_nhc *nhc;
0045 int ret = 0;
0046
0047 spin_lock_bh(&lowpan_nhc_lock);
0048
0049 nhc = lowpan_nexthdr_nhcs[hdr->nexthdr];
0050 if (!(nhc && nhc->compress))
0051 ret = -ENOENT;
0052
0053 spin_unlock_bh(&lowpan_nhc_lock);
0054
0055 return ret;
0056 }
0057
0058 int lowpan_nhc_do_compression(struct sk_buff *skb, const struct ipv6hdr *hdr,
0059 u8 **hc_ptr)
0060 {
0061 int ret;
0062 const struct lowpan_nhc *nhc;
0063
0064 spin_lock_bh(&lowpan_nhc_lock);
0065
0066 nhc = lowpan_nexthdr_nhcs[hdr->nexthdr];
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077 if (unlikely(!nhc || !nhc->compress)) {
0078 ret = -EINVAL;
0079 goto out;
0080 }
0081
0082
0083
0084
0085 if (skb->transport_header == skb->network_header)
0086 skb_set_transport_header(skb, sizeof(struct ipv6hdr));
0087
0088 ret = nhc->compress(skb, hc_ptr);
0089 if (ret < 0)
0090 goto out;
0091
0092
0093 skb_pull(skb, nhc->nexthdrlen);
0094
0095 out:
0096 spin_unlock_bh(&lowpan_nhc_lock);
0097
0098 return ret;
0099 }
0100
0101 int lowpan_nhc_do_uncompression(struct sk_buff *skb,
0102 const struct net_device *dev,
0103 struct ipv6hdr *hdr)
0104 {
0105 const struct lowpan_nhc *nhc;
0106 int ret;
0107
0108 spin_lock_bh(&lowpan_nhc_lock);
0109
0110 nhc = lowpan_nhc_by_nhcid(skb);
0111 if (nhc) {
0112 if (nhc->uncompress) {
0113 ret = nhc->uncompress(skb, sizeof(struct ipv6hdr) +
0114 nhc->nexthdrlen);
0115 if (ret < 0) {
0116 spin_unlock_bh(&lowpan_nhc_lock);
0117 return ret;
0118 }
0119 } else {
0120 spin_unlock_bh(&lowpan_nhc_lock);
0121 netdev_warn(dev, "received nhc id for %s which is not implemented.\n",
0122 nhc->name);
0123 return -ENOTSUPP;
0124 }
0125 } else {
0126 spin_unlock_bh(&lowpan_nhc_lock);
0127 netdev_warn(dev, "received unknown nhc id which was not found.\n");
0128 return -ENOENT;
0129 }
0130
0131 hdr->nexthdr = nhc->nexthdr;
0132 skb_reset_transport_header(skb);
0133 raw_dump_table(__func__, "raw transport header dump",
0134 skb_transport_header(skb), nhc->nexthdrlen);
0135
0136 spin_unlock_bh(&lowpan_nhc_lock);
0137
0138 return 0;
0139 }
0140
0141 int lowpan_nhc_add(const struct lowpan_nhc *nhc)
0142 {
0143 int ret = 0;
0144
0145 spin_lock_bh(&lowpan_nhc_lock);
0146
0147 if (lowpan_nexthdr_nhcs[nhc->nexthdr]) {
0148 ret = -EEXIST;
0149 goto out;
0150 }
0151
0152 lowpan_nexthdr_nhcs[nhc->nexthdr] = nhc;
0153 out:
0154 spin_unlock_bh(&lowpan_nhc_lock);
0155 return ret;
0156 }
0157 EXPORT_SYMBOL(lowpan_nhc_add);
0158
0159 void lowpan_nhc_del(const struct lowpan_nhc *nhc)
0160 {
0161 spin_lock_bh(&lowpan_nhc_lock);
0162
0163 lowpan_nexthdr_nhcs[nhc->nexthdr] = NULL;
0164
0165 spin_unlock_bh(&lowpan_nhc_lock);
0166
0167 synchronize_net();
0168 }
0169 EXPORT_SYMBOL(lowpan_nhc_del);