net: ipv6: add support for rpl sr exthdr
This patch adds rpl source routing receive handling. Everything works only if sysconf "rpl_seg_enabled" and source routing is enabled. Mostly the same behaviour as IPv6 segmentation routing. To handle compression and uncompression a rpl.c file is created which contains the necessary functionality. The receive handling will also care about IPv6 encapsulated so far it's specified as possible nexthdr in RFC 6554. Signed-off-by: Alexander Aring <alex.aring@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									f37c605936
								
							
						
					
					
						commit
						8610c7c6e3
					
				| @ -74,6 +74,7 @@ struct ipv6_devconf { | ||||
| 	__u32		addr_gen_mode; | ||||
| 	__s32		disable_policy; | ||||
| 	__s32           ndisc_tclass; | ||||
| 	__s32		rpl_seg_enabled; | ||||
| 
 | ||||
| 	struct ctl_table_header *sysctl_header; | ||||
| }; | ||||
|  | ||||
							
								
								
									
										34
									
								
								include/net/rpl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								include/net/rpl.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | ||||
| /* SPDX-License-Identifier: GPL-2.0-or-later */ | ||||
| /*
 | ||||
|  *  RPL implementation | ||||
|  * | ||||
|  *  Author: | ||||
|  *  (C) 2020 Alexander Aring <alex.aring@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _NET_RPL_H | ||||
| #define _NET_RPL_H | ||||
| 
 | ||||
| #include <linux/rpl.h> | ||||
| 
 | ||||
| /* Worst decompression memory usage ipv6 address (16) + pad 7 */ | ||||
| #define IPV6_RPL_SRH_WORST_SWAP_SIZE (sizeof(struct in6_addr) + 7) | ||||
| 
 | ||||
| static inline size_t ipv6_rpl_srh_alloc_size(unsigned char n) | ||||
| { | ||||
| 	return sizeof(struct ipv6_rpl_sr_hdr) + | ||||
| 		((n + 1) * sizeof(struct in6_addr)); | ||||
| } | ||||
| 
 | ||||
| size_t ipv6_rpl_srh_size(unsigned char n, unsigned char cmpri, | ||||
| 			 unsigned char cmpre); | ||||
| 
 | ||||
| void 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); | ||||
| 
 | ||||
| void 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); | ||||
| 
 | ||||
| #endif /* _NET_RPL_H */ | ||||
| @ -40,6 +40,7 @@ struct in6_ifreq { | ||||
| #define IPV6_SRCRT_STRICT	0x01	/* Deprecated; will be removed */ | ||||
| #define IPV6_SRCRT_TYPE_0	0	/* Deprecated; will be removed */ | ||||
| #define IPV6_SRCRT_TYPE_2	2	/* IPv6 type 2 Routing Header	*/ | ||||
| #define IPV6_SRCRT_TYPE_3	3	/* RPL Segment Routing with IPv6 */ | ||||
| #define IPV6_SRCRT_TYPE_4	4	/* Segment Routing with IPv6 */ | ||||
| 
 | ||||
| /*
 | ||||
| @ -187,6 +188,7 @@ enum { | ||||
| 	DEVCONF_DISABLE_POLICY, | ||||
| 	DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN, | ||||
| 	DEVCONF_NDISC_TCLASS, | ||||
| 	DEVCONF_RPL_SEG_ENABLED, | ||||
| 	DEVCONF_MAX | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -10,7 +10,7 @@ ipv6-objs :=	af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \ | ||||
| 		route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \
 | ||||
| 		raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o ping.o \
 | ||||
| 		exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o \
 | ||||
| 		udp_offload.o seg6.o fib6_notifier.o | ||||
| 		udp_offload.o seg6.o fib6_notifier.o rpl.o | ||||
| 
 | ||||
| ipv6-offload :=	ip6_offload.o tcpv6_offload.o exthdrs_offload.o | ||||
| 
 | ||||
|  | ||||
| @ -236,6 +236,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { | ||||
| 	.enhanced_dad           = 1, | ||||
| 	.addr_gen_mode		= IN6_ADDR_GEN_MODE_EUI64, | ||||
| 	.disable_policy		= 0, | ||||
| 	.rpl_seg_enabled	= 0, | ||||
| }; | ||||
| 
 | ||||
| static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { | ||||
| @ -290,6 +291,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { | ||||
| 	.enhanced_dad           = 1, | ||||
| 	.addr_gen_mode		= IN6_ADDR_GEN_MODE_EUI64, | ||||
| 	.disable_policy		= 0, | ||||
| 	.rpl_seg_enabled	= 0, | ||||
| }; | ||||
| 
 | ||||
| /* Check if link is ready: is it up and is a valid qdisc available */ | ||||
| @ -5520,6 +5522,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, | ||||
| 	array[DEVCONF_ADDR_GEN_MODE] = cnf->addr_gen_mode; | ||||
| 	array[DEVCONF_DISABLE_POLICY] = cnf->disable_policy; | ||||
| 	array[DEVCONF_NDISC_TCLASS] = cnf->ndisc_tclass; | ||||
| 	array[DEVCONF_RPL_SEG_ENABLED] = cnf->rpl_seg_enabled; | ||||
| } | ||||
| 
 | ||||
