mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	ipv6: sr: add support for SRH injection through setsockopt
This patch adds support for per-socket SRH injection with the setsockopt system call through the IPPROTO_IPV6, IPV6_RTHDR options. The SRH is pushed through the ipv6_push_nfrag_opts function. Signed-off-by: David Lebrun <david.lebrun@uclouvain.be> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									613fa3ca9e
								
							
						
					
					
						commit
						a149e7c7ce
					
				
					 2 changed files with 85 additions and 4 deletions
				
			
		| 
						 | 
				
			
			@ -864,9 +864,9 @@ int ipv6_parse_hopopts(struct sk_buff *skb)
 | 
			
		|||
 *	for headers.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
 | 
			
		||||
			    struct ipv6_rt_hdr *opt,
 | 
			
		||||
			    struct in6_addr **addr_p, struct in6_addr *saddr)
 | 
			
		||||
static void ipv6_push_rthdr0(struct sk_buff *skb, u8 *proto,
 | 
			
		||||
			     struct ipv6_rt_hdr *opt,
 | 
			
		||||
			     struct in6_addr **addr_p, struct in6_addr *saddr)
 | 
			
		||||
{
 | 
			
		||||
	struct rt0_hdr *phdr, *ihdr;
 | 
			
		||||
	int hops;
 | 
			
		||||
| 
						 | 
				
			
			@ -889,6 +889,62 @@ static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
 | 
			
		|||
	*proto = NEXTHDR_ROUTING;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ipv6_push_rthdr4(struct sk_buff *skb, u8 *proto,
 | 
			
		||||
			     struct ipv6_rt_hdr *opt,
 | 
			
		||||
			     struct in6_addr **addr_p, struct in6_addr *saddr)
 | 
			
		||||
{
 | 
			
		||||
	struct ipv6_sr_hdr *sr_phdr, *sr_ihdr;
 | 
			
		||||
	int plen, hops;
 | 
			
		||||
 | 
			
		||||
	sr_ihdr = (struct ipv6_sr_hdr *)opt;
 | 
			
		||||
	plen = (sr_ihdr->hdrlen + 1) << 3;
 | 
			
		||||
 | 
			
		||||
	sr_phdr = (struct ipv6_sr_hdr *)skb_push(skb, plen);
 | 
			
		||||
	memcpy(sr_phdr, sr_ihdr, sizeof(struct ipv6_sr_hdr));
 | 
			
		||||
 | 
			
		||||
	hops = sr_ihdr->first_segment + 1;
 | 
			
		||||
	memcpy(sr_phdr->segments + 1, sr_ihdr->segments + 1,
 | 
			
		||||
	       (hops - 1) * sizeof(struct in6_addr));
 | 
			
		||||
 | 
			
		||||
	sr_phdr->segments[0] = **addr_p;
 | 
			
		||||
	*addr_p = &sr_ihdr->segments[hops - 1];
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_IPV6_SEG6_HMAC
 | 
			
		||||
	if (sr_has_hmac(sr_phdr)) {
 | 
			
		||||
		struct net *net = NULL;
 | 
			
		||||
 | 
			
		||||
		if (skb->dev)
 | 
			
		||||
			net = dev_net(skb->dev);
 | 
			
		||||
		else if (skb->sk)
 | 
			
		||||
			net = sock_net(skb->sk);
 | 
			
		||||
 | 
			
		||||
		WARN_ON(!net);
 | 
			
		||||
 | 
			
		||||
		if (net)
 | 
			
		||||
			seg6_push_hmac(net, saddr, sr_phdr);
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	sr_phdr->nexthdr = *proto;
 | 
			
		||||
	*proto = NEXTHDR_ROUTING;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
 | 
			
		||||
			    struct ipv6_rt_hdr *opt,
 | 
			
		||||
			    struct in6_addr **addr_p, struct in6_addr *saddr)
 | 
			
		||||
{
 | 
			
		||||
	switch (opt->type) {
 | 
			
		||||
	case IPV6_SRCRT_TYPE_0:
 | 
			
		||||
		ipv6_push_rthdr0(skb, proto, opt, addr_p, saddr);
 | 
			
		||||
		break;
 | 
			
		||||
	case IPV6_SRCRT_TYPE_4:
 | 
			
		||||
		ipv6_push_rthdr4(skb, proto, opt, addr_p, saddr);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt)
 | 
			
		||||
{
 | 
			
		||||
	struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ipv6_optlen(opt));
 | 
			
		||||
| 
						 | 
				
			
			@ -1130,7 +1186,22 @@ struct in6_addr *fl6_update_dst(struct flowi6 *fl6,
 | 
			
		|||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	*orig = fl6->daddr;
 | 
			
		||||
	fl6->daddr = *((struct rt0_hdr *)opt->srcrt)->addr;
 | 
			
		||||
 | 
			
		||||
	switch (opt->srcrt->type) {
 | 
			
		||||
	case IPV6_SRCRT_TYPE_0:
 | 
			
		||||
		fl6->daddr = *((struct rt0_hdr *)opt->srcrt)->addr;
 | 
			
		||||
		break;
 | 
			
		||||
	case IPV6_SRCRT_TYPE_4:
 | 
			
		||||
	{
 | 
			
		||||
		struct ipv6_sr_hdr *srh = (struct ipv6_sr_hdr *)opt->srcrt;
 | 
			
		||||
 | 
			
		||||
		fl6->daddr = srh->segments[srh->first_segment];
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	default:
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return orig;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(fl6_update_dst);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,6 +52,7 @@
 | 
			
		|||
#include <net/udplite.h>
 | 
			
		||||
#include <net/xfrm.h>
 | 
			
		||||
#include <net/compat.h>
 | 
			
		||||
#include <net/seg6.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/uaccess.h>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -430,6 +431,15 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
 | 
			
		|||
 | 
			
		||||
				break;
 | 
			
		||||
#endif
 | 
			
		||||
			case IPV6_SRCRT_TYPE_4:
 | 
			
		||||
			{
 | 
			
		||||
				struct ipv6_sr_hdr *srh = (struct ipv6_sr_hdr *)
 | 
			
		||||
							  opt->srcrt;
 | 
			
		||||
 | 
			
		||||
				if (!seg6_validate_srh(srh, optlen))
 | 
			
		||||
					goto sticky_done;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			default:
 | 
			
		||||
				goto sticky_done;
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue