net: Fix soft lockups/OOM issues w/ unix garbage collector
[linux-2.6] / net / ipv6 / xfrm6_state.c
1 /*
2  * xfrm6_state.c: based on xfrm4_state.c
3  *
4  * Authors:
5  *      Mitsuru KANDA @USAGI
6  *      Kazunori MIYAZAWA @USAGI
7  *      Kunihiro Ishiguro <kunihiro@ipinfusion.com>
8  *              IPv6 support
9  *      YOSHIFUJI Hideaki @USAGI
10  *              Split up af-specific portion
11  *
12  */
13
14 #include <net/xfrm.h>
15 #include <linux/pfkeyv2.h>
16 #include <linux/ipsec.h>
17 #include <linux/netfilter_ipv6.h>
18 #include <net/dsfield.h>
19 #include <net/ipv6.h>
20 #include <net/addrconf.h>
21
22 static struct xfrm_state_afinfo xfrm6_state_afinfo;
23
24 static void
25 __xfrm6_init_tempsel(struct xfrm_state *x, struct flowi *fl,
26                      struct xfrm_tmpl *tmpl,
27                      xfrm_address_t *daddr, xfrm_address_t *saddr)
28 {
29         /* Initialize temporary selector matching only
30          * to current session. */
31         ipv6_addr_copy((struct in6_addr *)&x->sel.daddr, &fl->fl6_dst);
32         ipv6_addr_copy((struct in6_addr *)&x->sel.saddr, &fl->fl6_src);
33         x->sel.dport = xfrm_flowi_dport(fl);
34         x->sel.dport_mask = htons(0xffff);
35         x->sel.sport = xfrm_flowi_sport(fl);
36         x->sel.sport_mask = htons(0xffff);
37         x->sel.family = AF_INET6;
38         x->sel.prefixlen_d = 128;
39         x->sel.prefixlen_s = 128;
40         x->sel.proto = fl->proto;
41         x->sel.ifindex = fl->oif;
42         x->id = tmpl->id;
43         if (ipv6_addr_any((struct in6_addr*)&x->id.daddr))
44                 memcpy(&x->id.daddr, daddr, sizeof(x->sel.daddr));
45         memcpy(&x->props.saddr, &tmpl->saddr, sizeof(x->props.saddr));
46         if (ipv6_addr_any((struct in6_addr*)&x->props.saddr))
47                 memcpy(&x->props.saddr, saddr, sizeof(x->props.saddr));
48         x->props.mode = tmpl->mode;
49         x->props.reqid = tmpl->reqid;
50         x->props.family = AF_INET6;
51 }
52
53 /* distribution counting sort function for xfrm_state and xfrm_tmpl */
54 static int
55 __xfrm6_sort(void **dst, void **src, int n, int (*cmp)(void *p), int maxclass)
56 {
57         int i;
58         int class[XFRM_MAX_DEPTH];
59         int count[maxclass];
60
61         memset(count, 0, sizeof(count));
62
63         for (i = 0; i < n; i++) {
64                 int c;
65                 class[i] = c = cmp(src[i]);
66                 count[c]++;
67         }
68
69         for (i = 2; i < maxclass; i++)
70                 count[i] += count[i - 1];
71
72         for (i = 0; i < n; i++) {
73                 dst[count[class[i] - 1]++] = src[i];
74                 src[i] = 0;
75         }
76
77         return 0;
78 }
79
80 /*
81  * Rule for xfrm_state:
82  *
83  * rule 1: select IPsec transport except AH
84  * rule 2: select MIPv6 RO or inbound trigger
85  * rule 3: select IPsec transport AH
86  * rule 4: select IPsec tunnel
87  * rule 5: others
88  */
89 static int __xfrm6_state_sort_cmp(void *p)
90 {
91         struct xfrm_state *v = p;
92
93         switch (v->props.mode) {
94         case XFRM_MODE_TRANSPORT:
95                 if (v->id.proto != IPPROTO_AH)
96                         return 1;
97                 else
98                         return 3;
99 #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
100         case XFRM_MODE_ROUTEOPTIMIZATION:
101         case XFRM_MODE_IN_TRIGGER:
102                 return 2;
103 #endif
104         case XFRM_MODE_TUNNEL:
105         case XFRM_MODE_BEET:
106                 return 4;
107         }
108         return 5;
109 }
110
111 static int
112 __xfrm6_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n)
113 {
114         return __xfrm6_sort((void **)dst, (void **)src, n,
115                             __xfrm6_state_sort_cmp, 6);
116 }
117
118 /*
119  * Rule for xfrm_tmpl:
120  *
121  * rule 1: select IPsec transport
122  * rule 2: select MIPv6 RO or inbound trigger
123  * rule 3: select IPsec tunnel
124  * rule 4: others
125  */
126 static int __xfrm6_tmpl_sort_cmp(void *p)
127 {
128         struct xfrm_tmpl *v = p;
129         switch (v->mode) {
130         case XFRM_MODE_TRANSPORT:
131                 return 1;
132 #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
133         case XFRM_MODE_ROUTEOPTIMIZATION:
134         case XFRM_MODE_IN_TRIGGER:
135                 return 2;
136 #endif
137         case XFRM_MODE_TUNNEL:
138         case XFRM_MODE_BEET:
139                 return 3;
140         }
141         return 4;
142 }
143
144 static int
145 __xfrm6_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n)
146 {
147         return __xfrm6_sort((void **)dst, (void **)src, n,
148                             __xfrm6_tmpl_sort_cmp, 5);
149 }
150
151 int xfrm6_extract_header(struct sk_buff *skb)
152 {
153         struct ipv6hdr *iph = ipv6_hdr(skb);
154
155         XFRM_MODE_SKB_CB(skb)->ihl = sizeof(*iph);
156         XFRM_MODE_SKB_CB(skb)->id = 0;
157         XFRM_MODE_SKB_CB(skb)->frag_off = htons(IP_DF);
158         XFRM_MODE_SKB_CB(skb)->tos = ipv6_get_dsfield(iph);
159         XFRM_MODE_SKB_CB(skb)->ttl = iph->hop_limit;
160         XFRM_MODE_SKB_CB(skb)->optlen = 0;
161         memcpy(XFRM_MODE_SKB_CB(skb)->flow_lbl, iph->flow_lbl,
162                sizeof(XFRM_MODE_SKB_CB(skb)->flow_lbl));
163
164         return 0;
165 }
166
167 static struct xfrm_state_afinfo xfrm6_state_afinfo = {
168         .family                 = AF_INET6,
169         .proto                  = IPPROTO_IPV6,
170         .eth_proto              = htons(ETH_P_IPV6),
171         .owner                  = THIS_MODULE,
172         .init_tempsel           = __xfrm6_init_tempsel,
173         .tmpl_sort              = __xfrm6_tmpl_sort,
174         .state_sort             = __xfrm6_state_sort,
175         .output                 = xfrm6_output,
176         .extract_input          = xfrm6_extract_input,
177         .extract_output         = xfrm6_extract_output,
178         .transport_finish       = xfrm6_transport_finish,
179 };
180
181 int __init xfrm6_state_init(void)
182 {
183         return xfrm_state_register_afinfo(&xfrm6_state_afinfo);
184 }
185
186 void xfrm6_state_fini(void)
187 {
188         xfrm_state_unregister_afinfo(&xfrm6_state_afinfo);
189 }
190