Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  6LoWPAN IPv6 UDP compression according to RFC6282
0004  *
0005  *  Authors:
0006  *  Alexander Aring <aar@pengutronix.de>
0007  *
0008  *  Original written by:
0009  *  Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
0010  *  Jon Smirl <jonsmirl@gmail.com>
0011  */
0012 
0013 #include "nhc.h"
0014 
0015 #define LOWPAN_NHC_UDP_MASK     0xF8
0016 #define LOWPAN_NHC_UDP_ID       0xF0
0017 
0018 #define LOWPAN_NHC_UDP_4BIT_PORT    0xF0B0
0019 #define LOWPAN_NHC_UDP_4BIT_MASK    0xFFF0
0020 #define LOWPAN_NHC_UDP_8BIT_PORT    0xF000
0021 #define LOWPAN_NHC_UDP_8BIT_MASK    0xFF00
0022 
0023 /* values for port compression, _with checksum_ ie bit 5 set to 0 */
0024 
0025 /* all inline */
0026 #define LOWPAN_NHC_UDP_CS_P_00  0xF0
0027 /* source 16bit inline, dest = 0xF0 + 8 bit inline */
0028 #define LOWPAN_NHC_UDP_CS_P_01  0xF1
0029 /* source = 0xF0 + 8bit inline, dest = 16 bit inline */
0030 #define LOWPAN_NHC_UDP_CS_P_10  0xF2
0031 /* source & dest = 0xF0B + 4bit inline */
0032 #define LOWPAN_NHC_UDP_CS_P_11  0xF3
0033 /* checksum elided */
0034 #define LOWPAN_NHC_UDP_CS_C 0x04
0035 
0036 static int udp_uncompress(struct sk_buff *skb, size_t needed)
0037 {
0038     u8 tmp = 0, val = 0;
0039     struct udphdr uh;
0040     bool fail;
0041     int err;
0042 
0043     fail = lowpan_fetch_skb(skb, &tmp, sizeof(tmp));
0044 
0045     pr_debug("UDP header uncompression\n");
0046     switch (tmp & LOWPAN_NHC_UDP_CS_P_11) {
0047     case LOWPAN_NHC_UDP_CS_P_00:
0048         fail |= lowpan_fetch_skb(skb, &uh.source, sizeof(uh.source));
0049         fail |= lowpan_fetch_skb(skb, &uh.dest, sizeof(uh.dest));
0050         break;
0051     case LOWPAN_NHC_UDP_CS_P_01:
0052         fail |= lowpan_fetch_skb(skb, &uh.source, sizeof(uh.source));
0053         fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
0054         uh.dest = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
0055         break;
0056     case LOWPAN_NHC_UDP_CS_P_10:
0057         fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
0058         uh.source = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
0059         fail |= lowpan_fetch_skb(skb, &uh.dest, sizeof(uh.dest));
0060         break;
0061     case LOWPAN_NHC_UDP_CS_P_11:
0062         fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
0063         uh.source = htons(LOWPAN_NHC_UDP_4BIT_PORT + (val >> 4));
0064         uh.dest = htons(LOWPAN_NHC_UDP_4BIT_PORT + (val & 0x0f));
0065         break;
0066     default:
0067         BUG();
0068     }
0069 
0070     pr_debug("uncompressed UDP ports: src = %d, dst = %d\n",
0071          ntohs(uh.source), ntohs(uh.dest));
0072 
0073     /* checksum */
0074     if (tmp & LOWPAN_NHC_UDP_CS_C) {
0075         pr_debug_ratelimited("checksum elided currently not supported\n");
0076         fail = true;
0077     } else {
0078         fail |= lowpan_fetch_skb(skb, &uh.check, sizeof(uh.check));
0079     }
0080 
0081     if (fail)
0082         return -EINVAL;
0083 
0084     /* UDP length needs to be inferred from the lower layers
0085      * here, we obtain the hint from the remaining size of the
0086      * frame
0087      */
0088     switch (lowpan_dev(skb->dev)->lltype) {
0089     case LOWPAN_LLTYPE_IEEE802154:
0090         if (lowpan_802154_cb(skb)->d_size)
0091             uh.len = htons(lowpan_802154_cb(skb)->d_size -
0092                        sizeof(struct ipv6hdr));
0093         else
0094             uh.len = htons(skb->len + sizeof(struct udphdr));
0095         break;
0096     default:
0097         uh.len = htons(skb->len + sizeof(struct udphdr));
0098         break;
0099     }
0100     pr_debug("uncompressed UDP length: src = %d", ntohs(uh.len));
0101 
0102     /* replace the compressed UDP head by the uncompressed UDP
0103      * header
0104      */
0105     err = skb_cow(skb, needed);
0106     if (unlikely(err))
0107         return err;
0108 
0109     skb_push(skb, sizeof(struct udphdr));
0110     skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
0111 
0112     return 0;
0113 }
0114 
0115 static int udp_compress(struct sk_buff *skb, u8 **hc_ptr)
0116 {
0117     const struct udphdr *uh = udp_hdr(skb);
0118     u8 tmp;
0119 
0120     if (((ntohs(uh->source) & LOWPAN_NHC_UDP_4BIT_MASK) ==
0121          LOWPAN_NHC_UDP_4BIT_PORT) &&
0122         ((ntohs(uh->dest) & LOWPAN_NHC_UDP_4BIT_MASK) ==
0123          LOWPAN_NHC_UDP_4BIT_PORT)) {
0124         pr_debug("UDP header: both ports compression to 4 bits\n");
0125         /* compression value */
0126         tmp = LOWPAN_NHC_UDP_CS_P_11;
0127         lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
0128         /* source and destination port */
0129         tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_4BIT_PORT +
0130               ((ntohs(uh->source) - LOWPAN_NHC_UDP_4BIT_PORT) << 4);
0131         lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
0132     } else if ((ntohs(uh->dest) & LOWPAN_NHC_UDP_8BIT_MASK) ==
0133             LOWPAN_NHC_UDP_8BIT_PORT) {
0134         pr_debug("UDP header: remove 8 bits of dest\n");
0135         /* compression value */
0136         tmp = LOWPAN_NHC_UDP_CS_P_01;
0137         lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
0138         /* source port */
0139         lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
0140         /* destination port */
0141         tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_8BIT_PORT;
0142         lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
0143     } else if ((ntohs(uh->source) & LOWPAN_NHC_UDP_8BIT_MASK) ==
0144             LOWPAN_NHC_UDP_8BIT_PORT) {
0145         pr_debug("UDP header: remove 8 bits of source\n");
0146         /* compression value */
0147         tmp = LOWPAN_NHC_UDP_CS_P_10;
0148         lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
0149         /* source port */
0150         tmp = ntohs(uh->source) - LOWPAN_NHC_UDP_8BIT_PORT;
0151         lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
0152         /* destination port */
0153         lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
0154     } else {
0155         pr_debug("UDP header: can't compress\n");
0156         /* compression value */
0157         tmp = LOWPAN_NHC_UDP_CS_P_00;
0158         lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
0159         /* source port */
0160         lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
0161         /* destination port */
0162         lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
0163     }
0164 
0165     /* checksum is always inline */
0166     lowpan_push_hc_data(hc_ptr, &uh->check, sizeof(uh->check));
0167 
0168     return 0;
0169 }
0170 
0171 LOWPAN_NHC(nhc_udp, "RFC6282 UDP", NEXTHDR_UDP, sizeof(struct udphdr),
0172        LOWPAN_NHC_UDP_ID, LOWPAN_NHC_UDP_MASK, udp_uncompress, udp_compress);
0173 
0174 module_lowpan_nhc(nhc_udp);
0175 MODULE_DESCRIPTION("6LoWPAN next header RFC6282 UDP compression");
0176 MODULE_LICENSE("GPL");