Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / net / 6lowpan / nhc_udp.c
diff --git a/kernel/net/6lowpan/nhc_udp.c b/kernel/net/6lowpan/nhc_udp.c
new file mode 100644 (file)
index 0000000..c6bcaeb
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ *     6LoWPAN IPv6 UDP compression according to RFC6282
+ *
+ *
+ *     Authors:
+ *     Alexander Aring <aar@pengutronix.de>
+ *
+ *     Orignal written by:
+ *     Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
+ *     Jon Smirl <jonsmirl@gmail.com>
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License
+ *     as published by the Free Software Foundation; either version
+ *     2 of the License, or (at your option) any later version.
+ */
+
+#include "nhc.h"
+
+#define LOWPAN_NHC_UDP_IDLEN   1
+
+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
+        */
+       uh.len = htons(skb->len + sizeof(struct udphdr));
+       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;
+}
+
+static int udp_compress(struct sk_buff *skb, u8 **hc_ptr)
+{
+       const struct udphdr *uh = udp_hdr(skb);
+       u8 tmp;
+
+       if (((ntohs(uh->source) & LOWPAN_NHC_UDP_4BIT_MASK) ==
+            LOWPAN_NHC_UDP_4BIT_PORT) &&
+           ((ntohs(uh->dest) & LOWPAN_NHC_UDP_4BIT_MASK) ==
+            LOWPAN_NHC_UDP_4BIT_PORT)) {
+               pr_debug("UDP header: both ports compression to 4 bits\n");
+               /* compression value */
+               tmp = LOWPAN_NHC_UDP_CS_P_11;
+               lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+               /* source and destination port */
+               tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_4BIT_PORT +
+                     ((ntohs(uh->source) - LOWPAN_NHC_UDP_4BIT_PORT) << 4);
+               lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+       } else if ((ntohs(uh->dest) & LOWPAN_NHC_UDP_8BIT_MASK) ==
+                       LOWPAN_NHC_UDP_8BIT_PORT) {
+               pr_debug("UDP header: remove 8 bits of dest\n");
+               /* compression value */
+               tmp = LOWPAN_NHC_UDP_CS_P_01;
+               lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+               /* source port */
+               lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
+               /* destination port */
+               tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_8BIT_PORT;
+               lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+       } else if ((ntohs(uh->source) & LOWPAN_NHC_UDP_8BIT_MASK) ==
+                       LOWPAN_NHC_UDP_8BIT_PORT) {
+               pr_debug("UDP header: remove 8 bits of source\n");
+               /* compression value */
+               tmp = LOWPAN_NHC_UDP_CS_P_10;
+               lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+               /* source port */
+               tmp = ntohs(uh->source) - LOWPAN_NHC_UDP_8BIT_PORT;
+               lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+               /* destination port */
+               lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
+       } else {
+               pr_debug("UDP header: can't compress\n");
+               /* compression value */
+               tmp = LOWPAN_NHC_UDP_CS_P_00;
+               lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+               /* source port */
+               lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
+               /* destination port */
+               lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
+       }
+
+       /* checksum is always inline */
+       lowpan_push_hc_data(hc_ptr, &uh->check, sizeof(uh->check));
+
+       return 0;
+}
+
+static void udp_nhid_setup(struct lowpan_nhc *nhc)
+{
+       nhc->id[0] = LOWPAN_NHC_UDP_ID;
+       nhc->idmask[0] = LOWPAN_NHC_UDP_MASK;
+}
+
+LOWPAN_NHC(nhc_udp, "RFC6282 UDP", NEXTHDR_UDP, sizeof(struct udphdr),
+          udp_nhid_setup, LOWPAN_NHC_UDP_IDLEN, udp_uncompress, udp_compress);
+
+module_lowpan_nhc(nhc_udp);
+MODULE_DESCRIPTION("6LoWPAN next header RFC6282 UDP compression");
+MODULE_LICENSE("GPL");