~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/xfrm/xfrm_input.c

Version: ~ [ linux-5.7-rc7 ] ~ [ linux-5.6.14 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.42 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.124 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.181 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.224 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.224 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.84 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * xfrm_input.c
  3  *
  4  * Changes:
  5  *      YOSHIFUJI Hideaki @USAGI
  6  *              Split up af-specific portion
  7  *
  8  */
  9 
 10 #include <linux/slab.h>
 11 #include <linux/module.h>
 12 #include <linux/netdevice.h>
 13 #include <net/dst.h>
 14 #include <net/ip.h>
 15 #include <net/xfrm.h>
 16 
 17 static struct kmem_cache *secpath_cachep __read_mostly;
 18 
 19 static DEFINE_SPINLOCK(xfrm_input_afinfo_lock);
 20 static struct xfrm_input_afinfo __rcu *xfrm_input_afinfo[NPROTO];
 21 
 22 int xfrm_input_register_afinfo(struct xfrm_input_afinfo *afinfo)
 23 {
 24         int err = 0;
 25 
 26         if (unlikely(afinfo == NULL))
 27                 return -EINVAL;
 28         if (unlikely(afinfo->family >= NPROTO))
 29                 return -EAFNOSUPPORT;
 30         spin_lock_bh(&xfrm_input_afinfo_lock);
 31         if (unlikely(xfrm_input_afinfo[afinfo->family] != NULL))
 32                 err = -ENOBUFS;
 33         else
 34                 rcu_assign_pointer(xfrm_input_afinfo[afinfo->family], afinfo);
 35         spin_unlock_bh(&xfrm_input_afinfo_lock);
 36         return err;
 37 }
 38 EXPORT_SYMBOL(xfrm_input_register_afinfo);
 39 
 40 int xfrm_input_unregister_afinfo(struct xfrm_input_afinfo *afinfo)
 41 {
 42         int err = 0;
 43 
 44         if (unlikely(afinfo == NULL))
 45                 return -EINVAL;
 46         if (unlikely(afinfo->family >= NPROTO))
 47                 return -EAFNOSUPPORT;
 48         spin_lock_bh(&xfrm_input_afinfo_lock);
 49         if (likely(xfrm_input_afinfo[afinfo->family] != NULL)) {
 50                 if (unlikely(xfrm_input_afinfo[afinfo->family] != afinfo))
 51                         err = -EINVAL;
 52                 else
 53                         RCU_INIT_POINTER(xfrm_input_afinfo[afinfo->family], NULL);
 54         }
 55         spin_unlock_bh(&xfrm_input_afinfo_lock);
 56         synchronize_rcu();
 57         return err;
 58 }
 59 EXPORT_SYMBOL(xfrm_input_unregister_afinfo);
 60 
 61 static struct xfrm_input_afinfo *xfrm_input_get_afinfo(unsigned int family)
 62 {
 63         struct xfrm_input_afinfo *afinfo;
 64 
 65         if (unlikely(family >= NPROTO))
 66                 return NULL;
 67         rcu_read_lock();
 68         afinfo = rcu_dereference(xfrm_input_afinfo[family]);
 69         if (unlikely(!afinfo))
 70                 rcu_read_unlock();
 71         return afinfo;
 72 }
 73 
 74 static void xfrm_input_put_afinfo(struct xfrm_input_afinfo *afinfo)
 75 {
 76         rcu_read_unlock();
 77 }
 78 
 79 static int xfrm_rcv_cb(struct sk_buff *skb, unsigned int family, u8 protocol,
 80                        int err)
 81 {
 82         int ret;
 83         struct xfrm_input_afinfo *afinfo = xfrm_input_get_afinfo(family);
 84 
 85         if (!afinfo)
 86                 return -EAFNOSUPPORT;
 87 
 88         ret = afinfo->callback(skb, protocol, err);
 89         xfrm_input_put_afinfo(afinfo);
 90 
 91         return ret;
 92 }
 93 
 94 void __secpath_destroy(struct sec_path *sp)
 95 {
 96         int i;
 97         for (i = 0; i < sp->len; i++)
 98                 xfrm_state_put(sp->xvec[i]);
 99         kmem_cache_free(secpath_cachep, sp);
100 }
101 EXPORT_SYMBOL(__secpath_destroy);
102 
103 struct sec_path *secpath_dup(struct sec_path *src)
104 {
105         struct sec_path *sp;
106 
107         sp = kmem_cache_alloc(secpath_cachep, GFP_ATOMIC);
108         if (!sp)
109                 return NULL;
110 
111         sp->len = 0;
112         if (src) {
113                 int i;
114 
115                 memcpy(sp, src, sizeof(*sp));
116                 for (i = 0; i < sp->len; i++)
117                         xfrm_state_hold(sp->xvec[i]);
118         }
119         atomic_set(&sp->refcnt, 1);
120         return sp;
121 }
122 EXPORT_SYMBOL(secpath_dup);
123 
124 /* Fetch spi and seq from ipsec header */
125 
126 int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq)
127 {
128         int offset, offset_seq;
129         int hlen;
130 
131         switch (nexthdr) {
132         case IPPROTO_AH:
133                 hlen = sizeof(struct ip_auth_hdr);
134                 offset = offsetof(struct ip_auth_hdr, spi);
135                 offset_seq = offsetof(struct ip_auth_hdr, seq_no);
136                 break;
137         case IPPROTO_ESP:
138                 hlen = sizeof(struct ip_esp_hdr);
139                 offset = offsetof(struct ip_esp_hdr, spi);
140                 offset_seq = offsetof(struct ip_esp_hdr, seq_no);
141                 break;
142         case IPPROTO_COMP:
143                 if (!pskb_may_pull(skb, sizeof(struct ip_comp_hdr)))
144                         return -EINVAL;
145                 *spi = htonl(ntohs(*(__be16 *)(skb_transport_header(skb) + 2)));
146                 *seq = 0;
147                 return 0;
148         default:
149                 return 1;
150         }
151 
152         if (!pskb_may_pull(skb, hlen))
153                 return -EINVAL;
154 
155         *spi = *(__be32 *)(skb_transport_header(skb) + offset);
156         *seq = *(__be32 *)(skb_transport_header(skb) + offset_seq);
157         return 0;
158 }
159 
160 int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb)
161 {
162         struct xfrm_mode *inner_mode = x->inner_mode;
163         int err;
164 
165         err = x->outer_mode->afinfo->extract_input(x, skb);
166         if (err)
167                 return err;
168 
169         if (x->sel.family == AF_UNSPEC) {
170                 inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol);
171                 if (inner_mode == NULL)
172                         return -EAFNOSUPPORT;
173         }
174 
175         skb->protocol = inner_mode->afinfo->eth_proto;
176         return inner_mode->input2(x, skb);
177 }
178 EXPORT_SYMBOL(xfrm_prepare_input);
179 
180 int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
181 {
182         struct net *net = dev_net(skb->dev);
183         int err;
184         __be32 seq;
185         __be32 seq_hi;
186         struct xfrm_state *x = NULL;
187         xfrm_address_t *daddr;
188         struct xfrm_mode *inner_mode;
189         unsigned int family;
190         int decaps = 0;
191         int async = 0;
192 
193         /* A negative encap_type indicates async resumption. */
194         if (encap_type < 0) {
195                 async = 1;
196                 x = xfrm_input_state(skb);
197                 seq = XFRM_SKB_CB(skb)->seq.input.low;
198                 family = x->outer_mode->afinfo->family;
199                 goto resume;
200         }
201 
202         daddr = (xfrm_address_t *)(skb_network_header(skb) +
203                                    XFRM_SPI_SKB_CB(skb)->daddroff);
204         family = XFRM_SPI_SKB_CB(skb)->family;
205 
206         /* Allocate new secpath or COW existing one. */
207         if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
208                 struct sec_path *sp;
209 
210                 sp = secpath_dup(skb->sp);
211                 if (!sp) {
212                         XFRM_INC_STATS(net, LINUX_MIB_XFRMINERROR);
213                         goto drop;
214                 }
215                 if (skb->sp)
216                         secpath_put(skb->sp);
217                 skb->sp = sp;
218         }
219 
220         seq = 0;
221         if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) {
222                 XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
223                 goto drop;
224         }
225 
226         do {
227                 if (skb->sp->len == XFRM_MAX_DEPTH) {
228                         XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR);
229                         goto drop;
230                 }
231 
232                 x = xfrm_state_lookup(net, skb->mark, daddr, spi, nexthdr, family);
233                 if (x == NULL) {
234                         XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES);
235                         xfrm_audit_state_notfound(skb, family, spi, seq);
236                         goto drop;
237                 }
238 
239                 skb->sp->xvec[skb->sp->len++] = x;
240 
241                 if (xfrm_tunnel_check(skb, x, family)) {
242                         XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR);
243                         goto drop;
244                 }
245 
246                 spin_lock(&x->lock);
247                 if (unlikely(x->km.state == XFRM_STATE_ACQ)) {
248                         XFRM_INC_STATS(net, LINUX_MIB_XFRMACQUIREERROR);
249                         goto drop_unlock;
250                 }
251 
252                 if (unlikely(x->km.state != XFRM_STATE_VALID)) {
253                         XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEINVALID);
254                         goto drop_unlock;
255                 }
256 
257                 if ((x->encap ? x->encap->encap_type : 0) != encap_type) {
258                         XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMISMATCH);
259                         goto drop_unlock;
260                 }
261 
262                 if (x->repl->check(x, skb, seq)) {
263                         XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATESEQERROR);
264                         goto drop_unlock;
265                 }
266 
267                 if (xfrm_state_check_expire(x)) {
268                         XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEEXPIRED);
269                         goto drop_unlock;
270                 }
271 
272                 spin_unlock(&x->lock);
273 
274                 seq_hi = htonl(xfrm_replay_seqhi(x, seq));
275 
276                 XFRM_SKB_CB(skb)->seq.input.low = seq;
277                 XFRM_SKB_CB(skb)->seq.input.hi = seq_hi;
278 
279                 skb_dst_force(skb);
280 
281                 nexthdr = x->type->input(x, skb);
282 
283                 if (nexthdr == -EINPROGRESS)
284                         return 0;
285 resume:
286                 spin_lock(&x->lock);
287                 if (nexthdr <= 0) {
288                         if (nexthdr == -EBADMSG) {
289                                 xfrm_audit_state_icvfail(x, skb,
290                                                          x->type->proto);
291                                 x->stats.integrity_failed++;
292                         }
293                         XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEPROTOERROR);
294                         goto drop_unlock;
295                 }
296 
297                 /* only the first xfrm gets the encap type */
298                 encap_type = 0;
299 
300                 if (async && x->repl->recheck(x, skb, seq)) {
301                         XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATESEQERROR);
302                         goto drop_unlock;
303                 }
304 
305                 x->repl->advance(x, seq);
306 
307                 x->curlft.bytes += skb->len;
308                 x->curlft.packets++;
309 
310                 spin_unlock(&x->lock);
311 
312                 XFRM_MODE_SKB_CB(skb)->protocol = nexthdr;
313 
314                 inner_mode = x->inner_mode;
315 
316                 if (x->sel.family == AF_UNSPEC) {
317                         inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol);
318                         if (inner_mode == NULL)
319                                 goto drop;
320                 }
321 
322                 if (inner_mode->input(x, skb)) {
323                         XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR);
324                         goto drop;
325                 }
326 
327                 if (x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL) {
328                         decaps = 1;
329                         break;
330                 }
331 
332                 /*
333                  * We need the inner address.  However, we only get here for
334                  * transport mode so the outer address is identical.
335                  */
336                 daddr = &x->id.daddr;
337                 family = x->outer_mode->afinfo->family;
338 
339                 err = xfrm_parse_spi(skb, nexthdr, &spi, &seq);
340                 if (err < 0) {
341                         XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
342                         goto drop;
343                 }
344         } while (!err);
345 
346         err = xfrm_rcv_cb(skb, family, x->type->proto, 0);
347         if (err)
348                 goto drop;
349 
350         nf_reset(skb);
351 
352         if (decaps) {
353                 skb_dst_drop(skb);
354                 netif_rx(skb);
355                 return 0;
356         } else {
357                 return x->inner_mode->afinfo->transport_finish(skb, async);
358         }
359 
360 drop_unlock:
361         spin_unlock(&x->lock);
362 drop:
363         xfrm_rcv_cb(skb, family, x && x->type ? x->type->proto : nexthdr, -1);
364         kfree_skb(skb);
365         return 0;
366 }
367 EXPORT_SYMBOL(xfrm_input);
368 
369 int xfrm_input_resume(struct sk_buff *skb, int nexthdr)
370 {
371         return xfrm_input(skb, nexthdr, 0, -1);
372 }
373 EXPORT_SYMBOL(xfrm_input_resume);
374 
375 void __init xfrm_input_init(void)
376 {
377         secpath_cachep = kmem_cache_create("secpath_cache",
378                                            sizeof(struct sec_path),
379                                            0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
380                                            NULL);
381 }
382 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp