162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Authors: 462306a36Sopenharmony_ci * (C) 2020 Alexander Aring <alex.aring@gmail.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <net/ipv6.h> 862306a36Sopenharmony_ci#include <net/rpl.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define IPV6_PFXTAIL_LEN(x) (sizeof(struct in6_addr) - (x)) 1162306a36Sopenharmony_ci#define IPV6_RPL_BEST_ADDR_COMPRESSION 15 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_cistatic void ipv6_rpl_addr_decompress(struct in6_addr *dst, 1462306a36Sopenharmony_ci const struct in6_addr *daddr, 1562306a36Sopenharmony_ci const void *post, unsigned char pfx) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci memcpy(dst, daddr, pfx); 1862306a36Sopenharmony_ci memcpy(&dst->s6_addr[pfx], post, IPV6_PFXTAIL_LEN(pfx)); 1962306a36Sopenharmony_ci} 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic void ipv6_rpl_addr_compress(void *dst, const struct in6_addr *addr, 2262306a36Sopenharmony_ci unsigned char pfx) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci memcpy(dst, &addr->s6_addr[pfx], IPV6_PFXTAIL_LEN(pfx)); 2562306a36Sopenharmony_ci} 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic void *ipv6_rpl_segdata_pos(const struct ipv6_rpl_sr_hdr *hdr, int i) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci return (void *)&hdr->rpl_segdata[i * IPV6_PFXTAIL_LEN(hdr->cmpri)]; 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_civoid ipv6_rpl_srh_decompress(struct ipv6_rpl_sr_hdr *outhdr, 3362306a36Sopenharmony_ci const struct ipv6_rpl_sr_hdr *inhdr, 3462306a36Sopenharmony_ci const struct in6_addr *daddr, unsigned char n) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci int i; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci outhdr->nexthdr = inhdr->nexthdr; 3962306a36Sopenharmony_ci outhdr->hdrlen = (((n + 1) * sizeof(struct in6_addr)) >> 3); 4062306a36Sopenharmony_ci outhdr->pad = 0; 4162306a36Sopenharmony_ci outhdr->type = inhdr->type; 4262306a36Sopenharmony_ci outhdr->segments_left = inhdr->segments_left; 4362306a36Sopenharmony_ci outhdr->cmpri = 0; 4462306a36Sopenharmony_ci outhdr->cmpre = 0; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci for (i = 0; i < n; i++) 4762306a36Sopenharmony_ci ipv6_rpl_addr_decompress(&outhdr->rpl_segaddr[i], daddr, 4862306a36Sopenharmony_ci ipv6_rpl_segdata_pos(inhdr, i), 4962306a36Sopenharmony_ci inhdr->cmpri); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci ipv6_rpl_addr_decompress(&outhdr->rpl_segaddr[n], daddr, 5262306a36Sopenharmony_ci ipv6_rpl_segdata_pos(inhdr, n), 5362306a36Sopenharmony_ci inhdr->cmpre); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic unsigned char ipv6_rpl_srh_calc_cmpri(const struct ipv6_rpl_sr_hdr *inhdr, 5762306a36Sopenharmony_ci const struct in6_addr *daddr, 5862306a36Sopenharmony_ci unsigned char n) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci unsigned char plen; 6162306a36Sopenharmony_ci int i; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci for (plen = 0; plen < sizeof(*daddr); plen++) { 6462306a36Sopenharmony_ci for (i = 0; i < n; i++) { 6562306a36Sopenharmony_ci if (daddr->s6_addr[plen] != 6662306a36Sopenharmony_ci inhdr->rpl_segaddr[i].s6_addr[plen]) 6762306a36Sopenharmony_ci return plen; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci return IPV6_RPL_BEST_ADDR_COMPRESSION; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic unsigned char ipv6_rpl_srh_calc_cmpre(const struct in6_addr *daddr, 7562306a36Sopenharmony_ci const struct in6_addr *last_segment) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci unsigned int plen; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci for (plen = 0; plen < sizeof(*daddr); plen++) { 8062306a36Sopenharmony_ci if (daddr->s6_addr[plen] != last_segment->s6_addr[plen]) 8162306a36Sopenharmony_ci return plen; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return IPV6_RPL_BEST_ADDR_COMPRESSION; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_civoid ipv6_rpl_srh_compress(struct ipv6_rpl_sr_hdr *outhdr, 8862306a36Sopenharmony_ci const struct ipv6_rpl_sr_hdr *inhdr, 8962306a36Sopenharmony_ci const struct in6_addr *daddr, unsigned char n) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci unsigned char cmpri, cmpre; 9262306a36Sopenharmony_ci size_t seglen; 9362306a36Sopenharmony_ci int i; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci cmpri = ipv6_rpl_srh_calc_cmpri(inhdr, daddr, n); 9662306a36Sopenharmony_ci cmpre = ipv6_rpl_srh_calc_cmpre(daddr, &inhdr->rpl_segaddr[n]); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci outhdr->nexthdr = inhdr->nexthdr; 9962306a36Sopenharmony_ci seglen = (n * IPV6_PFXTAIL_LEN(cmpri)) + IPV6_PFXTAIL_LEN(cmpre); 10062306a36Sopenharmony_ci outhdr->hdrlen = seglen >> 3; 10162306a36Sopenharmony_ci if (seglen & 0x7) { 10262306a36Sopenharmony_ci outhdr->hdrlen++; 10362306a36Sopenharmony_ci outhdr->pad = 8 - (seglen & 0x7); 10462306a36Sopenharmony_ci } else { 10562306a36Sopenharmony_ci outhdr->pad = 0; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci outhdr->type = inhdr->type; 10862306a36Sopenharmony_ci outhdr->segments_left = inhdr->segments_left; 10962306a36Sopenharmony_ci outhdr->cmpri = cmpri; 11062306a36Sopenharmony_ci outhdr->cmpre = cmpre; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci for (i = 0; i < n; i++) 11362306a36Sopenharmony_ci ipv6_rpl_addr_compress(ipv6_rpl_segdata_pos(outhdr, i), 11462306a36Sopenharmony_ci &inhdr->rpl_segaddr[i], cmpri); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci ipv6_rpl_addr_compress(ipv6_rpl_segdata_pos(outhdr, n), 11762306a36Sopenharmony_ci &inhdr->rpl_segaddr[n], cmpre); 11862306a36Sopenharmony_ci} 119