xref: /openbmc/linux/net/xfrm/xfrm_compat.c (revision 5461fc0c8d9f23956b99f5907f69726a293ccb67)
1c9e7c76dSDmitry Safonov // SPDX-License-Identifier: GPL-2.0
2c9e7c76dSDmitry Safonov /*
3c9e7c76dSDmitry Safonov  * XFRM compat layer
4c9e7c76dSDmitry Safonov  * Author: Dmitry Safonov <dima@arista.com>
5c9e7c76dSDmitry Safonov  * Based on code and translator idea by: Florian Westphal <fw@strlen.de>
6c9e7c76dSDmitry Safonov  */
7c9e7c76dSDmitry Safonov #include <linux/compat.h>
8c9e7c76dSDmitry Safonov #include <linux/xfrm.h>
9c9e7c76dSDmitry Safonov #include <net/xfrm.h>
10c9e7c76dSDmitry Safonov 
11*5461fc0cSDmitry Safonov struct compat_xfrm_lifetime_cfg {
12*5461fc0cSDmitry Safonov 	compat_u64 soft_byte_limit, hard_byte_limit;
13*5461fc0cSDmitry Safonov 	compat_u64 soft_packet_limit, hard_packet_limit;
14*5461fc0cSDmitry Safonov 	compat_u64 soft_add_expires_seconds, hard_add_expires_seconds;
15*5461fc0cSDmitry Safonov 	compat_u64 soft_use_expires_seconds, hard_use_expires_seconds;
16*5461fc0cSDmitry Safonov }; /* same size on 32bit, but only 4 byte alignment required */
17*5461fc0cSDmitry Safonov 
18*5461fc0cSDmitry Safonov struct compat_xfrm_lifetime_cur {
19*5461fc0cSDmitry Safonov 	compat_u64 bytes, packets, add_time, use_time;
20*5461fc0cSDmitry Safonov }; /* same size on 32bit, but only 4 byte alignment required */
21*5461fc0cSDmitry Safonov 
22*5461fc0cSDmitry Safonov struct compat_xfrm_userpolicy_info {
23*5461fc0cSDmitry Safonov 	struct xfrm_selector sel;
24*5461fc0cSDmitry Safonov 	struct compat_xfrm_lifetime_cfg lft;
25*5461fc0cSDmitry Safonov 	struct compat_xfrm_lifetime_cur curlft;
26*5461fc0cSDmitry Safonov 	__u32 priority, index;
27*5461fc0cSDmitry Safonov 	u8 dir, action, flags, share;
28*5461fc0cSDmitry Safonov 	/* 4 bytes additional padding on 64bit */
29*5461fc0cSDmitry Safonov };
30*5461fc0cSDmitry Safonov 
31*5461fc0cSDmitry Safonov struct compat_xfrm_usersa_info {
32*5461fc0cSDmitry Safonov 	struct xfrm_selector sel;
33*5461fc0cSDmitry Safonov 	struct xfrm_id id;
34*5461fc0cSDmitry Safonov 	xfrm_address_t saddr;
35*5461fc0cSDmitry Safonov 	struct compat_xfrm_lifetime_cfg lft;
36*5461fc0cSDmitry Safonov 	struct compat_xfrm_lifetime_cur curlft;
37*5461fc0cSDmitry Safonov 	struct xfrm_stats stats;
38*5461fc0cSDmitry Safonov 	__u32 seq, reqid;
39*5461fc0cSDmitry Safonov 	u16 family;
40*5461fc0cSDmitry Safonov 	u8 mode, replay_window, flags;
41*5461fc0cSDmitry Safonov 	/* 4 bytes additional padding on 64bit */
42*5461fc0cSDmitry Safonov };
43*5461fc0cSDmitry Safonov 
44*5461fc0cSDmitry Safonov struct compat_xfrm_user_acquire {
45*5461fc0cSDmitry Safonov 	struct xfrm_id id;
46*5461fc0cSDmitry Safonov 	xfrm_address_t saddr;
47*5461fc0cSDmitry Safonov 	struct xfrm_selector sel;
48*5461fc0cSDmitry Safonov 	struct compat_xfrm_userpolicy_info policy;
49*5461fc0cSDmitry Safonov 	/* 4 bytes additional padding on 64bit */
50*5461fc0cSDmitry Safonov 	__u32 aalgos, ealgos, calgos, seq;
51*5461fc0cSDmitry Safonov };
52*5461fc0cSDmitry Safonov 
53*5461fc0cSDmitry Safonov struct compat_xfrm_userspi_info {
54*5461fc0cSDmitry Safonov 	struct compat_xfrm_usersa_info info;
55*5461fc0cSDmitry Safonov 	/* 4 bytes additional padding on 64bit */
56*5461fc0cSDmitry Safonov 	__u32 min, max;
57*5461fc0cSDmitry Safonov };
58*5461fc0cSDmitry Safonov 
59*5461fc0cSDmitry Safonov struct compat_xfrm_user_expire {
60*5461fc0cSDmitry Safonov 	struct compat_xfrm_usersa_info state;
61*5461fc0cSDmitry Safonov 	/* 8 bytes additional padding on 64bit */
62*5461fc0cSDmitry Safonov 	u8 hard;
63*5461fc0cSDmitry Safonov };
64*5461fc0cSDmitry Safonov 
65*5461fc0cSDmitry Safonov struct compat_xfrm_user_polexpire {
66*5461fc0cSDmitry Safonov 	struct compat_xfrm_userpolicy_info pol;
67*5461fc0cSDmitry Safonov 	/* 8 bytes additional padding on 64bit */
68*5461fc0cSDmitry Safonov 	u8 hard;
69*5461fc0cSDmitry Safonov };
70*5461fc0cSDmitry Safonov 
71*5461fc0cSDmitry Safonov #define XMSGSIZE(type) sizeof(struct type)
72*5461fc0cSDmitry Safonov 
73*5461fc0cSDmitry Safonov static const int compat_msg_min[XFRM_NR_MSGTYPES] = {
74*5461fc0cSDmitry Safonov 	[XFRM_MSG_NEWSA       - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_usersa_info),
75*5461fc0cSDmitry Safonov 	[XFRM_MSG_DELSA       - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id),
76*5461fc0cSDmitry Safonov 	[XFRM_MSG_GETSA       - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id),
77*5461fc0cSDmitry Safonov 	[XFRM_MSG_NEWPOLICY   - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_userpolicy_info),
78*5461fc0cSDmitry Safonov 	[XFRM_MSG_DELPOLICY   - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id),
79*5461fc0cSDmitry Safonov 	[XFRM_MSG_GETPOLICY   - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id),
80*5461fc0cSDmitry Safonov 	[XFRM_MSG_ALLOCSPI    - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_userspi_info),
81*5461fc0cSDmitry Safonov 	[XFRM_MSG_ACQUIRE     - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_user_acquire),
82*5461fc0cSDmitry Safonov 	[XFRM_MSG_EXPIRE      - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_user_expire),
83*5461fc0cSDmitry Safonov 	[XFRM_MSG_UPDPOLICY   - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_userpolicy_info),
84*5461fc0cSDmitry Safonov 	[XFRM_MSG_UPDSA       - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_usersa_info),
85*5461fc0cSDmitry Safonov 	[XFRM_MSG_POLEXPIRE   - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_user_polexpire),
86*5461fc0cSDmitry Safonov 	[XFRM_MSG_FLUSHSA     - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_flush),
87*5461fc0cSDmitry Safonov 	[XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = 0,
88*5461fc0cSDmitry Safonov 	[XFRM_MSG_NEWAE       - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id),
89*5461fc0cSDmitry Safonov 	[XFRM_MSG_GETAE       - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id),
90*5461fc0cSDmitry Safonov 	[XFRM_MSG_REPORT      - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report),
91*5461fc0cSDmitry Safonov 	[XFRM_MSG_MIGRATE     - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id),
92*5461fc0cSDmitry Safonov 	[XFRM_MSG_NEWSADINFO  - XFRM_MSG_BASE] = sizeof(u32),
93*5461fc0cSDmitry Safonov 	[XFRM_MSG_GETSADINFO  - XFRM_MSG_BASE] = sizeof(u32),
94*5461fc0cSDmitry Safonov 	[XFRM_MSG_NEWSPDINFO  - XFRM_MSG_BASE] = sizeof(u32),
95*5461fc0cSDmitry Safonov 	[XFRM_MSG_GETSPDINFO  - XFRM_MSG_BASE] = sizeof(u32),
96*5461fc0cSDmitry Safonov 	[XFRM_MSG_MAPPING     - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_mapping)
97*5461fc0cSDmitry Safonov };
98*5461fc0cSDmitry Safonov 
99*5461fc0cSDmitry Safonov static struct nlmsghdr *xfrm_nlmsg_put_compat(struct sk_buff *skb,
100*5461fc0cSDmitry Safonov 			const struct nlmsghdr *nlh_src, u16 type)
101*5461fc0cSDmitry Safonov {
102*5461fc0cSDmitry Safonov 	int payload = compat_msg_min[type];
103*5461fc0cSDmitry Safonov 	int src_len = xfrm_msg_min[type];
104*5461fc0cSDmitry Safonov 	struct nlmsghdr *nlh_dst;
105*5461fc0cSDmitry Safonov 
106*5461fc0cSDmitry Safonov 	/* Compat messages are shorter or equal to native (+padding) */
107*5461fc0cSDmitry Safonov 	if (WARN_ON_ONCE(src_len < payload))
108*5461fc0cSDmitry Safonov 		return ERR_PTR(-EMSGSIZE);
109*5461fc0cSDmitry Safonov 
110*5461fc0cSDmitry Safonov 	nlh_dst = nlmsg_put(skb, nlh_src->nlmsg_pid, nlh_src->nlmsg_seq,
111*5461fc0cSDmitry Safonov 			    nlh_src->nlmsg_type, payload, nlh_src->nlmsg_flags);
112*5461fc0cSDmitry Safonov 	if (!nlh_dst)
113*5461fc0cSDmitry Safonov 		return ERR_PTR(-EMSGSIZE);
114*5461fc0cSDmitry Safonov 
115*5461fc0cSDmitry Safonov 	memset(nlmsg_data(nlh_dst), 0, payload);
116*5461fc0cSDmitry Safonov 
117*5461fc0cSDmitry Safonov 	switch (nlh_src->nlmsg_type) {
118*5461fc0cSDmitry Safonov 	/* Compat message has the same layout as native */
119*5461fc0cSDmitry Safonov 	case XFRM_MSG_DELSA:
120*5461fc0cSDmitry Safonov 	case XFRM_MSG_DELPOLICY:
121*5461fc0cSDmitry Safonov 	case XFRM_MSG_FLUSHSA:
122*5461fc0cSDmitry Safonov 	case XFRM_MSG_FLUSHPOLICY:
123*5461fc0cSDmitry Safonov 	case XFRM_MSG_NEWAE:
124*5461fc0cSDmitry Safonov 	case XFRM_MSG_REPORT:
125*5461fc0cSDmitry Safonov 	case XFRM_MSG_MIGRATE:
126*5461fc0cSDmitry Safonov 	case XFRM_MSG_NEWSADINFO:
127*5461fc0cSDmitry Safonov 	case XFRM_MSG_NEWSPDINFO:
128*5461fc0cSDmitry Safonov 	case XFRM_MSG_MAPPING:
129*5461fc0cSDmitry Safonov 		WARN_ON_ONCE(src_len != payload);
130*5461fc0cSDmitry Safonov 		memcpy(nlmsg_data(nlh_dst), nlmsg_data(nlh_src), src_len);
131*5461fc0cSDmitry Safonov 		break;
132*5461fc0cSDmitry Safonov 	/* 4 byte alignment for trailing u64 on native, but not on compat */
133*5461fc0cSDmitry Safonov 	case XFRM_MSG_NEWSA:
134*5461fc0cSDmitry Safonov 	case XFRM_MSG_NEWPOLICY:
135*5461fc0cSDmitry Safonov 	case XFRM_MSG_UPDSA:
136*5461fc0cSDmitry Safonov 	case XFRM_MSG_UPDPOLICY:
137*5461fc0cSDmitry Safonov 		WARN_ON_ONCE(src_len != payload + 4);
138*5461fc0cSDmitry Safonov 		memcpy(nlmsg_data(nlh_dst), nlmsg_data(nlh_src), payload);
139*5461fc0cSDmitry Safonov 		break;
140*5461fc0cSDmitry Safonov 	case XFRM_MSG_EXPIRE: {
141*5461fc0cSDmitry Safonov 		const struct xfrm_user_expire *src_ue  = nlmsg_data(nlh_src);
142*5461fc0cSDmitry Safonov 		struct compat_xfrm_user_expire *dst_ue = nlmsg_data(nlh_dst);
143*5461fc0cSDmitry Safonov 
144*5461fc0cSDmitry Safonov 		/* compat_xfrm_user_expire has 4-byte smaller state */
145*5461fc0cSDmitry Safonov 		memcpy(dst_ue, src_ue, sizeof(dst_ue->state));
146*5461fc0cSDmitry Safonov 		dst_ue->hard = src_ue->hard;
147*5461fc0cSDmitry Safonov 		break;
148*5461fc0cSDmitry Safonov 	}
149*5461fc0cSDmitry Safonov 	case XFRM_MSG_ACQUIRE: {
150*5461fc0cSDmitry Safonov 		const struct xfrm_user_acquire *src_ua  = nlmsg_data(nlh_src);
151*5461fc0cSDmitry Safonov 		struct compat_xfrm_user_acquire *dst_ua = nlmsg_data(nlh_dst);
152*5461fc0cSDmitry Safonov 
153*5461fc0cSDmitry Safonov 		memcpy(dst_ua, src_ua, offsetof(struct compat_xfrm_user_acquire, aalgos));
154*5461fc0cSDmitry Safonov 		dst_ua->aalgos = src_ua->aalgos;
155*5461fc0cSDmitry Safonov 		dst_ua->ealgos = src_ua->ealgos;
156*5461fc0cSDmitry Safonov 		dst_ua->calgos = src_ua->calgos;
157*5461fc0cSDmitry Safonov 		dst_ua->seq    = src_ua->seq;
158*5461fc0cSDmitry Safonov 		break;
159*5461fc0cSDmitry Safonov 	}
160*5461fc0cSDmitry Safonov 	case XFRM_MSG_POLEXPIRE: {
161*5461fc0cSDmitry Safonov 		const struct xfrm_user_polexpire *src_upe  = nlmsg_data(nlh_src);
162*5461fc0cSDmitry Safonov 		struct compat_xfrm_user_polexpire *dst_upe = nlmsg_data(nlh_dst);
163*5461fc0cSDmitry Safonov 
164*5461fc0cSDmitry Safonov 		/* compat_xfrm_user_polexpire has 4-byte smaller state */
165*5461fc0cSDmitry Safonov 		memcpy(dst_upe, src_upe, sizeof(dst_upe->pol));
166*5461fc0cSDmitry Safonov 		dst_upe->hard = src_upe->hard;
167*5461fc0cSDmitry Safonov 		break;
168*5461fc0cSDmitry Safonov 	}
169*5461fc0cSDmitry Safonov 	case XFRM_MSG_ALLOCSPI: {
170*5461fc0cSDmitry Safonov 		const struct xfrm_userspi_info *src_usi = nlmsg_data(nlh_src);
171*5461fc0cSDmitry Safonov 		struct compat_xfrm_userspi_info *dst_usi = nlmsg_data(nlh_dst);
172*5461fc0cSDmitry Safonov 
173*5461fc0cSDmitry Safonov 		/* compat_xfrm_user_polexpire has 4-byte smaller state */
174*5461fc0cSDmitry Safonov 		memcpy(dst_usi, src_usi, sizeof(src_usi->info));
175*5461fc0cSDmitry Safonov 		dst_usi->min = src_usi->min;
176*5461fc0cSDmitry Safonov 		dst_usi->max = src_usi->max;
177*5461fc0cSDmitry Safonov 		break;
178*5461fc0cSDmitry Safonov 	}
179*5461fc0cSDmitry Safonov 	/* Not being sent by kernel */
180*5461fc0cSDmitry Safonov 	case XFRM_MSG_GETSA:
181*5461fc0cSDmitry Safonov 	case XFRM_MSG_GETPOLICY:
182*5461fc0cSDmitry Safonov 	case XFRM_MSG_GETAE:
183*5461fc0cSDmitry Safonov 	case XFRM_MSG_GETSADINFO:
184*5461fc0cSDmitry Safonov 	case XFRM_MSG_GETSPDINFO:
185*5461fc0cSDmitry Safonov 	default:
186*5461fc0cSDmitry Safonov 		WARN_ONCE(1, "unsupported nlmsg_type %d", nlh_src->nlmsg_type);
187*5461fc0cSDmitry Safonov 		return ERR_PTR(-EOPNOTSUPP);
188*5461fc0cSDmitry Safonov 	}
189*5461fc0cSDmitry Safonov 
190*5461fc0cSDmitry Safonov 	return nlh_dst;
191*5461fc0cSDmitry Safonov }
192*5461fc0cSDmitry Safonov 
193*5461fc0cSDmitry Safonov static int xfrm_nla_cpy(struct sk_buff *dst, const struct nlattr *src, int len)
194*5461fc0cSDmitry Safonov {
195*5461fc0cSDmitry Safonov 	return nla_put(dst, src->nla_type, len, nla_data(src));
196*5461fc0cSDmitry Safonov }
197*5461fc0cSDmitry Safonov 
198*5461fc0cSDmitry Safonov static int xfrm_xlate64_attr(struct sk_buff *dst, const struct nlattr *src)
199*5461fc0cSDmitry Safonov {
200*5461fc0cSDmitry Safonov 	switch (src->nla_type) {
201*5461fc0cSDmitry Safonov 	case XFRMA_PAD:
202*5461fc0cSDmitry Safonov 		/* Ignore */
203*5461fc0cSDmitry Safonov 		return 0;
204*5461fc0cSDmitry Safonov 	case XFRMA_ALG_AUTH:
205*5461fc0cSDmitry Safonov 	case XFRMA_ALG_CRYPT:
206*5461fc0cSDmitry Safonov 	case XFRMA_ALG_COMP:
207*5461fc0cSDmitry Safonov 	case XFRMA_ENCAP:
208*5461fc0cSDmitry Safonov 	case XFRMA_TMPL:
209*5461fc0cSDmitry Safonov 		return xfrm_nla_cpy(dst, src, nla_len(src));
210*5461fc0cSDmitry Safonov 	case XFRMA_SA:
211*5461fc0cSDmitry Safonov 		return xfrm_nla_cpy(dst, src, XMSGSIZE(compat_xfrm_usersa_info));
212*5461fc0cSDmitry Safonov 	case XFRMA_POLICY:
213*5461fc0cSDmitry Safonov 		return xfrm_nla_cpy(dst, src, XMSGSIZE(compat_xfrm_userpolicy_info));
214*5461fc0cSDmitry Safonov 	case XFRMA_SEC_CTX:
215*5461fc0cSDmitry Safonov 		return xfrm_nla_cpy(dst, src, nla_len(src));
216*5461fc0cSDmitry Safonov 	case XFRMA_LTIME_VAL:
217*5461fc0cSDmitry Safonov 		return nla_put_64bit(dst, src->nla_type, nla_len(src),
218*5461fc0cSDmitry Safonov 			nla_data(src), XFRMA_PAD);
219*5461fc0cSDmitry Safonov 	case XFRMA_REPLAY_VAL:
220*5461fc0cSDmitry Safonov 	case XFRMA_REPLAY_THRESH:
221*5461fc0cSDmitry Safonov 	case XFRMA_ETIMER_THRESH:
222*5461fc0cSDmitry Safonov 	case XFRMA_SRCADDR:
223*5461fc0cSDmitry Safonov 	case XFRMA_COADDR:
224*5461fc0cSDmitry Safonov 		return xfrm_nla_cpy(dst, src, nla_len(src));
225*5461fc0cSDmitry Safonov 	case XFRMA_LASTUSED:
226*5461fc0cSDmitry Safonov 		return nla_put_64bit(dst, src->nla_type, nla_len(src),
227*5461fc0cSDmitry Safonov 			nla_data(src), XFRMA_PAD);
228*5461fc0cSDmitry Safonov 	case XFRMA_POLICY_TYPE:
229*5461fc0cSDmitry Safonov 	case XFRMA_MIGRATE:
230*5461fc0cSDmitry Safonov 	case XFRMA_ALG_AEAD:
231*5461fc0cSDmitry Safonov 	case XFRMA_KMADDRESS:
232*5461fc0cSDmitry Safonov 	case XFRMA_ALG_AUTH_TRUNC:
233*5461fc0cSDmitry Safonov 	case XFRMA_MARK:
234*5461fc0cSDmitry Safonov 	case XFRMA_TFCPAD:
235*5461fc0cSDmitry Safonov 	case XFRMA_REPLAY_ESN_VAL:
236*5461fc0cSDmitry Safonov 	case XFRMA_SA_EXTRA_FLAGS:
237*5461fc0cSDmitry Safonov 	case XFRMA_PROTO:
238*5461fc0cSDmitry Safonov 	case XFRMA_ADDRESS_FILTER:
239*5461fc0cSDmitry Safonov 	case XFRMA_OFFLOAD_DEV:
240*5461fc0cSDmitry Safonov 	case XFRMA_SET_MARK:
241*5461fc0cSDmitry Safonov 	case XFRMA_SET_MARK_MASK:
242*5461fc0cSDmitry Safonov 	case XFRMA_IF_ID:
243*5461fc0cSDmitry Safonov 		return xfrm_nla_cpy(dst, src, nla_len(src));
244*5461fc0cSDmitry Safonov 	default:
245*5461fc0cSDmitry Safonov 		BUILD_BUG_ON(XFRMA_MAX != XFRMA_IF_ID);
246*5461fc0cSDmitry Safonov 		WARN_ONCE(1, "unsupported nla_type %d", src->nla_type);
247*5461fc0cSDmitry Safonov 		return -EOPNOTSUPP;
248*5461fc0cSDmitry Safonov 	}
249*5461fc0cSDmitry Safonov }
250*5461fc0cSDmitry Safonov 
251*5461fc0cSDmitry Safonov /* Take kernel-built (64bit layout) and create 32bit layout for userspace */
252*5461fc0cSDmitry Safonov static int xfrm_xlate64(struct sk_buff *dst, const struct nlmsghdr *nlh_src)
253*5461fc0cSDmitry Safonov {
254*5461fc0cSDmitry Safonov 	u16 type = nlh_src->nlmsg_type - XFRM_MSG_BASE;
255*5461fc0cSDmitry Safonov 	const struct nlattr *nla, *attrs;
256*5461fc0cSDmitry Safonov 	struct nlmsghdr *nlh_dst;
257*5461fc0cSDmitry Safonov 	int len, remaining;
258*5461fc0cSDmitry Safonov 
259*5461fc0cSDmitry Safonov 	nlh_dst = xfrm_nlmsg_put_compat(dst, nlh_src, type);
260*5461fc0cSDmitry Safonov 	if (IS_ERR(nlh_dst))
261*5461fc0cSDmitry Safonov 		return PTR_ERR(nlh_dst);
262*5461fc0cSDmitry Safonov 
263*5461fc0cSDmitry Safonov 	attrs = nlmsg_attrdata(nlh_src, xfrm_msg_min[type]);
264*5461fc0cSDmitry Safonov 	len = nlmsg_attrlen(nlh_src, xfrm_msg_min[type]);
265*5461fc0cSDmitry Safonov 
266*5461fc0cSDmitry Safonov 	nla_for_each_attr(nla, attrs, len, remaining) {
267*5461fc0cSDmitry Safonov 		int err = xfrm_xlate64_attr(dst, nla);
268*5461fc0cSDmitry Safonov 
269*5461fc0cSDmitry Safonov 		if (err)
270*5461fc0cSDmitry Safonov 			return err;
271*5461fc0cSDmitry Safonov 	}
272*5461fc0cSDmitry Safonov 
273*5461fc0cSDmitry Safonov 	nlmsg_end(dst, nlh_dst);
274*5461fc0cSDmitry Safonov 
275*5461fc0cSDmitry Safonov 	return 0;
276*5461fc0cSDmitry Safonov }
277*5461fc0cSDmitry Safonov 
278*5461fc0cSDmitry Safonov static int xfrm_alloc_compat(struct sk_buff *skb, const struct nlmsghdr *nlh_src)
279*5461fc0cSDmitry Safonov {
280*5461fc0cSDmitry Safonov 	u16 type = nlh_src->nlmsg_type - XFRM_MSG_BASE;
281*5461fc0cSDmitry Safonov 	struct sk_buff *new = NULL;
282*5461fc0cSDmitry Safonov 	int err;
283*5461fc0cSDmitry Safonov 
284*5461fc0cSDmitry Safonov 	if (WARN_ON_ONCE(type >= ARRAY_SIZE(xfrm_msg_min)))
285*5461fc0cSDmitry Safonov 		return -EOPNOTSUPP;
286*5461fc0cSDmitry Safonov 
287*5461fc0cSDmitry Safonov 	if (skb_shinfo(skb)->frag_list == NULL) {
288*5461fc0cSDmitry Safonov 		new = alloc_skb(skb->len + skb_tailroom(skb), GFP_ATOMIC);
289*5461fc0cSDmitry Safonov 		if (!new)
290*5461fc0cSDmitry Safonov 			return -ENOMEM;
291*5461fc0cSDmitry Safonov 		skb_shinfo(skb)->frag_list = new;
292*5461fc0cSDmitry Safonov 	}
293*5461fc0cSDmitry Safonov 
294*5461fc0cSDmitry Safonov 	err = xfrm_xlate64(skb_shinfo(skb)->frag_list, nlh_src);
295*5461fc0cSDmitry Safonov 	if (err) {
296*5461fc0cSDmitry Safonov 		if (new) {
297*5461fc0cSDmitry Safonov 			kfree_skb(new);
298*5461fc0cSDmitry Safonov 			skb_shinfo(skb)->frag_list = NULL;
299*5461fc0cSDmitry Safonov 		}
300*5461fc0cSDmitry Safonov 		return err;
301*5461fc0cSDmitry Safonov 	}
302*5461fc0cSDmitry Safonov 
303*5461fc0cSDmitry Safonov 	return 0;
304*5461fc0cSDmitry Safonov }
305*5461fc0cSDmitry Safonov 
306c9e7c76dSDmitry Safonov static struct xfrm_translator xfrm_translator = {
307c9e7c76dSDmitry Safonov 	.owner				= THIS_MODULE,
308*5461fc0cSDmitry Safonov 	.alloc_compat			= xfrm_alloc_compat,
309c9e7c76dSDmitry Safonov };
310c9e7c76dSDmitry Safonov 
311c9e7c76dSDmitry Safonov static int __init xfrm_compat_init(void)
312c9e7c76dSDmitry Safonov {
313c9e7c76dSDmitry Safonov 	return xfrm_register_translator(&xfrm_translator);
314c9e7c76dSDmitry Safonov }
315c9e7c76dSDmitry Safonov 
316c9e7c76dSDmitry Safonov static void __exit xfrm_compat_exit(void)
317c9e7c76dSDmitry Safonov {
318c9e7c76dSDmitry Safonov 	xfrm_unregister_translator(&xfrm_translator);
319c9e7c76dSDmitry Safonov }
320c9e7c76dSDmitry Safonov 
321c9e7c76dSDmitry Safonov module_init(xfrm_compat_init);
322c9e7c76dSDmitry Safonov module_exit(xfrm_compat_exit);
323c9e7c76dSDmitry Safonov MODULE_LICENSE("GPL");
324c9e7c76dSDmitry Safonov MODULE_AUTHOR("Dmitry Safonov");
325c9e7c76dSDmitry Safonov MODULE_DESCRIPTION("XFRM 32-bit compatibility layer");
326