18610c7c6SAlexander Aring // SPDX-License-Identifier: GPL-2.0-only
2f1f09df1SAndrew Lunn /*
38610c7c6SAlexander Aring * Authors:
48610c7c6SAlexander Aring * (C) 2020 Alexander Aring <alex.aring@gmail.com>
58610c7c6SAlexander Aring */
68610c7c6SAlexander Aring
78610c7c6SAlexander Aring #include <net/ipv6.h>
88610c7c6SAlexander Aring #include <net/rpl.h>
98610c7c6SAlexander Aring
108610c7c6SAlexander Aring #define IPV6_PFXTAIL_LEN(x) (sizeof(struct in6_addr) - (x))
1162e69776SAlexander Aring #define IPV6_RPL_BEST_ADDR_COMPRESSION 15
128610c7c6SAlexander Aring
ipv6_rpl_addr_decompress(struct in6_addr * dst,const struct in6_addr * daddr,const void * post,unsigned char pfx)138610c7c6SAlexander Aring static void ipv6_rpl_addr_decompress(struct in6_addr *dst,
148610c7c6SAlexander Aring const struct in6_addr *daddr,
158610c7c6SAlexander Aring const void *post, unsigned char pfx)
168610c7c6SAlexander Aring {
178610c7c6SAlexander Aring memcpy(dst, daddr, pfx);
188610c7c6SAlexander Aring memcpy(&dst->s6_addr[pfx], post, IPV6_PFXTAIL_LEN(pfx));
198610c7c6SAlexander Aring }
208610c7c6SAlexander Aring
ipv6_rpl_addr_compress(void * dst,const struct in6_addr * addr,unsigned char pfx)218610c7c6SAlexander Aring static void ipv6_rpl_addr_compress(void *dst, const struct in6_addr *addr,
228610c7c6SAlexander Aring unsigned char pfx)
238610c7c6SAlexander Aring {
248610c7c6SAlexander Aring memcpy(dst, &addr->s6_addr[pfx], IPV6_PFXTAIL_LEN(pfx));
258610c7c6SAlexander Aring }
268610c7c6SAlexander Aring
ipv6_rpl_segdata_pos(const struct ipv6_rpl_sr_hdr * hdr,int i)278610c7c6SAlexander Aring static void *ipv6_rpl_segdata_pos(const struct ipv6_rpl_sr_hdr *hdr, int i)
288610c7c6SAlexander Aring {
298610c7c6SAlexander Aring return (void *)&hdr->rpl_segdata[i * IPV6_PFXTAIL_LEN(hdr->cmpri)];
308610c7c6SAlexander Aring }
318610c7c6SAlexander Aring
ipv6_rpl_srh_decompress(struct ipv6_rpl_sr_hdr * outhdr,const struct ipv6_rpl_sr_hdr * inhdr,const struct in6_addr * daddr,unsigned char n)328610c7c6SAlexander Aring void ipv6_rpl_srh_decompress(struct ipv6_rpl_sr_hdr *outhdr,
338610c7c6SAlexander Aring const struct ipv6_rpl_sr_hdr *inhdr,
348610c7c6SAlexander Aring const struct in6_addr *daddr, unsigned char n)
35*4e006c7aSAlexander Aring {
36*4e006c7aSAlexander Aring int i;
378610c7c6SAlexander Aring
388610c7c6SAlexander Aring outhdr->nexthdr = inhdr->nexthdr;
398610c7c6SAlexander Aring outhdr->hdrlen = (((n + 1) * sizeof(struct in6_addr)) >> 3);
408610c7c6SAlexander Aring outhdr->pad = 0;
418610c7c6SAlexander Aring outhdr->type = inhdr->type;
428610c7c6SAlexander Aring outhdr->segments_left = inhdr->segments_left;
438610c7c6SAlexander Aring outhdr->cmpri = 0;
448610c7c6SAlexander Aring outhdr->cmpre = 0;
458610c7c6SAlexander Aring
468610c7c6SAlexander Aring for (i = 0; i < n; i++)
478610c7c6SAlexander Aring ipv6_rpl_addr_decompress(&outhdr->rpl_segaddr[i], daddr,
488610c7c6SAlexander Aring ipv6_rpl_segdata_pos(inhdr, i),
498610c7c6SAlexander Aring inhdr->cmpri);
508610c7c6SAlexander Aring
518610c7c6SAlexander Aring ipv6_rpl_addr_decompress(&outhdr->rpl_segaddr[n], daddr,
528610c7c6SAlexander Aring ipv6_rpl_segdata_pos(inhdr, n),
53a7f9a6f4SAlexander Aring inhdr->cmpre);
548610c7c6SAlexander Aring }
558610c7c6SAlexander Aring
ipv6_rpl_srh_calc_cmpri(const struct ipv6_rpl_sr_hdr * inhdr,const struct in6_addr * daddr,unsigned char n)568610c7c6SAlexander Aring static unsigned char ipv6_rpl_srh_calc_cmpri(const struct ipv6_rpl_sr_hdr *inhdr,
578610c7c6SAlexander Aring const struct in6_addr *daddr,
588610c7c6SAlexander Aring unsigned char n)
598610c7c6SAlexander Aring {
608610c7c6SAlexander Aring unsigned char plen;
618610c7c6SAlexander Aring int i;
628610c7c6SAlexander Aring
638610c7c6SAlexander Aring for (plen = 0; plen < sizeof(*daddr); plen++) {
648610c7c6SAlexander Aring for (i = 0; i < n; i++) {
658610c7c6SAlexander Aring if (daddr->s6_addr[plen] !=
668610c7c6SAlexander Aring inhdr->rpl_segaddr[i].s6_addr[plen])
678610c7c6SAlexander Aring return plen;
688610c7c6SAlexander Aring }
698610c7c6SAlexander Aring }
708610c7c6SAlexander Aring
71a7f9a6f4SAlexander Aring return IPV6_RPL_BEST_ADDR_COMPRESSION;
728610c7c6SAlexander Aring }
738610c7c6SAlexander Aring
ipv6_rpl_srh_calc_cmpre(const struct in6_addr * daddr,const struct in6_addr * last_segment)748610c7c6SAlexander Aring static unsigned char ipv6_rpl_srh_calc_cmpre(const struct in6_addr *daddr,
758610c7c6SAlexander Aring const struct in6_addr *last_segment)
768610c7c6SAlexander Aring {
778610c7c6SAlexander Aring unsigned int plen;
7862e69776SAlexander Aring
798610c7c6SAlexander Aring for (plen = 0; plen < sizeof(*daddr); plen++) {
808610c7c6SAlexander Aring if (daddr->s6_addr[plen] != last_segment->s6_addr[plen])
818610c7c6SAlexander Aring return plen;
828610c7c6SAlexander Aring }
838610c7c6SAlexander Aring
848610c7c6SAlexander Aring return IPV6_RPL_BEST_ADDR_COMPRESSION;
858610c7c6SAlexander Aring }
868610c7c6SAlexander Aring
ipv6_rpl_srh_compress(struct ipv6_rpl_sr_hdr * outhdr,const struct ipv6_rpl_sr_hdr * inhdr,const struct in6_addr * daddr,unsigned char n)878610c7c6SAlexander Aring void ipv6_rpl_srh_compress(struct ipv6_rpl_sr_hdr *outhdr,
8862e69776SAlexander Aring const struct ipv6_rpl_sr_hdr *inhdr,
898610c7c6SAlexander Aring const struct in6_addr *daddr, unsigned char n)
908610c7c6SAlexander Aring {
9162e69776SAlexander Aring unsigned char cmpri, cmpre;
928610c7c6SAlexander Aring size_t seglen;
938610c7c6SAlexander Aring int i;
948610c7c6SAlexander Aring
958610c7c6SAlexander Aring cmpri = ipv6_rpl_srh_calc_cmpri(inhdr, daddr, n);
968610c7c6SAlexander Aring cmpre = ipv6_rpl_srh_calc_cmpre(daddr, &inhdr->rpl_segaddr[n]);
978610c7c6SAlexander Aring
988610c7c6SAlexander Aring outhdr->nexthdr = inhdr->nexthdr;
998610c7c6SAlexander Aring seglen = (n * IPV6_PFXTAIL_LEN(cmpri)) + IPV6_PFXTAIL_LEN(cmpre);
1008610c7c6SAlexander Aring outhdr->hdrlen = seglen >> 3;
1018610c7c6SAlexander Aring if (seglen & 0x7) {
1028610c7c6SAlexander Aring outhdr->hdrlen++;
1038610c7c6SAlexander Aring outhdr->pad = 8 - (seglen & 0x7);
1048610c7c6SAlexander Aring } else {
1058610c7c6SAlexander Aring outhdr->pad = 0;
1068610c7c6SAlexander Aring }
1078610c7c6SAlexander Aring outhdr->type = inhdr->type;
1088610c7c6SAlexander Aring outhdr->segments_left = inhdr->segments_left;
1098610c7c6SAlexander Aring outhdr->cmpri = cmpri;
1108610c7c6SAlexander Aring outhdr->cmpre = cmpre;
1118610c7c6SAlexander Aring
1128610c7c6SAlexander Aring for (i = 0; i < n; i++)
1138610c7c6SAlexander Aring ipv6_rpl_addr_compress(ipv6_rpl_segdata_pos(outhdr, i),
1148610c7c6SAlexander Aring &inhdr->rpl_segaddr[i], cmpri);
1158610c7c6SAlexander Aring
1168610c7c6SAlexander Aring ipv6_rpl_addr_compress(ipv6_rpl_segdata_pos(outhdr, n),
1178610c7c6SAlexander Aring &inhdr->rpl_segaddr[n], cmpre);
1188610c7c6SAlexander Aring }
119a7f9a6f4SAlexander Aring