Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  1 /*
  2  * xfrm_output.c - Common IPsec encapsulation code.
  3  *
  4  * Copyright (c) 2007 Herbert Xu <herbert@gondor.apana.org.au>
  5  *
  6  * This program is free software; you can redistribute it and/or
  7  * modify it under the terms of the GNU General Public License
  8  * as published by the Free Software Foundation; either version
  9  * 2 of the License, or (at your option) any later version.
 10  */
 11 
 12 #include <linux/errno.h>
 13 #include <linux/module.h>
 14 #include <linux/netdevice.h>
 15 #include <linux/netfilter.h>
 16 #include <linux/skbuff.h>
 17 #include <linux/spinlock.h>
 18 #include <net/dst.h>
 19 #include <net/xfrm.h>
 20 
 21 static int xfrm_output2(struct sk_buff *skb);
 22 
 23 static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
 24 {
 25         struct dst_entry *dst = skb->dst;
 26         int nhead = dst->header_len + LL_RESERVED_SPACE(dst->dev)
 27                 - skb_headroom(skb);
 28 
 29         if (nhead > 0)
 30                 return pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
 31 
 32         /* Check tail too... */
 33         return 0;
 34 }
 35 
 36 static int xfrm_output_one(struct sk_buff *skb, int err)
 37 {
 38         struct dst_entry *dst = skb->dst;
 39         struct xfrm_state *x = dst->xfrm;
 40 
 41         if (err <= 0)
 42                 goto resume;
 43 
 44         do {
 45                 err = xfrm_state_check_space(x, skb);
 46                 if (err) {
 47                         XFRM_INC_STATS(LINUX_MIB_XFRMOUTERROR);
 48                         goto error_nolock;
 49                 }
 50 
 51                 err = x->outer_mode->output(x, skb);
 52                 if (err) {
 53                         XFRM_INC_STATS(LINUX_MIB_XFRMOUTSTATEMODEERROR);
 54                         goto error_nolock;
 55                 }
 56 
 57                 spin_lock_bh(&x->lock);
 58                 err = xfrm_state_check_expire(x);
 59                 if (err) {
 60                         XFRM_INC_STATS(LINUX_MIB_XFRMOUTSTATEEXPIRED);
 61                         goto error;
 62                 }
 63 
 64                 if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
 65                         XFRM_SKB_CB(skb)->seq.output = ++x->replay.oseq;
 66                         if (unlikely(x->replay.oseq == 0)) {
 67                                 XFRM_INC_STATS(LINUX_MIB_XFRMOUTSTATESEQERROR);
 68                                 x->replay.oseq--;
 69                                 xfrm_audit_state_replay_overflow(x, skb);
 70                                 err = -EOVERFLOW;
 71                                 goto error;
 72                         }
 73                         if (xfrm_aevent_is_on())
 74                                 xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
 75                 }
 76 
 77                 x->curlft.bytes += skb->len;
 78                 x->curlft.packets++;
 79 
 80                 spin_unlock_bh(&x->lock);
 81 
 82                 err = x->type->output(x, skb);
 83                 if (err == -EINPROGRESS)
 84                         goto out_exit;
 85 
 86 resume:
 87                 if (err) {
 88                         XFRM_INC_STATS(LINUX_MIB_XFRMOUTSTATEPROTOERROR);
 89                         goto error_nolock;
 90                 }
 91 
 92                 if (!(skb->dst = dst_pop(dst))) {
 93                         XFRM_INC_STATS(LINUX_MIB_XFRMOUTERROR);
 94                         err = -EHOSTUNREACH;
 95                         goto error_nolock;
 96                 }
 97                 dst = skb->dst;
 98                 x = dst->xfrm;
 99         } while (x && !(x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL));
100 
101         err = 0;
102 
103 out_exit:
104         return err;
105 error:
106         spin_unlock_bh(&x->lock);
107 error_nolock:
108         kfree_skb(skb);
109         goto out_exit;
110 }
111 
112 int xfrm_output_resume(struct sk_buff *skb, int err)
113 {
114         while (likely((err = xfrm_output_one(skb, err)) == 0)) {
115                 struct xfrm_state *x;
116 
117                 nf_reset(skb);
118 
119                 err = skb->dst->ops->local_out(skb);
120                 if (unlikely(err != 1))
121                         goto out;
122 
123                 x = skb->dst->xfrm;
124                 if (!x)
125                         return dst_output(skb);
126 
127                 err = nf_hook(skb->dst->ops->family,
128                               NF_INET_POST_ROUTING, skb,
129                               NULL, skb->dst->dev, xfrm_output2);
130                 if (unlikely(err != 1))
131                         goto out;
132         }
133 
134         if (err == -EINPROGRESS)
135                 err = 0;
136 
137 out:
138         return err;
139 }
140 EXPORT_SYMBOL_GPL(xfrm_output_resume);
141 
142 static int xfrm_output2(struct sk_buff *skb)
143 {
144         return xfrm_output_resume(skb, 1);
145 }
146 
147 static int xfrm_output_gso(struct sk_buff *skb)
148 {
149         struct sk_buff *segs;
150 
151         segs = skb_gso_segment(skb, 0);
152         kfree_skb(skb);
153         if (unlikely(IS_ERR(segs)))
154                 return PTR_ERR(segs);
155 
156         do {
157                 struct sk_buff *nskb = segs->next;
158                 int err;
159 
160                 segs->next = NULL;
161                 err = xfrm_output2(segs);
162 
163                 if (unlikely(err)) {
164                         while ((segs = nskb)) {
165                                 nskb = segs->next;
166                                 segs->next = NULL;
167                                 kfree_skb(segs);
168                         }
169                         return err;
170                 }
171 
172                 segs = nskb;
173         } while (segs);
174 
175         return 0;
176 }
177 
178 int xfrm_output(struct sk_buff *skb)
179 {
180         int err;
181 
182         if (skb_is_gso(skb))
183                 return xfrm_output_gso(skb);
184 
185         if (skb->ip_summed == CHECKSUM_PARTIAL) {
186                 err = skb_checksum_help(skb);
187                 if (err) {
188                         XFRM_INC_STATS(LINUX_MIB_XFRMOUTERROR);
189                         kfree_skb(skb);
190                         return err;
191                 }
192         }
193 
194         return xfrm_output2(skb);
195 }
196 
197 int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb)
198 {
199         struct xfrm_mode *inner_mode;
200         if (x->sel.family == AF_UNSPEC)
201                 inner_mode = xfrm_ip2inner_mode(x,
202                                 xfrm_af2proto(skb->dst->ops->family));
203         else
204                 inner_mode = x->inner_mode;
205 
206         if (inner_mode == NULL)
207                 return -EAFNOSUPPORT;
208         return inner_mode->afinfo->extract_output(x, skb);
209 }
210 
211 EXPORT_SYMBOL_GPL(xfrm_output);
212 EXPORT_SYMBOL_GPL(xfrm_inner_extract_output);
213 
  This page was automatically generated by the LXR engine.