xref: /openbmc/linux/net/ipv6/rpl.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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