| static inline size_t inet6_ifla6_size(void) | ||||
| @ -6900,6 +6903,13 @@ static const struct ctl_table addrconf_sysctl[] = { | ||||
| 		.extra1		= (void *)SYSCTL_ZERO, | ||||
| 		.extra2		= (void *)&two_five_five, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.procname	= "rpl_seg_enabled", | ||||
| 		.data		= &ipv6_devconf.rpl_seg_enabled, | ||||
| 		.maxlen		= sizeof(int), | ||||
| 		.mode		= 0644, | ||||
| 		.proc_handler	= proc_dointvec, | ||||
| 	}, | ||||
| 	{ | ||||
| 		/* sentinel */ | ||||
| 	} | ||||
|  | ||||
| @ -48,6 +48,7 @@ | ||||
| #ifdef CONFIG_IPV6_SEG6_HMAC | ||||
| #include <net/seg6_hmac.h> | ||||
| #endif | ||||
| #include <net/rpl.h> | ||||
| 
 | ||||
| #include <linux/uaccess.h> | ||||
| 
 | ||||
| @ -468,6 +469,195 @@ looped_back: | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| static int ipv6_rpl_srh_rcv(struct sk_buff *skb) | ||||
| { | ||||
| 	struct ipv6_rpl_sr_hdr *hdr, *ohdr, *chdr; | ||||
| 	struct inet6_skb_parm *opt = IP6CB(skb); | ||||
| 	struct net *net = dev_net(skb->dev); | ||||
| 	struct inet6_dev *idev; | ||||
| 	struct ipv6hdr *oldhdr; | ||||
| 	struct in6_addr addr; | ||||
| 	unsigned char *buf; | ||||
| 	int accept_rpl_seg; | ||||
| 	int i, err; | ||||
| 	u64 n = 0; | ||||
| 	u32 r; | ||||
| 
 | ||||
| 	idev = __in6_dev_get(skb->dev); | ||||
| 
 | ||||
| 	accept_rpl_seg = net->ipv6.devconf_all->rpl_seg_enabled; | ||||
| 	if (accept_rpl_seg > idev->cnf.rpl_seg_enabled) | ||||
| 		accept_rpl_seg = idev->cnf.rpl_seg_enabled; | ||||
| 
 | ||||
| 	if (!accept_rpl_seg) { | ||||
| 		kfree_skb(skb); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| looped_back: | ||||
| 	hdr = (struct ipv6_rpl_sr_hdr *)skb_transport_header(skb); | ||||
| 
 | ||||
| 	if (hdr->segments_left == 0) { | ||||
| 		if (hdr->nexthdr == NEXTHDR_IPV6) { | ||||
| 			int offset = (hdr->hdrlen + 1) << 3; | ||||
| 
 | ||||
| 			skb_postpull_rcsum(skb, skb_network_header(skb), | ||||
| 					   skb_network_header_len(skb)); | ||||
| 
 | ||||
| 			if (!pskb_pull(skb, offset)) { | ||||
| 				kfree_skb(skb); | ||||
| 				return -1; | ||||
| 			} | ||||
| 			skb_postpull_rcsum(skb, skb_transport_header(skb), | ||||
| 					   offset); | ||||
| 
 | ||||
| 			skb_reset_network_header(skb); | ||||
| 			skb_reset_transport_header(skb); | ||||
| 			skb->encapsulation = 0; | ||||
| 
 | ||||
| 			__skb_tunnel_rx(skb, skb->dev, net); | ||||
| 
 | ||||
| 			netif_rx(skb); | ||||
| 			return -1; | ||||
| 		} | ||||
| 
 | ||||
| 		opt->srcrt = skb_network_header_len(skb); | ||||
| 		opt->lastopt = opt->srcrt; | ||||
| 		skb->transport_header += (hdr->hdrlen + 1) << 3; | ||||
| 		opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb); | ||||
| 
 | ||||
| 		return 1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!pskb_may_pull(skb, sizeof(*hdr))) { | ||||
| 		kfree_skb(skb); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	n = (hdr->hdrlen << 3) - hdr->pad - (16 - hdr->cmpre); | ||||
| 	r = do_div(n, (16 - hdr->cmpri)); | ||||
| 	/* checks if calculation was without remainder and n fits into
 | ||||
| 	 * unsigned char which is segments_left field. Should not be | ||||
| 	 * higher than that. | ||||
| 	 */ | ||||
| 	if (r || (n + 1) > 255) { | ||||
| 		kfree_skb(skb); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (hdr->segments_left > n + 1) { | ||||
| 		__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); | ||||
| 		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, | ||||
| 				  ((&hdr->segments_left) - | ||||
| 				   skb_network_header(skb))); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (skb_cloned(skb)) { | ||||
| 		if (pskb_expand_head(skb, IPV6_RPL_SRH_WORST_SWAP_SIZE, 0, | ||||
| 				     GFP_ATOMIC)) { | ||||
| 			__IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), | ||||
| 					IPSTATS_MIB_OUTDISCARDS); | ||||
| 			kfree_skb(skb); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} else { | ||||
| 		err = skb_cow_head(skb, IPV6_RPL_SRH_WORST_SWAP_SIZE); | ||||
| 		if (unlikely(err)) { | ||||
| 			kfree_skb(skb); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	hdr = (struct ipv6_rpl_sr_hdr *)skb_transport_header(skb); | ||||
| 
 | ||||
| 	if (!pskb_may_pull(skb, ipv6_rpl_srh_size(n, hdr->cmpri, | ||||
| 						  hdr->cmpre))) { | ||||
| 		kfree_skb(skb); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	hdr->segments_left--; | ||||
| 	i = n - hdr->segments_left; | ||||
| 
 | ||||
| 	buf = kzalloc(ipv6_rpl_srh_alloc_size(n + 1) * 2, GFP_ATOMIC); | ||||
| 	if (unlikely(!buf)) { | ||||
| 		kfree_skb(skb); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	ohdr = (struct ipv6_rpl_sr_hdr *)buf; | ||||
| 	ipv6_rpl_srh_decompress(ohdr, hdr, &ipv6_hdr(skb)->daddr, n); | ||||
| 	chdr = (struct ipv6_rpl_sr_hdr *)(buf + ((ohdr->hdrlen + 1) << 3)); | ||||
| 
 | ||||
| 	if ((ipv6_addr_type(&ipv6_hdr(skb)->daddr) & IPV6_ADDR_MULTICAST) || | ||||
| 	    (ipv6_addr_type(&ohdr->rpl_segaddr[i]) & IPV6_ADDR_MULTICAST)) { | ||||
| 		kfree_skb(skb); | ||||
| 		kfree(buf); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	err = ipv6_chk_rpl_srh_loop(net, ohdr->rpl_segaddr, n + 1); | ||||
| 	if (err) { | ||||
| 		icmpv6_send(skb, ICMPV6_PARAMPROB, 0, 0); | ||||
| 		kfree_skb(skb); | ||||
| 		kfree(buf); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	addr = ipv6_hdr(skb)->daddr; | ||||
| 	ipv6_hdr(skb)->daddr = ohdr->rpl_segaddr[i]; | ||||
| 	ohdr->rpl_segaddr[i] = addr; | ||||
| 
 | ||||
| 	ipv6_rpl_srh_compress(chdr, ohdr, &ipv6_hdr(skb)->daddr, n); | ||||
| 
 | ||||
| 	oldhdr = ipv6_hdr(skb); | ||||
| 
 | ||||
| 	skb_pull(skb, ((hdr->hdrlen + 1) << 3)); | ||||
| 	skb_postpull_rcsum(skb, oldhdr, | ||||
| 			   sizeof(struct ipv6hdr) + ((hdr->hdrlen + 1) << 3)); | ||||
| 	skb_push(skb, ((chdr->hdrlen + 1) << 3) + sizeof(struct ipv6hdr)); | ||||
| 	skb_reset_network_header(skb); | ||||
| 	skb_mac_header_rebuild(skb); | ||||
| 	skb_set_transport_header(skb, sizeof(struct ipv6hdr)); | ||||
| 
 | ||||
| 	memmove(ipv6_hdr(skb), oldhdr, sizeof(struct ipv6hdr)); | ||||
| 	memcpy(skb_transport_header(skb), chdr, (chdr->hdrlen + 1) << 3); | ||||
| 
 | ||||
| 	ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); | ||||
| 	skb_postpush_rcsum(skb, ipv6_hdr(skb), | ||||
| 			   sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3)); | ||||
| 
 | ||||
| 	kfree(buf); | ||||
| 
 | ||||
| 	skb_dst_drop(skb); | ||||
| 
 | ||||
| 	ip6_route_input(skb); | ||||
| 
 | ||||
| 	if (skb_dst(skb)->error) { | ||||
| 		dst_input(skb); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (skb_dst(skb)->dev->flags & IFF_LOOPBACK) { | ||||
| 		if (ipv6_hdr(skb)->hop_limit <= 1) { | ||||
| 			__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); | ||||
| 			icmpv6_send(skb, ICMPV6_TIME_EXCEED, | ||||
| 				    ICMPV6_EXC_HOPLIMIT, 0); | ||||
| 			kfree_skb(skb); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		ipv6_hdr(skb)->hop_limit--; | ||||
| 
 | ||||
| 		skb_pull(skb, sizeof(struct ipv6hdr)); | ||||
| 		goto looped_back; | ||||
| 	} | ||||
| 
 | ||||
| 	dst_input(skb); | ||||
| 
 | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| /********************************
 | ||||
|   Routing header. | ||||
|  ********************************/ | ||||
| @ -506,9 +696,16 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb) | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* segment routing */ | ||||
| 	if (hdr->type == IPV6_SRCRT_TYPE_4) | ||||
| 	switch (hdr->type) { | ||||
| 	case IPV6_SRCRT_TYPE_4: | ||||
| 		/* segment routing */ | ||||
| 		return ipv6_srh_rcv(skb); | ||||
| 	case IPV6_SRCRT_TYPE_3: | ||||
| 		/* rpl segment routing */ | ||||
| 		return ipv6_rpl_srh_rcv(skb); | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| looped_back: | ||||
| 	if (hdr->segments_left == 0) { | ||||
|  | ||||
							
								
								
									
										123
									
								
								net/ipv6/rpl.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								net/ipv6/rpl.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,123 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0-only
 | ||||
| /**
 | ||||
|  * Authors: | ||||
|  * (C) 2020 Alexander Aring <alex.aring@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| #include <net/ipv6.h> | ||||
| #include <net/rpl.h> | ||||
| 
 | ||||
| #define IPV6_PFXTAIL_LEN(x) (sizeof(struct in6_addr) - (x)) | ||||
| 
 | ||||
| static void ipv6_rpl_addr_decompress(struct in6_addr *dst, | ||||
| 				     const struct in6_addr *daddr, | ||||
| 				     const void *post, unsigned char pfx) | ||||
| { | ||||
| 	memcpy(dst, daddr, pfx); | ||||
| 	memcpy(&dst->s6_addr[pfx], post, IPV6_PFXTAIL_LEN(pfx)); | ||||
| } | ||||
| 
 | ||||
| static void ipv6_rpl_addr_compress(void *dst, const struct in6_addr *addr, | ||||
| 				   unsigned char pfx) | ||||
| { | ||||
| 	memcpy(dst, &addr->s6_addr[pfx], IPV6_PFXTAIL_LEN(pfx)); | ||||
| } | ||||
| 
 | ||||
| static void *ipv6_rpl_segdata_pos(const struct ipv6_rpl_sr_hdr *hdr, int i) | ||||
| { | ||||
| 	return (void *)&hdr->rpl_segdata[i * IPV6_PFXTAIL_LEN(hdr->cmpri)]; | ||||
| } | ||||
| 
 | ||||
| size_t ipv6_rpl_srh_size(unsigned char n, unsigned char cmpri, | ||||
| 			 unsigned char cmpre) | ||||
| { | ||||
| 	return (n * IPV6_PFXTAIL_LEN(cmpri)) + IPV6_PFXTAIL_LEN(cmpre); | ||||
| } | ||||
| 
 | ||||
| void 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) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	outhdr->nexthdr = inhdr->nexthdr; | ||||
| 	outhdr->hdrlen = (((n + 1) * sizeof(struct in6_addr)) >> 3); | ||||
| 	outhdr->pad = 0; | ||||
| 	outhdr->type = inhdr->type; | ||||
| 	outhdr->segments_left = inhdr->segments_left; | ||||
| 	outhdr->cmpri = 0; | ||||
| 	outhdr->cmpre = 0; | ||||
| 
 | ||||
| 	for (i = 0; i <= n; i++) | ||||
| 		ipv6_rpl_addr_decompress(&outhdr->rpl_segaddr[i], daddr, | ||||
| 					 ipv6_rpl_segdata_pos(inhdr, i), | ||||
| 					 inhdr->cmpri); | ||||
| 
 | ||||
| 	ipv6_rpl_addr_decompress(&outhdr->rpl_segaddr[n], daddr, | ||||
| 				 ipv6_rpl_segdata_pos(inhdr, n), | ||||
| 				 inhdr->cmpre); | ||||
| } | ||||
| 
 | ||||
| static unsigned char ipv6_rpl_srh_calc_cmpri(const struct ipv6_rpl_sr_hdr *inhdr, | ||||
| 					     const struct in6_addr *daddr, | ||||
| 					     unsigned char n) | ||||
| { | ||||
| 	unsigned char plen; | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (plen = 0; plen < sizeof(*daddr); plen++) { | ||||
| 		for (i = 0; i <= n; i++) { | ||||
| 			if (daddr->s6_addr[plen] != | ||||
| 			    inhdr->rpl_segaddr[i].s6_addr[plen]) | ||||
| 				return plen; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return plen; | ||||
| } | ||||
| 
 | ||||
| static unsigned char ipv6_rpl_srh_calc_cmpre(const struct in6_addr *daddr, | ||||
| 					     const struct in6_addr *last_segment) | ||||
| { | ||||
| 	unsigned int plen; | ||||
| 
 | ||||
| 	for (plen = 0; plen < sizeof(*daddr); plen++) { | ||||
| 		if (daddr->s6_addr[plen] != last_segment->s6_addr[plen]) | ||||
| 			break; | ||||
| 	} | ||||
| 
 | ||||
| 	return plen; | ||||
| } | ||||
| 
 | ||||
| void 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) | ||||
| { | ||||
| 	unsigned char cmpri, cmpre; | ||||
| 	size_t seglen; | ||||
| 	int i; | ||||
| 
 | ||||
| 	cmpri = ipv6_rpl_srh_calc_cmpri(inhdr, daddr, n); | ||||
| 	cmpre = ipv6_rpl_srh_calc_cmpre(daddr, &inhdr->rpl_segaddr[n]); | ||||
| 
 | ||||
| 	outhdr->nexthdr = inhdr->nexthdr; | ||||
| 	seglen = (n * IPV6_PFXTAIL_LEN(cmpri)) + IPV6_PFXTAIL_LEN(cmpre); | ||||
| 	outhdr->hdrlen = seglen >> 3; | ||||
| 	if (seglen & 0x7) { | ||||
| 		outhdr->hdrlen++; | ||||
| 		outhdr->pad = 8 - (seglen & 0x7); | ||||
| 	} else { | ||||
| 		outhdr->pad = 0; | ||||
| 	} | ||||
| 	outhdr->type = inhdr->type; | ||||
| 	outhdr->segments_left = inhdr->segments_left; | ||||
| 	outhdr->cmpri = cmpri; | ||||
| 	outhdr->cmpre = cmpre; | ||||
| 
 | ||||
| 	for (i = 0; i <= n; i++) | ||||
| 		ipv6_rpl_addr_compress(ipv6_rpl_segdata_pos(outhdr, i), | ||||
| 				       &inhdr->rpl_segaddr[i], cmpri); | ||||
| 
 | ||||
| 	ipv6_rpl_addr_compress(ipv6_rpl_segdata_pos(outhdr, n), | ||||
| 			       &inhdr->rpl_segaddr[n], cmpre); | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user