xref: /linux/net/ipv6/xfrm6_input.c (revision e0bf6c5ca2d3281f231c5f0c9bf145e9513644de)
1 /*
2  * xfrm6_input.c: based on net/ipv4/xfrm4_input.c
3  *
4  * Authors:
5  *	Mitsuru KANDA @USAGI
6  *	Kazunori MIYAZAWA @USAGI
7  *	Kunihiro Ishiguro <kunihiro@ipinfusion.com>
8  *	YOSHIFUJI Hideaki @USAGI
9  *		IPv6 support
10  */
11 
12 #include <linux/module.h>
13 #include <linux/string.h>
14 #include <linux/netfilter.h>
15 #include <linux/netfilter_ipv6.h>
16 #include <net/ipv6.h>
17 #include <net/xfrm.h>
18 
19 int xfrm6_extract_input(struct xfrm_state *x, struct sk_buff *skb)
20 {
21 	return xfrm6_extract_header(skb);
22 }
23 
24 int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi)
25 {
26 	XFRM_SPI_SKB_CB(skb)->family = AF_INET6;
27 	XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct ipv6hdr, daddr);
28 	return xfrm_input(skb, nexthdr, spi, 0);
29 }
30 EXPORT_SYMBOL(xfrm6_rcv_spi);
31 
32 int xfrm6_transport_finish(struct sk_buff *skb, int async)
33 {
34 	skb_network_header(skb)[IP6CB(skb)->nhoff] =
35 		XFRM_MODE_SKB_CB(skb)->protocol;
36 
37 #ifndef CONFIG_NETFILTER
38 	if (!async)
39 		return 1;
40 #endif
41 
42 	ipv6_hdr(skb)->payload_len = htons(skb->len);
43 	__skb_push(skb, skb->data - skb_network_header(skb));
44 
45 	NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, skb, skb->dev, NULL,
46 		ip6_rcv_finish);
47 	return -1;
48 }
49 
50 int xfrm6_rcv(struct sk_buff *skb)
51 {
52 	return xfrm6_rcv_spi(skb, skb_network_header(skb)[IP6CB(skb)->nhoff],
53 			     0);
54 }
55 EXPORT_SYMBOL(xfrm6_rcv);
56 
57 int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr,
58 		     xfrm_address_t *saddr, u8 proto)
59 {
60 	struct net *net = dev_net(skb->dev);
61 	struct xfrm_state *x = NULL;
62 	int i = 0;
63 
64 	/* Allocate new secpath or COW existing one. */
65 	if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
66 		struct sec_path *sp;
67 
68 		sp = secpath_dup(skb->sp);
69 		if (!sp) {
70 			XFRM_INC_STATS(net, LINUX_MIB_XFRMINERROR);
71 			goto drop;
72 		}
73 		if (skb->sp)
74 			secpath_put(skb->sp);
75 		skb->sp = sp;
76 	}
77 
78 	if (1 + skb->sp->len == XFRM_MAX_DEPTH) {
79 		XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR);
80 		goto drop;
81 	}
82 
83 	for (i = 0; i < 3; i++) {
84 		xfrm_address_t *dst, *src;
85 
86 		switch (i) {
87 		case 0:
88 			dst = daddr;
89 			src = saddr;
90 			break;
91 		case 1:
92 			/* lookup state with wild-card source address */
93 			dst = daddr;
94 			src = (xfrm_address_t *)&in6addr_any;
95 			break;
96 		default:
97 			/* lookup state with wild-card addresses */
98 			dst = (xfrm_address_t *)&in6addr_any;
99 			src = (xfrm_address_t *)&in6addr_any;
100 			break;
101 		}
102 
103 		x = xfrm_state_lookup_byaddr(net, skb->mark, dst, src, proto, AF_INET6);
104 		if (!x)
105 			continue;
106 
107 		spin_lock(&x->lock);
108 
109 		if ((!i || (x->props.flags & XFRM_STATE_WILDRECV)) &&
110 		    likely(x->km.state == XFRM_STATE_VALID) &&
111 		    !xfrm_state_check_expire(x)) {
112 			spin_unlock(&x->lock);
113 			if (x->type->input(x, skb) > 0) {
114 				/* found a valid state */
115 				break;
116 			}
117 		} else
118 			spin_unlock(&x->lock);
119 
120 		xfrm_state_put(x);
121 		x = NULL;
122 	}
123 
124 	if (!x) {
125 		XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES);
126 		xfrm_audit_state_notfound_simple(skb, AF_INET6);
127 		goto drop;
128 	}
129 
130 	skb->sp->xvec[skb->sp->len++] = x;
131 
132 	spin_lock(&x->lock);
133 
134 	x->curlft.bytes += skb->len;
135 	x->curlft.packets++;
136 
137 	spin_unlock(&x->lock);
138 
139 	return 1;
140 
141 drop:
142 	return -1;
143 }
144 EXPORT_SYMBOL(xfrm6_input_addr);
145