109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /* xfrm_user.c: User interface to configure xfrm engine.
31da177e4SLinus Torvalds *
41da177e4SLinus Torvalds * Copyright (C) 2002 David S. Miller (davem@redhat.com)
51da177e4SLinus Torvalds *
61da177e4SLinus Torvalds * Changes:
71da177e4SLinus Torvalds * Mitsuru KANDA @USAGI
81da177e4SLinus Torvalds * Kazunori MIYAZAWA @USAGI
91da177e4SLinus Torvalds * Kunihiro Ishiguro <kunihiro@ipinfusion.com>
101da177e4SLinus Torvalds * IPv6 support
111da177e4SLinus Torvalds *
121da177e4SLinus Torvalds */
131da177e4SLinus Torvalds
14b6459415SJakub Kicinski #include <linux/compat.h>
159409f38aSHerbert Xu #include <linux/crypto.h>
161da177e4SLinus Torvalds #include <linux/module.h>
171da177e4SLinus Torvalds #include <linux/kernel.h>
181da177e4SLinus Torvalds #include <linux/types.h>
191da177e4SLinus Torvalds #include <linux/slab.h>
201da177e4SLinus Torvalds #include <linux/socket.h>
211da177e4SLinus Torvalds #include <linux/string.h>
221da177e4SLinus Torvalds #include <linux/net.h>
231da177e4SLinus Torvalds #include <linux/skbuff.h>
241da177e4SLinus Torvalds #include <linux/pfkeyv2.h>
251da177e4SLinus Torvalds #include <linux/ipsec.h>
261da177e4SLinus Torvalds #include <linux/init.h>
271da177e4SLinus Torvalds #include <linux/security.h>
281da177e4SLinus Torvalds #include <net/sock.h>
291da177e4SLinus Torvalds #include <net/xfrm.h>
3088fc2c84SThomas Graf #include <net/netlink.h>
31fa6dd8a2SNicolas Dichtel #include <net/ah.h>
327c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
33dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
34e23c7194SMasahide NAKAMURA #include <linux/in6.h>
35e23c7194SMasahide NAKAMURA #endif
36e33d4f13SSowmini Varadhan #include <asm/unaligned.h>
371da177e4SLinus Torvalds
verify_one_alg(struct nlattr ** attrs,enum xfrm_attr_type_t type,struct netlink_ext_ack * extack)381fc8fde5SSabrina Dubroca static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type,
391fc8fde5SSabrina Dubroca struct netlink_ext_ack *extack)
401da177e4SLinus Torvalds {
415424f32eSThomas Graf struct nlattr *rt = attrs[type];
421da177e4SLinus Torvalds struct xfrm_algo *algp;
431da177e4SLinus Torvalds
441da177e4SLinus Torvalds if (!rt)
451da177e4SLinus Torvalds return 0;
461da177e4SLinus Torvalds
475424f32eSThomas Graf algp = nla_data(rt);
481fc8fde5SSabrina Dubroca if (nla_len(rt) < (int)xfrm_alg_len(algp)) {
491fc8fde5SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid AUTH/CRYPT/COMP attribute length");
5031c26852SHerbert Xu return -EINVAL;
511fc8fde5SSabrina Dubroca }
5231c26852SHerbert Xu
531da177e4SLinus Torvalds switch (type) {
541da177e4SLinus Torvalds case XFRMA_ALG_AUTH:
551da177e4SLinus Torvalds case XFRMA_ALG_CRYPT:
561da177e4SLinus Torvalds case XFRMA_ALG_COMP:
571da177e4SLinus Torvalds break;
581da177e4SLinus Torvalds
591da177e4SLinus Torvalds default:
601fc8fde5SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid algorithm attribute type");
611da177e4SLinus Torvalds return -EINVAL;
623ff50b79SStephen Hemminger }
631da177e4SLinus Torvalds
64633439f5SHerbert Xu algp->alg_name[sizeof(algp->alg_name) - 1] = '\0';
651da177e4SLinus Torvalds return 0;
661da177e4SLinus Torvalds }
671da177e4SLinus Torvalds
verify_auth_trunc(struct nlattr ** attrs,struct netlink_ext_ack * extack)681fc8fde5SSabrina Dubroca static int verify_auth_trunc(struct nlattr **attrs,
691fc8fde5SSabrina Dubroca struct netlink_ext_ack *extack)
704447bb33SMartin Willi {
714447bb33SMartin Willi struct nlattr *rt = attrs[XFRMA_ALG_AUTH_TRUNC];
724447bb33SMartin Willi struct xfrm_algo_auth *algp;
734447bb33SMartin Willi
744447bb33SMartin Willi if (!rt)
754447bb33SMartin Willi return 0;
764447bb33SMartin Willi
774447bb33SMartin Willi algp = nla_data(rt);
781fc8fde5SSabrina Dubroca if (nla_len(rt) < (int)xfrm_alg_auth_len(algp)) {
791fc8fde5SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid AUTH_TRUNC attribute length");
804447bb33SMartin Willi return -EINVAL;
811fc8fde5SSabrina Dubroca }
824447bb33SMartin Willi
83633439f5SHerbert Xu algp->alg_name[sizeof(algp->alg_name) - 1] = '\0';
844447bb33SMartin Willi return 0;
854447bb33SMartin Willi }
864447bb33SMartin Willi
verify_aead(struct nlattr ** attrs,struct netlink_ext_ack * extack)871fc8fde5SSabrina Dubroca static int verify_aead(struct nlattr **attrs, struct netlink_ext_ack *extack)
881a6509d9SHerbert Xu {
891a6509d9SHerbert Xu struct nlattr *rt = attrs[XFRMA_ALG_AEAD];
901a6509d9SHerbert Xu struct xfrm_algo_aead *algp;
911a6509d9SHerbert Xu
921a6509d9SHerbert Xu if (!rt)
931a6509d9SHerbert Xu return 0;
941a6509d9SHerbert Xu
951a6509d9SHerbert Xu algp = nla_data(rt);
961fc8fde5SSabrina Dubroca if (nla_len(rt) < (int)aead_len(algp)) {
971fc8fde5SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid AEAD attribute length");
981a6509d9SHerbert Xu return -EINVAL;
991fc8fde5SSabrina Dubroca }
1001a6509d9SHerbert Xu
101633439f5SHerbert Xu algp->alg_name[sizeof(algp->alg_name) - 1] = '\0';
1021a6509d9SHerbert Xu return 0;
1031a6509d9SHerbert Xu }
1041a6509d9SHerbert Xu
verify_one_addr(struct nlattr ** attrs,enum xfrm_attr_type_t type,xfrm_address_t ** addrp)1055424f32eSThomas Graf static void verify_one_addr(struct nlattr **attrs, enum xfrm_attr_type_t type,
106eb2971b6SMasahide NAKAMURA xfrm_address_t **addrp)
107eb2971b6SMasahide NAKAMURA {
1085424f32eSThomas Graf struct nlattr *rt = attrs[type];
109eb2971b6SMasahide NAKAMURA
110cf5cb79fSThomas Graf if (rt && addrp)
1115424f32eSThomas Graf *addrp = nla_data(rt);
112eb2971b6SMasahide NAKAMURA }
113df71837dSTrent Jaeger
verify_sec_ctx_len(struct nlattr ** attrs,struct netlink_ext_ack * extack)11408a717e4SSabrina Dubroca static inline int verify_sec_ctx_len(struct nlattr **attrs, struct netlink_ext_ack *extack)
115df71837dSTrent Jaeger {
1165424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX];
117df71837dSTrent Jaeger struct xfrm_user_sec_ctx *uctx;
118df71837dSTrent Jaeger
119df71837dSTrent Jaeger if (!rt)
120df71837dSTrent Jaeger return 0;
121df71837dSTrent Jaeger
1225424f32eSThomas Graf uctx = nla_data(rt);
123171d449aSXin Long if (uctx->len > nla_len(rt) ||
12408a717e4SSabrina Dubroca uctx->len != (sizeof(struct xfrm_user_sec_ctx) + uctx->ctx_len)) {
12508a717e4SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid security context length");
126df71837dSTrent Jaeger return -EINVAL;
12708a717e4SSabrina Dubroca }
128df71837dSTrent Jaeger
129df71837dSTrent Jaeger return 0;
130df71837dSTrent Jaeger }
131df71837dSTrent Jaeger
verify_replay(struct xfrm_usersa_info * p,struct nlattr ** attrs,struct netlink_ext_ack * extack)132d8647b79SSteffen Klassert static inline int verify_replay(struct xfrm_usersa_info *p,
133785b87b2SSabrina Dubroca struct nlattr **attrs,
134785b87b2SSabrina Dubroca struct netlink_ext_ack *extack)
135d8647b79SSteffen Klassert {
136d8647b79SSteffen Klassert struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL];
137ecd79187SMathias Krause struct xfrm_replay_state_esn *rs;
138d8647b79SSteffen Klassert
139785b87b2SSabrina Dubroca if (!rt) {
140785b87b2SSabrina Dubroca if (p->flags & XFRM_STATE_ESN) {
141785b87b2SSabrina Dubroca NL_SET_ERR_MSG(extack, "Missing required attribute for ESN");
142785b87b2SSabrina Dubroca return -EINVAL;
143785b87b2SSabrina Dubroca }
144785b87b2SSabrina Dubroca return 0;
145785b87b2SSabrina Dubroca }
1467833aa05SSteffen Klassert
147ecd79187SMathias Krause rs = nla_data(rt);
148ecd79187SMathias Krause
149785b87b2SSabrina Dubroca if (rs->bmp_len > XFRMA_REPLAY_ESN_MAX / sizeof(rs->bmp[0]) / 8) {
150785b87b2SSabrina Dubroca NL_SET_ERR_MSG(extack, "ESN bitmap length must be <= 128");
151ecd79187SMathias Krause return -EINVAL;
152785b87b2SSabrina Dubroca }
153ecd79187SMathias Krause
1545e708e47SAlexey Dobriyan if (nla_len(rt) < (int)xfrm_replay_state_esn_len(rs) &&
155785b87b2SSabrina Dubroca nla_len(rt) != sizeof(*rs)) {
156785b87b2SSabrina Dubroca NL_SET_ERR_MSG(extack, "ESN attribute is too short to fit the full bitmap length");
157ecd79187SMathias Krause return -EINVAL;
158785b87b2SSabrina Dubroca }
159d8647b79SSteffen Klassert
16001714109SFan Du /* As only ESP and AH support ESN feature. */
161785b87b2SSabrina Dubroca if ((p->id.proto != IPPROTO_ESP) && (p->id.proto != IPPROTO_AH)) {
162785b87b2SSabrina Dubroca NL_SET_ERR_MSG(extack, "ESN only supported for ESP and AH");
16302aadf72SSteffen Klassert return -EINVAL;
164785b87b2SSabrina Dubroca }
16502aadf72SSteffen Klassert
166785b87b2SSabrina Dubroca if (p->replay_window != 0) {
167785b87b2SSabrina Dubroca NL_SET_ERR_MSG(extack, "ESN not compatible with legacy replay_window");
168d8647b79SSteffen Klassert return -EINVAL;
169785b87b2SSabrina Dubroca }
170d8647b79SSteffen Klassert
171d8647b79SSteffen Klassert return 0;
172d8647b79SSteffen Klassert }
173df71837dSTrent Jaeger
verify_newsa_info(struct xfrm_usersa_info * p,struct nlattr ** attrs,struct netlink_ext_ack * extack)1741da177e4SLinus Torvalds static int verify_newsa_info(struct xfrm_usersa_info *p,
1756999aae1SSabrina Dubroca struct nlattr **attrs,
1766999aae1SSabrina Dubroca struct netlink_ext_ack *extack)
1771da177e4SLinus Torvalds {
1781da177e4SLinus Torvalds int err;
179*7d986818SSabrina Dubroca u16 family = p->sel.family;
1801da177e4SLinus Torvalds
1811da177e4SLinus Torvalds err = -EINVAL;
1821da177e4SLinus Torvalds switch (p->family) {
1831da177e4SLinus Torvalds case AF_INET:
184b38ff407SAnirudh Gupta break;
185b38ff407SAnirudh Gupta
186b38ff407SAnirudh Gupta case AF_INET6:
187b38ff407SAnirudh Gupta #if IS_ENABLED(CONFIG_IPV6)
188b38ff407SAnirudh Gupta break;
189b38ff407SAnirudh Gupta #else
190b38ff407SAnirudh Gupta err = -EAFNOSUPPORT;
1916999aae1SSabrina Dubroca NL_SET_ERR_MSG(extack, "IPv6 support disabled");
192b38ff407SAnirudh Gupta goto out;
193b38ff407SAnirudh Gupta #endif
194b38ff407SAnirudh Gupta
195b38ff407SAnirudh Gupta default:
1966999aae1SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid address family");
197b38ff407SAnirudh Gupta goto out;
198b38ff407SAnirudh Gupta }
199b38ff407SAnirudh Gupta
200*7d986818SSabrina Dubroca if (!family && !(p->flags & XFRM_STATE_AF_UNSPEC))
201*7d986818SSabrina Dubroca family = p->family;
202*7d986818SSabrina Dubroca
203*7d986818SSabrina Dubroca switch (family) {
204b8d6d007SNicolas Dichtel case AF_UNSPEC:
205b8d6d007SNicolas Dichtel break;
206b8d6d007SNicolas Dichtel
207b38ff407SAnirudh Gupta case AF_INET:
2086999aae1SSabrina Dubroca if (p->sel.prefixlen_d > 32 || p->sel.prefixlen_s > 32) {
2096999aae1SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid prefix length in selector (must be <= 32 for IPv4)");
21007bf7908SSteffen Klassert goto out;
2116999aae1SSabrina Dubroca }
21207bf7908SSteffen Klassert
2131da177e4SLinus Torvalds break;
2141da177e4SLinus Torvalds
2151da177e4SLinus Torvalds case AF_INET6:
216dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
2176999aae1SSabrina Dubroca if (p->sel.prefixlen_d > 128 || p->sel.prefixlen_s > 128) {
2186999aae1SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid prefix length in selector (must be <= 128 for IPv6)");
21907bf7908SSteffen Klassert goto out;
2206999aae1SSabrina Dubroca }
22107bf7908SSteffen Klassert
2221da177e4SLinus Torvalds break;
2231da177e4SLinus Torvalds #else
2246999aae1SSabrina Dubroca NL_SET_ERR_MSG(extack, "IPv6 support disabled");
2251da177e4SLinus Torvalds err = -EAFNOSUPPORT;
2261da177e4SLinus Torvalds goto out;
2271da177e4SLinus Torvalds #endif
2281da177e4SLinus Torvalds
2291da177e4SLinus Torvalds default:
2306999aae1SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid address family in selector");
2311da177e4SLinus Torvalds goto out;
2323ff50b79SStephen Hemminger }
2331da177e4SLinus Torvalds
2341da177e4SLinus Torvalds err = -EINVAL;
2351da177e4SLinus Torvalds switch (p->id.proto) {
2361da177e4SLinus Torvalds case IPPROTO_AH:
2376999aae1SSabrina Dubroca if (!attrs[XFRMA_ALG_AUTH] &&
2386999aae1SSabrina Dubroca !attrs[XFRMA_ALG_AUTH_TRUNC]) {
2396999aae1SSabrina Dubroca NL_SET_ERR_MSG(extack, "Missing required attribute for AH: AUTH_TRUNC or AUTH");
2406999aae1SSabrina Dubroca goto out;
2416999aae1SSabrina Dubroca }
2426999aae1SSabrina Dubroca
2436999aae1SSabrina Dubroca if (attrs[XFRMA_ALG_AEAD] ||
24435a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT] ||
24535d2856bSMartin Willi attrs[XFRMA_ALG_COMP] ||
2466999aae1SSabrina Dubroca attrs[XFRMA_TFCPAD]) {
2476999aae1SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid attributes for AH: AEAD, CRYPT, COMP, TFCPAD");
2481da177e4SLinus Torvalds goto out;
2496999aae1SSabrina Dubroca }
2501da177e4SLinus Torvalds break;
2511da177e4SLinus Torvalds
2521da177e4SLinus Torvalds case IPPROTO_ESP:
2536999aae1SSabrina Dubroca if (attrs[XFRMA_ALG_COMP]) {
2546999aae1SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid attribute for ESP: COMP");
2551a6509d9SHerbert Xu goto out;
2566999aae1SSabrina Dubroca }
2576999aae1SSabrina Dubroca
2581a6509d9SHerbert Xu if (!attrs[XFRMA_ALG_AUTH] &&
2594447bb33SMartin Willi !attrs[XFRMA_ALG_AUTH_TRUNC] &&
2601a6509d9SHerbert Xu !attrs[XFRMA_ALG_CRYPT] &&
2616999aae1SSabrina Dubroca !attrs[XFRMA_ALG_AEAD]) {
2626999aae1SSabrina Dubroca NL_SET_ERR_MSG(extack, "Missing required attribute for ESP: at least one of AUTH, AUTH_TRUNC, CRYPT, AEAD");
2631a6509d9SHerbert Xu goto out;
2646999aae1SSabrina Dubroca }
2656999aae1SSabrina Dubroca
2661a6509d9SHerbert Xu if ((attrs[XFRMA_ALG_AUTH] ||
2674447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC] ||
2681a6509d9SHerbert Xu attrs[XFRMA_ALG_CRYPT]) &&
2696999aae1SSabrina Dubroca attrs[XFRMA_ALG_AEAD]) {
2706999aae1SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid attribute combination for ESP: AEAD can't be used with AUTH, AUTH_TRUNC, CRYPT");
2711da177e4SLinus Torvalds goto out;
2726999aae1SSabrina Dubroca }
2736999aae1SSabrina Dubroca
27435d2856bSMartin Willi if (attrs[XFRMA_TFCPAD] &&
2756999aae1SSabrina Dubroca p->mode != XFRM_MODE_TUNNEL) {
2766999aae1SSabrina Dubroca NL_SET_ERR_MSG(extack, "TFC padding can only be used in tunnel mode");
27735d2856bSMartin Willi goto out;
2786999aae1SSabrina Dubroca }
2791da177e4SLinus Torvalds break;
2801da177e4SLinus Torvalds
2811da177e4SLinus Torvalds case IPPROTO_COMP:
2826999aae1SSabrina Dubroca if (!attrs[XFRMA_ALG_COMP]) {
2836999aae1SSabrina Dubroca NL_SET_ERR_MSG(extack, "Missing required attribute for COMP: COMP");
2846999aae1SSabrina Dubroca goto out;
2856999aae1SSabrina Dubroca }
2866999aae1SSabrina Dubroca
2876999aae1SSabrina Dubroca if (attrs[XFRMA_ALG_AEAD] ||
28835a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH] ||
2894447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC] ||
29035d2856bSMartin Willi attrs[XFRMA_ALG_CRYPT] ||
2916999aae1SSabrina Dubroca attrs[XFRMA_TFCPAD]) {
2926999aae1SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid attributes for COMP: AEAD, AUTH, AUTH_TRUNC, CRYPT, TFCPAD");
2931da177e4SLinus Torvalds goto out;
2946999aae1SSabrina Dubroca }
2956999aae1SSabrina Dubroca
2966999aae1SSabrina Dubroca if (ntohl(p->id.spi) >= 0x10000) {
2976999aae1SSabrina Dubroca NL_SET_ERR_MSG(extack, "SPI is too large for COMP (must be < 0x10000)");
2986999aae1SSabrina Dubroca goto out;
2996999aae1SSabrina Dubroca }
3001da177e4SLinus Torvalds break;
3011da177e4SLinus Torvalds
302dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
303e23c7194SMasahide NAKAMURA case IPPROTO_DSTOPTS:
304e23c7194SMasahide NAKAMURA case IPPROTO_ROUTING:
30535a7aa08SThomas Graf if (attrs[XFRMA_ALG_COMP] ||
30635a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH] ||
3074447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC] ||
3081a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] ||
30935a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT] ||
31035a7aa08SThomas Graf attrs[XFRMA_ENCAP] ||
31135a7aa08SThomas Graf attrs[XFRMA_SEC_CTX] ||
3126999aae1SSabrina Dubroca attrs[XFRMA_TFCPAD]) {
3136999aae1SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid attributes for DSTOPTS/ROUTING");
314e23c7194SMasahide NAKAMURA goto out;
3156999aae1SSabrina Dubroca }
3166999aae1SSabrina Dubroca
3176999aae1SSabrina Dubroca if (!attrs[XFRMA_COADDR]) {
3186999aae1SSabrina Dubroca NL_SET_ERR_MSG(extack, "Missing required COADDR attribute for DSTOPTS/ROUTING");
3196999aae1SSabrina Dubroca goto out;
3206999aae1SSabrina Dubroca }
321e23c7194SMasahide NAKAMURA break;
322e23c7194SMasahide NAKAMURA #endif
323e23c7194SMasahide NAKAMURA
3241da177e4SLinus Torvalds default:
3256999aae1SSabrina Dubroca NL_SET_ERR_MSG(extack, "Unsupported protocol");
3261da177e4SLinus Torvalds goto out;
3273ff50b79SStephen Hemminger }
3281da177e4SLinus Torvalds
3291fc8fde5SSabrina Dubroca if ((err = verify_aead(attrs, extack)))
3301a6509d9SHerbert Xu goto out;
3311fc8fde5SSabrina Dubroca if ((err = verify_auth_trunc(attrs, extack)))
3324447bb33SMartin Willi goto out;
3331fc8fde5SSabrina Dubroca if ((err = verify_one_alg(attrs, XFRMA_ALG_AUTH, extack)))
3341da177e4SLinus Torvalds goto out;
3351fc8fde5SSabrina Dubroca if ((err = verify_one_alg(attrs, XFRMA_ALG_CRYPT, extack)))
3361da177e4SLinus Torvalds goto out;
3371fc8fde5SSabrina Dubroca if ((err = verify_one_alg(attrs, XFRMA_ALG_COMP, extack)))
3381da177e4SLinus Torvalds goto out;
3396999aae1SSabrina Dubroca if ((err = verify_sec_ctx_len(attrs, extack)))
340df71837dSTrent Jaeger goto out;
341785b87b2SSabrina Dubroca if ((err = verify_replay(p, attrs, extack)))
342d8647b79SSteffen Klassert goto out;
3431da177e4SLinus Torvalds
3441da177e4SLinus Torvalds err = -EINVAL;
3451da177e4SLinus Torvalds switch (p->mode) {
3467e49e6deSMasahide NAKAMURA case XFRM_MODE_TRANSPORT:
3477e49e6deSMasahide NAKAMURA case XFRM_MODE_TUNNEL:
348060f02a3SNoriaki TAKAMIYA case XFRM_MODE_ROUTEOPTIMIZATION:
3490a69452cSDiego Beltrami case XFRM_MODE_BEET:
3501da177e4SLinus Torvalds break;
3511da177e4SLinus Torvalds
3521da177e4SLinus Torvalds default:
3536999aae1SSabrina Dubroca NL_SET_ERR_MSG(extack, "Unsupported mode");
3541da177e4SLinus Torvalds goto out;
3553ff50b79SStephen Hemminger }
3561da177e4SLinus Torvalds
3571da177e4SLinus Torvalds err = 0;
3581da177e4SLinus Torvalds
3596999aae1SSabrina Dubroca if (attrs[XFRMA_MTIMER_THRESH]) {
3606999aae1SSabrina Dubroca if (!attrs[XFRMA_ENCAP]) {
3616999aae1SSabrina Dubroca NL_SET_ERR_MSG(extack, "MTIMER_THRESH attribute can only be set on ENCAP states");
3624e484b3eSAntony Antony err = -EINVAL;
3636999aae1SSabrina Dubroca goto out;
3646999aae1SSabrina Dubroca }
3656999aae1SSabrina Dubroca }
3664e484b3eSAntony Antony
3671da177e4SLinus Torvalds out:
3681da177e4SLinus Torvalds return err;
3691da177e4SLinus Torvalds }
3701da177e4SLinus Torvalds
attach_one_algo(struct xfrm_algo ** algpp,u8 * props,struct xfrm_algo_desc * (* get_byname)(const char *,int),struct nlattr * rta,struct netlink_ext_ack * extack)3711da177e4SLinus Torvalds static int attach_one_algo(struct xfrm_algo **algpp, u8 *props,
3726f2f19edSDavid S. Miller struct xfrm_algo_desc *(*get_byname)(const char *, int),
3732b916826SSabrina Dubroca struct nlattr *rta, struct netlink_ext_ack *extack)
3741da177e4SLinus Torvalds {
3751da177e4SLinus Torvalds struct xfrm_algo *p, *ualg;
3761da177e4SLinus Torvalds struct xfrm_algo_desc *algo;
3771da177e4SLinus Torvalds
3781da177e4SLinus Torvalds if (!rta)
3791da177e4SLinus Torvalds return 0;
3801da177e4SLinus Torvalds
3815424f32eSThomas Graf ualg = nla_data(rta);
3821da177e4SLinus Torvalds
3831da177e4SLinus Torvalds algo = get_byname(ualg->alg_name, 1);
3842b916826SSabrina Dubroca if (!algo) {
3852b916826SSabrina Dubroca NL_SET_ERR_MSG(extack, "Requested COMP algorithm not found");
3861da177e4SLinus Torvalds return -ENOSYS;
3872b916826SSabrina Dubroca }
3881da177e4SLinus Torvalds *props = algo->desc.sadb_alg_id;
3891da177e4SLinus Torvalds
3900f99be0dSEric Dumazet p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL);
3911da177e4SLinus Torvalds if (!p)
3921da177e4SLinus Torvalds return -ENOMEM;
3931da177e4SLinus Torvalds
39404ff1260SHerbert Xu strcpy(p->alg_name, algo->name);
3951da177e4SLinus Torvalds *algpp = p;
3961da177e4SLinus Torvalds return 0;
3971da177e4SLinus Torvalds }
3981da177e4SLinus Torvalds
attach_crypt(struct xfrm_state * x,struct nlattr * rta,struct netlink_ext_ack * extack)3992b916826SSabrina Dubroca static int attach_crypt(struct xfrm_state *x, struct nlattr *rta,
4002b916826SSabrina Dubroca struct netlink_ext_ack *extack)
40169b0137fSHerbert Xu {
40269b0137fSHerbert Xu struct xfrm_algo *p, *ualg;
40369b0137fSHerbert Xu struct xfrm_algo_desc *algo;
40469b0137fSHerbert Xu
40569b0137fSHerbert Xu if (!rta)
40669b0137fSHerbert Xu return 0;
40769b0137fSHerbert Xu
40869b0137fSHerbert Xu ualg = nla_data(rta);
40969b0137fSHerbert Xu
41069b0137fSHerbert Xu algo = xfrm_ealg_get_byname(ualg->alg_name, 1);
4112b916826SSabrina Dubroca if (!algo) {
4122b916826SSabrina Dubroca NL_SET_ERR_MSG(extack, "Requested CRYPT algorithm not found");
41369b0137fSHerbert Xu return -ENOSYS;
4142b916826SSabrina Dubroca }
41569b0137fSHerbert Xu x->props.ealgo = algo->desc.sadb_alg_id;
41669b0137fSHerbert Xu
41769b0137fSHerbert Xu p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL);
41869b0137fSHerbert Xu if (!p)
41969b0137fSHerbert Xu return -ENOMEM;
42069b0137fSHerbert Xu
42169b0137fSHerbert Xu strcpy(p->alg_name, algo->name);
42269b0137fSHerbert Xu x->ealg = p;
42369b0137fSHerbert Xu x->geniv = algo->uinfo.encr.geniv;
42469b0137fSHerbert Xu return 0;
42569b0137fSHerbert Xu }
42669b0137fSHerbert Xu
attach_auth(struct xfrm_algo_auth ** algpp,u8 * props,struct nlattr * rta,struct netlink_ext_ack * extack)4274447bb33SMartin Willi static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props,
4282b916826SSabrina Dubroca struct nlattr *rta, struct netlink_ext_ack *extack)
4294447bb33SMartin Willi {
4304447bb33SMartin Willi struct xfrm_algo *ualg;
4314447bb33SMartin Willi struct xfrm_algo_auth *p;
4324447bb33SMartin Willi struct xfrm_algo_desc *algo;
4334447bb33SMartin Willi
4344447bb33SMartin Willi if (!rta)
4354447bb33SMartin Willi return 0;
4364447bb33SMartin Willi
4374447bb33SMartin Willi ualg = nla_data(rta);
4384447bb33SMartin Willi
4394447bb33SMartin Willi algo = xfrm_aalg_get_byname(ualg->alg_name, 1);
4402b916826SSabrina Dubroca if (!algo) {
4412b916826SSabrina Dubroca NL_SET_ERR_MSG(extack, "Requested AUTH algorithm not found");
4424447bb33SMartin Willi return -ENOSYS;
4432b916826SSabrina Dubroca }
4444447bb33SMartin Willi *props = algo->desc.sadb_alg_id;
4454447bb33SMartin Willi
4464447bb33SMartin Willi p = kmalloc(sizeof(*p) + (ualg->alg_key_len + 7) / 8, GFP_KERNEL);
4474447bb33SMartin Willi if (!p)
4484447bb33SMartin Willi return -ENOMEM;
4494447bb33SMartin Willi
4504447bb33SMartin Willi strcpy(p->alg_name, algo->name);
4514447bb33SMartin Willi p->alg_key_len = ualg->alg_key_len;
4524447bb33SMartin Willi p->alg_trunc_len = algo->uinfo.auth.icv_truncbits;
4534447bb33SMartin Willi memcpy(p->alg_key, ualg->alg_key, (ualg->alg_key_len + 7) / 8);
4544447bb33SMartin Willi
4554447bb33SMartin Willi *algpp = p;
4564447bb33SMartin Willi return 0;
4574447bb33SMartin Willi }
4584447bb33SMartin Willi
attach_auth_trunc(struct xfrm_algo_auth ** algpp,u8 * props,struct nlattr * rta,struct netlink_ext_ack * extack)4594447bb33SMartin Willi static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props,
4602b916826SSabrina Dubroca struct nlattr *rta, struct netlink_ext_ack *extack)
4614447bb33SMartin Willi {
4624447bb33SMartin Willi struct xfrm_algo_auth *p, *ualg;
4634447bb33SMartin Willi struct xfrm_algo_desc *algo;
4644447bb33SMartin Willi
4654447bb33SMartin Willi if (!rta)
4664447bb33SMartin Willi return 0;
4674447bb33SMartin Willi
4684447bb33SMartin Willi ualg = nla_data(rta);
4694447bb33SMartin Willi
4704447bb33SMartin Willi algo = xfrm_aalg_get_byname(ualg->alg_name, 1);
4712b916826SSabrina Dubroca if (!algo) {
4722b916826SSabrina Dubroca NL_SET_ERR_MSG(extack, "Requested AUTH_TRUNC algorithm not found");
4734447bb33SMartin Willi return -ENOSYS;
4742b916826SSabrina Dubroca }
4752b916826SSabrina Dubroca if (ualg->alg_trunc_len > algo->uinfo.auth.icv_fullbits) {
4762b916826SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid length requested for truncated ICV");
4774447bb33SMartin Willi return -EINVAL;
4782b916826SSabrina Dubroca }
4794447bb33SMartin Willi *props = algo->desc.sadb_alg_id;
4804447bb33SMartin Willi
4814447bb33SMartin Willi p = kmemdup(ualg, xfrm_alg_auth_len(ualg), GFP_KERNEL);
4824447bb33SMartin Willi if (!p)
4834447bb33SMartin Willi return -ENOMEM;
4844447bb33SMartin Willi
4854447bb33SMartin Willi strcpy(p->alg_name, algo->name);
4864447bb33SMartin Willi if (!p->alg_trunc_len)
4874447bb33SMartin Willi p->alg_trunc_len = algo->uinfo.auth.icv_truncbits;
4884447bb33SMartin Willi
4894447bb33SMartin Willi *algpp = p;
4904447bb33SMartin Willi return 0;
4914447bb33SMartin Willi }
4924447bb33SMartin Willi
attach_aead(struct xfrm_state * x,struct nlattr * rta,struct netlink_ext_ack * extack)4932b916826SSabrina Dubroca static int attach_aead(struct xfrm_state *x, struct nlattr *rta,
4942b916826SSabrina Dubroca struct netlink_ext_ack *extack)
4951a6509d9SHerbert Xu {
4961a6509d9SHerbert Xu struct xfrm_algo_aead *p, *ualg;
4971a6509d9SHerbert Xu struct xfrm_algo_desc *algo;
4981a6509d9SHerbert Xu
4991a6509d9SHerbert Xu if (!rta)
5001a6509d9SHerbert Xu return 0;
5011a6509d9SHerbert Xu
5021a6509d9SHerbert Xu ualg = nla_data(rta);
5031a6509d9SHerbert Xu
5041a6509d9SHerbert Xu algo = xfrm_aead_get_byname(ualg->alg_name, ualg->alg_icv_len, 1);
5052b916826SSabrina Dubroca if (!algo) {
5062b916826SSabrina Dubroca NL_SET_ERR_MSG(extack, "Requested AEAD algorithm not found");
5071a6509d9SHerbert Xu return -ENOSYS;
5082b916826SSabrina Dubroca }
50969b0137fSHerbert Xu x->props.ealgo = algo->desc.sadb_alg_id;
5101a6509d9SHerbert Xu
5111a6509d9SHerbert Xu p = kmemdup(ualg, aead_len(ualg), GFP_KERNEL);
5121a6509d9SHerbert Xu if (!p)
5131a6509d9SHerbert Xu return -ENOMEM;
5141a6509d9SHerbert Xu
5151a6509d9SHerbert Xu strcpy(p->alg_name, algo->name);
51669b0137fSHerbert Xu x->aead = p;
51769b0137fSHerbert Xu x->geniv = algo->uinfo.aead.geniv;
5181a6509d9SHerbert Xu return 0;
5191a6509d9SHerbert Xu }
5201a6509d9SHerbert Xu
xfrm_replay_verify_len(struct xfrm_replay_state_esn * replay_esn,struct nlattr * rp,struct netlink_ext_ack * extack)521e2b19125SSteffen Klassert static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_esn,
522643bc1a2SSabrina Dubroca struct nlattr *rp,
523643bc1a2SSabrina Dubroca struct netlink_ext_ack *extack)
524e2b19125SSteffen Klassert {
525e2b19125SSteffen Klassert struct xfrm_replay_state_esn *up;
5265e708e47SAlexey Dobriyan unsigned int ulen;
527e2b19125SSteffen Klassert
528e2b19125SSteffen Klassert if (!replay_esn || !rp)
529e2b19125SSteffen Klassert return 0;
530e2b19125SSteffen Klassert
531e2b19125SSteffen Klassert up = nla_data(rp);
532ecd79187SMathias Krause ulen = xfrm_replay_state_esn_len(up);
533e2b19125SSteffen Klassert
534f843ee6dSAndy Whitcroft /* Check the overall length and the internal bitmap length to avoid
535f843ee6dSAndy Whitcroft * potential overflow. */
536643bc1a2SSabrina Dubroca if (nla_len(rp) < (int)ulen) {
537643bc1a2SSabrina Dubroca NL_SET_ERR_MSG(extack, "ESN attribute is too short");
538e2b19125SSteffen Klassert return -EINVAL;
539643bc1a2SSabrina Dubroca }
540e2b19125SSteffen Klassert
541643bc1a2SSabrina Dubroca if (xfrm_replay_state_esn_len(replay_esn) != ulen) {
542643bc1a2SSabrina Dubroca NL_SET_ERR_MSG(extack, "New ESN size doesn't match the existing SA's ESN size");
543677e806dSAndy Whitcroft return -EINVAL;
544643bc1a2SSabrina Dubroca }
545643bc1a2SSabrina Dubroca
546643bc1a2SSabrina Dubroca if (replay_esn->bmp_len != up->bmp_len) {
547643bc1a2SSabrina Dubroca NL_SET_ERR_MSG(extack, "New ESN bitmap size doesn't match the existing SA's ESN bitmap");
548643bc1a2SSabrina Dubroca return -EINVAL;
549643bc1a2SSabrina Dubroca }
550643bc1a2SSabrina Dubroca
551643bc1a2SSabrina Dubroca if (up->replay_window > up->bmp_len * sizeof(__u32) * 8) {
552643bc1a2SSabrina Dubroca NL_SET_ERR_MSG(extack, "ESN replay window is longer than the bitmap");
553643bc1a2SSabrina Dubroca return -EINVAL;
554643bc1a2SSabrina Dubroca }
555677e806dSAndy Whitcroft
556e2b19125SSteffen Klassert return 0;
557e2b19125SSteffen Klassert }
558e2b19125SSteffen Klassert
xfrm_alloc_replay_state_esn(struct xfrm_replay_state_esn ** replay_esn,struct xfrm_replay_state_esn ** preplay_esn,struct nlattr * rta)559d8647b79SSteffen Klassert static int xfrm_alloc_replay_state_esn(struct xfrm_replay_state_esn **replay_esn,
560d8647b79SSteffen Klassert struct xfrm_replay_state_esn **preplay_esn,
561d8647b79SSteffen Klassert struct nlattr *rta)
562d8647b79SSteffen Klassert {
563d8647b79SSteffen Klassert struct xfrm_replay_state_esn *p, *pp, *up;
5645e708e47SAlexey Dobriyan unsigned int klen, ulen;
565d8647b79SSteffen Klassert
566d8647b79SSteffen Klassert if (!rta)
567d8647b79SSteffen Klassert return 0;
568d8647b79SSteffen Klassert
569d8647b79SSteffen Klassert up = nla_data(rta);
570ecd79187SMathias Krause klen = xfrm_replay_state_esn_len(up);
5715e708e47SAlexey Dobriyan ulen = nla_len(rta) >= (int)klen ? klen : sizeof(*up);
572d8647b79SSteffen Klassert
573ecd79187SMathias Krause p = kzalloc(klen, GFP_KERNEL);
574d8647b79SSteffen Klassert if (!p)
575d8647b79SSteffen Klassert return -ENOMEM;
576d8647b79SSteffen Klassert
577ecd79187SMathias Krause pp = kzalloc(klen, GFP_KERNEL);
578d8647b79SSteffen Klassert if (!pp) {
579d8647b79SSteffen Klassert kfree(p);
580d8647b79SSteffen Klassert return -ENOMEM;
581d8647b79SSteffen Klassert }
582d8647b79SSteffen Klassert
583ecd79187SMathias Krause memcpy(p, up, ulen);
584ecd79187SMathias Krause memcpy(pp, up, ulen);
585ecd79187SMathias Krause
586d8647b79SSteffen Klassert *replay_esn = p;
587d8647b79SSteffen Klassert *preplay_esn = pp;
588d8647b79SSteffen Klassert
589d8647b79SSteffen Klassert return 0;
590d8647b79SSteffen Klassert }
591d8647b79SSteffen Klassert
xfrm_user_sec_ctx_size(struct xfrm_sec_ctx * xfrm_ctx)592a1b831f2SAlexey Dobriyan static inline unsigned int xfrm_user_sec_ctx_size(struct xfrm_sec_ctx *xfrm_ctx)
593df71837dSTrent Jaeger {
594a1b831f2SAlexey Dobriyan unsigned int len = 0;
595df71837dSTrent Jaeger
596df71837dSTrent Jaeger if (xfrm_ctx) {
597df71837dSTrent Jaeger len += sizeof(struct xfrm_user_sec_ctx);
598df71837dSTrent Jaeger len += xfrm_ctx->ctx_len;
599df71837dSTrent Jaeger }
600df71837dSTrent Jaeger return len;
601df71837dSTrent Jaeger }
602df71837dSTrent Jaeger
copy_from_user_state(struct xfrm_state * x,struct xfrm_usersa_info * p)6031da177e4SLinus Torvalds static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p)
6041da177e4SLinus Torvalds {
6051da177e4SLinus Torvalds memcpy(&x->id, &p->id, sizeof(x->id));
6061da177e4SLinus Torvalds memcpy(&x->sel, &p->sel, sizeof(x->sel));
6071da177e4SLinus Torvalds memcpy(&x->lft, &p->lft, sizeof(x->lft));
6081da177e4SLinus Torvalds x->props.mode = p->mode;
60933fce60dSFan Du x->props.replay_window = min_t(unsigned int, p->replay_window,
61033fce60dSFan Du sizeof(x->replay.bitmap) * 8);
6111da177e4SLinus Torvalds x->props.reqid = p->reqid;
6121da177e4SLinus Torvalds x->props.family = p->family;
61354489c14SDavid S. Miller memcpy(&x->props.saddr, &p->saddr, sizeof(x->props.saddr));
6141da177e4SLinus Torvalds x->props.flags = p->flags;
615196b0036SHerbert Xu
616ccf9b3b8SSteffen Klassert if (!x->sel.family && !(p->flags & XFRM_STATE_AF_UNSPEC))
617196b0036SHerbert Xu x->sel.family = p->family;
6181da177e4SLinus Torvalds }
6191da177e4SLinus Torvalds
620d51d081dSJamal Hadi Salim /*
621d51d081dSJamal Hadi Salim * someday when pfkey also has support, we could have the code
622d51d081dSJamal Hadi Salim * somehow made shareable and move it to xfrm_state.c - JHS
623d51d081dSJamal Hadi Salim *
624d51d081dSJamal Hadi Salim */
xfrm_update_ae_params(struct xfrm_state * x,struct nlattr ** attrs,int update_esn)625e3ac104dSMathias Krause static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs,
626e3ac104dSMathias Krause int update_esn)
627d51d081dSJamal Hadi Salim {
6285424f32eSThomas Graf struct nlattr *rp = attrs[XFRMA_REPLAY_VAL];
629e3ac104dSMathias Krause struct nlattr *re = update_esn ? attrs[XFRMA_REPLAY_ESN_VAL] : NULL;
6305424f32eSThomas Graf struct nlattr *lt = attrs[XFRMA_LTIME_VAL];
6315424f32eSThomas Graf struct nlattr *et = attrs[XFRMA_ETIMER_THRESH];
6325424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH];
6334e484b3eSAntony Antony struct nlattr *mt = attrs[XFRMA_MTIMER_THRESH];
634d51d081dSJamal Hadi Salim
63500374d9bSLin Ma if (re && x->replay_esn && x->preplay_esn) {
636d8647b79SSteffen Klassert struct xfrm_replay_state_esn *replay_esn;
637d8647b79SSteffen Klassert replay_esn = nla_data(re);
638d8647b79SSteffen Klassert memcpy(x->replay_esn, replay_esn,
639d8647b79SSteffen Klassert xfrm_replay_state_esn_len(replay_esn));
640d8647b79SSteffen Klassert memcpy(x->preplay_esn, replay_esn,
641d8647b79SSteffen Klassert xfrm_replay_state_esn_len(replay_esn));
642d8647b79SSteffen Klassert }
643d8647b79SSteffen Klassert
644d51d081dSJamal Hadi Salim if (rp) {
645d51d081dSJamal Hadi Salim struct xfrm_replay_state *replay;
6465424f32eSThomas Graf replay = nla_data(rp);
647d51d081dSJamal Hadi Salim memcpy(&x->replay, replay, sizeof(*replay));
648d51d081dSJamal Hadi Salim memcpy(&x->preplay, replay, sizeof(*replay));
649d51d081dSJamal Hadi Salim }
650d51d081dSJamal Hadi Salim
651d51d081dSJamal Hadi Salim if (lt) {
652d51d081dSJamal Hadi Salim struct xfrm_lifetime_cur *ltime;
6535424f32eSThomas Graf ltime = nla_data(lt);
654d51d081dSJamal Hadi Salim x->curlft.bytes = ltime->bytes;
655d51d081dSJamal Hadi Salim x->curlft.packets = ltime->packets;
656d51d081dSJamal Hadi Salim x->curlft.add_time = ltime->add_time;
657d51d081dSJamal Hadi Salim x->curlft.use_time = ltime->use_time;
658d51d081dSJamal Hadi Salim }
659d51d081dSJamal Hadi Salim
660cf5cb79fSThomas Graf if (et)
6615424f32eSThomas Graf x->replay_maxage = nla_get_u32(et);
662d51d081dSJamal Hadi Salim
663cf5cb79fSThomas Graf if (rt)
6645424f32eSThomas Graf x->replay_maxdiff = nla_get_u32(rt);
6654e484b3eSAntony Antony
6664e484b3eSAntony Antony if (mt)
6674e484b3eSAntony Antony x->mapping_maxage = nla_get_u32(mt);
668d51d081dSJamal Hadi Salim }
669d51d081dSJamal Hadi Salim
xfrm_smark_init(struct nlattr ** attrs,struct xfrm_mark * m)6709b42c1f1SSteffen Klassert static void xfrm_smark_init(struct nlattr **attrs, struct xfrm_mark *m)
6719b42c1f1SSteffen Klassert {
6729b42c1f1SSteffen Klassert if (attrs[XFRMA_SET_MARK]) {
6739b42c1f1SSteffen Klassert m->v = nla_get_u32(attrs[XFRMA_SET_MARK]);
6749b42c1f1SSteffen Klassert if (attrs[XFRMA_SET_MARK_MASK])
6759b42c1f1SSteffen Klassert m->m = nla_get_u32(attrs[XFRMA_SET_MARK_MASK]);
6769b42c1f1SSteffen Klassert else
6779b42c1f1SSteffen Klassert m->m = 0xffffffff;
6789b42c1f1SSteffen Klassert } else {
6799b42c1f1SSteffen Klassert m->v = m->m = 0;
6809b42c1f1SSteffen Klassert }
6819b42c1f1SSteffen Klassert }
6829b42c1f1SSteffen Klassert
xfrm_state_construct(struct net * net,struct xfrm_usersa_info * p,struct nlattr ** attrs,int * errp,struct netlink_ext_ack * extack)683fc34acd3SAlexey Dobriyan static struct xfrm_state *xfrm_state_construct(struct net *net,
684fc34acd3SAlexey Dobriyan struct xfrm_usersa_info *p,
6855424f32eSThomas Graf struct nlattr **attrs,
686adb5c33eSSabrina Dubroca int *errp,
687adb5c33eSSabrina Dubroca struct netlink_ext_ack *extack)
6881da177e4SLinus Torvalds {
689fc34acd3SAlexey Dobriyan struct xfrm_state *x = xfrm_state_alloc(net);
6901da177e4SLinus Torvalds int err = -ENOMEM;
6911da177e4SLinus Torvalds
6921da177e4SLinus Torvalds if (!x)
6931da177e4SLinus Torvalds goto error_no_put;
6941da177e4SLinus Torvalds
6951da177e4SLinus Torvalds copy_from_user_state(x, p);
6961da177e4SLinus Torvalds
6976fd06963SSteffen Klassert if (attrs[XFRMA_ENCAP]) {
6986fd06963SSteffen Klassert x->encap = kmemdup(nla_data(attrs[XFRMA_ENCAP]),
6996fd06963SSteffen Klassert sizeof(*x->encap), GFP_KERNEL);
7006fd06963SSteffen Klassert if (x->encap == NULL)
7016fd06963SSteffen Klassert goto error;
7026fd06963SSteffen Klassert }
7036fd06963SSteffen Klassert
7046fd06963SSteffen Klassert if (attrs[XFRMA_COADDR]) {
7056fd06963SSteffen Klassert x->coaddr = kmemdup(nla_data(attrs[XFRMA_COADDR]),
7066fd06963SSteffen Klassert sizeof(*x->coaddr), GFP_KERNEL);
7076fd06963SSteffen Klassert if (x->coaddr == NULL)
7086fd06963SSteffen Klassert goto error;
7096fd06963SSteffen Klassert }
7106fd06963SSteffen Klassert
711a947b0a9SNicolas Dichtel if (attrs[XFRMA_SA_EXTRA_FLAGS])
712a947b0a9SNicolas Dichtel x->props.extra_flags = nla_get_u32(attrs[XFRMA_SA_EXTRA_FLAGS]);
713a947b0a9SNicolas Dichtel
7142b916826SSabrina Dubroca if ((err = attach_aead(x, attrs[XFRMA_ALG_AEAD], extack)))
7151a6509d9SHerbert Xu goto error;
7164447bb33SMartin Willi if ((err = attach_auth_trunc(&x->aalg, &x->props.aalgo,
7172b916826SSabrina Dubroca attrs[XFRMA_ALG_AUTH_TRUNC], extack)))
7184447bb33SMartin Willi goto error;
7194447bb33SMartin Willi if (!x->props.aalgo) {
7204447bb33SMartin Willi if ((err = attach_auth(&x->aalg, &x->props.aalgo,
7212b916826SSabrina Dubroca attrs[XFRMA_ALG_AUTH], extack)))
7221da177e4SLinus Torvalds goto error;
7234447bb33SMartin Willi }
7242b916826SSabrina Dubroca if ((err = attach_crypt(x, attrs[XFRMA_ALG_CRYPT], extack)))
7251da177e4SLinus Torvalds goto error;
7261da177e4SLinus Torvalds if ((err = attach_one_algo(&x->calg, &x->props.calgo,
7271da177e4SLinus Torvalds xfrm_calg_get_byname,
7282b916826SSabrina Dubroca attrs[XFRMA_ALG_COMP], extack)))
7291da177e4SLinus Torvalds goto error;
730fd21150aSThomas Graf
73135d2856bSMartin Willi if (attrs[XFRMA_TFCPAD])
73235d2856bSMartin Willi x->tfcpad = nla_get_u32(attrs[XFRMA_TFCPAD]);
73335d2856bSMartin Willi
7346f26b61eSJamal Hadi Salim xfrm_mark_get(attrs, &x->mark);
7356f26b61eSJamal Hadi Salim
7369b42c1f1SSteffen Klassert xfrm_smark_init(attrs, &x->props.smark);
737077fbac4SLorenzo Colitti
738a3d9001bSKai Lueke if (attrs[XFRMA_IF_ID])
7397e652640SSteffen Klassert x->if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
740fd21150aSThomas Graf
741741f9a10SSabrina Dubroca err = __xfrm_init_state(x, false, attrs[XFRMA_OFFLOAD_DEV], extack);
7421da177e4SLinus Torvalds if (err)
7431da177e4SLinus Torvalds goto error;
7441da177e4SLinus Torvalds
7452f30ea50SMathias Krause if (attrs[XFRMA_SEC_CTX]) {
7462f30ea50SMathias Krause err = security_xfrm_state_alloc(x,
7472f30ea50SMathias Krause nla_data(attrs[XFRMA_SEC_CTX]));
7482f30ea50SMathias Krause if (err)
749df71837dSTrent Jaeger goto error;
7502f30ea50SMathias Krause }
751df71837dSTrent Jaeger
752d8647b79SSteffen Klassert if ((err = xfrm_alloc_replay_state_esn(&x->replay_esn, &x->preplay_esn,
753d8647b79SSteffen Klassert attrs[XFRMA_REPLAY_ESN_VAL])))
754d8647b79SSteffen Klassert goto error;
755d8647b79SSteffen Klassert
7561da177e4SLinus Torvalds x->km.seq = p->seq;
757b27aeadbSAlexey Dobriyan x->replay_maxdiff = net->xfrm.sysctl_aevent_rseqth;
758d51d081dSJamal Hadi Salim /* sysctl_xfrm_aevent_etime is in 100ms units */
759b27aeadbSAlexey Dobriyan x->replay_maxage = (net->xfrm.sysctl_aevent_etime*HZ)/XFRM_AE_ETH_M;
760d51d081dSJamal Hadi Salim
7611cf9a3aeSSabrina Dubroca if ((err = xfrm_init_replay(x, extack)))
7629fdc4883SSteffen Klassert goto error;
763d51d081dSJamal Hadi Salim
7649fdc4883SSteffen Klassert /* override default values from above */
765e3ac104dSMathias Krause xfrm_update_ae_params(x, attrs, 0);
7661da177e4SLinus Torvalds
767cc01572eSYossi Kuperman /* configure the hardware if offload is requested */
768cc01572eSYossi Kuperman if (attrs[XFRMA_OFFLOAD_DEV]) {
769cc01572eSYossi Kuperman err = xfrm_dev_state_add(net, x,
770adb5c33eSSabrina Dubroca nla_data(attrs[XFRMA_OFFLOAD_DEV]),
771adb5c33eSSabrina Dubroca extack);
772cc01572eSYossi Kuperman if (err)
773cc01572eSYossi Kuperman goto error;
774cc01572eSYossi Kuperman }
775cc01572eSYossi Kuperman
7761da177e4SLinus Torvalds return x;
7771da177e4SLinus Torvalds
7781da177e4SLinus Torvalds error:
7791da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD;
7801da177e4SLinus Torvalds xfrm_state_put(x);
7811da177e4SLinus Torvalds error_no_put:
7821da177e4SLinus Torvalds *errp = err;
7831da177e4SLinus Torvalds return NULL;
7841da177e4SLinus Torvalds }
7851da177e4SLinus Torvalds
xfrm_add_sa(struct sk_buff * skb,struct nlmsghdr * nlh,struct nlattr ** attrs,struct netlink_ext_ack * extack)78622e70050SChristoph Hellwig static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
7873bec6c3eSSabrina Dubroca struct nlattr **attrs, struct netlink_ext_ack *extack)
7881da177e4SLinus Torvalds {
789fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk);
7907b67c857SThomas Graf struct xfrm_usersa_info *p = nlmsg_data(nlh);
7911da177e4SLinus Torvalds struct xfrm_state *x;
7921da177e4SLinus Torvalds int err;
79326b15dadSJamal Hadi Salim struct km_event c;
7941da177e4SLinus Torvalds
7956999aae1SSabrina Dubroca err = verify_newsa_info(p, attrs, extack);
7961da177e4SLinus Torvalds if (err)
7971da177e4SLinus Torvalds return err;
7981da177e4SLinus Torvalds
799adb5c33eSSabrina Dubroca x = xfrm_state_construct(net, p, attrs, &err, extack);
8001da177e4SLinus Torvalds if (!x)
8011da177e4SLinus Torvalds return err;
8021da177e4SLinus Torvalds
80326b15dadSJamal Hadi Salim xfrm_state_hold(x);
8041da177e4SLinus Torvalds if (nlh->nlmsg_type == XFRM_MSG_NEWSA)
8051da177e4SLinus Torvalds err = xfrm_state_add(x);
8061da177e4SLinus Torvalds else
8071da177e4SLinus Torvalds err = xfrm_state_update(x);
8081da177e4SLinus Torvalds
8092e71029eSTetsuo Handa xfrm_audit_state_add(x, err ? 0 : 1, true);
810161a09e7SJoy Latten
8111da177e4SLinus Torvalds if (err < 0) {
8121da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD;
813c5d4d7d8SSteffen Klassert xfrm_dev_state_delete(x);
81421380b81SHerbert Xu __xfrm_state_put(x);
8157d6dfe1fSPatrick McHardy goto out;
8161da177e4SLinus Torvalds }
8171da177e4SLinus Torvalds
818cc01572eSYossi Kuperman if (x->km.state == XFRM_STATE_VOID)
819cc01572eSYossi Kuperman x->km.state = XFRM_STATE_VALID;
820cc01572eSYossi Kuperman
82126b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq;
82215e47304SEric W. Biederman c.portid = nlh->nlmsg_pid;
823f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type;
82426b15dadSJamal Hadi Salim
82526b15dadSJamal Hadi Salim km_state_notify(x, &c);
8267d6dfe1fSPatrick McHardy out:
82726b15dadSJamal Hadi Salim xfrm_state_put(x);
8281da177e4SLinus Torvalds return err;
8291da177e4SLinus Torvalds }
8301da177e4SLinus Torvalds
xfrm_user_state_lookup(struct net * net,struct xfrm_usersa_id * p,struct nlattr ** attrs,int * errp)831fc34acd3SAlexey Dobriyan static struct xfrm_state *xfrm_user_state_lookup(struct net *net,
832fc34acd3SAlexey Dobriyan struct xfrm_usersa_id *p,
8335424f32eSThomas Graf struct nlattr **attrs,
834eb2971b6SMasahide NAKAMURA int *errp)
835eb2971b6SMasahide NAKAMURA {
836eb2971b6SMasahide NAKAMURA struct xfrm_state *x = NULL;
8376f26b61eSJamal Hadi Salim struct xfrm_mark m;
838eb2971b6SMasahide NAKAMURA int err;
8396f26b61eSJamal Hadi Salim u32 mark = xfrm_mark_get(attrs, &m);
840eb2971b6SMasahide NAKAMURA
841eb2971b6SMasahide NAKAMURA if (xfrm_id_proto_match(p->proto, IPSEC_PROTO_ANY)) {
842eb2971b6SMasahide NAKAMURA err = -ESRCH;
8436f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &p->daddr, p->spi, p->proto, p->family);
844eb2971b6SMasahide NAKAMURA } else {
845eb2971b6SMasahide NAKAMURA xfrm_address_t *saddr = NULL;
846eb2971b6SMasahide NAKAMURA
84735a7aa08SThomas Graf verify_one_addr(attrs, XFRMA_SRCADDR, &saddr);
848eb2971b6SMasahide NAKAMURA if (!saddr) {
849eb2971b6SMasahide NAKAMURA err = -EINVAL;
850eb2971b6SMasahide NAKAMURA goto out;
851eb2971b6SMasahide NAKAMURA }
852eb2971b6SMasahide NAKAMURA
8539abbffeeSMasahide NAKAMURA err = -ESRCH;
8546f26b61eSJamal Hadi Salim x = xfrm_state_lookup_byaddr(net, mark,
8556f26b61eSJamal Hadi Salim &p->daddr, saddr,
856221df1edSAlexey Dobriyan p->proto, p->family);
857eb2971b6SMasahide NAKAMURA }
858eb2971b6SMasahide NAKAMURA
859eb2971b6SMasahide NAKAMURA out:
860eb2971b6SMasahide NAKAMURA if (!x && errp)
861eb2971b6SMasahide NAKAMURA *errp = err;
862eb2971b6SMasahide NAKAMURA return x;
863eb2971b6SMasahide NAKAMURA }
864eb2971b6SMasahide NAKAMURA
xfrm_del_sa(struct sk_buff * skb,struct nlmsghdr * nlh,struct nlattr ** attrs,struct netlink_ext_ack * extack)86522e70050SChristoph Hellwig static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
8663bec6c3eSSabrina Dubroca struct nlattr **attrs, struct netlink_ext_ack *extack)
8671da177e4SLinus Torvalds {
868fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk);
8691da177e4SLinus Torvalds struct xfrm_state *x;
870eb2971b6SMasahide NAKAMURA int err = -ESRCH;
87126b15dadSJamal Hadi Salim struct km_event c;
8727b67c857SThomas Graf struct xfrm_usersa_id *p = nlmsg_data(nlh);
8731da177e4SLinus Torvalds
874fc34acd3SAlexey Dobriyan x = xfrm_user_state_lookup(net, p, attrs, &err);
8751da177e4SLinus Torvalds if (x == NULL)
876eb2971b6SMasahide NAKAMURA return err;
8771da177e4SLinus Torvalds
8786f68dc37SDavid S. Miller if ((err = security_xfrm_state_delete(x)) != 0)
879c8c05a8eSCatherine Zhang goto out;
880c8c05a8eSCatherine Zhang
8811da177e4SLinus Torvalds if (xfrm_state_kern(x)) {
882880e475dSSabrina Dubroca NL_SET_ERR_MSG(extack, "SA is in use by tunnels");
883c8c05a8eSCatherine Zhang err = -EPERM;
884c8c05a8eSCatherine Zhang goto out;
8851da177e4SLinus Torvalds }
8861da177e4SLinus Torvalds
88726b15dadSJamal Hadi Salim err = xfrm_state_delete(x);
888c8c05a8eSCatherine Zhang if (err < 0)
889c8c05a8eSCatherine Zhang goto out;
89026b15dadSJamal Hadi Salim
89126b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq;
89215e47304SEric W. Biederman c.portid = nlh->nlmsg_pid;
893f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type;
89426b15dadSJamal Hadi Salim km_state_notify(x, &c);
8951da177e4SLinus Torvalds
896c8c05a8eSCatherine Zhang out:
8972e71029eSTetsuo Handa xfrm_audit_state_delete(x, err ? 0 : 1, true);
898c8c05a8eSCatherine Zhang xfrm_state_put(x);
89926b15dadSJamal Hadi Salim return err;
9001da177e4SLinus Torvalds }
9011da177e4SLinus Torvalds
copy_to_user_state(struct xfrm_state * x,struct xfrm_usersa_info * p)9021da177e4SLinus Torvalds static void copy_to_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p)
9031da177e4SLinus Torvalds {
904f778a636SMathias Krause memset(p, 0, sizeof(*p));
9051da177e4SLinus Torvalds memcpy(&p->id, &x->id, sizeof(p->id));
9061da177e4SLinus Torvalds memcpy(&p->sel, &x->sel, sizeof(p->sel));
9071da177e4SLinus Torvalds memcpy(&p->lft, &x->lft, sizeof(p->lft));
908c9fa320bSRaed Salem if (x->xso.dev)
909c9fa320bSRaed Salem xfrm_dev_state_update_curlft(x);
9101da177e4SLinus Torvalds memcpy(&p->curlft, &x->curlft, sizeof(p->curlft));
911e33d4f13SSowmini Varadhan put_unaligned(x->stats.replay_window, &p->stats.replay_window);
912e33d4f13SSowmini Varadhan put_unaligned(x->stats.replay, &p->stats.replay);
913e33d4f13SSowmini Varadhan put_unaligned(x->stats.integrity_failed, &p->stats.integrity_failed);
91454489c14SDavid S. Miller memcpy(&p->saddr, &x->props.saddr, sizeof(p->saddr));
9151da177e4SLinus Torvalds p->mode = x->props.mode;
9161da177e4SLinus Torvalds p->replay_window = x->props.replay_window;
9171da177e4SLinus Torvalds p->reqid = x->props.reqid;
9181da177e4SLinus Torvalds p->family = x->props.family;
9191da177e4SLinus Torvalds p->flags = x->props.flags;
9201da177e4SLinus Torvalds p->seq = x->km.seq;
9211da177e4SLinus Torvalds }
9221da177e4SLinus Torvalds
9231da177e4SLinus Torvalds struct xfrm_dump_info {
9241da177e4SLinus Torvalds struct sk_buff *in_skb;
9251da177e4SLinus Torvalds struct sk_buff *out_skb;
9261da177e4SLinus Torvalds u32 nlmsg_seq;
9271da177e4SLinus Torvalds u16 nlmsg_flags;
9281da177e4SLinus Torvalds };
9291da177e4SLinus Torvalds
copy_sec_ctx(struct xfrm_sec_ctx * s,struct sk_buff * skb)930c0144beaSThomas Graf static int copy_sec_ctx(struct xfrm_sec_ctx *s, struct sk_buff *skb)
931c0144beaSThomas Graf {
932c0144beaSThomas Graf struct xfrm_user_sec_ctx *uctx;
933c0144beaSThomas Graf struct nlattr *attr;
93468325d3bSHerbert Xu int ctx_size = sizeof(*uctx) + s->ctx_len;
935c0144beaSThomas Graf
936c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_SEC_CTX, ctx_size);
937c0144beaSThomas Graf if (attr == NULL)
938c0144beaSThomas Graf return -EMSGSIZE;
939c0144beaSThomas Graf
940c0144beaSThomas Graf uctx = nla_data(attr);
941c0144beaSThomas Graf uctx->exttype = XFRMA_SEC_CTX;
942c0144beaSThomas Graf uctx->len = ctx_size;
943c0144beaSThomas Graf uctx->ctx_doi = s->ctx_doi;
944c0144beaSThomas Graf uctx->ctx_alg = s->ctx_alg;
945c0144beaSThomas Graf uctx->ctx_len = s->ctx_len;
946c0144beaSThomas Graf memcpy(uctx + 1, s->ctx_str, s->ctx_len);
947c0144beaSThomas Graf
948c0144beaSThomas Graf return 0;
949c0144beaSThomas Graf }
950c0144beaSThomas Graf
copy_user_offload(struct xfrm_dev_offload * xso,struct sk_buff * skb)95187e0a94eSLeon Romanovsky static int copy_user_offload(struct xfrm_dev_offload *xso, struct sk_buff *skb)
952d77e38e6SSteffen Klassert {
953d77e38e6SSteffen Klassert struct xfrm_user_offload *xuo;
954d77e38e6SSteffen Klassert struct nlattr *attr;
955d77e38e6SSteffen Klassert
956d77e38e6SSteffen Klassert attr = nla_reserve(skb, XFRMA_OFFLOAD_DEV, sizeof(*xuo));
957d77e38e6SSteffen Klassert if (attr == NULL)
958d77e38e6SSteffen Klassert return -EMSGSIZE;
959d77e38e6SSteffen Klassert
960d77e38e6SSteffen Klassert xuo = nla_data(attr);
9615fe0d4bdSMathias Krause memset(xuo, 0, sizeof(*xuo));
962d77e38e6SSteffen Klassert xuo->ifindex = xso->dev->ifindex;
963482db2f1SLeon Romanovsky if (xso->dir == XFRM_DEV_OFFLOAD_IN)
964482db2f1SLeon Romanovsky xuo->flags = XFRM_OFFLOAD_INBOUND;
965d14f28b8SLeon Romanovsky if (xso->type == XFRM_DEV_OFFLOAD_PACKET)
966d14f28b8SLeon Romanovsky xuo->flags |= XFRM_OFFLOAD_PACKET;
967d77e38e6SSteffen Klassert
968d77e38e6SSteffen Klassert return 0;
969d77e38e6SSteffen Klassert }
970d77e38e6SSteffen Klassert
xfrm_redact(void)971c7a5899eSAntony Antony static bool xfrm_redact(void)
972c7a5899eSAntony Antony {
973c7a5899eSAntony Antony return IS_ENABLED(CONFIG_SECURITY) &&
974c7a5899eSAntony Antony security_locked_down(LOCKDOWN_XFRM_SECRET);
975c7a5899eSAntony Antony }
976c7a5899eSAntony Antony
copy_to_user_auth(struct xfrm_algo_auth * auth,struct sk_buff * skb)9774447bb33SMartin Willi static int copy_to_user_auth(struct xfrm_algo_auth *auth, struct sk_buff *skb)
9784447bb33SMartin Willi {
9794447bb33SMartin Willi struct xfrm_algo *algo;
980c7a5899eSAntony Antony struct xfrm_algo_auth *ap;
9814447bb33SMartin Willi struct nlattr *nla;
982c7a5899eSAntony Antony bool redact_secret = xfrm_redact();
9834447bb33SMartin Willi
9844447bb33SMartin Willi nla = nla_reserve(skb, XFRMA_ALG_AUTH,
9854447bb33SMartin Willi sizeof(*algo) + (auth->alg_key_len + 7) / 8);
9864447bb33SMartin Willi if (!nla)
9874447bb33SMartin Willi return -EMSGSIZE;
9884447bb33SMartin Willi algo = nla_data(nla);
9894c87308bSMathias Krause strncpy(algo->alg_name, auth->alg_name, sizeof(algo->alg_name));
990c7a5899eSAntony Antony
991c7a5899eSAntony Antony if (redact_secret && auth->alg_key_len)
992c7a5899eSAntony Antony memset(algo->alg_key, 0, (auth->alg_key_len + 7) / 8);
993c7a5899eSAntony Antony else
994c7a5899eSAntony Antony memcpy(algo->alg_key, auth->alg_key,
995c7a5899eSAntony Antony (auth->alg_key_len + 7) / 8);
9964447bb33SMartin Willi algo->alg_key_len = auth->alg_key_len;
9974447bb33SMartin Willi
998c7a5899eSAntony Antony nla = nla_reserve(skb, XFRMA_ALG_AUTH_TRUNC, xfrm_alg_auth_len(auth));
999c7a5899eSAntony Antony if (!nla)
1000c7a5899eSAntony Antony return -EMSGSIZE;
1001c7a5899eSAntony Antony ap = nla_data(nla);
1002c73bca72SPetr Vaganov strscpy_pad(ap->alg_name, auth->alg_name, sizeof(ap->alg_name));
1003c73bca72SPetr Vaganov ap->alg_key_len = auth->alg_key_len;
1004c73bca72SPetr Vaganov ap->alg_trunc_len = auth->alg_trunc_len;
1005c7a5899eSAntony Antony if (redact_secret && auth->alg_key_len)
1006c7a5899eSAntony Antony memset(ap->alg_key, 0, (auth->alg_key_len + 7) / 8);
1007c7a5899eSAntony Antony else
1008c7a5899eSAntony Antony memcpy(ap->alg_key, auth->alg_key,
1009c7a5899eSAntony Antony (auth->alg_key_len + 7) / 8);
1010c7a5899eSAntony Antony return 0;
1011c7a5899eSAntony Antony }
1012c7a5899eSAntony Antony
copy_to_user_aead(struct xfrm_algo_aead * aead,struct sk_buff * skb)1013c7a5899eSAntony Antony static int copy_to_user_aead(struct xfrm_algo_aead *aead, struct sk_buff *skb)
1014c7a5899eSAntony Antony {
1015c7a5899eSAntony Antony struct nlattr *nla = nla_reserve(skb, XFRMA_ALG_AEAD, aead_len(aead));
1016c7a5899eSAntony Antony struct xfrm_algo_aead *ap;
1017c7a5899eSAntony Antony bool redact_secret = xfrm_redact();
1018c7a5899eSAntony Antony
1019c7a5899eSAntony Antony if (!nla)
1020c7a5899eSAntony Antony return -EMSGSIZE;
1021c7a5899eSAntony Antony
1022c7a5899eSAntony Antony ap = nla_data(nla);
10238222d591SHerbert Xu strscpy_pad(ap->alg_name, aead->alg_name, sizeof(ap->alg_name));
10248222d591SHerbert Xu ap->alg_key_len = aead->alg_key_len;
10258222d591SHerbert Xu ap->alg_icv_len = aead->alg_icv_len;
1026c7a5899eSAntony Antony
1027c7a5899eSAntony Antony if (redact_secret && aead->alg_key_len)
1028c7a5899eSAntony Antony memset(ap->alg_key, 0, (aead->alg_key_len + 7) / 8);
1029c7a5899eSAntony Antony else
1030c7a5899eSAntony Antony memcpy(ap->alg_key, aead->alg_key,
1031c7a5899eSAntony Antony (aead->alg_key_len + 7) / 8);
1032c7a5899eSAntony Antony return 0;
1033c7a5899eSAntony Antony }
1034c7a5899eSAntony Antony
copy_to_user_ealg(struct xfrm_algo * ealg,struct sk_buff * skb)1035c7a5899eSAntony Antony static int copy_to_user_ealg(struct xfrm_algo *ealg, struct sk_buff *skb)
1036c7a5899eSAntony Antony {
1037c7a5899eSAntony Antony struct xfrm_algo *ap;
1038c7a5899eSAntony Antony bool redact_secret = xfrm_redact();
1039c7a5899eSAntony Antony struct nlattr *nla = nla_reserve(skb, XFRMA_ALG_CRYPT,
1040c7a5899eSAntony Antony xfrm_alg_len(ealg));
1041c7a5899eSAntony Antony if (!nla)
1042c7a5899eSAntony Antony return -EMSGSIZE;
1043c7a5899eSAntony Antony
1044c7a5899eSAntony Antony ap = nla_data(nla);
10458222d591SHerbert Xu strscpy_pad(ap->alg_name, ealg->alg_name, sizeof(ap->alg_name));
10468222d591SHerbert Xu ap->alg_key_len = ealg->alg_key_len;
1047c7a5899eSAntony Antony
1048c7a5899eSAntony Antony if (redact_secret && ealg->alg_key_len)
1049c7a5899eSAntony Antony memset(ap->alg_key, 0, (ealg->alg_key_len + 7) / 8);
1050c7a5899eSAntony Antony else
1051c7a5899eSAntony Antony memcpy(ap->alg_key, ealg->alg_key,
1052c7a5899eSAntony Antony (ealg->alg_key_len + 7) / 8);
1053c7a5899eSAntony Antony
10544447bb33SMartin Willi return 0;
10554447bb33SMartin Willi }
10564447bb33SMartin Willi
copy_to_user_calg(struct xfrm_algo * calg,struct sk_buff * skb)10578222d591SHerbert Xu static int copy_to_user_calg(struct xfrm_algo *calg, struct sk_buff *skb)
10588222d591SHerbert Xu {
10598222d591SHerbert Xu struct nlattr *nla = nla_reserve(skb, XFRMA_ALG_COMP, sizeof(*calg));
10608222d591SHerbert Xu struct xfrm_algo *ap;
10618222d591SHerbert Xu
10628222d591SHerbert Xu if (!nla)
10638222d591SHerbert Xu return -EMSGSIZE;
10648222d591SHerbert Xu
10658222d591SHerbert Xu ap = nla_data(nla);
10668222d591SHerbert Xu strscpy_pad(ap->alg_name, calg->alg_name, sizeof(ap->alg_name));
10678222d591SHerbert Xu ap->alg_key_len = 0;
10688222d591SHerbert Xu
10698222d591SHerbert Xu return 0;
10708222d591SHerbert Xu }
10718222d591SHerbert Xu
copy_to_user_encap(struct xfrm_encap_tmpl * ep,struct sk_buff * skb)10728222d591SHerbert Xu static int copy_to_user_encap(struct xfrm_encap_tmpl *ep, struct sk_buff *skb)
10738222d591SHerbert Xu {
10748222d591SHerbert Xu struct nlattr *nla = nla_reserve(skb, XFRMA_ENCAP, sizeof(*ep));
10758222d591SHerbert Xu struct xfrm_encap_tmpl *uep;
10768222d591SHerbert Xu
10778222d591SHerbert Xu if (!nla)
10788222d591SHerbert Xu return -EMSGSIZE;
10798222d591SHerbert Xu
10808222d591SHerbert Xu uep = nla_data(nla);
10818222d591SHerbert Xu memset(uep, 0, sizeof(*uep));
10828222d591SHerbert Xu
10838222d591SHerbert Xu uep->encap_type = ep->encap_type;
10848222d591SHerbert Xu uep->encap_sport = ep->encap_sport;
10858222d591SHerbert Xu uep->encap_dport = ep->encap_dport;
10868222d591SHerbert Xu uep->encap_oa = ep->encap_oa;
10878222d591SHerbert Xu
10888222d591SHerbert Xu return 0;
10898222d591SHerbert Xu }
10908222d591SHerbert Xu
xfrm_smark_put(struct sk_buff * skb,struct xfrm_mark * m)10919b42c1f1SSteffen Klassert static int xfrm_smark_put(struct sk_buff *skb, struct xfrm_mark *m)
10929b42c1f1SSteffen Klassert {
10939b42c1f1SSteffen Klassert int ret = 0;
10949b42c1f1SSteffen Klassert
10959b42c1f1SSteffen Klassert if (m->v | m->m) {
10969b42c1f1SSteffen Klassert ret = nla_put_u32(skb, XFRMA_SET_MARK, m->v);
10979b42c1f1SSteffen Klassert if (!ret)
10989b42c1f1SSteffen Klassert ret = nla_put_u32(skb, XFRMA_SET_MARK_MASK, m->m);
10999b42c1f1SSteffen Klassert }
11009b42c1f1SSteffen Klassert return ret;
11019b42c1f1SSteffen Klassert }
11029b42c1f1SSteffen Klassert
110368325d3bSHerbert Xu /* Don't change this without updating xfrm_sa_len! */
copy_to_user_state_extra(struct xfrm_state * x,struct xfrm_usersa_info * p,struct sk_buff * skb)110468325d3bSHerbert Xu static int copy_to_user_state_extra(struct xfrm_state *x,
110568325d3bSHerbert Xu struct xfrm_usersa_info *p,
110668325d3bSHerbert Xu struct sk_buff *skb)
11071da177e4SLinus Torvalds {
11081d1e34ddSDavid S. Miller int ret = 0;
11091d1e34ddSDavid S. Miller
11101da177e4SLinus Torvalds copy_to_user_state(x, p);
11111da177e4SLinus Torvalds
1112a947b0a9SNicolas Dichtel if (x->props.extra_flags) {
1113a947b0a9SNicolas Dichtel ret = nla_put_u32(skb, XFRMA_SA_EXTRA_FLAGS,
1114a947b0a9SNicolas Dichtel x->props.extra_flags);
1115a947b0a9SNicolas Dichtel if (ret)
1116a947b0a9SNicolas Dichtel goto out;
1117a947b0a9SNicolas Dichtel }
1118a947b0a9SNicolas Dichtel
11191d1e34ddSDavid S. Miller if (x->coaddr) {
11201d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_COADDR, sizeof(*x->coaddr), x->coaddr);
11211d1e34ddSDavid S. Miller if (ret)
11221d1e34ddSDavid S. Miller goto out;
11231d1e34ddSDavid S. Miller }
11241d1e34ddSDavid S. Miller if (x->lastused) {
1125de95c4a4SNicolas Dichtel ret = nla_put_u64_64bit(skb, XFRMA_LASTUSED, x->lastused,
1126de95c4a4SNicolas Dichtel XFRMA_PAD);
11271d1e34ddSDavid S. Miller if (ret)
11281d1e34ddSDavid S. Miller goto out;
11291d1e34ddSDavid S. Miller }
11301d1e34ddSDavid S. Miller if (x->aead) {
1131c7a5899eSAntony Antony ret = copy_to_user_aead(x->aead, skb);
11321d1e34ddSDavid S. Miller if (ret)
11331d1e34ddSDavid S. Miller goto out;
11341d1e34ddSDavid S. Miller }
11351d1e34ddSDavid S. Miller if (x->aalg) {
11361d1e34ddSDavid S. Miller ret = copy_to_user_auth(x->aalg, skb);
11371d1e34ddSDavid S. Miller if (ret)
11381d1e34ddSDavid S. Miller goto out;
11391d1e34ddSDavid S. Miller }
11401d1e34ddSDavid S. Miller if (x->ealg) {
1141c7a5899eSAntony Antony ret = copy_to_user_ealg(x->ealg, skb);
11421d1e34ddSDavid S. Miller if (ret)
11431d1e34ddSDavid S. Miller goto out;
11441d1e34ddSDavid S. Miller }
11451d1e34ddSDavid S. Miller if (x->calg) {
11468222d591SHerbert Xu ret = copy_to_user_calg(x->calg, skb);
11471d1e34ddSDavid S. Miller if (ret)
11481d1e34ddSDavid S. Miller goto out;
11491d1e34ddSDavid S. Miller }
11501d1e34ddSDavid S. Miller if (x->encap) {
11518222d591SHerbert Xu ret = copy_to_user_encap(x->encap, skb);
11521d1e34ddSDavid S. Miller if (ret)
11531d1e34ddSDavid S. Miller goto out;
11541d1e34ddSDavid S. Miller }
11551d1e34ddSDavid S. Miller if (x->tfcpad) {
11561d1e34ddSDavid S. Miller ret = nla_put_u32(skb, XFRMA_TFCPAD, x->tfcpad);
11571d1e34ddSDavid S. Miller if (ret)
11581d1e34ddSDavid S. Miller goto out;
11591d1e34ddSDavid S. Miller }
11601d1e34ddSDavid S. Miller ret = xfrm_mark_put(skb, &x->mark);
11611d1e34ddSDavid S. Miller if (ret)
11621d1e34ddSDavid S. Miller goto out;
11639b42c1f1SSteffen Klassert
11649b42c1f1SSteffen Klassert ret = xfrm_smark_put(skb, &x->props.smark);
11659b42c1f1SSteffen Klassert if (ret)
11669b42c1f1SSteffen Klassert goto out;
11679b42c1f1SSteffen Klassert
1168f293a5e3Sdingzhi if (x->replay_esn)
11691d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_REPLAY_ESN_VAL,
1170d0fde795SDavid S. Miller xfrm_replay_state_esn_len(x->replay_esn),
11711d1e34ddSDavid S. Miller x->replay_esn);
1172f293a5e3Sdingzhi else
1173f293a5e3Sdingzhi ret = nla_put(skb, XFRMA_REPLAY_VAL, sizeof(x->replay),
1174f293a5e3Sdingzhi &x->replay);
11751d1e34ddSDavid S. Miller if (ret)
11761d1e34ddSDavid S. Miller goto out;
1177d77e38e6SSteffen Klassert if(x->xso.dev)
1178d77e38e6SSteffen Klassert ret = copy_user_offload(&x->xso, skb);
1179d77e38e6SSteffen Klassert if (ret)
1180d77e38e6SSteffen Klassert goto out;
11817e652640SSteffen Klassert if (x->if_id) {
11827e652640SSteffen Klassert ret = nla_put_u32(skb, XFRMA_IF_ID, x->if_id);
1183077fbac4SLorenzo Colitti if (ret)
1184077fbac4SLorenzo Colitti goto out;
1185077fbac4SLorenzo Colitti }
11864e484b3eSAntony Antony if (x->security) {
11878598112dSSteffen Klassert ret = copy_sec_ctx(x->security, skb);
11884e484b3eSAntony Antony if (ret)
11894e484b3eSAntony Antony goto out;
11904e484b3eSAntony Antony }
11914e484b3eSAntony Antony if (x->mapping_maxage)
11924e484b3eSAntony Antony ret = nla_put_u32(skb, XFRMA_MTIMER_THRESH, x->mapping_maxage);
11931d1e34ddSDavid S. Miller out:
11941d1e34ddSDavid S. Miller return ret;
119568325d3bSHerbert Xu }
119668325d3bSHerbert Xu
dump_one_state(struct xfrm_state * x,int count,void * ptr)119768325d3bSHerbert Xu static int dump_one_state(struct xfrm_state *x, int count, void *ptr)
119868325d3bSHerbert Xu {
119968325d3bSHerbert Xu struct xfrm_dump_info *sp = ptr;
120068325d3bSHerbert Xu struct sk_buff *in_skb = sp->in_skb;
120168325d3bSHerbert Xu struct sk_buff *skb = sp->out_skb;
12025f3eea6bSDmitry Safonov struct xfrm_translator *xtr;
120368325d3bSHerbert Xu struct xfrm_usersa_info *p;
120468325d3bSHerbert Xu struct nlmsghdr *nlh;
120568325d3bSHerbert Xu int err;
120668325d3bSHerbert Xu
120715e47304SEric W. Biederman nlh = nlmsg_put(skb, NETLINK_CB(in_skb).portid, sp->nlmsg_seq,
120868325d3bSHerbert Xu XFRM_MSG_NEWSA, sizeof(*p), sp->nlmsg_flags);
120968325d3bSHerbert Xu if (nlh == NULL)
121068325d3bSHerbert Xu return -EMSGSIZE;
121168325d3bSHerbert Xu
121268325d3bSHerbert Xu p = nlmsg_data(nlh);
121368325d3bSHerbert Xu
121468325d3bSHerbert Xu err = copy_to_user_state_extra(x, p, skb);
12151d1e34ddSDavid S. Miller if (err) {
12169825069dSThomas Graf nlmsg_cancel(skb, nlh);
121768325d3bSHerbert Xu return err;
12181da177e4SLinus Torvalds }
12191d1e34ddSDavid S. Miller nlmsg_end(skb, nlh);
12205f3eea6bSDmitry Safonov
12215f3eea6bSDmitry Safonov xtr = xfrm_get_translator();
12225f3eea6bSDmitry Safonov if (xtr) {
12235f3eea6bSDmitry Safonov err = xtr->alloc_compat(skb, nlh);
12245f3eea6bSDmitry Safonov
12255f3eea6bSDmitry Safonov xfrm_put_translator(xtr);
12265f3eea6bSDmitry Safonov if (err) {
12275f3eea6bSDmitry Safonov nlmsg_cancel(skb, nlh);
12285f3eea6bSDmitry Safonov return err;
12295f3eea6bSDmitry Safonov }
12305f3eea6bSDmitry Safonov }
12315f3eea6bSDmitry Safonov
12321d1e34ddSDavid S. Miller return 0;
12331d1e34ddSDavid S. Miller }
12341da177e4SLinus Torvalds
xfrm_dump_sa_done(struct netlink_callback * cb)12354c563f76STimo Teras static int xfrm_dump_sa_done(struct netlink_callback *cb)
12364c563f76STimo Teras {
12374c563f76STimo Teras struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1];
1238283bc9f3SFan Du struct sock *sk = cb->skb->sk;
1239283bc9f3SFan Du struct net *net = sock_net(sk);
1240283bc9f3SFan Du
12411ba5bf99SVegard Nossum if (cb->args[0])
1242283bc9f3SFan Du xfrm_state_walk_done(walk, net);
12434c563f76STimo Teras return 0;
12444c563f76STimo Teras }
12454c563f76STimo Teras
xfrm_dump_sa(struct sk_buff * skb,struct netlink_callback * cb)12461da177e4SLinus Torvalds static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb)
12471da177e4SLinus Torvalds {
1248fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk);
12494c563f76STimo Teras struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1];
12501da177e4SLinus Torvalds struct xfrm_dump_info info;
12511da177e4SLinus Torvalds
12524c563f76STimo Teras BUILD_BUG_ON(sizeof(struct xfrm_state_walk) >
12534c563f76STimo Teras sizeof(cb->args) - sizeof(cb->args[0]));
12544c563f76STimo Teras
12551da177e4SLinus Torvalds info.in_skb = cb->skb;
12561da177e4SLinus Torvalds info.out_skb = skb;
12571da177e4SLinus Torvalds info.nlmsg_seq = cb->nlh->nlmsg_seq;
12581da177e4SLinus Torvalds info.nlmsg_flags = NLM_F_MULTI;
12594c563f76STimo Teras
12604c563f76STimo Teras if (!cb->args[0]) {
1261d3623099SNicolas Dichtel struct nlattr *attrs[XFRMA_MAX+1];
1262870a2df4SNicolas Dichtel struct xfrm_address_filter *filter = NULL;
1263d3623099SNicolas Dichtel u8 proto = 0;
1264d3623099SNicolas Dichtel int err;
1265d3623099SNicolas Dichtel
12668cb08174SJohannes Berg err = nlmsg_parse_deprecated(cb->nlh, 0, attrs, XFRMA_MAX,
12678cb08174SJohannes Berg xfrma_policy, cb->extack);
1268d3623099SNicolas Dichtel if (err < 0)
1269d3623099SNicolas Dichtel return err;
1270d3623099SNicolas Dichtel
1271870a2df4SNicolas Dichtel if (attrs[XFRMA_ADDRESS_FILTER]) {
1272df367561SAndrzej Hajda filter = kmemdup(nla_data(attrs[XFRMA_ADDRESS_FILTER]),
1273df367561SAndrzej Hajda sizeof(*filter), GFP_KERNEL);
1274d3623099SNicolas Dichtel if (filter == NULL)
1275d3623099SNicolas Dichtel return -ENOMEM;
1276dfa73c17SLin Ma
1277dfa73c17SLin Ma /* see addr_match(), (prefix length >> 5) << 2
1278dfa73c17SLin Ma * will be used to compare xfrm_address_t
1279dfa73c17SLin Ma */
1280dfa73c17SLin Ma if (filter->splen > (sizeof(xfrm_address_t) << 3) ||
1281dfa73c17SLin Ma filter->dplen > (sizeof(xfrm_address_t) << 3)) {
1282dfa73c17SLin Ma kfree(filter);
1283dfa73c17SLin Ma return -EINVAL;
1284dfa73c17SLin Ma }
1285d3623099SNicolas Dichtel }
1286d3623099SNicolas Dichtel
1287d3623099SNicolas Dichtel if (attrs[XFRMA_PROTO])
1288d3623099SNicolas Dichtel proto = nla_get_u8(attrs[XFRMA_PROTO]);
1289d3623099SNicolas Dichtel
1290d3623099SNicolas Dichtel xfrm_state_walk_init(walk, proto, filter);
12911ba5bf99SVegard Nossum cb->args[0] = 1;
12924c563f76STimo Teras }
12934c563f76STimo Teras
1294fc34acd3SAlexey Dobriyan (void) xfrm_state_walk(net, walk, dump_one_state, &info);
12951da177e4SLinus Torvalds
12961da177e4SLinus Torvalds return skb->len;
12971da177e4SLinus Torvalds }
12981da177e4SLinus Torvalds
xfrm_state_netlink(struct sk_buff * in_skb,struct xfrm_state * x,u32 seq)12991da177e4SLinus Torvalds static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb,
13001da177e4SLinus Torvalds struct xfrm_state *x, u32 seq)
13011da177e4SLinus Torvalds {
13021da177e4SLinus Torvalds struct xfrm_dump_info info;
13031da177e4SLinus Torvalds struct sk_buff *skb;
1304864745d2SMathias Krause int err;
13051da177e4SLinus Torvalds
13067deb2264SThomas Graf skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
13071da177e4SLinus Torvalds if (!skb)
13081da177e4SLinus Torvalds return ERR_PTR(-ENOMEM);
13091da177e4SLinus Torvalds
13101da177e4SLinus Torvalds info.in_skb = in_skb;
13111da177e4SLinus Torvalds info.out_skb = skb;
13121da177e4SLinus Torvalds info.nlmsg_seq = seq;
13131da177e4SLinus Torvalds info.nlmsg_flags = 0;
13141da177e4SLinus Torvalds
1315864745d2SMathias Krause err = dump_one_state(x, 0, &info);
1316864745d2SMathias Krause if (err) {
13171da177e4SLinus Torvalds kfree_skb(skb);
1318864745d2SMathias Krause return ERR_PTR(err);
13191da177e4SLinus Torvalds }
13201da177e4SLinus Torvalds
13211da177e4SLinus Torvalds return skb;
13221da177e4SLinus Torvalds }
13231da177e4SLinus Torvalds
132421ee543eSMichal Kubecek /* A wrapper for nlmsg_multicast() checking that nlsk is still available.
132521ee543eSMichal Kubecek * Must be called with RCU read lock.
132621ee543eSMichal Kubecek */
xfrm_nlmsg_multicast(struct net * net,struct sk_buff * skb,u32 pid,unsigned int group)132721ee543eSMichal Kubecek static inline int xfrm_nlmsg_multicast(struct net *net, struct sk_buff *skb,
132821ee543eSMichal Kubecek u32 pid, unsigned int group)
132921ee543eSMichal Kubecek {
133021ee543eSMichal Kubecek struct sock *nlsk = rcu_dereference(net->xfrm.nlsk);
13315461fc0cSDmitry Safonov struct xfrm_translator *xtr;
133221ee543eSMichal Kubecek
133386126b77SFlorian Westphal if (!nlsk) {
133486126b77SFlorian Westphal kfree_skb(skb);
133586126b77SFlorian Westphal return -EPIPE;
133686126b77SFlorian Westphal }
133786126b77SFlorian Westphal
13385461fc0cSDmitry Safonov xtr = xfrm_get_translator();
13395461fc0cSDmitry Safonov if (xtr) {
13405461fc0cSDmitry Safonov int err = xtr->alloc_compat(skb, nlmsg_hdr(skb));
13415461fc0cSDmitry Safonov
13425461fc0cSDmitry Safonov xfrm_put_translator(xtr);
13435461fc0cSDmitry Safonov if (err) {
13445461fc0cSDmitry Safonov kfree_skb(skb);
13455461fc0cSDmitry Safonov return err;
13465461fc0cSDmitry Safonov }
13475461fc0cSDmitry Safonov }
13485461fc0cSDmitry Safonov
134921ee543eSMichal Kubecek return nlmsg_multicast(nlsk, skb, pid, group, GFP_ATOMIC);
135021ee543eSMichal Kubecek }
135121ee543eSMichal Kubecek
xfrm_spdinfo_msgsize(void)1352a1b831f2SAlexey Dobriyan static inline unsigned int xfrm_spdinfo_msgsize(void)
13537deb2264SThomas Graf {
13547deb2264SThomas Graf return NLMSG_ALIGN(4)
13557deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_spdinfo))
1356880a6fabSChristophe Gouault + nla_total_size(sizeof(struct xfrmu_spdhinfo))
1357880a6fabSChristophe Gouault + nla_total_size(sizeof(struct xfrmu_spdhthresh))
1358880a6fabSChristophe Gouault + nla_total_size(sizeof(struct xfrmu_spdhthresh));
13597deb2264SThomas Graf }
13607deb2264SThomas Graf
build_spdinfo(struct sk_buff * skb,struct net * net,u32 portid,u32 seq,u32 flags)1361e071041bSAlexey Dobriyan static int build_spdinfo(struct sk_buff *skb, struct net *net,
136215e47304SEric W. Biederman u32 portid, u32 seq, u32 flags)
1363ecfd6b18SJamal Hadi Salim {
13645a6d3416SJamal Hadi Salim struct xfrmk_spdinfo si;
13655a6d3416SJamal Hadi Salim struct xfrmu_spdinfo spc;
13665a6d3416SJamal Hadi Salim struct xfrmu_spdhinfo sph;
1367880a6fabSChristophe Gouault struct xfrmu_spdhthresh spt4, spt6;
1368ecfd6b18SJamal Hadi Salim struct nlmsghdr *nlh;
13691d1e34ddSDavid S. Miller int err;
1370ecfd6b18SJamal Hadi Salim u32 *f;
1371880a6fabSChristophe Gouault unsigned lseq;
1372ecfd6b18SJamal Hadi Salim
137315e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0);
137425985edcSLucas De Marchi if (nlh == NULL) /* shouldn't really happen ... */
1375ecfd6b18SJamal Hadi Salim return -EMSGSIZE;
1376ecfd6b18SJamal Hadi Salim
1377ecfd6b18SJamal Hadi Salim f = nlmsg_data(nlh);
1378ecfd6b18SJamal Hadi Salim *f = flags;
1379e071041bSAlexey Dobriyan xfrm_spd_getinfo(net, &si);
13805a6d3416SJamal Hadi Salim spc.incnt = si.incnt;
13815a6d3416SJamal Hadi Salim spc.outcnt = si.outcnt;
13825a6d3416SJamal Hadi Salim spc.fwdcnt = si.fwdcnt;
13835a6d3416SJamal Hadi Salim spc.inscnt = si.inscnt;
13845a6d3416SJamal Hadi Salim spc.outscnt = si.outscnt;
13855a6d3416SJamal Hadi Salim spc.fwdscnt = si.fwdscnt;
13865a6d3416SJamal Hadi Salim sph.spdhcnt = si.spdhcnt;
13875a6d3416SJamal Hadi Salim sph.spdhmcnt = si.spdhmcnt;
1388ecfd6b18SJamal Hadi Salim
1389880a6fabSChristophe Gouault do {
1390880a6fabSChristophe Gouault lseq = read_seqbegin(&net->xfrm.policy_hthresh.lock);
1391880a6fabSChristophe Gouault
1392880a6fabSChristophe Gouault spt4.lbits = net->xfrm.policy_hthresh.lbits4;
1393880a6fabSChristophe Gouault spt4.rbits = net->xfrm.policy_hthresh.rbits4;
1394880a6fabSChristophe Gouault spt6.lbits = net->xfrm.policy_hthresh.lbits6;
1395880a6fabSChristophe Gouault spt6.rbits = net->xfrm.policy_hthresh.rbits6;
1396880a6fabSChristophe Gouault } while (read_seqretry(&net->xfrm.policy_hthresh.lock, lseq));
1397880a6fabSChristophe Gouault
13981d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_SPD_INFO, sizeof(spc), &spc);
13991d1e34ddSDavid S. Miller if (!err)
14001d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_SPD_HINFO, sizeof(sph), &sph);
1401880a6fabSChristophe Gouault if (!err)
1402880a6fabSChristophe Gouault err = nla_put(skb, XFRMA_SPD_IPV4_HTHRESH, sizeof(spt4), &spt4);
1403880a6fabSChristophe Gouault if (!err)
1404880a6fabSChristophe Gouault err = nla_put(skb, XFRMA_SPD_IPV6_HTHRESH, sizeof(spt6), &spt6);
14051d1e34ddSDavid S. Miller if (err) {
14061d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh);
14071d1e34ddSDavid S. Miller return err;
14081d1e34ddSDavid S. Miller }
1409ecfd6b18SJamal Hadi Salim
1410053c095aSJohannes Berg nlmsg_end(skb, nlh);
1411053c095aSJohannes Berg return 0;
1412ecfd6b18SJamal Hadi Salim }
1413ecfd6b18SJamal Hadi Salim
xfrm_set_spdinfo(struct sk_buff * skb,struct nlmsghdr * nlh,struct nlattr ** attrs,struct netlink_ext_ack * extack)1414880a6fabSChristophe Gouault static int xfrm_set_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
14153bec6c3eSSabrina Dubroca struct nlattr **attrs,
14163bec6c3eSSabrina Dubroca struct netlink_ext_ack *extack)
1417880a6fabSChristophe Gouault {
1418880a6fabSChristophe Gouault struct net *net = sock_net(skb->sk);
1419880a6fabSChristophe Gouault struct xfrmu_spdhthresh *thresh4 = NULL;
1420880a6fabSChristophe Gouault struct xfrmu_spdhthresh *thresh6 = NULL;
1421880a6fabSChristophe Gouault
1422880a6fabSChristophe Gouault /* selector prefixlen thresholds to hash policies */
1423880a6fabSChristophe Gouault if (attrs[XFRMA_SPD_IPV4_HTHRESH]) {
1424880a6fabSChristophe Gouault struct nlattr *rta = attrs[XFRMA_SPD_IPV4_HTHRESH];
1425880a6fabSChristophe Gouault
1426a7417216SSabrina Dubroca if (nla_len(rta) < sizeof(*thresh4)) {
1427a7417216SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid SPD_IPV4_HTHRESH attribute length");
1428880a6fabSChristophe Gouault return -EINVAL;
1429a7417216SSabrina Dubroca }
1430880a6fabSChristophe Gouault thresh4 = nla_data(rta);
1431a7417216SSabrina Dubroca if (thresh4->lbits > 32 || thresh4->rbits > 32) {
1432a7417216SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid hash threshold (must be <= 32 for IPv4)");
1433880a6fabSChristophe Gouault return -EINVAL;
1434880a6fabSChristophe Gouault }
1435a7417216SSabrina Dubroca }
1436880a6fabSChristophe Gouault if (attrs[XFRMA_SPD_IPV6_HTHRESH]) {
1437880a6fabSChristophe Gouault struct nlattr *rta = attrs[XFRMA_SPD_IPV6_HTHRESH];
1438880a6fabSChristophe Gouault
1439a7417216SSabrina Dubroca if (nla_len(rta) < sizeof(*thresh6)) {
1440a7417216SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid SPD_IPV6_HTHRESH attribute length");
1441880a6fabSChristophe Gouault return -EINVAL;
1442a7417216SSabrina Dubroca }
1443880a6fabSChristophe Gouault thresh6 = nla_data(rta);
1444a7417216SSabrina Dubroca if (thresh6->lbits > 128 || thresh6->rbits > 128) {
1445a7417216SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid hash threshold (must be <= 128 for IPv6)");
1446880a6fabSChristophe Gouault return -EINVAL;
1447880a6fabSChristophe Gouault }
1448a7417216SSabrina Dubroca }
1449880a6fabSChristophe Gouault
1450880a6fabSChristophe Gouault if (thresh4 || thresh6) {
1451880a6fabSChristophe Gouault write_seqlock(&net->xfrm.policy_hthresh.lock);
1452880a6fabSChristophe Gouault if (thresh4) {
1453880a6fabSChristophe Gouault net->xfrm.policy_hthresh.lbits4 = thresh4->lbits;
1454880a6fabSChristophe Gouault net->xfrm.policy_hthresh.rbits4 = thresh4->rbits;
1455880a6fabSChristophe Gouault }
1456880a6fabSChristophe Gouault if (thresh6) {
1457880a6fabSChristophe Gouault net->xfrm.policy_hthresh.lbits6 = thresh6->lbits;
1458880a6fabSChristophe Gouault net->xfrm.policy_hthresh.rbits6 = thresh6->rbits;
1459880a6fabSChristophe Gouault }
1460880a6fabSChristophe Gouault write_sequnlock(&net->xfrm.policy_hthresh.lock);
1461880a6fabSChristophe Gouault
1462880a6fabSChristophe Gouault xfrm_policy_hash_rebuild(net);
1463880a6fabSChristophe Gouault }
1464880a6fabSChristophe Gouault
1465880a6fabSChristophe Gouault return 0;
1466880a6fabSChristophe Gouault }
1467880a6fabSChristophe Gouault
xfrm_get_spdinfo(struct sk_buff * skb,struct nlmsghdr * nlh,struct nlattr ** attrs,struct netlink_ext_ack * extack)1468ecfd6b18SJamal Hadi Salim static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
14693bec6c3eSSabrina Dubroca struct nlattr **attrs,
14703bec6c3eSSabrina Dubroca struct netlink_ext_ack *extack)
1471ecfd6b18SJamal Hadi Salim {
1472a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk);
1473ecfd6b18SJamal Hadi Salim struct sk_buff *r_skb;
14747b67c857SThomas Graf u32 *flags = nlmsg_data(nlh);
147515e47304SEric W. Biederman u32 sportid = NETLINK_CB(skb).portid;
1476ecfd6b18SJamal Hadi Salim u32 seq = nlh->nlmsg_seq;
14772fc5f83bSGustavo A. R. Silva int err;
1478ecfd6b18SJamal Hadi Salim
14797deb2264SThomas Graf r_skb = nlmsg_new(xfrm_spdinfo_msgsize(), GFP_ATOMIC);
1480ecfd6b18SJamal Hadi Salim if (r_skb == NULL)
1481ecfd6b18SJamal Hadi Salim return -ENOMEM;
1482ecfd6b18SJamal Hadi Salim
14832fc5f83bSGustavo A. R. Silva err = build_spdinfo(r_skb, net, sportid, seq, *flags);
14842fc5f83bSGustavo A. R. Silva BUG_ON(err < 0);
1485ecfd6b18SJamal Hadi Salim
148615e47304SEric W. Biederman return nlmsg_unicast(net->xfrm.nlsk, r_skb, sportid);
1487ecfd6b18SJamal Hadi Salim }
1488ecfd6b18SJamal Hadi Salim
xfrm_sadinfo_msgsize(void)1489a1b831f2SAlexey Dobriyan static inline unsigned int xfrm_sadinfo_msgsize(void)
14907deb2264SThomas Graf {
14917deb2264SThomas Graf return NLMSG_ALIGN(4)
14927deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_sadhinfo))
14937deb2264SThomas Graf + nla_total_size(4); /* XFRMA_SAD_CNT */
14947deb2264SThomas Graf }
14957deb2264SThomas Graf
build_sadinfo(struct sk_buff * skb,struct net * net,u32 portid,u32 seq,u32 flags)1496e071041bSAlexey Dobriyan static int build_sadinfo(struct sk_buff *skb, struct net *net,
149715e47304SEric W. Biederman u32 portid, u32 seq, u32 flags)
149828d8909bSJamal Hadi Salim {
1499af11e316SJamal Hadi Salim struct xfrmk_sadinfo si;
1500af11e316SJamal Hadi Salim struct xfrmu_sadhinfo sh;
150128d8909bSJamal Hadi Salim struct nlmsghdr *nlh;
15021d1e34ddSDavid S. Miller int err;
150328d8909bSJamal Hadi Salim u32 *f;
150428d8909bSJamal Hadi Salim
150515e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, XFRM_MSG_NEWSADINFO, sizeof(u32), 0);
150625985edcSLucas De Marchi if (nlh == NULL) /* shouldn't really happen ... */
150728d8909bSJamal Hadi Salim return -EMSGSIZE;
150828d8909bSJamal Hadi Salim
150928d8909bSJamal Hadi Salim f = nlmsg_data(nlh);
151028d8909bSJamal Hadi Salim *f = flags;
1511e071041bSAlexey Dobriyan xfrm_sad_getinfo(net, &si);
151228d8909bSJamal Hadi Salim
1513af11e316SJamal Hadi Salim sh.sadhmcnt = si.sadhmcnt;
1514af11e316SJamal Hadi Salim sh.sadhcnt = si.sadhcnt;
1515af11e316SJamal Hadi Salim
15161d1e34ddSDavid S. Miller err = nla_put_u32(skb, XFRMA_SAD_CNT, si.sadcnt);
15171d1e34ddSDavid S. Miller if (!err)
15181d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_SAD_HINFO, sizeof(sh), &sh);
15191d1e34ddSDavid S. Miller if (err) {
15201d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh);
15211d1e34ddSDavid S. Miller return err;
15221d1e34ddSDavid S. Miller }
152328d8909bSJamal Hadi Salim
1524053c095aSJohannes Berg nlmsg_end(skb, nlh);
1525053c095aSJohannes Berg return 0;
152628d8909bSJamal Hadi Salim }
152728d8909bSJamal Hadi Salim
xfrm_get_sadinfo(struct sk_buff * skb,struct nlmsghdr * nlh,struct nlattr ** attrs,struct netlink_ext_ack * extack)152828d8909bSJamal Hadi Salim static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
15293bec6c3eSSabrina Dubroca struct nlattr **attrs,
15303bec6c3eSSabrina Dubroca struct netlink_ext_ack *extack)
153128d8909bSJamal Hadi Salim {
1532a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk);
153328d8909bSJamal Hadi Salim struct sk_buff *r_skb;
15347b67c857SThomas Graf u32 *flags = nlmsg_data(nlh);
153515e47304SEric W. Biederman u32 sportid = NETLINK_CB(skb).portid;
153628d8909bSJamal Hadi Salim u32 seq = nlh->nlmsg_seq;
15372fc5f83bSGustavo A. R. Silva int err;
153828d8909bSJamal Hadi Salim
15397deb2264SThomas Graf r_skb = nlmsg_new(xfrm_sadinfo_msgsize(), GFP_ATOMIC);
154028d8909bSJamal Hadi Salim if (r_skb == NULL)
154128d8909bSJamal Hadi Salim return -ENOMEM;
154228d8909bSJamal Hadi Salim
15432fc5f83bSGustavo A. R. Silva err = build_sadinfo(r_skb, net, sportid, seq, *flags);
15442fc5f83bSGustavo A. R. Silva BUG_ON(err < 0);
154528d8909bSJamal Hadi Salim
154615e47304SEric W. Biederman return nlmsg_unicast(net->xfrm.nlsk, r_skb, sportid);
154728d8909bSJamal Hadi Salim }
154828d8909bSJamal Hadi Salim
xfrm_get_sa(struct sk_buff * skb,struct nlmsghdr * nlh,struct nlattr ** attrs,struct netlink_ext_ack * extack)154922e70050SChristoph Hellwig static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
15503bec6c3eSSabrina Dubroca struct nlattr **attrs, struct netlink_ext_ack *extack)
15511da177e4SLinus Torvalds {
1552fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk);
15537b67c857SThomas Graf struct xfrm_usersa_id *p = nlmsg_data(nlh);
15541da177e4SLinus Torvalds struct xfrm_state *x;
15551da177e4SLinus Torvalds struct sk_buff *resp_skb;
1556eb2971b6SMasahide NAKAMURA int err = -ESRCH;
15571da177e4SLinus Torvalds
1558fc34acd3SAlexey Dobriyan x = xfrm_user_state_lookup(net, p, attrs, &err);
15591da177e4SLinus Torvalds if (x == NULL)
15601da177e4SLinus Torvalds goto out_noput;
15611da177e4SLinus Torvalds
15621da177e4SLinus Torvalds resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq);
15631da177e4SLinus Torvalds if (IS_ERR(resp_skb)) {
15641da177e4SLinus Torvalds err = PTR_ERR(resp_skb);
15651da177e4SLinus Torvalds } else {
156615e47304SEric W. Biederman err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).portid);
15671da177e4SLinus Torvalds }
15681da177e4SLinus Torvalds xfrm_state_put(x);
15691da177e4SLinus Torvalds out_noput:
15701da177e4SLinus Torvalds return err;
15711da177e4SLinus Torvalds }
15721da177e4SLinus Torvalds
xfrm_alloc_userspi(struct sk_buff * skb,struct nlmsghdr * nlh,struct nlattr ** attrs,struct netlink_ext_ack * extack)157322e70050SChristoph Hellwig static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
15743bec6c3eSSabrina Dubroca struct nlattr **attrs,
15753bec6c3eSSabrina Dubroca struct netlink_ext_ack *extack)
15761da177e4SLinus Torvalds {
1577fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk);
15781da177e4SLinus Torvalds struct xfrm_state *x;
15791da177e4SLinus Torvalds struct xfrm_userspi_info *p;
15805f3eea6bSDmitry Safonov struct xfrm_translator *xtr;
15811da177e4SLinus Torvalds struct sk_buff *resp_skb;
15821da177e4SLinus Torvalds xfrm_address_t *daddr;
15831da177e4SLinus Torvalds int family;
15841da177e4SLinus Torvalds int err;
15856f26b61eSJamal Hadi Salim u32 mark;
15866f26b61eSJamal Hadi Salim struct xfrm_mark m;
15877e652640SSteffen Klassert u32 if_id = 0;
15881da177e4SLinus Torvalds
15897b67c857SThomas Graf p = nlmsg_data(nlh);
1590c2dad11eSSabrina Dubroca err = verify_spi_info(p->info.id.proto, p->min, p->max, extack);
15911da177e4SLinus Torvalds if (err)
15921da177e4SLinus Torvalds goto out_noput;
15931da177e4SLinus Torvalds
15941da177e4SLinus Torvalds family = p->info.family;
15951da177e4SLinus Torvalds daddr = &p->info.id.daddr;
15961da177e4SLinus Torvalds
15971da177e4SLinus Torvalds x = NULL;
15986f26b61eSJamal Hadi Salim
15996f26b61eSJamal Hadi Salim mark = xfrm_mark_get(attrs, &m);
16007e652640SSteffen Klassert
1601a3d9001bSKai Lueke if (attrs[XFRMA_IF_ID])
16027e652640SSteffen Klassert if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
16037e652640SSteffen Klassert
16041da177e4SLinus Torvalds if (p->info.seq) {
16056f26b61eSJamal Hadi Salim x = xfrm_find_acq_byseq(net, mark, p->info.seq);
160670e94e66SYOSHIFUJI Hideaki / 吉藤英明 if (x && !xfrm_addr_equal(&x->id.daddr, daddr, family)) {
16071da177e4SLinus Torvalds xfrm_state_put(x);
16081da177e4SLinus Torvalds x = NULL;
16091da177e4SLinus Torvalds }
16101da177e4SLinus Torvalds }
16111da177e4SLinus Torvalds
16121da177e4SLinus Torvalds if (!x)
16136f26b61eSJamal Hadi Salim x = xfrm_find_acq(net, &m, p->info.mode, p->info.reqid,
16147e652640SSteffen Klassert if_id, p->info.id.proto, daddr,
16151da177e4SLinus Torvalds &p->info.saddr, 1,
16161da177e4SLinus Torvalds family);
16171da177e4SLinus Torvalds err = -ENOENT;
1618c2dad11eSSabrina Dubroca if (!x) {
1619c2dad11eSSabrina Dubroca NL_SET_ERR_MSG(extack, "Target ACQUIRE not found");
16201da177e4SLinus Torvalds goto out_noput;
1621c2dad11eSSabrina Dubroca }
16221da177e4SLinus Torvalds
1623c2dad11eSSabrina Dubroca err = xfrm_alloc_spi(x, p->min, p->max, extack);
1624658b219eSHerbert Xu if (err)
1625658b219eSHerbert Xu goto out;
16261da177e4SLinus Torvalds
16271da177e4SLinus Torvalds resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq);
16281da177e4SLinus Torvalds if (IS_ERR(resp_skb)) {
16291da177e4SLinus Torvalds err = PTR_ERR(resp_skb);
16301da177e4SLinus Torvalds goto out;
16311da177e4SLinus Torvalds }
16321da177e4SLinus Torvalds
16335f3eea6bSDmitry Safonov xtr = xfrm_get_translator();
16345f3eea6bSDmitry Safonov if (xtr) {
16355f3eea6bSDmitry Safonov err = xtr->alloc_compat(skb, nlmsg_hdr(skb));
16365f3eea6bSDmitry Safonov
16375f3eea6bSDmitry Safonov xfrm_put_translator(xtr);
16385f3eea6bSDmitry Safonov if (err) {
16395f3eea6bSDmitry Safonov kfree_skb(resp_skb);
16405f3eea6bSDmitry Safonov goto out;
16415f3eea6bSDmitry Safonov }
16425f3eea6bSDmitry Safonov }
16435f3eea6bSDmitry Safonov
164415e47304SEric W. Biederman err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).portid);
16451da177e4SLinus Torvalds
16461da177e4SLinus Torvalds out:
16471da177e4SLinus Torvalds xfrm_state_put(x);
16481da177e4SLinus Torvalds out_noput:
16491da177e4SLinus Torvalds return err;
16501da177e4SLinus Torvalds }
16511da177e4SLinus Torvalds
verify_policy_dir(u8 dir,struct netlink_ext_ack * extack)165224fc544fSSabrina Dubroca static int verify_policy_dir(u8 dir, struct netlink_ext_ack *extack)
16531da177e4SLinus Torvalds {
16541da177e4SLinus Torvalds switch (dir) {
16551da177e4SLinus Torvalds case XFRM_POLICY_IN:
16561da177e4SLinus Torvalds case XFRM_POLICY_OUT:
16571da177e4SLinus Torvalds case XFRM_POLICY_FWD:
16581da177e4SLinus Torvalds break;
16591da177e4SLinus Torvalds
16601da177e4SLinus Torvalds default:
166124fc544fSSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid policy direction");
16621da177e4SLinus Torvalds return -EINVAL;
16633ff50b79SStephen Hemminger }
16641da177e4SLinus Torvalds
16651da177e4SLinus Torvalds return 0;
16661da177e4SLinus Torvalds }
16671da177e4SLinus Torvalds
verify_policy_type(u8 type,struct netlink_ext_ack * extack)1668fb7deabaSSabrina Dubroca static int verify_policy_type(u8 type, struct netlink_ext_ack *extack)
1669f7b6983fSMasahide NAKAMURA {
1670f7b6983fSMasahide NAKAMURA switch (type) {
1671f7b6983fSMasahide NAKAMURA case XFRM_POLICY_TYPE_MAIN:
1672f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY
1673f7b6983fSMasahide NAKAMURA case XFRM_POLICY_TYPE_SUB:
1674f7b6983fSMasahide NAKAMURA #endif
1675f7b6983fSMasahide NAKAMURA break;
1676f7b6983fSMasahide NAKAMURA
1677f7b6983fSMasahide NAKAMURA default:
1678fb7deabaSSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid policy type");
1679f7b6983fSMasahide NAKAMURA return -EINVAL;
16803ff50b79SStephen Hemminger }
1681f7b6983fSMasahide NAKAMURA
1682f7b6983fSMasahide NAKAMURA return 0;
1683f7b6983fSMasahide NAKAMURA }
1684f7b6983fSMasahide NAKAMURA
verify_newpolicy_info(struct xfrm_userpolicy_info * p,struct netlink_ext_ack * extack)1685ec2b4f01SSabrina Dubroca static int verify_newpolicy_info(struct xfrm_userpolicy_info *p,
1686ec2b4f01SSabrina Dubroca struct netlink_ext_ack *extack)
16871da177e4SLinus Torvalds {
1688e682adf0SFan Du int ret;
1689e682adf0SFan Du
16901da177e4SLinus Torvalds switch (p->share) {
16911da177e4SLinus Torvalds case XFRM_SHARE_ANY:
16921da177e4SLinus Torvalds case XFRM_SHARE_SESSION:
16931da177e4SLinus Torvalds case XFRM_SHARE_USER:
16941da177e4SLinus Torvalds case XFRM_SHARE_UNIQUE:
16951da177e4SLinus Torvalds break;
16961da177e4SLinus Torvalds
16971da177e4SLinus Torvalds default:
1698ec2b4f01SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid policy share");
16991da177e4SLinus Torvalds return -EINVAL;
17003ff50b79SStephen Hemminger }
17011da177e4SLinus Torvalds
17021da177e4SLinus Torvalds switch (p->action) {
17031da177e4SLinus Torvalds case XFRM_POLICY_ALLOW:
17041da177e4SLinus Torvalds case XFRM_POLICY_BLOCK:
17051da177e4SLinus Torvalds break;
17061da177e4SLinus Torvalds
17071da177e4SLinus Torvalds default:
1708ec2b4f01SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid policy action");
17091da177e4SLinus Torvalds return -EINVAL;
17103ff50b79SStephen Hemminger }
17111da177e4SLinus Torvalds
17121da177e4SLinus Torvalds switch (p->sel.family) {
17131da177e4SLinus Torvalds case AF_INET:
1714ec2b4f01SSabrina Dubroca if (p->sel.prefixlen_d > 32 || p->sel.prefixlen_s > 32) {
1715ec2b4f01SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid prefix length in selector (must be <= 32 for IPv4)");
171607bf7908SSteffen Klassert return -EINVAL;
1717ec2b4f01SSabrina Dubroca }
171807bf7908SSteffen Klassert
17191da177e4SLinus Torvalds break;
17201da177e4SLinus Torvalds
17211da177e4SLinus Torvalds case AF_INET6:
1722dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
1723ec2b4f01SSabrina Dubroca if (p->sel.prefixlen_d > 128 || p->sel.prefixlen_s > 128) {
1724ec2b4f01SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid prefix length in selector (must be <= 128 for IPv6)");
172507bf7908SSteffen Klassert return -EINVAL;
1726ec2b4f01SSabrina Dubroca }
172707bf7908SSteffen Klassert
17281da177e4SLinus Torvalds break;
17291da177e4SLinus Torvalds #else
1730ec2b4f01SSabrina Dubroca NL_SET_ERR_MSG(extack, "IPv6 support disabled");
17311da177e4SLinus Torvalds return -EAFNOSUPPORT;
17321da177e4SLinus Torvalds #endif
17331da177e4SLinus Torvalds
17341da177e4SLinus Torvalds default:
1735ec2b4f01SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid selector family");
17361da177e4SLinus Torvalds return -EINVAL;
17373ff50b79SStephen Hemminger }
17381da177e4SLinus Torvalds
173924fc544fSSabrina Dubroca ret = verify_policy_dir(p->dir, extack);
1740e682adf0SFan Du if (ret)
1741e682adf0SFan Du return ret;
1742ec2b4f01SSabrina Dubroca if (p->index && (xfrm_policy_id2dir(p->index) != p->dir)) {
1743ec2b4f01SSabrina Dubroca NL_SET_ERR_MSG(extack, "Policy index doesn't match direction");
1744e682adf0SFan Du return -EINVAL;
1745ec2b4f01SSabrina Dubroca }
1746e682adf0SFan Du
1747e682adf0SFan Du return 0;
17481da177e4SLinus Torvalds }
17491da177e4SLinus Torvalds
copy_from_user_sec_ctx(struct xfrm_policy * pol,struct nlattr ** attrs)17505424f32eSThomas Graf static int copy_from_user_sec_ctx(struct xfrm_policy *pol, struct nlattr **attrs)
1751df71837dSTrent Jaeger {
17525424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX];
1753df71837dSTrent Jaeger struct xfrm_user_sec_ctx *uctx;
1754df71837dSTrent Jaeger
1755df71837dSTrent Jaeger if (!rt)
1756df71837dSTrent Jaeger return 0;
1757df71837dSTrent Jaeger
17585424f32eSThomas Graf uctx = nla_data(rt);
175952a4c640SNikolay Aleksandrov return security_xfrm_policy_alloc(&pol->security, uctx, GFP_KERNEL);
1760df71837dSTrent Jaeger }
1761df71837dSTrent Jaeger
copy_templates(struct xfrm_policy * xp,struct xfrm_user_tmpl * ut,int nr)17621da177e4SLinus Torvalds static void copy_templates(struct xfrm_policy *xp, struct xfrm_user_tmpl *ut,
17631da177e4SLinus Torvalds int nr)
17641da177e4SLinus Torvalds {
17651da177e4SLinus Torvalds int i;
17661da177e4SLinus Torvalds
17671da177e4SLinus Torvalds xp->xfrm_nr = nr;
17681da177e4SLinus Torvalds for (i = 0; i < nr; i++, ut++) {
17691da177e4SLinus Torvalds struct xfrm_tmpl *t = &xp->xfrm_vec[i];
17701da177e4SLinus Torvalds
17711da177e4SLinus Torvalds memcpy(&t->id, &ut->id, sizeof(struct xfrm_id));
17721da177e4SLinus Torvalds memcpy(&t->saddr, &ut->saddr,
17731da177e4SLinus Torvalds sizeof(xfrm_address_t));
17741da177e4SLinus Torvalds t->reqid = ut->reqid;
17751da177e4SLinus Torvalds t->mode = ut->mode;
17761da177e4SLinus Torvalds t->share = ut->share;
17771da177e4SLinus Torvalds t->optional = ut->optional;
17781da177e4SLinus Torvalds t->aalgos = ut->aalgos;
17791da177e4SLinus Torvalds t->ealgos = ut->ealgos;
17801da177e4SLinus Torvalds t->calgos = ut->calgos;
1781c5d18e98SHerbert Xu /* If all masks are ~0, then we allow all algorithms. */
1782c5d18e98SHerbert Xu t->allalgs = !~(t->aalgos & t->ealgos & t->calgos);
17838511d01dSMiika Komu t->encap_family = ut->family;
17841da177e4SLinus Torvalds }
17851da177e4SLinus Torvalds }
17861da177e4SLinus Torvalds
validate_tmpl(int nr,struct xfrm_user_tmpl * ut,u16 family,int dir,struct netlink_ext_ack * extack)1787d37bed89SSabrina Dubroca static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family,
17883d776e31STobias Brunner int dir, struct netlink_ext_ack *extack)
1789b4ad86bfSDavid S. Miller {
1790732706afSSteffen Klassert u16 prev_family;
1791b4ad86bfSDavid S. Miller int i;
1792b4ad86bfSDavid S. Miller
1793d37bed89SSabrina Dubroca if (nr > XFRM_MAX_DEPTH) {
1794d37bed89SSabrina Dubroca NL_SET_ERR_MSG(extack, "Template count must be <= XFRM_MAX_DEPTH (" __stringify(XFRM_MAX_DEPTH) ")");
1795b4ad86bfSDavid S. Miller return -EINVAL;
1796d37bed89SSabrina Dubroca }
1797b4ad86bfSDavid S. Miller
1798732706afSSteffen Klassert prev_family = family;
1799732706afSSteffen Klassert
1800b4ad86bfSDavid S. Miller for (i = 0; i < nr; i++) {
1801b4ad86bfSDavid S. Miller /* We never validated the ut->family value, so many
1802b4ad86bfSDavid S. Miller * applications simply leave it at zero. The check was
1803b4ad86bfSDavid S. Miller * never made and ut->family was ignored because all
1804b4ad86bfSDavid S. Miller * templates could be assumed to have the same family as
1805b4ad86bfSDavid S. Miller * the policy itself. Now that we will have ipv4-in-ipv6
1806b4ad86bfSDavid S. Miller * and ipv6-in-ipv4 tunnels, this is no longer true.
1807b4ad86bfSDavid S. Miller */
1808b4ad86bfSDavid S. Miller if (!ut[i].family)
1809b4ad86bfSDavid S. Miller ut[i].family = family;
1810b4ad86bfSDavid S. Miller
181135e61038SFlorian Westphal switch (ut[i].mode) {
181235e61038SFlorian Westphal case XFRM_MODE_TUNNEL:
181335e61038SFlorian Westphal case XFRM_MODE_BEET:
18143d776e31STobias Brunner if (ut[i].optional && dir == XFRM_POLICY_OUT) {
18153d776e31STobias Brunner NL_SET_ERR_MSG(extack, "Mode in optional template not allowed in outbound policy");
18163d776e31STobias Brunner return -EINVAL;
18173d776e31STobias Brunner }
181835e61038SFlorian Westphal break;
181935e61038SFlorian Westphal default:
1820d37bed89SSabrina Dubroca if (ut[i].family != prev_family) {
1821d37bed89SSabrina Dubroca NL_SET_ERR_MSG(extack, "Mode in template doesn't support a family change");
1822732706afSSteffen Klassert return -EINVAL;
1823d37bed89SSabrina Dubroca }
182435e61038SFlorian Westphal break;
182535e61038SFlorian Westphal }
1826d37bed89SSabrina Dubroca if (ut[i].mode >= XFRM_MODE_MAX) {
1827d37bed89SSabrina Dubroca NL_SET_ERR_MSG(extack, "Mode in template must be < XFRM_MODE_MAX (" __stringify(XFRM_MODE_MAX) ")");
182832bf94fbSSean Tranchetti return -EINVAL;
1829d37bed89SSabrina Dubroca }
183032bf94fbSSean Tranchetti
1831732706afSSteffen Klassert prev_family = ut[i].family;
1832732706afSSteffen Klassert
1833b4ad86bfSDavid S. Miller switch (ut[i].family) {
1834b4ad86bfSDavid S. Miller case AF_INET:
1835b4ad86bfSDavid S. Miller break;
1836dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
1837b4ad86bfSDavid S. Miller case AF_INET6:
1838b4ad86bfSDavid S. Miller break;
1839b4ad86bfSDavid S. Miller #endif
1840b4ad86bfSDavid S. Miller default:
1841d37bed89SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid family in template");
1842b4ad86bfSDavid S. Miller return -EINVAL;
18433ff50b79SStephen Hemminger }
18446a53b759SCong Wang
1845d37bed89SSabrina Dubroca if (!xfrm_id_proto_valid(ut[i].id.proto)) {
1846d37bed89SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid XFRM protocol in template");
18476a53b759SCong Wang return -EINVAL;
18486a53b759SCong Wang }
1849d37bed89SSabrina Dubroca }
18506a53b759SCong Wang
1851b4ad86bfSDavid S. Miller return 0;
1852b4ad86bfSDavid S. Miller }
1853b4ad86bfSDavid S. Miller
copy_from_user_tmpl(struct xfrm_policy * pol,struct nlattr ** attrs,int dir,struct netlink_ext_ack * extack)1854d37bed89SSabrina Dubroca static int copy_from_user_tmpl(struct xfrm_policy *pol, struct nlattr **attrs,
18553d776e31STobias Brunner int dir, struct netlink_ext_ack *extack)
18561da177e4SLinus Torvalds {
18575424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_TMPL];
18581da177e4SLinus Torvalds
18591da177e4SLinus Torvalds if (!rt) {
18601da177e4SLinus Torvalds pol->xfrm_nr = 0;
18611da177e4SLinus Torvalds } else {
18625424f32eSThomas Graf struct xfrm_user_tmpl *utmpl = nla_data(rt);
18635424f32eSThomas Graf int nr = nla_len(rt) / sizeof(*utmpl);
1864b4ad86bfSDavid S. Miller int err;
18651da177e4SLinus Torvalds
18663d776e31STobias Brunner err = validate_tmpl(nr, utmpl, pol->family, dir, extack);
1867b4ad86bfSDavid S. Miller if (err)
1868b4ad86bfSDavid S. Miller return err;
18691da177e4SLinus Torvalds
18705424f32eSThomas Graf copy_templates(pol, utmpl, nr);
18711da177e4SLinus Torvalds }
18721da177e4SLinus Torvalds return 0;
18731da177e4SLinus Torvalds }
18741da177e4SLinus Torvalds
copy_from_user_policy_type(u8 * tp,struct nlattr ** attrs,struct netlink_ext_ack * extack)1875fb7deabaSSabrina Dubroca static int copy_from_user_policy_type(u8 *tp, struct nlattr **attrs,
1876fb7deabaSSabrina Dubroca struct netlink_ext_ack *extack)
1877f7b6983fSMasahide NAKAMURA {
18785424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_POLICY_TYPE];
1879f7b6983fSMasahide NAKAMURA struct xfrm_userpolicy_type *upt;
1880b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN;
1881f7b6983fSMasahide NAKAMURA int err;
1882f7b6983fSMasahide NAKAMURA
1883f7b6983fSMasahide NAKAMURA if (rt) {
18845424f32eSThomas Graf upt = nla_data(rt);
1885f7b6983fSMasahide NAKAMURA type = upt->type;
1886f7b6983fSMasahide NAKAMURA }
1887f7b6983fSMasahide NAKAMURA
1888fb7deabaSSabrina Dubroca err = verify_policy_type(type, extack);
1889f7b6983fSMasahide NAKAMURA if (err)
1890f7b6983fSMasahide NAKAMURA return err;
1891f7b6983fSMasahide NAKAMURA
1892f7b6983fSMasahide NAKAMURA *tp = type;
1893f7b6983fSMasahide NAKAMURA return 0;
1894f7b6983fSMasahide NAKAMURA }
1895f7b6983fSMasahide NAKAMURA
copy_from_user_policy(struct xfrm_policy * xp,struct xfrm_userpolicy_info * p)18961da177e4SLinus Torvalds static void copy_from_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p)
18971da177e4SLinus Torvalds {
18981da177e4SLinus Torvalds xp->priority = p->priority;
18991da177e4SLinus Torvalds xp->index = p->index;
19001da177e4SLinus Torvalds memcpy(&xp->selector, &p->sel, sizeof(xp->selector));
19011da177e4SLinus Torvalds memcpy(&xp->lft, &p->lft, sizeof(xp->lft));
19021da177e4SLinus Torvalds xp->action = p->action;
19031da177e4SLinus Torvalds xp->flags = p->flags;
19041da177e4SLinus Torvalds xp->family = p->sel.family;
19051da177e4SLinus Torvalds /* XXX xp->share = p->share; */
19061da177e4SLinus Torvalds }
19071da177e4SLinus Torvalds
copy_to_user_policy(struct xfrm_policy * xp,struct xfrm_userpolicy_info * p,int dir)19081da177e4SLinus Torvalds static void copy_to_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p, int dir)
19091da177e4SLinus Torvalds {
19107b789836SMathias Krause memset(p, 0, sizeof(*p));
19111da177e4SLinus Torvalds memcpy(&p->sel, &xp->selector, sizeof(p->sel));
19121da177e4SLinus Torvalds memcpy(&p->lft, &xp->lft, sizeof(p->lft));
19131da177e4SLinus Torvalds memcpy(&p->curlft, &xp->curlft, sizeof(p->curlft));
19141da177e4SLinus Torvalds p->priority = xp->priority;
19151da177e4SLinus Torvalds p->index = xp->index;
19161da177e4SLinus Torvalds p->sel.family = xp->family;
19171da177e4SLinus Torvalds p->dir = dir;
19181da177e4SLinus Torvalds p->action = xp->action;
19191da177e4SLinus Torvalds p->flags = xp->flags;
19201da177e4SLinus Torvalds p->share = XFRM_SHARE_ANY; /* XXX xp->share */
19211da177e4SLinus Torvalds }
19221da177e4SLinus Torvalds
xfrm_policy_construct(struct net * net,struct xfrm_userpolicy_info * p,struct nlattr ** attrs,int * errp,struct netlink_ext_ack * extack)1923fb7deabaSSabrina Dubroca static struct xfrm_policy *xfrm_policy_construct(struct net *net,
1924fb7deabaSSabrina Dubroca struct xfrm_userpolicy_info *p,
1925fb7deabaSSabrina Dubroca struct nlattr **attrs,
1926fb7deabaSSabrina Dubroca int *errp,
1927fb7deabaSSabrina Dubroca struct netlink_ext_ack *extack)
19281da177e4SLinus Torvalds {
1929fc34acd3SAlexey Dobriyan struct xfrm_policy *xp = xfrm_policy_alloc(net, GFP_KERNEL);
19301da177e4SLinus Torvalds int err;
19311da177e4SLinus Torvalds
19321da177e4SLinus Torvalds if (!xp) {
19331da177e4SLinus Torvalds *errp = -ENOMEM;
19341da177e4SLinus Torvalds return NULL;
19351da177e4SLinus Torvalds }
19361da177e4SLinus Torvalds
19371da177e4SLinus Torvalds copy_from_user_policy(xp, p);
1938df71837dSTrent Jaeger
1939fb7deabaSSabrina Dubroca err = copy_from_user_policy_type(&xp->type, attrs, extack);
1940f7b6983fSMasahide NAKAMURA if (err)
1941f7b6983fSMasahide NAKAMURA goto error;
1942f7b6983fSMasahide NAKAMURA
19433d776e31STobias Brunner if (!(err = copy_from_user_tmpl(xp, attrs, p->dir, extack)))
194435a7aa08SThomas Graf err = copy_from_user_sec_ctx(xp, attrs);
1945f7b6983fSMasahide NAKAMURA if (err)
1946f7b6983fSMasahide NAKAMURA goto error;
19471da177e4SLinus Torvalds
1948295fae56SJamal Hadi Salim xfrm_mark_get(attrs, &xp->mark);
1949295fae56SJamal Hadi Salim
1950a3d9001bSKai Lueke if (attrs[XFRMA_IF_ID])
19517e652640SSteffen Klassert xp->if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
19527e652640SSteffen Klassert
1953919e43faSLeon Romanovsky /* configure the hardware if offload is requested */
1954919e43faSLeon Romanovsky if (attrs[XFRMA_OFFLOAD_DEV]) {
1955919e43faSLeon Romanovsky err = xfrm_dev_policy_add(net, xp,
1956919e43faSLeon Romanovsky nla_data(attrs[XFRMA_OFFLOAD_DEV]),
1957919e43faSLeon Romanovsky p->dir, extack);
1958919e43faSLeon Romanovsky if (err)
1959919e43faSLeon Romanovsky goto error;
1960919e43faSLeon Romanovsky }
1961919e43faSLeon Romanovsky
19621da177e4SLinus Torvalds return xp;
1963f7b6983fSMasahide NAKAMURA error:
1964f7b6983fSMasahide NAKAMURA *errp = err;
196512a169e7SHerbert Xu xp->walk.dead = 1;
196664c31b3fSWANG Cong xfrm_policy_destroy(xp);
1967f7b6983fSMasahide NAKAMURA return NULL;
19681da177e4SLinus Torvalds }
19691da177e4SLinus Torvalds
xfrm_add_policy(struct sk_buff * skb,struct nlmsghdr * nlh,struct nlattr ** attrs,struct netlink_ext_ack * extack)197022e70050SChristoph Hellwig static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
19713bec6c3eSSabrina Dubroca struct nlattr **attrs,
19723bec6c3eSSabrina Dubroca struct netlink_ext_ack *extack)
19731da177e4SLinus Torvalds {
1974fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk);
19757b67c857SThomas Graf struct xfrm_userpolicy_info *p = nlmsg_data(nlh);
19761da177e4SLinus Torvalds struct xfrm_policy *xp;
197726b15dadSJamal Hadi Salim struct km_event c;
19781da177e4SLinus Torvalds int err;
19791da177e4SLinus Torvalds int excl;
19801da177e4SLinus Torvalds
1981ec2b4f01SSabrina Dubroca err = verify_newpolicy_info(p, extack);
19821da177e4SLinus Torvalds if (err)
19831da177e4SLinus Torvalds return err;
198408a717e4SSabrina Dubroca err = verify_sec_ctx_len(attrs, extack);
1985df71837dSTrent Jaeger if (err)
1986df71837dSTrent Jaeger return err;
19871da177e4SLinus Torvalds
1988fb7deabaSSabrina Dubroca xp = xfrm_policy_construct(net, p, attrs, &err, extack);
19891da177e4SLinus Torvalds if (!xp)
19901da177e4SLinus Torvalds return err;
19911da177e4SLinus Torvalds
199225985edcSLucas De Marchi /* shouldn't excl be based on nlh flags??
199326b15dadSJamal Hadi Salim * Aha! this is anti-netlink really i.e more pfkey derived
1994a7fd0e6dSBhaskar Chowdhury * in netlink excl is a flag and you wouldn't need
199526b15dadSJamal Hadi Salim * a type XFRM_MSG_UPDPOLICY - JHS */
19961da177e4SLinus Torvalds excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY;
19971da177e4SLinus Torvalds err = xfrm_policy_insert(p->dir, xp, excl);
19982e71029eSTetsuo Handa xfrm_audit_policy_add(xp, err ? 0 : 1, true);
1999161a09e7SJoy Latten
20001da177e4SLinus Torvalds if (err) {
2001919e43faSLeon Romanovsky xfrm_dev_policy_delete(xp);
200294b95dfaSLeon Romanovsky xfrm_dev_policy_free(xp);
200303e1ad7bSPaul Moore security_xfrm_policy_free(xp->security);
20041da177e4SLinus Torvalds kfree(xp);
20051da177e4SLinus Torvalds return err;
20061da177e4SLinus Torvalds }
20071da177e4SLinus Torvalds
2008f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type;
200926b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq;
201015e47304SEric W. Biederman c.portid = nlh->nlmsg_pid;
201126b15dadSJamal Hadi Salim km_policy_notify(xp, p->dir, &c);
201226b15dadSJamal Hadi Salim
20131da177e4SLinus Torvalds xfrm_pol_put(xp);
20141da177e4SLinus Torvalds
20151da177e4SLinus Torvalds return 0;
20161da177e4SLinus Torvalds }
20171da177e4SLinus Torvalds
copy_to_user_tmpl(struct xfrm_policy * xp,struct sk_buff * skb)20181da177e4SLinus Torvalds static int copy_to_user_tmpl(struct xfrm_policy *xp, struct sk_buff *skb)
20191da177e4SLinus Torvalds {
20201da177e4SLinus Torvalds struct xfrm_user_tmpl vec[XFRM_MAX_DEPTH];
20211da177e4SLinus Torvalds int i;
20221da177e4SLinus Torvalds
20231da177e4SLinus Torvalds if (xp->xfrm_nr == 0)
20241da177e4SLinus Torvalds return 0;
20251da177e4SLinus Torvalds
2026b4752267SNathan Chancellor if (xp->xfrm_nr > XFRM_MAX_DEPTH)
2027b4752267SNathan Chancellor return -ENOBUFS;
2028b4752267SNathan Chancellor
20291da177e4SLinus Torvalds for (i = 0; i < xp->xfrm_nr; i++) {
20301da177e4SLinus Torvalds struct xfrm_user_tmpl *up = &vec[i];
20311da177e4SLinus Torvalds struct xfrm_tmpl *kp = &xp->xfrm_vec[i];
20321da177e4SLinus Torvalds
20331f86840fSMathias Krause memset(up, 0, sizeof(*up));
20341da177e4SLinus Torvalds memcpy(&up->id, &kp->id, sizeof(up->id));
20358511d01dSMiika Komu up->family = kp->encap_family;
20361da177e4SLinus Torvalds memcpy(&up->saddr, &kp->saddr, sizeof(up->saddr));
20371da177e4SLinus Torvalds up->reqid = kp->reqid;
20381da177e4SLinus Torvalds up->mode = kp->mode;
20391da177e4SLinus Torvalds up->share = kp->share;
20401da177e4SLinus Torvalds up->optional = kp->optional;
20411da177e4SLinus Torvalds up->aalgos = kp->aalgos;
20421da177e4SLinus Torvalds up->ealgos = kp->ealgos;
20431da177e4SLinus Torvalds up->calgos = kp->calgos;
20441da177e4SLinus Torvalds }
20451da177e4SLinus Torvalds
2046c0144beaSThomas Graf return nla_put(skb, XFRMA_TMPL,
2047c0144beaSThomas Graf sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr, vec);
2048df71837dSTrent Jaeger }
2049df71837dSTrent Jaeger
copy_to_user_state_sec_ctx(struct xfrm_state * x,struct sk_buff * skb)20500d681623SSerge Hallyn static inline int copy_to_user_state_sec_ctx(struct xfrm_state *x, struct sk_buff *skb)
20510d681623SSerge Hallyn {
20520d681623SSerge Hallyn if (x->security) {
20530d681623SSerge Hallyn return copy_sec_ctx(x->security, skb);
20540d681623SSerge Hallyn }
20550d681623SSerge Hallyn return 0;
20560d681623SSerge Hallyn }
20570d681623SSerge Hallyn
copy_to_user_sec_ctx(struct xfrm_policy * xp,struct sk_buff * skb)20580d681623SSerge Hallyn static inline int copy_to_user_sec_ctx(struct xfrm_policy *xp, struct sk_buff *skb)
20590d681623SSerge Hallyn {
20601d1e34ddSDavid S. Miller if (xp->security)
20610d681623SSerge Hallyn return copy_sec_ctx(xp->security, skb);
20620d681623SSerge Hallyn return 0;
20630d681623SSerge Hallyn }
userpolicy_type_attrsize(void)2064a1b831f2SAlexey Dobriyan static inline unsigned int userpolicy_type_attrsize(void)
2065cfbfd45aSThomas Graf {
2066cfbfd45aSThomas Graf #ifdef CONFIG_XFRM_SUB_POLICY
2067cfbfd45aSThomas Graf return nla_total_size(sizeof(struct xfrm_userpolicy_type));
2068cfbfd45aSThomas Graf #else
2069cfbfd45aSThomas Graf return 0;
2070cfbfd45aSThomas Graf #endif
2071cfbfd45aSThomas Graf }
20720d681623SSerge Hallyn
2073f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY
copy_to_user_policy_type(u8 type,struct sk_buff * skb)2074b798a9edSJamal Hadi Salim static int copy_to_user_policy_type(u8 type, struct sk_buff *skb)
2075f7b6983fSMasahide NAKAMURA {
207645c180bcSEric Dumazet struct xfrm_userpolicy_type upt;
207745c180bcSEric Dumazet
207845c180bcSEric Dumazet /* Sadly there are two holes in struct xfrm_userpolicy_type */
207945c180bcSEric Dumazet memset(&upt, 0, sizeof(upt));
208045c180bcSEric Dumazet upt.type = type;
2081f7b6983fSMasahide NAKAMURA
2082c0144beaSThomas Graf return nla_put(skb, XFRMA_POLICY_TYPE, sizeof(upt), &upt);
2083f7b6983fSMasahide NAKAMURA }
2084f7b6983fSMasahide NAKAMURA
2085f7b6983fSMasahide NAKAMURA #else
copy_to_user_policy_type(u8 type,struct sk_buff * skb)2086b798a9edSJamal Hadi Salim static inline int copy_to_user_policy_type(u8 type, struct sk_buff *skb)
2087f7b6983fSMasahide NAKAMURA {
2088f7b6983fSMasahide NAKAMURA return 0;
2089f7b6983fSMasahide NAKAMURA }
2090f7b6983fSMasahide NAKAMURA #endif
2091f7b6983fSMasahide NAKAMURA
dump_one_policy(struct xfrm_policy * xp,int dir,int count,void * ptr)20921da177e4SLinus Torvalds static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr)
20931da177e4SLinus Torvalds {
20941da177e4SLinus Torvalds struct xfrm_dump_info *sp = ptr;
20951da177e4SLinus Torvalds struct xfrm_userpolicy_info *p;
20961da177e4SLinus Torvalds struct sk_buff *in_skb = sp->in_skb;
20971da177e4SLinus Torvalds struct sk_buff *skb = sp->out_skb;
20985f3eea6bSDmitry Safonov struct xfrm_translator *xtr;
20991da177e4SLinus Torvalds struct nlmsghdr *nlh;
21001d1e34ddSDavid S. Miller int err;
21011da177e4SLinus Torvalds
210215e47304SEric W. Biederman nlh = nlmsg_put(skb, NETLINK_CB(in_skb).portid, sp->nlmsg_seq,
210379b8b7f4SThomas Graf XFRM_MSG_NEWPOLICY, sizeof(*p), sp->nlmsg_flags);
210479b8b7f4SThomas Graf if (nlh == NULL)
210579b8b7f4SThomas Graf return -EMSGSIZE;
21061da177e4SLinus Torvalds
21077b67c857SThomas Graf p = nlmsg_data(nlh);
21081da177e4SLinus Torvalds copy_to_user_policy(xp, p, dir);
21091d1e34ddSDavid S. Miller err = copy_to_user_tmpl(xp, skb);
21101d1e34ddSDavid S. Miller if (!err)
21111d1e34ddSDavid S. Miller err = copy_to_user_sec_ctx(xp, skb);
21121d1e34ddSDavid S. Miller if (!err)
21131d1e34ddSDavid S. Miller err = copy_to_user_policy_type(xp->type, skb);
21141d1e34ddSDavid S. Miller if (!err)
21151d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &xp->mark);
21167e652640SSteffen Klassert if (!err)
21177e652640SSteffen Klassert err = xfrm_if_id_put(skb, xp->if_id);
2118919e43faSLeon Romanovsky if (!err && xp->xdo.dev)
2119919e43faSLeon Romanovsky err = copy_user_offload(&xp->xdo, skb);
21201d1e34ddSDavid S. Miller if (err) {
21211d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh);
21221d1e34ddSDavid S. Miller return err;
21231d1e34ddSDavid S. Miller }
21249825069dSThomas Graf nlmsg_end(skb, nlh);
21255f3eea6bSDmitry Safonov
21265f3eea6bSDmitry Safonov xtr = xfrm_get_translator();
21275f3eea6bSDmitry Safonov if (xtr) {
21285f3eea6bSDmitry Safonov err = xtr->alloc_compat(skb, nlh);
21295f3eea6bSDmitry Safonov
21305f3eea6bSDmitry Safonov xfrm_put_translator(xtr);
21315f3eea6bSDmitry Safonov if (err) {
21325f3eea6bSDmitry Safonov nlmsg_cancel(skb, nlh);
21335f3eea6bSDmitry Safonov return err;
21345f3eea6bSDmitry Safonov }
21355f3eea6bSDmitry Safonov }
21365f3eea6bSDmitry Safonov
21371da177e4SLinus Torvalds return 0;
21381da177e4SLinus Torvalds }
21391da177e4SLinus Torvalds
xfrm_dump_policy_done(struct netlink_callback * cb)21404c563f76STimo Teras static int xfrm_dump_policy_done(struct netlink_callback *cb)
21414c563f76STimo Teras {
21421137b5e2SHerbert Xu struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *)cb->args;
2143283bc9f3SFan Du struct net *net = sock_net(cb->skb->sk);
21444c563f76STimo Teras
2145283bc9f3SFan Du xfrm_policy_walk_done(walk, net);
21464c563f76STimo Teras return 0;
21474c563f76STimo Teras }
21484c563f76STimo Teras
xfrm_dump_policy_start(struct netlink_callback * cb)21491137b5e2SHerbert Xu static int xfrm_dump_policy_start(struct netlink_callback *cb)
21501137b5e2SHerbert Xu {
21511137b5e2SHerbert Xu struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *)cb->args;
21521137b5e2SHerbert Xu
21531137b5e2SHerbert Xu BUILD_BUG_ON(sizeof(*walk) > sizeof(cb->args));
21541137b5e2SHerbert Xu
21551137b5e2SHerbert Xu xfrm_policy_walk_init(walk, XFRM_POLICY_TYPE_ANY);
21561137b5e2SHerbert Xu return 0;
21571137b5e2SHerbert Xu }
21581137b5e2SHerbert Xu
xfrm_dump_policy(struct sk_buff * skb,struct netlink_callback * cb)21591da177e4SLinus Torvalds static int xfrm_dump_policy(struct sk_buff *skb, struct netlink_callback *cb)
21601da177e4SLinus Torvalds {
2161fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk);
21621137b5e2SHerbert Xu struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *)cb->args;
21631da177e4SLinus Torvalds struct xfrm_dump_info info;
21641da177e4SLinus Torvalds
21651da177e4SLinus Torvalds info.in_skb = cb->skb;
21661da177e4SLinus Torvalds info.out_skb = skb;
21671da177e4SLinus Torvalds info.nlmsg_seq = cb->nlh->nlmsg_seq;
21681da177e4SLinus Torvalds info.nlmsg_flags = NLM_F_MULTI;
21694c563f76STimo Teras
2170fc34acd3SAlexey Dobriyan (void) xfrm_policy_walk(net, walk, dump_one_policy, &info);
21711da177e4SLinus Torvalds
21721da177e4SLinus Torvalds return skb->len;
21731da177e4SLinus Torvalds }
21741da177e4SLinus Torvalds
xfrm_policy_netlink(struct sk_buff * in_skb,struct xfrm_policy * xp,int dir,u32 seq)21751da177e4SLinus Torvalds static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb,
21761da177e4SLinus Torvalds struct xfrm_policy *xp,
21771da177e4SLinus Torvalds int dir, u32 seq)
21781da177e4SLinus Torvalds {
21791da177e4SLinus Torvalds struct xfrm_dump_info info;
21801da177e4SLinus Torvalds struct sk_buff *skb;
2181c2546372SMathias Krause int err;
21821da177e4SLinus Torvalds
21837deb2264SThomas Graf skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
21841da177e4SLinus Torvalds if (!skb)
21851da177e4SLinus Torvalds return ERR_PTR(-ENOMEM);
21861da177e4SLinus Torvalds
21871da177e4SLinus Torvalds info.in_skb = in_skb;
21881da177e4SLinus Torvalds info.out_skb = skb;
21891da177e4SLinus Torvalds info.nlmsg_seq = seq;
21901da177e4SLinus Torvalds info.nlmsg_flags = 0;
21911da177e4SLinus Torvalds
2192c2546372SMathias Krause err = dump_one_policy(xp, dir, 0, &info);
2193c2546372SMathias Krause if (err) {
21941da177e4SLinus Torvalds kfree_skb(skb);
2195c2546372SMathias Krause return ERR_PTR(err);
21961da177e4SLinus Torvalds }
21971da177e4SLinus Torvalds
21981da177e4SLinus Torvalds return skb;
21991da177e4SLinus Torvalds }
22001da177e4SLinus Torvalds
xfrm_notify_userpolicy(struct net * net)220188d0adb5SNicolas Dichtel static int xfrm_notify_userpolicy(struct net *net)
220288d0adb5SNicolas Dichtel {
220388d0adb5SNicolas Dichtel struct xfrm_userpolicy_default *up;
220488d0adb5SNicolas Dichtel int len = NLMSG_ALIGN(sizeof(*up));
220588d0adb5SNicolas Dichtel struct nlmsghdr *nlh;
220688d0adb5SNicolas Dichtel struct sk_buff *skb;
220793ec1320SNicolas Dichtel int err;
220888d0adb5SNicolas Dichtel
220988d0adb5SNicolas Dichtel skb = nlmsg_new(len, GFP_ATOMIC);
221088d0adb5SNicolas Dichtel if (skb == NULL)
221188d0adb5SNicolas Dichtel return -ENOMEM;
221288d0adb5SNicolas Dichtel
221388d0adb5SNicolas Dichtel nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_GETDEFAULT, sizeof(*up), 0);
221488d0adb5SNicolas Dichtel if (nlh == NULL) {
221588d0adb5SNicolas Dichtel kfree_skb(skb);
221688d0adb5SNicolas Dichtel return -EMSGSIZE;
221788d0adb5SNicolas Dichtel }
221888d0adb5SNicolas Dichtel
221988d0adb5SNicolas Dichtel up = nlmsg_data(nlh);
2220b58b1f56SNicolas Dichtel up->in = net->xfrm.policy_default[XFRM_POLICY_IN];
2221b58b1f56SNicolas Dichtel up->fwd = net->xfrm.policy_default[XFRM_POLICY_FWD];
2222b58b1f56SNicolas Dichtel up->out = net->xfrm.policy_default[XFRM_POLICY_OUT];
222388d0adb5SNicolas Dichtel
222488d0adb5SNicolas Dichtel nlmsg_end(skb, nlh);
222588d0adb5SNicolas Dichtel
222693ec1320SNicolas Dichtel rcu_read_lock();
222793ec1320SNicolas Dichtel err = xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_POLICY);
222893ec1320SNicolas Dichtel rcu_read_unlock();
222993ec1320SNicolas Dichtel
223093ec1320SNicolas Dichtel return err;
223188d0adb5SNicolas Dichtel }
223288d0adb5SNicolas Dichtel
xfrm_userpolicy_is_valid(__u8 policy)2233b58b1f56SNicolas Dichtel static bool xfrm_userpolicy_is_valid(__u8 policy)
2234b58b1f56SNicolas Dichtel {
2235b58b1f56SNicolas Dichtel return policy == XFRM_USERPOLICY_BLOCK ||
2236b58b1f56SNicolas Dichtel policy == XFRM_USERPOLICY_ACCEPT;
2237b58b1f56SNicolas Dichtel }
2238b58b1f56SNicolas Dichtel
xfrm_set_default(struct sk_buff * skb,struct nlmsghdr * nlh,struct nlattr ** attrs,struct netlink_ext_ack * extack)22392d151d39SSteffen Klassert static int xfrm_set_default(struct sk_buff *skb, struct nlmsghdr *nlh,
22403bec6c3eSSabrina Dubroca struct nlattr **attrs, struct netlink_ext_ack *extack)
22412d151d39SSteffen Klassert {
22422d151d39SSteffen Klassert struct net *net = sock_net(skb->sk);
22432d151d39SSteffen Klassert struct xfrm_userpolicy_default *up = nlmsg_data(nlh);
22442d151d39SSteffen Klassert
2245b58b1f56SNicolas Dichtel if (xfrm_userpolicy_is_valid(up->in))
2246b58b1f56SNicolas Dichtel net->xfrm.policy_default[XFRM_POLICY_IN] = up->in;
22475d8dbb7fSPavel Skripkin
2248b58b1f56SNicolas Dichtel if (xfrm_userpolicy_is_valid(up->fwd))
2249b58b1f56SNicolas Dichtel net->xfrm.policy_default[XFRM_POLICY_FWD] = up->fwd;
22505d8dbb7fSPavel Skripkin
2251b58b1f56SNicolas Dichtel if (xfrm_userpolicy_is_valid(up->out))
2252b58b1f56SNicolas Dichtel net->xfrm.policy_default[XFRM_POLICY_OUT] = up->out;
22532d151d39SSteffen Klassert
22542d151d39SSteffen Klassert rt_genid_bump_all(net);
22552d151d39SSteffen Klassert
225688d0adb5SNicolas Dichtel xfrm_notify_userpolicy(net);
22572d151d39SSteffen Klassert return 0;
22582d151d39SSteffen Klassert }
22592d151d39SSteffen Klassert
xfrm_get_default(struct sk_buff * skb,struct nlmsghdr * nlh,struct nlattr ** attrs,struct netlink_ext_ack * extack)22602d151d39SSteffen Klassert static int xfrm_get_default(struct sk_buff *skb, struct nlmsghdr *nlh,
22613bec6c3eSSabrina Dubroca struct nlattr **attrs, struct netlink_ext_ack *extack)
22622d151d39SSteffen Klassert {
22632d151d39SSteffen Klassert struct sk_buff *r_skb;
22642d151d39SSteffen Klassert struct nlmsghdr *r_nlh;
22652d151d39SSteffen Klassert struct net *net = sock_net(skb->sk);
2266f8d858e6SNicolas Dichtel struct xfrm_userpolicy_default *r_up;
22672d151d39SSteffen Klassert int len = NLMSG_ALIGN(sizeof(struct xfrm_userpolicy_default));
22682d151d39SSteffen Klassert u32 portid = NETLINK_CB(skb).portid;
22692d151d39SSteffen Klassert u32 seq = nlh->nlmsg_seq;
22702d151d39SSteffen Klassert
22712d151d39SSteffen Klassert r_skb = nlmsg_new(len, GFP_ATOMIC);
22722d151d39SSteffen Klassert if (!r_skb)
22732d151d39SSteffen Klassert return -ENOMEM;
22742d151d39SSteffen Klassert
22752d151d39SSteffen Klassert r_nlh = nlmsg_put(r_skb, portid, seq, XFRM_MSG_GETDEFAULT, sizeof(*r_up), 0);
22762d151d39SSteffen Klassert if (!r_nlh) {
22772d151d39SSteffen Klassert kfree_skb(r_skb);
22782d151d39SSteffen Klassert return -EMSGSIZE;
22792d151d39SSteffen Klassert }
22802d151d39SSteffen Klassert
22812d151d39SSteffen Klassert r_up = nlmsg_data(r_nlh);
2282b58b1f56SNicolas Dichtel r_up->in = net->xfrm.policy_default[XFRM_POLICY_IN];
2283b58b1f56SNicolas Dichtel r_up->fwd = net->xfrm.policy_default[XFRM_POLICY_FWD];
2284b58b1f56SNicolas Dichtel r_up->out = net->xfrm.policy_default[XFRM_POLICY_OUT];
22852d151d39SSteffen Klassert nlmsg_end(r_skb, r_nlh);
22862d151d39SSteffen Klassert
22872d151d39SSteffen Klassert return nlmsg_unicast(net->xfrm.nlsk, r_skb, portid);
22882d151d39SSteffen Klassert }
22892d151d39SSteffen Klassert
xfrm_get_policy(struct sk_buff * skb,struct nlmsghdr * nlh,struct nlattr ** attrs,struct netlink_ext_ack * extack)229022e70050SChristoph Hellwig static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
22913bec6c3eSSabrina Dubroca struct nlattr **attrs,
22923bec6c3eSSabrina Dubroca struct netlink_ext_ack *extack)
22931da177e4SLinus Torvalds {
2294fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk);
22951da177e4SLinus Torvalds struct xfrm_policy *xp;
22961da177e4SLinus Torvalds struct xfrm_userpolicy_id *p;
2297b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN;
22981da177e4SLinus Torvalds int err;
229926b15dadSJamal Hadi Salim struct km_event c;
23001da177e4SLinus Torvalds int delete;
2301295fae56SJamal Hadi Salim struct xfrm_mark m;
23027e652640SSteffen Klassert u32 if_id = 0;
23031da177e4SLinus Torvalds
23047b67c857SThomas Graf p = nlmsg_data(nlh);
23051da177e4SLinus Torvalds delete = nlh->nlmsg_type == XFRM_MSG_DELPOLICY;
23061da177e4SLinus Torvalds
2307fb7deabaSSabrina Dubroca err = copy_from_user_policy_type(&type, attrs, extack);
2308f7b6983fSMasahide NAKAMURA if (err)
2309f7b6983fSMasahide NAKAMURA return err;
2310f7b6983fSMasahide NAKAMURA
231124fc544fSSabrina Dubroca err = verify_policy_dir(p->dir, extack);
23121da177e4SLinus Torvalds if (err)
23131da177e4SLinus Torvalds return err;
23141da177e4SLinus Torvalds
23157e652640SSteffen Klassert if (attrs[XFRMA_IF_ID])
23167e652640SSteffen Klassert if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
23177e652640SSteffen Klassert
23184f47e8abSXin Long xfrm_mark_get(attrs, &m);
23194f47e8abSXin Long
23201da177e4SLinus Torvalds if (p->index)
23214f47e8abSXin Long xp = xfrm_policy_byid(net, &m, if_id, type, p->dir,
23224f47e8abSXin Long p->index, delete, &err);
2323df71837dSTrent Jaeger else {
23245424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX];
232503e1ad7bSPaul Moore struct xfrm_sec_ctx *ctx;
2326df71837dSTrent Jaeger
232708a717e4SSabrina Dubroca err = verify_sec_ctx_len(attrs, extack);
2328df71837dSTrent Jaeger if (err)
2329df71837dSTrent Jaeger return err;
2330df71837dSTrent Jaeger
23312c8dd116SDenis V. Lunev ctx = NULL;
2332df71837dSTrent Jaeger if (rt) {
23335424f32eSThomas Graf struct xfrm_user_sec_ctx *uctx = nla_data(rt);
2334df71837dSTrent Jaeger
233552a4c640SNikolay Aleksandrov err = security_xfrm_policy_alloc(&ctx, uctx, GFP_KERNEL);
233603e1ad7bSPaul Moore if (err)
2337df71837dSTrent Jaeger return err;
23382c8dd116SDenis V. Lunev }
23394f47e8abSXin Long xp = xfrm_policy_bysel_ctx(net, &m, if_id, type, p->dir,
23404f47e8abSXin Long &p->sel, ctx, delete, &err);
234103e1ad7bSPaul Moore security_xfrm_policy_free(ctx);
2342df71837dSTrent Jaeger }
23431da177e4SLinus Torvalds if (xp == NULL)
23441da177e4SLinus Torvalds return -ENOENT;
23451da177e4SLinus Torvalds
23461da177e4SLinus Torvalds if (!delete) {
23471da177e4SLinus Torvalds struct sk_buff *resp_skb;
23481da177e4SLinus Torvalds
23491da177e4SLinus Torvalds resp_skb = xfrm_policy_netlink(skb, xp, p->dir, nlh->nlmsg_seq);
23501da177e4SLinus Torvalds if (IS_ERR(resp_skb)) {
23511da177e4SLinus Torvalds err = PTR_ERR(resp_skb);
23521da177e4SLinus Torvalds } else {
2353a6483b79SAlexey Dobriyan err = nlmsg_unicast(net->xfrm.nlsk, resp_skb,
235415e47304SEric W. Biederman NETLINK_CB(skb).portid);
23551da177e4SLinus Torvalds }
235626b15dadSJamal Hadi Salim } else {
23572e71029eSTetsuo Handa xfrm_audit_policy_delete(xp, err ? 0 : 1, true);
235813fcfbb0SDavid S. Miller
235913fcfbb0SDavid S. Miller if (err != 0)
2360c8c05a8eSCatherine Zhang goto out;
236113fcfbb0SDavid S. Miller
2362e7443892SHerbert Xu c.data.byid = p->index;
2363f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type;
236426b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq;
236515e47304SEric W. Biederman c.portid = nlh->nlmsg_pid;
236626b15dadSJamal Hadi Salim km_policy_notify(xp, p->dir, &c);
23671da177e4SLinus Torvalds }
23681da177e4SLinus Torvalds
2369c8c05a8eSCatherine Zhang out:
2370ef41aaa0SEric Paris xfrm_pol_put(xp);
23711da177e4SLinus Torvalds return err;
23721da177e4SLinus Torvalds }
23731da177e4SLinus Torvalds
xfrm_flush_sa(struct sk_buff * skb,struct nlmsghdr * nlh,struct nlattr ** attrs,struct netlink_ext_ack * extack)237422e70050SChristoph Hellwig static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
23753bec6c3eSSabrina Dubroca struct nlattr **attrs,
23763bec6c3eSSabrina Dubroca struct netlink_ext_ack *extack)
23771da177e4SLinus Torvalds {
2378fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk);
237926b15dadSJamal Hadi Salim struct km_event c;
23807b67c857SThomas Graf struct xfrm_usersa_flush *p = nlmsg_data(nlh);
23814aa2e62cSJoy Latten int err;
23821da177e4SLinus Torvalds
2383f75a2804SCong Wang err = xfrm_state_flush(net, p->proto, true, false);
23849e64cc95SJamal Hadi Salim if (err) {
23859e64cc95SJamal Hadi Salim if (err == -ESRCH) /* empty table */
23869e64cc95SJamal Hadi Salim return 0;
2387069c474eSDavid S. Miller return err;
23889e64cc95SJamal Hadi Salim }
2389bf08867fSHerbert Xu c.data.proto = p->proto;
2390f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type;
239126b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq;
239215e47304SEric W. Biederman c.portid = nlh->nlmsg_pid;
23937067802eSAlexey Dobriyan c.net = net;
239426b15dadSJamal Hadi Salim km_state_notify(NULL, &c);
239526b15dadSJamal Hadi Salim
23961da177e4SLinus Torvalds return 0;
23971da177e4SLinus Torvalds }
23981da177e4SLinus Torvalds
xfrm_aevent_msgsize(struct xfrm_state * x)2399a1b831f2SAlexey Dobriyan static inline unsigned int xfrm_aevent_msgsize(struct xfrm_state *x)
24007deb2264SThomas Graf {
2401a1b831f2SAlexey Dobriyan unsigned int replay_size = x->replay_esn ?
2402d8647b79SSteffen Klassert xfrm_replay_state_esn_len(x->replay_esn) :
2403d8647b79SSteffen Klassert sizeof(struct xfrm_replay_state);
2404d8647b79SSteffen Klassert
24057deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_aevent_id))
2406d8647b79SSteffen Klassert + nla_total_size(replay_size)
2407de95c4a4SNicolas Dichtel + nla_total_size_64bit(sizeof(struct xfrm_lifetime_cur))
24086f26b61eSJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark))
24097deb2264SThomas Graf + nla_total_size(4) /* XFRM_AE_RTHR */
24107deb2264SThomas Graf + nla_total_size(4); /* XFRM_AE_ETHR */
24117deb2264SThomas Graf }
2412d51d081dSJamal Hadi Salim
build_aevent(struct sk_buff * skb,struct xfrm_state * x,const struct km_event * c)2413214e005bSDavid S. Miller static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c)
2414d51d081dSJamal Hadi Salim {
2415d51d081dSJamal Hadi Salim struct xfrm_aevent_id *id;
2416d51d081dSJamal Hadi Salim struct nlmsghdr *nlh;
24171d1e34ddSDavid S. Miller int err;
2418d51d081dSJamal Hadi Salim
241915e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, c->seq, XFRM_MSG_NEWAE, sizeof(*id), 0);
242079b8b7f4SThomas Graf if (nlh == NULL)
242179b8b7f4SThomas Graf return -EMSGSIZE;
2422d51d081dSJamal Hadi Salim
24237b67c857SThomas Graf id = nlmsg_data(nlh);
2424931e79d7SMathias Krause memset(&id->sa_id, 0, sizeof(id->sa_id));
24252b5f6dccSJamal Hadi Salim memcpy(&id->sa_id.daddr, &x->id.daddr, sizeof(x->id.daddr));
2426d51d081dSJamal Hadi Salim id->sa_id.spi = x->id.spi;
2427d51d081dSJamal Hadi Salim id->sa_id.family = x->props.family;
2428d51d081dSJamal Hadi Salim id->sa_id.proto = x->id.proto;
24292b5f6dccSJamal Hadi Salim memcpy(&id->saddr, &x->props.saddr, sizeof(x->props.saddr));
24302b5f6dccSJamal Hadi Salim id->reqid = x->props.reqid;
2431d51d081dSJamal Hadi Salim id->flags = c->data.aevent;
2432d51d081dSJamal Hadi Salim
2433d0fde795SDavid S. Miller if (x->replay_esn) {
24341d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_REPLAY_ESN_VAL,
2435d8647b79SSteffen Klassert xfrm_replay_state_esn_len(x->replay_esn),
24361d1e34ddSDavid S. Miller x->replay_esn);
2437d0fde795SDavid S. Miller } else {
24381d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_REPLAY_VAL, sizeof(x->replay),
24391d1e34ddSDavid S. Miller &x->replay);
2440d0fde795SDavid S. Miller }
24411d1e34ddSDavid S. Miller if (err)
24421d1e34ddSDavid S. Miller goto out_cancel;
2443de95c4a4SNicolas Dichtel err = nla_put_64bit(skb, XFRMA_LTIME_VAL, sizeof(x->curlft), &x->curlft,
2444de95c4a4SNicolas Dichtel XFRMA_PAD);
24451d1e34ddSDavid S. Miller if (err)
24461d1e34ddSDavid S. Miller goto out_cancel;
2447d8647b79SSteffen Klassert
24481d1e34ddSDavid S. Miller if (id->flags & XFRM_AE_RTHR) {
24491d1e34ddSDavid S. Miller err = nla_put_u32(skb, XFRMA_REPLAY_THRESH, x->replay_maxdiff);
24501d1e34ddSDavid S. Miller if (err)
24511d1e34ddSDavid S. Miller goto out_cancel;
24521d1e34ddSDavid S. Miller }
24531d1e34ddSDavid S. Miller if (id->flags & XFRM_AE_ETHR) {
24541d1e34ddSDavid S. Miller err = nla_put_u32(skb, XFRMA_ETIMER_THRESH,
24551d1e34ddSDavid S. Miller x->replay_maxage * 10 / HZ);
24561d1e34ddSDavid S. Miller if (err)
24571d1e34ddSDavid S. Miller goto out_cancel;
24581d1e34ddSDavid S. Miller }
24591d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &x->mark);
24601d1e34ddSDavid S. Miller if (err)
24611d1e34ddSDavid S. Miller goto out_cancel;
24626f26b61eSJamal Hadi Salim
24637e652640SSteffen Klassert err = xfrm_if_id_put(skb, x->if_id);
24647e652640SSteffen Klassert if (err)
24657e652640SSteffen Klassert goto out_cancel;
24667e652640SSteffen Klassert
2467053c095aSJohannes Berg nlmsg_end(skb, nlh);
2468053c095aSJohannes Berg return 0;
2469d51d081dSJamal Hadi Salim
24701d1e34ddSDavid S. Miller out_cancel:
24719825069dSThomas Graf nlmsg_cancel(skb, nlh);
24721d1e34ddSDavid S. Miller return err;
2473d51d081dSJamal Hadi Salim }
2474d51d081dSJamal Hadi Salim
xfrm_get_ae(struct sk_buff * skb,struct nlmsghdr * nlh,struct nlattr ** attrs,struct netlink_ext_ack * extack)247522e70050SChristoph Hellwig static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
24763bec6c3eSSabrina Dubroca struct nlattr **attrs, struct netlink_ext_ack *extack)
2477d51d081dSJamal Hadi Salim {
2478fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk);
2479d51d081dSJamal Hadi Salim struct xfrm_state *x;
2480d51d081dSJamal Hadi Salim struct sk_buff *r_skb;
2481d51d081dSJamal Hadi Salim int err;
2482d51d081dSJamal Hadi Salim struct km_event c;
24836f26b61eSJamal Hadi Salim u32 mark;
24846f26b61eSJamal Hadi Salim struct xfrm_mark m;
24857b67c857SThomas Graf struct xfrm_aevent_id *p = nlmsg_data(nlh);
2486d51d081dSJamal Hadi Salim struct xfrm_usersa_id *id = &p->sa_id;
2487d51d081dSJamal Hadi Salim
24886f26b61eSJamal Hadi Salim mark = xfrm_mark_get(attrs, &m);
24896f26b61eSJamal Hadi Salim
24906f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &id->daddr, id->spi, id->proto, id->family);
2491d8647b79SSteffen Klassert if (x == NULL)
2492d51d081dSJamal Hadi Salim return -ESRCH;
2493d8647b79SSteffen Klassert
2494d8647b79SSteffen Klassert r_skb = nlmsg_new(xfrm_aevent_msgsize(x), GFP_ATOMIC);
2495d8647b79SSteffen Klassert if (r_skb == NULL) {
2496d8647b79SSteffen Klassert xfrm_state_put(x);
2497d8647b79SSteffen Klassert return -ENOMEM;
2498d51d081dSJamal Hadi Salim }
2499d51d081dSJamal Hadi Salim
2500d51d081dSJamal Hadi Salim /*
2501d51d081dSJamal Hadi Salim * XXX: is this lock really needed - none of the other
2502d51d081dSJamal Hadi Salim * gets lock (the concern is things getting updated
2503d51d081dSJamal Hadi Salim * while we are still reading) - jhs
2504d51d081dSJamal Hadi Salim */
2505d51d081dSJamal Hadi Salim spin_lock_bh(&x->lock);
2506d51d081dSJamal Hadi Salim c.data.aevent = p->flags;
2507d51d081dSJamal Hadi Salim c.seq = nlh->nlmsg_seq;
250815e47304SEric W. Biederman c.portid = nlh->nlmsg_pid;
2509d51d081dSJamal Hadi Salim
25102fc5f83bSGustavo A. R. Silva err = build_aevent(r_skb, x, &c);
25112fc5f83bSGustavo A. R. Silva BUG_ON(err < 0);
25122fc5f83bSGustavo A. R. Silva
251315e47304SEric W. Biederman err = nlmsg_unicast(net->xfrm.nlsk, r_skb, NETLINK_CB(skb).portid);
2514d51d081dSJamal Hadi Salim spin_unlock_bh(&x->lock);
2515d51d081dSJamal Hadi Salim xfrm_state_put(x);
2516d51d081dSJamal Hadi Salim return err;
2517d51d081dSJamal Hadi Salim }
2518d51d081dSJamal Hadi Salim
xfrm_new_ae(struct sk_buff * skb,struct nlmsghdr * nlh,struct nlattr ** attrs,struct netlink_ext_ack * extack)251922e70050SChristoph Hellwig static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
25203bec6c3eSSabrina Dubroca struct nlattr **attrs, struct netlink_ext_ack *extack)
2521d51d081dSJamal Hadi Salim {
2522fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk);
2523d51d081dSJamal Hadi Salim struct xfrm_state *x;
2524d51d081dSJamal Hadi Salim struct km_event c;
2525d51d081dSJamal Hadi Salim int err = -EINVAL;
25266f26b61eSJamal Hadi Salim u32 mark = 0;
25276f26b61eSJamal Hadi Salim struct xfrm_mark m;
25287b67c857SThomas Graf struct xfrm_aevent_id *p = nlmsg_data(nlh);
25295424f32eSThomas Graf struct nlattr *rp = attrs[XFRMA_REPLAY_VAL];
2530d8647b79SSteffen Klassert struct nlattr *re = attrs[XFRMA_REPLAY_ESN_VAL];
25315424f32eSThomas Graf struct nlattr *lt = attrs[XFRMA_LTIME_VAL];
25324e077237SMichael Rossberg struct nlattr *et = attrs[XFRMA_ETIMER_THRESH];
25334e077237SMichael Rossberg struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH];
2534d51d081dSJamal Hadi Salim
2535643bc1a2SSabrina Dubroca if (!lt && !rp && !re && !et && !rt) {
2536643bc1a2SSabrina Dubroca NL_SET_ERR_MSG(extack, "Missing required attribute for AE");
2537d51d081dSJamal Hadi Salim return err;
2538643bc1a2SSabrina Dubroca }
2539d51d081dSJamal Hadi Salim
2540d51d081dSJamal Hadi Salim /* pedantic mode - thou shalt sayeth replaceth */
2541643bc1a2SSabrina Dubroca if (!(nlh->nlmsg_flags & NLM_F_REPLACE)) {
2542643bc1a2SSabrina Dubroca NL_SET_ERR_MSG(extack, "NLM_F_REPLACE flag is required");
2543d51d081dSJamal Hadi Salim return err;
2544643bc1a2SSabrina Dubroca }
2545d51d081dSJamal Hadi Salim
25466f26b61eSJamal Hadi Salim mark = xfrm_mark_get(attrs, &m);
25476f26b61eSJamal Hadi Salim
25486f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &p->sa_id.daddr, p->sa_id.spi, p->sa_id.proto, p->sa_id.family);
2549d51d081dSJamal Hadi Salim if (x == NULL)
2550d51d081dSJamal Hadi Salim return -ESRCH;
2551d51d081dSJamal Hadi Salim
2552643bc1a2SSabrina Dubroca if (x->km.state != XFRM_STATE_VALID) {
2553643bc1a2SSabrina Dubroca NL_SET_ERR_MSG(extack, "SA must be in VALID state");
2554d51d081dSJamal Hadi Salim goto out;
2555643bc1a2SSabrina Dubroca }
2556d51d081dSJamal Hadi Salim
2557643bc1a2SSabrina Dubroca err = xfrm_replay_verify_len(x->replay_esn, re, extack);
2558e2b19125SSteffen Klassert if (err)
2559e2b19125SSteffen Klassert goto out;
2560e2b19125SSteffen Klassert
2561d51d081dSJamal Hadi Salim spin_lock_bh(&x->lock);
2562e3ac104dSMathias Krause xfrm_update_ae_params(x, attrs, 1);
2563d51d081dSJamal Hadi Salim spin_unlock_bh(&x->lock);
2564d51d081dSJamal Hadi Salim
2565d51d081dSJamal Hadi Salim c.event = nlh->nlmsg_type;
2566d51d081dSJamal Hadi Salim c.seq = nlh->nlmsg_seq;
256715e47304SEric W. Biederman c.portid = nlh->nlmsg_pid;
2568d51d081dSJamal Hadi Salim c.data.aevent = XFRM_AE_CU;
2569d51d081dSJamal Hadi Salim km_state_notify(x, &c);
2570d51d081dSJamal Hadi Salim err = 0;
2571d51d081dSJamal Hadi Salim out:
2572d51d081dSJamal Hadi Salim xfrm_state_put(x);
2573d51d081dSJamal Hadi Salim return err;
2574d51d081dSJamal Hadi Salim }
2575d51d081dSJamal Hadi Salim
xfrm_flush_policy(struct sk_buff * skb,struct nlmsghdr * nlh,struct nlattr ** attrs,struct netlink_ext_ack * extack)257622e70050SChristoph Hellwig static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
25773bec6c3eSSabrina Dubroca struct nlattr **attrs,
25783bec6c3eSSabrina Dubroca struct netlink_ext_ack *extack)
25791da177e4SLinus Torvalds {
2580fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk);
258126b15dadSJamal Hadi Salim struct km_event c;
2582b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN;
2583f7b6983fSMasahide NAKAMURA int err;
258426b15dadSJamal Hadi Salim
2585fb7deabaSSabrina Dubroca err = copy_from_user_policy_type(&type, attrs, extack);
2586f7b6983fSMasahide NAKAMURA if (err)
2587f7b6983fSMasahide NAKAMURA return err;
2588f7b6983fSMasahide NAKAMURA
25892e71029eSTetsuo Handa err = xfrm_policy_flush(net, type, true);
25902f1eb65fSJamal Hadi Salim if (err) {
25912f1eb65fSJamal Hadi Salim if (err == -ESRCH) /* empty table */
25922f1eb65fSJamal Hadi Salim return 0;
2593069c474eSDavid S. Miller return err;
25942f1eb65fSJamal Hadi Salim }
25952f1eb65fSJamal Hadi Salim
2596f7b6983fSMasahide NAKAMURA c.data.type = type;
2597f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type;
259826b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq;
259915e47304SEric W. Biederman c.portid = nlh->nlmsg_pid;
26007067802eSAlexey Dobriyan c.net = net;
260126b15dadSJamal Hadi Salim km_policy_notify(NULL, 0, &c);
26021da177e4SLinus Torvalds return 0;
26031da177e4SLinus Torvalds }
26041da177e4SLinus Torvalds
xfrm_add_pol_expire(struct sk_buff * skb,struct nlmsghdr * nlh,struct nlattr ** attrs,struct netlink_ext_ack * extack)260522e70050SChristoph Hellwig static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
26063bec6c3eSSabrina Dubroca struct nlattr **attrs,
26073bec6c3eSSabrina Dubroca struct netlink_ext_ack *extack)
26086c5c8ca7SJamal Hadi Salim {
2609fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk);
26106c5c8ca7SJamal Hadi Salim struct xfrm_policy *xp;
26117b67c857SThomas Graf struct xfrm_user_polexpire *up = nlmsg_data(nlh);
26126c5c8ca7SJamal Hadi Salim struct xfrm_userpolicy_info *p = &up->pol;
2613b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN;
26146c5c8ca7SJamal Hadi Salim int err = -ENOENT;
2615295fae56SJamal Hadi Salim struct xfrm_mark m;
26167e652640SSteffen Klassert u32 if_id = 0;
26176c5c8ca7SJamal Hadi Salim
2618fb7deabaSSabrina Dubroca err = copy_from_user_policy_type(&type, attrs, extack);
2619f7b6983fSMasahide NAKAMURA if (err)
2620f7b6983fSMasahide NAKAMURA return err;
2621f7b6983fSMasahide NAKAMURA
262224fc544fSSabrina Dubroca err = verify_policy_dir(p->dir, extack);
2623c8bf4d04STimo Teräs if (err)
2624c8bf4d04STimo Teräs return err;
2625c8bf4d04STimo Teräs
26267e652640SSteffen Klassert if (attrs[XFRMA_IF_ID])
26277e652640SSteffen Klassert if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
26287e652640SSteffen Klassert
26294f47e8abSXin Long xfrm_mark_get(attrs, &m);
26304f47e8abSXin Long
26316c5c8ca7SJamal Hadi Salim if (p->index)
26324f47e8abSXin Long xp = xfrm_policy_byid(net, &m, if_id, type, p->dir, p->index,
26334f47e8abSXin Long 0, &err);
26346c5c8ca7SJamal Hadi Salim else {
26355424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX];
263603e1ad7bSPaul Moore struct xfrm_sec_ctx *ctx;
26376c5c8ca7SJamal Hadi Salim
263808a717e4SSabrina Dubroca err = verify_sec_ctx_len(attrs, extack);
26396c5c8ca7SJamal Hadi Salim if (err)
26406c5c8ca7SJamal Hadi Salim return err;
26416c5c8ca7SJamal Hadi Salim
26422c8dd116SDenis V. Lunev ctx = NULL;
26436c5c8ca7SJamal Hadi Salim if (rt) {
26445424f32eSThomas Graf struct xfrm_user_sec_ctx *uctx = nla_data(rt);
26456c5c8ca7SJamal Hadi Salim
264652a4c640SNikolay Aleksandrov err = security_xfrm_policy_alloc(&ctx, uctx, GFP_KERNEL);
264703e1ad7bSPaul Moore if (err)
26486c5c8ca7SJamal Hadi Salim return err;
26492c8dd116SDenis V. Lunev }
26504f47e8abSXin Long xp = xfrm_policy_bysel_ctx(net, &m, if_id, type, p->dir,
26516f26b61eSJamal Hadi Salim &p->sel, ctx, 0, &err);
265203e1ad7bSPaul Moore security_xfrm_policy_free(ctx);
26536c5c8ca7SJamal Hadi Salim }
26546c5c8ca7SJamal Hadi Salim if (xp == NULL)
2655ef41aaa0SEric Paris return -ENOENT;
265603e1ad7bSPaul Moore
2657ea2dea9dSTimo Teräs if (unlikely(xp->walk.dead))
26586c5c8ca7SJamal Hadi Salim goto out;
26596c5c8ca7SJamal Hadi Salim
26606c5c8ca7SJamal Hadi Salim err = 0;
26616c5c8ca7SJamal Hadi Salim if (up->hard) {
26626c5c8ca7SJamal Hadi Salim xfrm_policy_delete(xp, p->dir);
26632e71029eSTetsuo Handa xfrm_audit_policy_delete(xp, 1, true);
26646c5c8ca7SJamal Hadi Salim }
2665c6bb8136SEric W. Biederman km_policy_expired(xp, p->dir, up->hard, nlh->nlmsg_pid);
26666c5c8ca7SJamal Hadi Salim
26676c5c8ca7SJamal Hadi Salim out:
26686c5c8ca7SJamal Hadi Salim xfrm_pol_put(xp);
26696c5c8ca7SJamal Hadi Salim return err;
26706c5c8ca7SJamal Hadi Salim }
26716c5c8ca7SJamal Hadi Salim
xfrm_add_sa_expire(struct sk_buff * skb,struct nlmsghdr * nlh,struct nlattr ** attrs,struct netlink_ext_ack * extack)267222e70050SChristoph Hellwig static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
26733bec6c3eSSabrina Dubroca struct nlattr **attrs,
26743bec6c3eSSabrina Dubroca struct netlink_ext_ack *extack)
267553bc6b4dSJamal Hadi Salim {
2676fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk);
267753bc6b4dSJamal Hadi Salim struct xfrm_state *x;
267853bc6b4dSJamal Hadi Salim int err;
26797b67c857SThomas Graf struct xfrm_user_expire *ue = nlmsg_data(nlh);
268053bc6b4dSJamal Hadi Salim struct xfrm_usersa_info *p = &ue->state;
26816f26b61eSJamal Hadi Salim struct xfrm_mark m;
2682928497f0SNicolas Dichtel u32 mark = xfrm_mark_get(attrs, &m);
268353bc6b4dSJamal Hadi Salim
26846f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &p->id.daddr, p->id.spi, p->id.proto, p->family);
268553bc6b4dSJamal Hadi Salim
26863a765aa5SDavid S. Miller err = -ENOENT;
268753bc6b4dSJamal Hadi Salim if (x == NULL)
268853bc6b4dSJamal Hadi Salim return err;
268953bc6b4dSJamal Hadi Salim
269053bc6b4dSJamal Hadi Salim spin_lock_bh(&x->lock);
26913a765aa5SDavid S. Miller err = -EINVAL;
2692a25b19f3SSabrina Dubroca if (x->km.state != XFRM_STATE_VALID) {
2693a25b19f3SSabrina Dubroca NL_SET_ERR_MSG(extack, "SA must be in VALID state");
269453bc6b4dSJamal Hadi Salim goto out;
2695a25b19f3SSabrina Dubroca }
2696a25b19f3SSabrina Dubroca
2697c6bb8136SEric W. Biederman km_state_expired(x, ue->hard, nlh->nlmsg_pid);
269853bc6b4dSJamal Hadi Salim
2699161a09e7SJoy Latten if (ue->hard) {
270053bc6b4dSJamal Hadi Salim __xfrm_state_delete(x);
27012e71029eSTetsuo Handa xfrm_audit_state_delete(x, 1, true);
2702161a09e7SJoy Latten }
27033a765aa5SDavid S. Miller err = 0;
270453bc6b4dSJamal Hadi Salim out:
270553bc6b4dSJamal Hadi Salim spin_unlock_bh(&x->lock);
270653bc6b4dSJamal Hadi Salim xfrm_state_put(x);
270753bc6b4dSJamal Hadi Salim return err;
270853bc6b4dSJamal Hadi Salim }
270953bc6b4dSJamal Hadi Salim
xfrm_add_acquire(struct sk_buff * skb,struct nlmsghdr * nlh,struct nlattr ** attrs,struct netlink_ext_ack * extack)271022e70050SChristoph Hellwig static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh,
27113bec6c3eSSabrina Dubroca struct nlattr **attrs,
27123bec6c3eSSabrina Dubroca struct netlink_ext_ack *extack)
2713980ebd25SJamal Hadi Salim {
2714fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk);
2715980ebd25SJamal Hadi Salim struct xfrm_policy *xp;
2716980ebd25SJamal Hadi Salim struct xfrm_user_tmpl *ut;
2717980ebd25SJamal Hadi Salim int i;
27185424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_TMPL];
27196f26b61eSJamal Hadi Salim struct xfrm_mark mark;
2720980ebd25SJamal Hadi Salim
27217b67c857SThomas Graf struct xfrm_user_acquire *ua = nlmsg_data(nlh);
2722fc34acd3SAlexey Dobriyan struct xfrm_state *x = xfrm_state_alloc(net);
2723980ebd25SJamal Hadi Salim int err = -ENOMEM;
2724980ebd25SJamal Hadi Salim
2725980ebd25SJamal Hadi Salim if (!x)
2726d8eb9307SIlpo Järvinen goto nomem;
2727980ebd25SJamal Hadi Salim
27286f26b61eSJamal Hadi Salim xfrm_mark_get(attrs, &mark);
27296f26b61eSJamal Hadi Salim
2730ec2b4f01SSabrina Dubroca err = verify_newpolicy_info(&ua->policy, extack);
2731d8eb9307SIlpo Järvinen if (err)
273273efc324SVegard Nossum goto free_state;
273308a717e4SSabrina Dubroca err = verify_sec_ctx_len(attrs, extack);
2734a1a7e3a3SXin Long if (err)
2735a1a7e3a3SXin Long goto free_state;
2736980ebd25SJamal Hadi Salim
2737980ebd25SJamal Hadi Salim /* build an XP */
2738fb7deabaSSabrina Dubroca xp = xfrm_policy_construct(net, &ua->policy, attrs, &err, extack);
2739d8eb9307SIlpo Järvinen if (!xp)
2740d8eb9307SIlpo Järvinen goto free_state;
2741980ebd25SJamal Hadi Salim
2742980ebd25SJamal Hadi Salim memcpy(&x->id, &ua->id, sizeof(ua->id));
2743980ebd25SJamal Hadi Salim memcpy(&x->props.saddr, &ua->saddr, sizeof(ua->saddr));
2744980ebd25SJamal Hadi Salim memcpy(&x->sel, &ua->sel, sizeof(ua->sel));
27456f26b61eSJamal Hadi Salim xp->mark.m = x->mark.m = mark.m;
27466f26b61eSJamal Hadi Salim xp->mark.v = x->mark.v = mark.v;
27475424f32eSThomas Graf ut = nla_data(rt);
2748980ebd25SJamal Hadi Salim /* extract the templates and for each call km_key */
2749980ebd25SJamal Hadi Salim for (i = 0; i < xp->xfrm_nr; i++, ut++) {
2750980ebd25SJamal Hadi Salim struct xfrm_tmpl *t = &xp->xfrm_vec[i];
2751980ebd25SJamal Hadi Salim memcpy(&x->id, &t->id, sizeof(x->id));
2752980ebd25SJamal Hadi Salim x->props.mode = t->mode;
2753980ebd25SJamal Hadi Salim x->props.reqid = t->reqid;
2754980ebd25SJamal Hadi Salim x->props.family = ut->family;
2755980ebd25SJamal Hadi Salim t->aalgos = ua->aalgos;
2756980ebd25SJamal Hadi Salim t->ealgos = ua->ealgos;
2757980ebd25SJamal Hadi Salim t->calgos = ua->calgos;
2758980ebd25SJamal Hadi Salim err = km_query(x, t, xp);
2759980ebd25SJamal Hadi Salim
2760980ebd25SJamal Hadi Salim }
2761980ebd25SJamal Hadi Salim
27624a135e53SMathias Krause xfrm_state_free(x);
2763980ebd25SJamal Hadi Salim kfree(xp);
2764980ebd25SJamal Hadi Salim
2765980ebd25SJamal Hadi Salim return 0;
2766d8eb9307SIlpo Järvinen
2767d8eb9307SIlpo Järvinen free_state:
27684a135e53SMathias Krause xfrm_state_free(x);
2769d8eb9307SIlpo Järvinen nomem:
2770d8eb9307SIlpo Järvinen return err;
2771980ebd25SJamal Hadi Salim }
2772980ebd25SJamal Hadi Salim
27735c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE
copy_from_user_migrate(struct xfrm_migrate * ma,struct xfrm_kmaddress * k,struct nlattr ** attrs,int * num,struct netlink_ext_ack * extack)27745c79de6eSShinta Sugimoto static int copy_from_user_migrate(struct xfrm_migrate *ma,
277513c1d189SArnaud Ebalard struct xfrm_kmaddress *k,
2776bd122403SSabrina Dubroca struct nlattr **attrs, int *num,
2777bd122403SSabrina Dubroca struct netlink_ext_ack *extack)
27785c79de6eSShinta Sugimoto {
27795424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_MIGRATE];
27805c79de6eSShinta Sugimoto struct xfrm_user_migrate *um;
27815c79de6eSShinta Sugimoto int i, num_migrate;
27825c79de6eSShinta Sugimoto
278313c1d189SArnaud Ebalard if (k != NULL) {
278413c1d189SArnaud Ebalard struct xfrm_user_kmaddress *uk;
278513c1d189SArnaud Ebalard
278613c1d189SArnaud Ebalard uk = nla_data(attrs[XFRMA_KMADDRESS]);
278713c1d189SArnaud Ebalard memcpy(&k->local, &uk->local, sizeof(k->local));
278813c1d189SArnaud Ebalard memcpy(&k->remote, &uk->remote, sizeof(k->remote));
278913c1d189SArnaud Ebalard k->family = uk->family;
279013c1d189SArnaud Ebalard k->reserved = uk->reserved;
279113c1d189SArnaud Ebalard }
279213c1d189SArnaud Ebalard
27935424f32eSThomas Graf um = nla_data(rt);
27945424f32eSThomas Graf num_migrate = nla_len(rt) / sizeof(*um);
27955c79de6eSShinta Sugimoto
2796bd122403SSabrina Dubroca if (num_migrate <= 0 || num_migrate > XFRM_MAX_DEPTH) {
2797bd122403SSabrina Dubroca NL_SET_ERR_MSG(extack, "Invalid number of SAs to migrate, must be 0 < num <= XFRM_MAX_DEPTH (6)");
27985c79de6eSShinta Sugimoto return -EINVAL;
2799bd122403SSabrina Dubroca }
28005c79de6eSShinta Sugimoto
28015c79de6eSShinta Sugimoto for (i = 0; i < num_migrate; i++, um++, ma++) {
28025c79de6eSShinta Sugimoto memcpy(&ma->old_daddr, &um->old_daddr, sizeof(ma->old_daddr));
28035c79de6eSShinta Sugimoto memcpy(&ma->old_saddr, &um->old_saddr, sizeof(ma->old_saddr));
28045c79de6eSShinta Sugimoto memcpy(&ma->new_daddr, &um->new_daddr, sizeof(ma->new_daddr));
28055c79de6eSShinta Sugimoto memcpy(&ma->new_saddr, &um->new_saddr, sizeof(ma->new_saddr));
28065c79de6eSShinta Sugimoto
28075c79de6eSShinta Sugimoto ma->proto = um->proto;
28085c79de6eSShinta Sugimoto ma->mode = um->mode;
28095c79de6eSShinta Sugimoto ma->reqid = um->reqid;
28105c79de6eSShinta Sugimoto
28115c79de6eSShinta Sugimoto ma->old_family = um->old_family;
28125c79de6eSShinta Sugimoto ma->new_family = um->new_family;
28135c79de6eSShinta Sugimoto }
28145c79de6eSShinta Sugimoto
28155c79de6eSShinta Sugimoto *num = i;
28165c79de6eSShinta Sugimoto return 0;
28175c79de6eSShinta Sugimoto }
28185c79de6eSShinta Sugimoto
xfrm_do_migrate(struct sk_buff * skb,struct nlmsghdr * nlh,struct nlattr ** attrs,struct netlink_ext_ack * extack)28195c79de6eSShinta Sugimoto static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
28203bec6c3eSSabrina Dubroca struct nlattr **attrs, struct netlink_ext_ack *extack)
28215c79de6eSShinta Sugimoto {
28227b67c857SThomas Graf struct xfrm_userpolicy_id *pi = nlmsg_data(nlh);
28235c79de6eSShinta Sugimoto struct xfrm_migrate m[XFRM_MAX_DEPTH];
282413c1d189SArnaud Ebalard struct xfrm_kmaddress km, *kmp;
28255c79de6eSShinta Sugimoto u8 type;
28265c79de6eSShinta Sugimoto int err;
28275c79de6eSShinta Sugimoto int n = 0;
28288d549c4fSFan Du struct net *net = sock_net(skb->sk);
28294ab47d47SAntony Antony struct xfrm_encap_tmpl *encap = NULL;
2830c1aca308SYan Yan u32 if_id = 0;
28315c79de6eSShinta Sugimoto
2832bd122403SSabrina Dubroca if (!attrs[XFRMA_MIGRATE]) {
2833bd122403SSabrina Dubroca NL_SET_ERR_MSG(extack, "Missing required MIGRATE attribute");
2834cf5cb79fSThomas Graf return -EINVAL;
2835bd122403SSabrina Dubroca }
28365c79de6eSShinta Sugimoto
283713c1d189SArnaud Ebalard kmp = attrs[XFRMA_KMADDRESS] ? &km : NULL;
283813c1d189SArnaud Ebalard
2839fb7deabaSSabrina Dubroca err = copy_from_user_policy_type(&type, attrs, extack);
28405c79de6eSShinta Sugimoto if (err)
28415c79de6eSShinta Sugimoto return err;
28425c79de6eSShinta Sugimoto
2843bd122403SSabrina Dubroca err = copy_from_user_migrate(m, kmp, attrs, &n, extack);
28445c79de6eSShinta Sugimoto if (err)
28455c79de6eSShinta Sugimoto return err;
28465c79de6eSShinta Sugimoto
28475c79de6eSShinta Sugimoto if (!n)
28485c79de6eSShinta Sugimoto return 0;
28495c79de6eSShinta Sugimoto
28504ab47d47SAntony Antony if (attrs[XFRMA_ENCAP]) {
28514ab47d47SAntony Antony encap = kmemdup(nla_data(attrs[XFRMA_ENCAP]),
28524ab47d47SAntony Antony sizeof(*encap), GFP_KERNEL);
28534ab47d47SAntony Antony if (!encap)
28544ac7a6eeSZheng Yongjun return -ENOMEM;
28555c79de6eSShinta Sugimoto }
28564ab47d47SAntony Antony
2857c1aca308SYan Yan if (attrs[XFRMA_IF_ID])
2858c1aca308SYan Yan if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
2859c1aca308SYan Yan
2860bd122403SSabrina Dubroca err = xfrm_migrate(&pi->sel, pi->dir, type, m, n, kmp, net, encap,
2861bd122403SSabrina Dubroca if_id, extack);
28624ab47d47SAntony Antony
28634ab47d47SAntony Antony kfree(encap);
28644ab47d47SAntony Antony
28654ab47d47SAntony Antony return err;
28664ab47d47SAntony Antony }
28675c79de6eSShinta Sugimoto #else
xfrm_do_migrate(struct sk_buff * skb,struct nlmsghdr * nlh,struct nlattr ** attrs,struct netlink_ext_ack * extack)28685c79de6eSShinta Sugimoto static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
28693bec6c3eSSabrina Dubroca struct nlattr **attrs, struct netlink_ext_ack *extack)
28705c79de6eSShinta Sugimoto {
28715c79de6eSShinta Sugimoto return -ENOPROTOOPT;
28725c79de6eSShinta Sugimoto }
28735c79de6eSShinta Sugimoto #endif
28745c79de6eSShinta Sugimoto
28755c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE
copy_to_user_migrate(const struct xfrm_migrate * m,struct sk_buff * skb)2876183cad12SDavid S. Miller static int copy_to_user_migrate(const struct xfrm_migrate *m, struct sk_buff *skb)
28775c79de6eSShinta Sugimoto {
28785c79de6eSShinta Sugimoto struct xfrm_user_migrate um;
28795c79de6eSShinta Sugimoto
28805c79de6eSShinta Sugimoto memset(&um, 0, sizeof(um));
28815c79de6eSShinta Sugimoto um.proto = m->proto;
28825c79de6eSShinta Sugimoto um.mode = m->mode;
28835c79de6eSShinta Sugimoto um.reqid = m->reqid;
28845c79de6eSShinta Sugimoto um.old_family = m->old_family;
28855c79de6eSShinta Sugimoto memcpy(&um.old_daddr, &m->old_daddr, sizeof(um.old_daddr));
28865c79de6eSShinta Sugimoto memcpy(&um.old_saddr, &m->old_saddr, sizeof(um.old_saddr));
28875c79de6eSShinta Sugimoto um.new_family = m->new_family;
28885c79de6eSShinta Sugimoto memcpy(&um.new_daddr, &m->new_daddr, sizeof(um.new_daddr));
28895c79de6eSShinta Sugimoto memcpy(&um.new_saddr, &m->new_saddr, sizeof(um.new_saddr));
28905c79de6eSShinta Sugimoto
2891c0144beaSThomas Graf return nla_put(skb, XFRMA_MIGRATE, sizeof(um), &um);
28925c79de6eSShinta Sugimoto }
28935c79de6eSShinta Sugimoto
copy_to_user_kmaddress(const struct xfrm_kmaddress * k,struct sk_buff * skb)2894183cad12SDavid S. Miller static int copy_to_user_kmaddress(const struct xfrm_kmaddress *k, struct sk_buff *skb)
289513c1d189SArnaud Ebalard {
289613c1d189SArnaud Ebalard struct xfrm_user_kmaddress uk;
289713c1d189SArnaud Ebalard
289813c1d189SArnaud Ebalard memset(&uk, 0, sizeof(uk));
289913c1d189SArnaud Ebalard uk.family = k->family;
290013c1d189SArnaud Ebalard uk.reserved = k->reserved;
290113c1d189SArnaud Ebalard memcpy(&uk.local, &k->local, sizeof(uk.local));
2902a1caa322SArnaud Ebalard memcpy(&uk.remote, &k->remote, sizeof(uk.remote));
290313c1d189SArnaud Ebalard
290413c1d189SArnaud Ebalard return nla_put(skb, XFRMA_KMADDRESS, sizeof(uk), &uk);
290513c1d189SArnaud Ebalard }
290613c1d189SArnaud Ebalard
xfrm_migrate_msgsize(int num_migrate,int with_kma,int with_encp)2907a1b831f2SAlexey Dobriyan static inline unsigned int xfrm_migrate_msgsize(int num_migrate, int with_kma,
29088bafd730SAntony Antony int with_encp)
29097deb2264SThomas Graf {
29107deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_userpolicy_id))
291113c1d189SArnaud Ebalard + (with_kma ? nla_total_size(sizeof(struct xfrm_kmaddress)) : 0)
29128bafd730SAntony Antony + (with_encp ? nla_total_size(sizeof(struct xfrm_encap_tmpl)) : 0)
29137deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_migrate) * num_migrate)
29147deb2264SThomas Graf + userpolicy_type_attrsize();
29157deb2264SThomas Graf }
29167deb2264SThomas Graf
build_migrate(struct sk_buff * skb,const struct xfrm_migrate * m,int num_migrate,const struct xfrm_kmaddress * k,const struct xfrm_selector * sel,const struct xfrm_encap_tmpl * encap,u8 dir,u8 type)2917183cad12SDavid S. Miller static int build_migrate(struct sk_buff *skb, const struct xfrm_migrate *m,
2918183cad12SDavid S. Miller int num_migrate, const struct xfrm_kmaddress *k,
29198bafd730SAntony Antony const struct xfrm_selector *sel,
29208bafd730SAntony Antony const struct xfrm_encap_tmpl *encap, u8 dir, u8 type)
29215c79de6eSShinta Sugimoto {
2922183cad12SDavid S. Miller const struct xfrm_migrate *mp;
29235c79de6eSShinta Sugimoto struct xfrm_userpolicy_id *pol_id;
29245c79de6eSShinta Sugimoto struct nlmsghdr *nlh;
29251d1e34ddSDavid S. Miller int i, err;
29265c79de6eSShinta Sugimoto
292779b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_MIGRATE, sizeof(*pol_id), 0);
292879b8b7f4SThomas Graf if (nlh == NULL)
292979b8b7f4SThomas Graf return -EMSGSIZE;
29305c79de6eSShinta Sugimoto
29317b67c857SThomas Graf pol_id = nlmsg_data(nlh);
29325c79de6eSShinta Sugimoto /* copy data from selector, dir, and type to the pol_id */
29335c79de6eSShinta Sugimoto memset(pol_id, 0, sizeof(*pol_id));
29345c79de6eSShinta Sugimoto memcpy(&pol_id->sel, sel, sizeof(pol_id->sel));
29355c79de6eSShinta Sugimoto pol_id->dir = dir;
29365c79de6eSShinta Sugimoto
29371d1e34ddSDavid S. Miller if (k != NULL) {
29381d1e34ddSDavid S. Miller err = copy_to_user_kmaddress(k, skb);
29391d1e34ddSDavid S. Miller if (err)
29401d1e34ddSDavid S. Miller goto out_cancel;
29411d1e34ddSDavid S. Miller }
29428bafd730SAntony Antony if (encap) {
29438bafd730SAntony Antony err = nla_put(skb, XFRMA_ENCAP, sizeof(*encap), encap);
29448bafd730SAntony Antony if (err)
29458bafd730SAntony Antony goto out_cancel;
29468bafd730SAntony Antony }
29471d1e34ddSDavid S. Miller err = copy_to_user_policy_type(type, skb);
29481d1e34ddSDavid S. Miller if (err)
29491d1e34ddSDavid S. Miller goto out_cancel;
29505c79de6eSShinta Sugimoto for (i = 0, mp = m ; i < num_migrate; i++, mp++) {
29511d1e34ddSDavid S. Miller err = copy_to_user_migrate(mp, skb);
29521d1e34ddSDavid S. Miller if (err)
29531d1e34ddSDavid S. Miller goto out_cancel;
29545c79de6eSShinta Sugimoto }
29555c79de6eSShinta Sugimoto
2956053c095aSJohannes Berg nlmsg_end(skb, nlh);
2957053c095aSJohannes Berg return 0;
29581d1e34ddSDavid S. Miller
29591d1e34ddSDavid S. Miller out_cancel:
29609825069dSThomas Graf nlmsg_cancel(skb, nlh);
29611d1e34ddSDavid S. Miller return err;
29625c79de6eSShinta Sugimoto }
29635c79de6eSShinta Sugimoto
xfrm_send_migrate(const struct xfrm_selector * sel,u8 dir,u8 type,const struct xfrm_migrate * m,int num_migrate,const struct xfrm_kmaddress * k,const struct xfrm_encap_tmpl * encap)2964183cad12SDavid S. Miller static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
2965183cad12SDavid S. Miller const struct xfrm_migrate *m, int num_migrate,
29668bafd730SAntony Antony const struct xfrm_kmaddress *k,
29678bafd730SAntony Antony const struct xfrm_encap_tmpl *encap)
29685c79de6eSShinta Sugimoto {
2969a6483b79SAlexey Dobriyan struct net *net = &init_net;
29705c79de6eSShinta Sugimoto struct sk_buff *skb;
29712fc5f83bSGustavo A. R. Silva int err;
29725c79de6eSShinta Sugimoto
29738bafd730SAntony Antony skb = nlmsg_new(xfrm_migrate_msgsize(num_migrate, !!k, !!encap),
29748bafd730SAntony Antony GFP_ATOMIC);
29755c79de6eSShinta Sugimoto if (skb == NULL)
29765c79de6eSShinta Sugimoto return -ENOMEM;
29775c79de6eSShinta Sugimoto
29785c79de6eSShinta Sugimoto /* build migrate */
29792fc5f83bSGustavo A. R. Silva err = build_migrate(skb, m, num_migrate, k, sel, encap, dir, type);
29802fc5f83bSGustavo A. R. Silva BUG_ON(err < 0);
29815c79de6eSShinta Sugimoto
298221ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_MIGRATE);
29835c79de6eSShinta Sugimoto }
29845c79de6eSShinta Sugimoto #else
xfrm_send_migrate(const struct xfrm_selector * sel,u8 dir,u8 type,const struct xfrm_migrate * m,int num_migrate,const struct xfrm_kmaddress * k,const struct xfrm_encap_tmpl * encap)2985183cad12SDavid S. Miller static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
2986183cad12SDavid S. Miller const struct xfrm_migrate *m, int num_migrate,
29878bafd730SAntony Antony const struct xfrm_kmaddress *k,
29888bafd730SAntony Antony const struct xfrm_encap_tmpl *encap)
29895c79de6eSShinta Sugimoto {
29905c79de6eSShinta Sugimoto return -ENOPROTOOPT;
29915c79de6eSShinta Sugimoto }
29925c79de6eSShinta Sugimoto #endif
2993d51d081dSJamal Hadi Salim
2994a7bd9a45SThomas Graf #define XMSGSIZE(type) sizeof(struct type)
2995492b558bSThomas Graf
29965461fc0cSDmitry Safonov const int xfrm_msg_min[XFRM_NR_MSGTYPES] = {
299766f9a259SDavid S. Miller [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_info),
2998492b558bSThomas Graf [XFRM_MSG_DELSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id),
2999492b558bSThomas Graf [XFRM_MSG_GETSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id),
3000492b558bSThomas Graf [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_info),
3001492b558bSThomas Graf [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id),
3002492b558bSThomas Graf [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id),
3003492b558bSThomas Graf [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userspi_info),
3004980ebd25SJamal Hadi Salim [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_acquire),
300553bc6b4dSJamal Hadi Salim [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_expire),
3006492b558bSThomas Graf [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_info),
300766f9a259SDavid S. Miller [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_info),
30086c5c8ca7SJamal Hadi Salim [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_polexpire),
3009492b558bSThomas Graf [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_flush),
3010a7bd9a45SThomas Graf [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = 0,
3011d51d081dSJamal Hadi Salim [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id),
3012d51d081dSJamal Hadi Salim [XFRM_MSG_GETAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id),
301397a64b45SMasahide NAKAMURA [XFRM_MSG_REPORT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report),
30145c79de6eSShinta Sugimoto [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id),
3015a7bd9a45SThomas Graf [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = sizeof(u32),
3016880a6fabSChristophe Gouault [XFRM_MSG_NEWSPDINFO - XFRM_MSG_BASE] = sizeof(u32),
3017a7bd9a45SThomas Graf [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32),
30182d151d39SSteffen Klassert [XFRM_MSG_SETDEFAULT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_default),
30192d151d39SSteffen Klassert [XFRM_MSG_GETDEFAULT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_default),
30201da177e4SLinus Torvalds };
30215461fc0cSDmitry Safonov EXPORT_SYMBOL_GPL(xfrm_msg_min);
30221da177e4SLinus Torvalds
3023492b558bSThomas Graf #undef XMSGSIZE
3024492b558bSThomas Graf
30255106f4a8SDmitry Safonov const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
3026c28e9304Sjamal [XFRMA_SA] = { .len = sizeof(struct xfrm_usersa_info)},
3027c28e9304Sjamal [XFRMA_POLICY] = { .len = sizeof(struct xfrm_userpolicy_info)},
3028c28e9304Sjamal [XFRMA_LASTUSED] = { .type = NLA_U64},
3029c28e9304Sjamal [XFRMA_ALG_AUTH_TRUNC] = { .len = sizeof(struct xfrm_algo_auth)},
30301a6509d9SHerbert Xu [XFRMA_ALG_AEAD] = { .len = sizeof(struct xfrm_algo_aead) },
3031cf5cb79fSThomas Graf [XFRMA_ALG_AUTH] = { .len = sizeof(struct xfrm_algo) },
3032cf5cb79fSThomas Graf [XFRMA_ALG_CRYPT] = { .len = sizeof(struct xfrm_algo) },
3033cf5cb79fSThomas Graf [XFRMA_ALG_COMP] = { .len = sizeof(struct xfrm_algo) },
3034cf5cb79fSThomas Graf [XFRMA_ENCAP] = { .len = sizeof(struct xfrm_encap_tmpl) },
3035cf5cb79fSThomas Graf [XFRMA_TMPL] = { .len = sizeof(struct xfrm_user_tmpl) },
3036d1e0e61dSLin Ma [XFRMA_SEC_CTX] = { .len = sizeof(struct xfrm_user_sec_ctx) },
3037cf5cb79fSThomas Graf [XFRMA_LTIME_VAL] = { .len = sizeof(struct xfrm_lifetime_cur) },
3038cf5cb79fSThomas Graf [XFRMA_REPLAY_VAL] = { .len = sizeof(struct xfrm_replay_state) },
3039cf5cb79fSThomas Graf [XFRMA_REPLAY_THRESH] = { .type = NLA_U32 },
3040cf5cb79fSThomas Graf [XFRMA_ETIMER_THRESH] = { .type = NLA_U32 },
3041cf5cb79fSThomas Graf [XFRMA_SRCADDR] = { .len = sizeof(xfrm_address_t) },
3042cf5cb79fSThomas Graf [XFRMA_COADDR] = { .len = sizeof(xfrm_address_t) },
3043cf5cb79fSThomas Graf [XFRMA_POLICY_TYPE] = { .len = sizeof(struct xfrm_userpolicy_type)},
3044cf5cb79fSThomas Graf [XFRMA_MIGRATE] = { .len = sizeof(struct xfrm_user_migrate) },
304513c1d189SArnaud Ebalard [XFRMA_KMADDRESS] = { .len = sizeof(struct xfrm_user_kmaddress) },
30466f26b61eSJamal Hadi Salim [XFRMA_MARK] = { .len = sizeof(struct xfrm_mark) },
304735d2856bSMartin Willi [XFRMA_TFCPAD] = { .type = NLA_U32 },
3048d8647b79SSteffen Klassert [XFRMA_REPLAY_ESN_VAL] = { .len = sizeof(struct xfrm_replay_state_esn) },
3049a947b0a9SNicolas Dichtel [XFRMA_SA_EXTRA_FLAGS] = { .type = NLA_U32 },
3050d3623099SNicolas Dichtel [XFRMA_PROTO] = { .type = NLA_U8 },
3051870a2df4SNicolas Dichtel [XFRMA_ADDRESS_FILTER] = { .len = sizeof(struct xfrm_address_filter) },
3052d77e38e6SSteffen Klassert [XFRMA_OFFLOAD_DEV] = { .len = sizeof(struct xfrm_user_offload) },
30539b42c1f1SSteffen Klassert [XFRMA_SET_MARK] = { .type = NLA_U32 },
30549b42c1f1SSteffen Klassert [XFRMA_SET_MARK_MASK] = { .type = NLA_U32 },
30557e652640SSteffen Klassert [XFRMA_IF_ID] = { .type = NLA_U32 },
30565e242470SLin Ma [XFRMA_MTIMER_THRESH] = { .type = NLA_U32 },
3057cf5cb79fSThomas Graf };
30585106f4a8SDmitry Safonov EXPORT_SYMBOL_GPL(xfrma_policy);
3059cf5cb79fSThomas Graf
3060880a6fabSChristophe Gouault static const struct nla_policy xfrma_spd_policy[XFRMA_SPD_MAX+1] = {
3061880a6fabSChristophe Gouault [XFRMA_SPD_IPV4_HTHRESH] = { .len = sizeof(struct xfrmu_spdhthresh) },
3062880a6fabSChristophe Gouault [XFRMA_SPD_IPV6_HTHRESH] = { .len = sizeof(struct xfrmu_spdhthresh) },
3063880a6fabSChristophe Gouault };
3064880a6fabSChristophe Gouault
306505600a79SMathias Krause static const struct xfrm_link {
30663bec6c3eSSabrina Dubroca int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **,
30673bec6c3eSSabrina Dubroca struct netlink_ext_ack *);
30681137b5e2SHerbert Xu int (*start)(struct netlink_callback *);
30691da177e4SLinus Torvalds int (*dump)(struct sk_buff *, struct netlink_callback *);
30704c563f76STimo Teras int (*done)(struct netlink_callback *);
3071880a6fabSChristophe Gouault const struct nla_policy *nla_pol;
3072880a6fabSChristophe Gouault int nla_max;
3073492b558bSThomas Graf } xfrm_dispatch[XFRM_NR_MSGTYPES] = {
3074492b558bSThomas Graf [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa },
3075492b558bSThomas Graf [XFRM_MSG_DELSA - XFRM_MSG_BASE] = { .doit = xfrm_del_sa },
3076492b558bSThomas Graf [XFRM_MSG_GETSA - XFRM_MSG_BASE] = { .doit = xfrm_get_sa,
30774c563f76STimo Teras .dump = xfrm_dump_sa,
30784c563f76STimo Teras .done = xfrm_dump_sa_done },
3079492b558bSThomas Graf [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy },
3080492b558bSThomas Graf [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy },
3081492b558bSThomas Graf [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy,
30821137b5e2SHerbert Xu .start = xfrm_dump_policy_start,
30834c563f76STimo Teras .dump = xfrm_dump_policy,
30844c563f76STimo Teras .done = xfrm_dump_policy_done },
3085492b558bSThomas Graf [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = { .doit = xfrm_alloc_userspi },
3086980ebd25SJamal Hadi Salim [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_acquire },
308753bc6b4dSJamal Hadi Salim [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_sa_expire },
3088492b558bSThomas Graf [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy },
3089492b558bSThomas Graf [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa },
30906c5c8ca7SJamal Hadi Salim [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_pol_expire},
3091492b558bSThomas Graf [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = { .doit = xfrm_flush_sa },
3092492b558bSThomas Graf [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_flush_policy },
3093d51d081dSJamal Hadi Salim [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = { .doit = xfrm_new_ae },
3094d51d081dSJamal Hadi Salim [XFRM_MSG_GETAE - XFRM_MSG_BASE] = { .doit = xfrm_get_ae },
30955c79de6eSShinta Sugimoto [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate },
309628d8909bSJamal Hadi Salim [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_sadinfo },
3097880a6fabSChristophe Gouault [XFRM_MSG_NEWSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_set_spdinfo,
3098880a6fabSChristophe Gouault .nla_pol = xfrma_spd_policy,
3099880a6fabSChristophe Gouault .nla_max = XFRMA_SPD_MAX },
3100ecfd6b18SJamal Hadi Salim [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo },
31012d151d39SSteffen Klassert [XFRM_MSG_SETDEFAULT - XFRM_MSG_BASE] = { .doit = xfrm_set_default },
31022d151d39SSteffen Klassert [XFRM_MSG_GETDEFAULT - XFRM_MSG_BASE] = { .doit = xfrm_get_default },
31031da177e4SLinus Torvalds };
31041da177e4SLinus Torvalds
xfrm_user_rcv_msg(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)31052d4bc933SJohannes Berg static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
31062d4bc933SJohannes Berg struct netlink_ext_ack *extack)
31071da177e4SLinus Torvalds {
3108a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk);
310935a7aa08SThomas Graf struct nlattr *attrs[XFRMA_MAX+1];
311005600a79SMathias Krause const struct xfrm_link *link;
31115106f4a8SDmitry Safonov struct nlmsghdr *nlh64 = NULL;
3112a7bd9a45SThomas Graf int type, err;
31131da177e4SLinus Torvalds
31141da177e4SLinus Torvalds type = nlh->nlmsg_type;
31151da177e4SLinus Torvalds if (type > XFRM_MSG_MAX)
31161d00a4ebSThomas Graf return -EINVAL;
31171da177e4SLinus Torvalds
31181da177e4SLinus Torvalds type -= XFRM_MSG_BASE;
31191da177e4SLinus Torvalds link = &xfrm_dispatch[type];
31201da177e4SLinus Torvalds
31211da177e4SLinus Torvalds /* All operations require privileges, even GET */
312290f62cf3SEric W. Biederman if (!netlink_net_capable(skb, CAP_NET_ADMIN))
31231d00a4ebSThomas Graf return -EPERM;
31241da177e4SLinus Torvalds
31255106f4a8SDmitry Safonov if (in_compat_syscall()) {
31265106f4a8SDmitry Safonov struct xfrm_translator *xtr = xfrm_get_translator();
31275106f4a8SDmitry Safonov
31285106f4a8SDmitry Safonov if (!xtr)
31295106f4a8SDmitry Safonov return -EOPNOTSUPP;
31305106f4a8SDmitry Safonov
31315106f4a8SDmitry Safonov nlh64 = xtr->rcv_msg_compat(nlh, link->nla_max,
31325106f4a8SDmitry Safonov link->nla_pol, extack);
31335106f4a8SDmitry Safonov xfrm_put_translator(xtr);
31345106f4a8SDmitry Safonov if (IS_ERR(nlh64))
31355106f4a8SDmitry Safonov return PTR_ERR(nlh64);
31365106f4a8SDmitry Safonov if (nlh64)
31375106f4a8SDmitry Safonov nlh = nlh64;
31385106f4a8SDmitry Safonov }
31395106f4a8SDmitry Safonov
3140492b558bSThomas Graf if ((type == (XFRM_MSG_GETSA - XFRM_MSG_BASE) ||
3141492b558bSThomas Graf type == (XFRM_MSG_GETPOLICY - XFRM_MSG_BASE)) &&
3142b8f3ab42SDavid S. Miller (nlh->nlmsg_flags & NLM_F_DUMP)) {
314380d326faSPablo Neira Ayuso struct netlink_dump_control c = {
31441137b5e2SHerbert Xu .start = link->start,
314580d326faSPablo Neira Ayuso .dump = link->dump,
314680d326faSPablo Neira Ayuso .done = link->done,
314780d326faSPablo Neira Ayuso };
31485106f4a8SDmitry Safonov
31495106f4a8SDmitry Safonov if (link->dump == NULL) {
31505106f4a8SDmitry Safonov err = -EINVAL;
31515106f4a8SDmitry Safonov goto err;
315280d326faSPablo Neira Ayuso }
31535106f4a8SDmitry Safonov
31545106f4a8SDmitry Safonov err = netlink_dump_start(net->xfrm.nlsk, skb, nlh, &c);
31555106f4a8SDmitry Safonov goto err;
31561da177e4SLinus Torvalds }
31571da177e4SLinus Torvalds
31588cb08174SJohannes Berg err = nlmsg_parse_deprecated(nlh, xfrm_msg_min[type], attrs,
3159880a6fabSChristophe Gouault link->nla_max ? : XFRMA_MAX,
3160fe52145fSJohannes Berg link->nla_pol ? : xfrma_policy, extack);
3161a7bd9a45SThomas Graf if (err < 0)
31625106f4a8SDmitry Safonov goto err;
31635106f4a8SDmitry Safonov
31645106f4a8SDmitry Safonov if (link->doit == NULL) {
31655106f4a8SDmitry Safonov err = -EINVAL;
31665106f4a8SDmitry Safonov goto err;
31675106f4a8SDmitry Safonov }
31685106f4a8SDmitry Safonov
31693bec6c3eSSabrina Dubroca err = link->doit(skb, nlh, attrs, extack);
31705106f4a8SDmitry Safonov
31717c1a80e8SPavel Skripkin /* We need to free skb allocated in xfrm_alloc_compat() before
31727c1a80e8SPavel Skripkin * returning from this function, because consume_skb() won't take
31737c1a80e8SPavel Skripkin * care of frag_list since netlink destructor sets
31747c1a80e8SPavel Skripkin * sbk->head to NULL. (see netlink_skb_destructor())
31757c1a80e8SPavel Skripkin */
31767c1a80e8SPavel Skripkin if (skb_has_frag_list(skb)) {
31777c1a80e8SPavel Skripkin kfree_skb(skb_shinfo(skb)->frag_list);
31787c1a80e8SPavel Skripkin skb_shinfo(skb)->frag_list = NULL;
31797c1a80e8SPavel Skripkin }
31807c1a80e8SPavel Skripkin
31815106f4a8SDmitry Safonov err:
31825106f4a8SDmitry Safonov kvfree(nlh64);
3183a7bd9a45SThomas Graf return err;
31841da177e4SLinus Torvalds }
31851da177e4SLinus Torvalds
xfrm_netlink_rcv(struct sk_buff * skb)3186cd40b7d3SDenis V. Lunev static void xfrm_netlink_rcv(struct sk_buff *skb)
31871da177e4SLinus Torvalds {
3188283bc9f3SFan Du struct net *net = sock_net(skb->sk);
3189283bc9f3SFan Du
3190283bc9f3SFan Du mutex_lock(&net->xfrm.xfrm_cfg_mutex);
3191cd40b7d3SDenis V. Lunev netlink_rcv_skb(skb, &xfrm_user_rcv_msg);
3192283bc9f3SFan Du mutex_unlock(&net->xfrm.xfrm_cfg_mutex);
31931da177e4SLinus Torvalds }
31941da177e4SLinus Torvalds
xfrm_expire_msgsize(void)3195a1b831f2SAlexey Dobriyan static inline unsigned int xfrm_expire_msgsize(void)
31967deb2264SThomas Graf {
31976f26b61eSJamal Hadi Salim return NLMSG_ALIGN(sizeof(struct xfrm_user_expire))
31986f26b61eSJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark));
31997deb2264SThomas Graf }
32007deb2264SThomas Graf
build_expire(struct sk_buff * skb,struct xfrm_state * x,const struct km_event * c)3201214e005bSDavid S. Miller static int build_expire(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c)
32021da177e4SLinus Torvalds {
32031da177e4SLinus Torvalds struct xfrm_user_expire *ue;
32041da177e4SLinus Torvalds struct nlmsghdr *nlh;
32051d1e34ddSDavid S. Miller int err;
32061da177e4SLinus Torvalds
320715e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, 0, XFRM_MSG_EXPIRE, sizeof(*ue), 0);
320879b8b7f4SThomas Graf if (nlh == NULL)
320979b8b7f4SThomas Graf return -EMSGSIZE;
32101da177e4SLinus Torvalds
32117b67c857SThomas Graf ue = nlmsg_data(nlh);
32121da177e4SLinus Torvalds copy_to_user_state(x, &ue->state);
3213d51d081dSJamal Hadi Salim ue->hard = (c->data.hard != 0) ? 1 : 0;
3214e3e5fc16SMathias Krause /* clear the padding bytes */
3215caf283d0SKees Cook memset_after(ue, 0, hard);
32161da177e4SLinus Torvalds
32171d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &x->mark);
32181d1e34ddSDavid S. Miller if (err)
32191d1e34ddSDavid S. Miller return err;
32206f26b61eSJamal Hadi Salim
32217e652640SSteffen Klassert err = xfrm_if_id_put(skb, x->if_id);
32227e652640SSteffen Klassert if (err)
32237e652640SSteffen Klassert return err;
32247e652640SSteffen Klassert
3225053c095aSJohannes Berg nlmsg_end(skb, nlh);
3226053c095aSJohannes Berg return 0;
32271da177e4SLinus Torvalds }
32281da177e4SLinus Torvalds
xfrm_exp_state_notify(struct xfrm_state * x,const struct km_event * c)3229214e005bSDavid S. Miller static int xfrm_exp_state_notify(struct xfrm_state *x, const struct km_event *c)
32301da177e4SLinus Torvalds {
3231fc34acd3SAlexey Dobriyan struct net *net = xs_net(x);
32321da177e4SLinus Torvalds struct sk_buff *skb;
32331da177e4SLinus Torvalds
32347deb2264SThomas Graf skb = nlmsg_new(xfrm_expire_msgsize(), GFP_ATOMIC);
32351da177e4SLinus Torvalds if (skb == NULL)
32361da177e4SLinus Torvalds return -ENOMEM;
32371da177e4SLinus Torvalds
32386f26b61eSJamal Hadi Salim if (build_expire(skb, x, c) < 0) {
32396f26b61eSJamal Hadi Salim kfree_skb(skb);
32406f26b61eSJamal Hadi Salim return -EMSGSIZE;
32416f26b61eSJamal Hadi Salim }
32421da177e4SLinus Torvalds
324321ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE);
32441da177e4SLinus Torvalds }
32451da177e4SLinus Torvalds
xfrm_aevent_state_notify(struct xfrm_state * x,const struct km_event * c)3246214e005bSDavid S. Miller static int xfrm_aevent_state_notify(struct xfrm_state *x, const struct km_event *c)
3247d51d081dSJamal Hadi Salim {
3248fc34acd3SAlexey Dobriyan struct net *net = xs_net(x);
3249d51d081dSJamal Hadi Salim struct sk_buff *skb;
32502fc5f83bSGustavo A. R. Silva int err;
3251d51d081dSJamal Hadi Salim
3252d8647b79SSteffen Klassert skb = nlmsg_new(xfrm_aevent_msgsize(x), GFP_ATOMIC);
3253d51d081dSJamal Hadi Salim if (skb == NULL)
3254d51d081dSJamal Hadi Salim return -ENOMEM;
3255d51d081dSJamal Hadi Salim
32562fc5f83bSGustavo A. R. Silva err = build_aevent(skb, x, c);
32572fc5f83bSGustavo A. R. Silva BUG_ON(err < 0);
3258d51d081dSJamal Hadi Salim
325921ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_AEVENTS);
3260d51d081dSJamal Hadi Salim }
3261d51d081dSJamal Hadi Salim
xfrm_notify_sa_flush(const struct km_event * c)3262214e005bSDavid S. Miller static int xfrm_notify_sa_flush(const struct km_event *c)
326326b15dadSJamal Hadi Salim {
32647067802eSAlexey Dobriyan struct net *net = c->net;
326526b15dadSJamal Hadi Salim struct xfrm_usersa_flush *p;
326626b15dadSJamal Hadi Salim struct nlmsghdr *nlh;
326726b15dadSJamal Hadi Salim struct sk_buff *skb;
32687deb2264SThomas Graf int len = NLMSG_ALIGN(sizeof(struct xfrm_usersa_flush));
326926b15dadSJamal Hadi Salim
32707deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC);
327126b15dadSJamal Hadi Salim if (skb == NULL)
327226b15dadSJamal Hadi Salim return -ENOMEM;
327326b15dadSJamal Hadi Salim
327415e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, c->seq, XFRM_MSG_FLUSHSA, sizeof(*p), 0);
327579b8b7f4SThomas Graf if (nlh == NULL) {
327679b8b7f4SThomas Graf kfree_skb(skb);
327779b8b7f4SThomas Graf return -EMSGSIZE;
327879b8b7f4SThomas Graf }
327926b15dadSJamal Hadi Salim
32807b67c857SThomas Graf p = nlmsg_data(nlh);
3281bf08867fSHerbert Xu p->proto = c->data.proto;
328226b15dadSJamal Hadi Salim
32839825069dSThomas Graf nlmsg_end(skb, nlh);
328426b15dadSJamal Hadi Salim
328521ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_SA);
328626b15dadSJamal Hadi Salim }
328726b15dadSJamal Hadi Salim
xfrm_sa_len(struct xfrm_state * x)3288a1b831f2SAlexey Dobriyan static inline unsigned int xfrm_sa_len(struct xfrm_state *x)
328926b15dadSJamal Hadi Salim {
3290a1b831f2SAlexey Dobriyan unsigned int l = 0;
32911a6509d9SHerbert Xu if (x->aead)
32921a6509d9SHerbert Xu l += nla_total_size(aead_len(x->aead));
32934447bb33SMartin Willi if (x->aalg) {
32944447bb33SMartin Willi l += nla_total_size(sizeof(struct xfrm_algo) +
32954447bb33SMartin Willi (x->aalg->alg_key_len + 7) / 8);
32964447bb33SMartin Willi l += nla_total_size(xfrm_alg_auth_len(x->aalg));
32974447bb33SMartin Willi }
329826b15dadSJamal Hadi Salim if (x->ealg)
32990f99be0dSEric Dumazet l += nla_total_size(xfrm_alg_len(x->ealg));
330026b15dadSJamal Hadi Salim if (x->calg)
33017deb2264SThomas Graf l += nla_total_size(sizeof(*x->calg));
330226b15dadSJamal Hadi Salim if (x->encap)
33037deb2264SThomas Graf l += nla_total_size(sizeof(*x->encap));
330435d2856bSMartin Willi if (x->tfcpad)
330535d2856bSMartin Willi l += nla_total_size(sizeof(x->tfcpad));
3306d8647b79SSteffen Klassert if (x->replay_esn)
3307d8647b79SSteffen Klassert l += nla_total_size(xfrm_replay_state_esn_len(x->replay_esn));
3308f293a5e3Sdingzhi else
3309f293a5e3Sdingzhi l += nla_total_size(sizeof(struct xfrm_replay_state));
331068325d3bSHerbert Xu if (x->security)
331168325d3bSHerbert Xu l += nla_total_size(sizeof(struct xfrm_user_sec_ctx) +
331268325d3bSHerbert Xu x->security->ctx_len);
331368325d3bSHerbert Xu if (x->coaddr)
331468325d3bSHerbert Xu l += nla_total_size(sizeof(*x->coaddr));
3315a947b0a9SNicolas Dichtel if (x->props.extra_flags)
3316a947b0a9SNicolas Dichtel l += nla_total_size(sizeof(x->props.extra_flags));
3317d77e38e6SSteffen Klassert if (x->xso.dev)
33187770a39dSEric Dumazet l += nla_total_size(sizeof(struct xfrm_user_offload));
33199b42c1f1SSteffen Klassert if (x->props.smark.v | x->props.smark.m) {
33209b42c1f1SSteffen Klassert l += nla_total_size(sizeof(x->props.smark.v));
33219b42c1f1SSteffen Klassert l += nla_total_size(sizeof(x->props.smark.m));
33229b42c1f1SSteffen Klassert }
33237e652640SSteffen Klassert if (x->if_id)
33247e652640SSteffen Klassert l += nla_total_size(sizeof(x->if_id));
332568325d3bSHerbert Xu
3326d26f3984SHerbert Xu /* Must count x->lastused as it may become non-zero behind our back. */
3327de95c4a4SNicolas Dichtel l += nla_total_size_64bit(sizeof(u64));
332826b15dadSJamal Hadi Salim
33294e484b3eSAntony Antony if (x->mapping_maxage)
33304e484b3eSAntony Antony l += nla_total_size(sizeof(x->mapping_maxage));
33314e484b3eSAntony Antony
333226b15dadSJamal Hadi Salim return l;
333326b15dadSJamal Hadi Salim }
333426b15dadSJamal Hadi Salim
xfrm_notify_sa(struct xfrm_state * x,const struct km_event * c)3335214e005bSDavid S. Miller static int xfrm_notify_sa(struct xfrm_state *x, const struct km_event *c)
333626b15dadSJamal Hadi Salim {
3337fc34acd3SAlexey Dobriyan struct net *net = xs_net(x);
333826b15dadSJamal Hadi Salim struct xfrm_usersa_info *p;
33390603eac0SHerbert Xu struct xfrm_usersa_id *id;
334026b15dadSJamal Hadi Salim struct nlmsghdr *nlh;
334126b15dadSJamal Hadi Salim struct sk_buff *skb;
3342a1b831f2SAlexey Dobriyan unsigned int len = xfrm_sa_len(x);
3343a1b831f2SAlexey Dobriyan unsigned int headlen;
3344a1b831f2SAlexey Dobriyan int err;
33450603eac0SHerbert Xu
33460603eac0SHerbert Xu headlen = sizeof(*p);
33470603eac0SHerbert Xu if (c->event == XFRM_MSG_DELSA) {
33487deb2264SThomas Graf len += nla_total_size(headlen);
33490603eac0SHerbert Xu headlen = sizeof(*id);
33506f26b61eSJamal Hadi Salim len += nla_total_size(sizeof(struct xfrm_mark));
33510603eac0SHerbert Xu }
33527deb2264SThomas Graf len += NLMSG_ALIGN(headlen);
335326b15dadSJamal Hadi Salim
33547deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC);
335526b15dadSJamal Hadi Salim if (skb == NULL)
335626b15dadSJamal Hadi Salim return -ENOMEM;
335726b15dadSJamal Hadi Salim
335815e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, c->seq, c->event, headlen, 0);
33591d1e34ddSDavid S. Miller err = -EMSGSIZE;
336079b8b7f4SThomas Graf if (nlh == NULL)
33611d1e34ddSDavid S. Miller goto out_free_skb;
336226b15dadSJamal Hadi Salim
33637b67c857SThomas Graf p = nlmsg_data(nlh);
33640603eac0SHerbert Xu if (c->event == XFRM_MSG_DELSA) {
3365c0144beaSThomas Graf struct nlattr *attr;
3366c0144beaSThomas Graf
33677b67c857SThomas Graf id = nlmsg_data(nlh);
336850329c8aSMathias Krause memset(id, 0, sizeof(*id));
33690603eac0SHerbert Xu memcpy(&id->daddr, &x->id.daddr, sizeof(id->daddr));
33700603eac0SHerbert Xu id->spi = x->id.spi;
33710603eac0SHerbert Xu id->family = x->props.family;
33720603eac0SHerbert Xu id->proto = x->id.proto;
33730603eac0SHerbert Xu
3374c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_SA, sizeof(*p));
33751d1e34ddSDavid S. Miller err = -EMSGSIZE;
3376c0144beaSThomas Graf if (attr == NULL)
33771d1e34ddSDavid S. Miller goto out_free_skb;
3378c0144beaSThomas Graf
3379c0144beaSThomas Graf p = nla_data(attr);
33800603eac0SHerbert Xu }
33811d1e34ddSDavid S. Miller err = copy_to_user_state_extra(x, p, skb);
33821d1e34ddSDavid S. Miller if (err)
33831d1e34ddSDavid S. Miller goto out_free_skb;
338426b15dadSJamal Hadi Salim
33859825069dSThomas Graf nlmsg_end(skb, nlh);
338626b15dadSJamal Hadi Salim
338721ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_SA);
338826b15dadSJamal Hadi Salim
33891d1e34ddSDavid S. Miller out_free_skb:
339026b15dadSJamal Hadi Salim kfree_skb(skb);
33911d1e34ddSDavid S. Miller return err;
339226b15dadSJamal Hadi Salim }
339326b15dadSJamal Hadi Salim
xfrm_send_state_notify(struct xfrm_state * x,const struct km_event * c)3394214e005bSDavid S. Miller static int xfrm_send_state_notify(struct xfrm_state *x, const struct km_event *c)
339526b15dadSJamal Hadi Salim {
339626b15dadSJamal Hadi Salim
339726b15dadSJamal Hadi Salim switch (c->event) {
3398f60f6b8fSHerbert Xu case XFRM_MSG_EXPIRE:
339926b15dadSJamal Hadi Salim return xfrm_exp_state_notify(x, c);
3400d51d081dSJamal Hadi Salim case XFRM_MSG_NEWAE:
3401d51d081dSJamal Hadi Salim return xfrm_aevent_state_notify(x, c);
3402f60f6b8fSHerbert Xu case XFRM_MSG_DELSA:
3403f60f6b8fSHerbert Xu case XFRM_MSG_UPDSA:
3404f60f6b8fSHerbert Xu case XFRM_MSG_NEWSA:
340526b15dadSJamal Hadi Salim return xfrm_notify_sa(x, c);
3406f60f6b8fSHerbert Xu case XFRM_MSG_FLUSHSA:
340726b15dadSJamal Hadi Salim return xfrm_notify_sa_flush(c);
340826b15dadSJamal Hadi Salim default:
340962db5cfdSstephen hemminger printk(KERN_NOTICE "xfrm_user: Unknown SA event %d\n",
341062db5cfdSstephen hemminger c->event);
341126b15dadSJamal Hadi Salim break;
341226b15dadSJamal Hadi Salim }
341326b15dadSJamal Hadi Salim
341426b15dadSJamal Hadi Salim return 0;
341526b15dadSJamal Hadi Salim
341626b15dadSJamal Hadi Salim }
341726b15dadSJamal Hadi Salim
xfrm_acquire_msgsize(struct xfrm_state * x,struct xfrm_policy * xp)3418a1b831f2SAlexey Dobriyan static inline unsigned int xfrm_acquire_msgsize(struct xfrm_state *x,
34197deb2264SThomas Graf struct xfrm_policy *xp)
34207deb2264SThomas Graf {
34217deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_acquire))
34227deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr)
34236f26b61eSJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark))
34247deb2264SThomas Graf + nla_total_size(xfrm_user_sec_ctx_size(x->security))
34257deb2264SThomas Graf + userpolicy_type_attrsize();
34267deb2264SThomas Graf }
34277deb2264SThomas Graf
build_acquire(struct sk_buff * skb,struct xfrm_state * x,struct xfrm_tmpl * xt,struct xfrm_policy * xp)34281da177e4SLinus Torvalds static int build_acquire(struct sk_buff *skb, struct xfrm_state *x,
342965e0736bSFan Du struct xfrm_tmpl *xt, struct xfrm_policy *xp)
34301da177e4SLinus Torvalds {
34311d1e34ddSDavid S. Miller __u32 seq = xfrm_get_acqseq();
34321da177e4SLinus Torvalds struct xfrm_user_acquire *ua;
34331da177e4SLinus Torvalds struct nlmsghdr *nlh;
34341d1e34ddSDavid S. Miller int err;
34351da177e4SLinus Torvalds
343679b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_ACQUIRE, sizeof(*ua), 0);
343779b8b7f4SThomas Graf if (nlh == NULL)
343879b8b7f4SThomas Graf return -EMSGSIZE;
34391da177e4SLinus Torvalds
34407b67c857SThomas Graf ua = nlmsg_data(nlh);
34411da177e4SLinus Torvalds memcpy(&ua->id, &x->id, sizeof(ua->id));
34421da177e4SLinus Torvalds memcpy(&ua->saddr, &x->props.saddr, sizeof(ua->saddr));
34431da177e4SLinus Torvalds memcpy(&ua->sel, &x->sel, sizeof(ua->sel));
344465e0736bSFan Du copy_to_user_policy(xp, &ua->policy, XFRM_POLICY_OUT);
34451da177e4SLinus Torvalds ua->aalgos = xt->aalgos;
34461da177e4SLinus Torvalds ua->ealgos = xt->ealgos;
34471da177e4SLinus Torvalds ua->calgos = xt->calgos;
34481da177e4SLinus Torvalds ua->seq = x->km.seq = seq;
34491da177e4SLinus Torvalds
34501d1e34ddSDavid S. Miller err = copy_to_user_tmpl(xp, skb);
34511d1e34ddSDavid S. Miller if (!err)
34521d1e34ddSDavid S. Miller err = copy_to_user_state_sec_ctx(x, skb);
34531d1e34ddSDavid S. Miller if (!err)
34541d1e34ddSDavid S. Miller err = copy_to_user_policy_type(xp->type, skb);
34551d1e34ddSDavid S. Miller if (!err)
34561d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &xp->mark);
34577e652640SSteffen Klassert if (!err)
34587e652640SSteffen Klassert err = xfrm_if_id_put(skb, xp->if_id);
3459919e43faSLeon Romanovsky if (!err && xp->xdo.dev)
3460919e43faSLeon Romanovsky err = copy_user_offload(&xp->xdo, skb);
34611d1e34ddSDavid S. Miller if (err) {
34621d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh);
34631d1e34ddSDavid S. Miller return err;
34641d1e34ddSDavid S. Miller }
34651da177e4SLinus Torvalds
3466053c095aSJohannes Berg nlmsg_end(skb, nlh);
3467053c095aSJohannes Berg return 0;
34681da177e4SLinus Torvalds }
34691da177e4SLinus Torvalds
xfrm_send_acquire(struct xfrm_state * x,struct xfrm_tmpl * xt,struct xfrm_policy * xp)34701da177e4SLinus Torvalds static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt,
347165e0736bSFan Du struct xfrm_policy *xp)
34721da177e4SLinus Torvalds {
3473a6483b79SAlexey Dobriyan struct net *net = xs_net(x);
34741da177e4SLinus Torvalds struct sk_buff *skb;
34752fc5f83bSGustavo A. R. Silva int err;
34761da177e4SLinus Torvalds
34777deb2264SThomas Graf skb = nlmsg_new(xfrm_acquire_msgsize(x, xp), GFP_ATOMIC);
34781da177e4SLinus Torvalds if (skb == NULL)
34791da177e4SLinus Torvalds return -ENOMEM;
34801da177e4SLinus Torvalds
34812fc5f83bSGustavo A. R. Silva err = build_acquire(skb, x, xt, xp);
34822fc5f83bSGustavo A. R. Silva BUG_ON(err < 0);
34831da177e4SLinus Torvalds
348421ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_ACQUIRE);
34851da177e4SLinus Torvalds }
34861da177e4SLinus Torvalds
34871da177e4SLinus Torvalds /* User gives us xfrm_user_policy_info followed by an array of 0
34881da177e4SLinus Torvalds * or more templates.
34891da177e4SLinus Torvalds */
xfrm_compile_policy(struct sock * sk,int opt,u8 * data,int len,int * dir)3490cb969f07SVenkat Yekkirala static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt,
34911da177e4SLinus Torvalds u8 *data, int len, int *dir)
34921da177e4SLinus Torvalds {
3493fc34acd3SAlexey Dobriyan struct net *net = sock_net(sk);
34941da177e4SLinus Torvalds struct xfrm_userpolicy_info *p = (struct xfrm_userpolicy_info *)data;
34951da177e4SLinus Torvalds struct xfrm_user_tmpl *ut = (struct xfrm_user_tmpl *) (p + 1);
34961da177e4SLinus Torvalds struct xfrm_policy *xp;
34971da177e4SLinus Torvalds int nr;
34981da177e4SLinus Torvalds
3499cb969f07SVenkat Yekkirala switch (sk->sk_family) {
35001da177e4SLinus Torvalds case AF_INET:
35011da177e4SLinus Torvalds if (opt != IP_XFRM_POLICY) {
35021da177e4SLinus Torvalds *dir = -EOPNOTSUPP;
35031da177e4SLinus Torvalds return NULL;
35041da177e4SLinus Torvalds }
35051da177e4SLinus Torvalds break;
3506dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
35071da177e4SLinus Torvalds case AF_INET6:
35081da177e4SLinus Torvalds if (opt != IPV6_XFRM_POLICY) {
35091da177e4SLinus Torvalds *dir = -EOPNOTSUPP;
35101da177e4SLinus Torvalds return NULL;
35111da177e4SLinus Torvalds }
35121da177e4SLinus Torvalds break;
35131da177e4SLinus Torvalds #endif
35141da177e4SLinus Torvalds default:
35151da177e4SLinus Torvalds *dir = -EINVAL;
35161da177e4SLinus Torvalds return NULL;
35171da177e4SLinus Torvalds }
35181da177e4SLinus Torvalds
35191da177e4SLinus Torvalds *dir = -EINVAL;
35201da177e4SLinus Torvalds
35211da177e4SLinus Torvalds if (len < sizeof(*p) ||
3522ec2b4f01SSabrina Dubroca verify_newpolicy_info(p, NULL))
35231da177e4SLinus Torvalds return NULL;
35241da177e4SLinus Torvalds
35251da177e4SLinus Torvalds nr = ((len - sizeof(*p)) / sizeof(*ut));
35263d776e31STobias Brunner if (validate_tmpl(nr, ut, p->sel.family, p->dir, NULL))
35271da177e4SLinus Torvalds return NULL;
35281da177e4SLinus Torvalds
3529a4f1bac6SHerbert Xu if (p->dir > XFRM_POLICY_OUT)
3530a4f1bac6SHerbert Xu return NULL;
3531a4f1bac6SHerbert Xu
35322f09a4d5SHerbert Xu xp = xfrm_policy_alloc(net, GFP_ATOMIC);
35331da177e4SLinus Torvalds if (xp == NULL) {
35341da177e4SLinus Torvalds *dir = -ENOBUFS;
35351da177e4SLinus Torvalds return NULL;
35361da177e4SLinus Torvalds }
35371da177e4SLinus Torvalds
35381da177e4SLinus Torvalds copy_from_user_policy(xp, p);
3539f7b6983fSMasahide NAKAMURA xp->type = XFRM_POLICY_TYPE_MAIN;
35401da177e4SLinus Torvalds copy_templates(xp, ut, nr);
35411da177e4SLinus Torvalds
35421da177e4SLinus Torvalds *dir = p->dir;
35431da177e4SLinus Torvalds
35441da177e4SLinus Torvalds return xp;
35451da177e4SLinus Torvalds }
35461da177e4SLinus Torvalds
xfrm_polexpire_msgsize(struct xfrm_policy * xp)3547a1b831f2SAlexey Dobriyan static inline unsigned int xfrm_polexpire_msgsize(struct xfrm_policy *xp)
35487deb2264SThomas Graf {
35497deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_polexpire))
35507deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr)
35517deb2264SThomas Graf + nla_total_size(xfrm_user_sec_ctx_size(xp->security))
3552295fae56SJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark))
35537deb2264SThomas Graf + userpolicy_type_attrsize();
35547deb2264SThomas Graf }
35557deb2264SThomas Graf
build_polexpire(struct sk_buff * skb,struct xfrm_policy * xp,int dir,const struct km_event * c)35561da177e4SLinus Torvalds static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp,
3557214e005bSDavid S. Miller int dir, const struct km_event *c)
35581da177e4SLinus Torvalds {
35591da177e4SLinus Torvalds struct xfrm_user_polexpire *upe;
3560d51d081dSJamal Hadi Salim int hard = c->data.hard;
35611d1e34ddSDavid S. Miller struct nlmsghdr *nlh;
35621d1e34ddSDavid S. Miller int err;
35631da177e4SLinus Torvalds
356415e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, 0, XFRM_MSG_POLEXPIRE, sizeof(*upe), 0);
356579b8b7f4SThomas Graf if (nlh == NULL)
356679b8b7f4SThomas Graf return -EMSGSIZE;
35671da177e4SLinus Torvalds
35687b67c857SThomas Graf upe = nlmsg_data(nlh);
35691da177e4SLinus Torvalds copy_to_user_policy(xp, &upe->pol, dir);
35701d1e34ddSDavid S. Miller err = copy_to_user_tmpl(xp, skb);
35711d1e34ddSDavid S. Miller if (!err)
35721d1e34ddSDavid S. Miller err = copy_to_user_sec_ctx(xp, skb);
35731d1e34ddSDavid S. Miller if (!err)
35741d1e34ddSDavid S. Miller err = copy_to_user_policy_type(xp->type, skb);
35751d1e34ddSDavid S. Miller if (!err)
35761d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &xp->mark);
35777e652640SSteffen Klassert if (!err)
35787e652640SSteffen Klassert err = xfrm_if_id_put(skb, xp->if_id);
3579919e43faSLeon Romanovsky if (!err && xp->xdo.dev)
3580919e43faSLeon Romanovsky err = copy_user_offload(&xp->xdo, skb);
35811d1e34ddSDavid S. Miller if (err) {
35821d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh);
35831d1e34ddSDavid S. Miller return err;
35841d1e34ddSDavid S. Miller }
35851da177e4SLinus Torvalds upe->hard = !!hard;
35861da177e4SLinus Torvalds
3587053c095aSJohannes Berg nlmsg_end(skb, nlh);
3588053c095aSJohannes Berg return 0;
35891da177e4SLinus Torvalds }
35901da177e4SLinus Torvalds
xfrm_exp_policy_notify(struct xfrm_policy * xp,int dir,const struct km_event * c)3591214e005bSDavid S. Miller static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c)
35921da177e4SLinus Torvalds {
3593fc34acd3SAlexey Dobriyan struct net *net = xp_net(xp);
35941da177e4SLinus Torvalds struct sk_buff *skb;
35952fc5f83bSGustavo A. R. Silva int err;
35961da177e4SLinus Torvalds
35977deb2264SThomas Graf skb = nlmsg_new(xfrm_polexpire_msgsize(xp), GFP_ATOMIC);
35981da177e4SLinus Torvalds if (skb == NULL)
35991da177e4SLinus Torvalds return -ENOMEM;
36001da177e4SLinus Torvalds
36012fc5f83bSGustavo A. R. Silva err = build_polexpire(skb, xp, dir, c);
36022fc5f83bSGustavo A. R. Silva BUG_ON(err < 0);
36031da177e4SLinus Torvalds
360421ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE);
36051da177e4SLinus Torvalds }
36061da177e4SLinus Torvalds
xfrm_notify_policy(struct xfrm_policy * xp,int dir,const struct km_event * c)3607214e005bSDavid S. Miller static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_event *c)
360826b15dadSJamal Hadi Salim {
3609a1b831f2SAlexey Dobriyan unsigned int len = nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr);
3610fc34acd3SAlexey Dobriyan struct net *net = xp_net(xp);
361126b15dadSJamal Hadi Salim struct xfrm_userpolicy_info *p;
36120603eac0SHerbert Xu struct xfrm_userpolicy_id *id;
361326b15dadSJamal Hadi Salim struct nlmsghdr *nlh;
361426b15dadSJamal Hadi Salim struct sk_buff *skb;
3615a1b831f2SAlexey Dobriyan unsigned int headlen;
3616a1b831f2SAlexey Dobriyan int err;
36170603eac0SHerbert Xu
36180603eac0SHerbert Xu headlen = sizeof(*p);
36190603eac0SHerbert Xu if (c->event == XFRM_MSG_DELPOLICY) {
36207deb2264SThomas Graf len += nla_total_size(headlen);
36210603eac0SHerbert Xu headlen = sizeof(*id);
36220603eac0SHerbert Xu }
3623cfbfd45aSThomas Graf len += userpolicy_type_attrsize();
3624295fae56SJamal Hadi Salim len += nla_total_size(sizeof(struct xfrm_mark));
36257deb2264SThomas Graf len += NLMSG_ALIGN(headlen);
362626b15dadSJamal Hadi Salim
36277deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC);
362826b15dadSJamal Hadi Salim if (skb == NULL)
362926b15dadSJamal Hadi Salim return -ENOMEM;
363026b15dadSJamal Hadi Salim
363115e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, c->seq, c->event, headlen, 0);
36321d1e34ddSDavid S. Miller err = -EMSGSIZE;
363379b8b7f4SThomas Graf if (nlh == NULL)
36341d1e34ddSDavid S. Miller goto out_free_skb;
363526b15dadSJamal Hadi Salim
36367b67c857SThomas Graf p = nlmsg_data(nlh);
36370603eac0SHerbert Xu if (c->event == XFRM_MSG_DELPOLICY) {
3638c0144beaSThomas Graf struct nlattr *attr;
3639c0144beaSThomas Graf
36407b67c857SThomas Graf id = nlmsg_data(nlh);
36410603eac0SHerbert Xu memset(id, 0, sizeof(*id));
36420603eac0SHerbert Xu id->dir = dir;
36430603eac0SHerbert Xu if (c->data.byid)
36440603eac0SHerbert Xu id->index = xp->index;
36450603eac0SHerbert Xu else
36460603eac0SHerbert Xu memcpy(&id->sel, &xp->selector, sizeof(id->sel));
36470603eac0SHerbert Xu
3648c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_POLICY, sizeof(*p));
36491d1e34ddSDavid S. Miller err = -EMSGSIZE;
3650c0144beaSThomas Graf if (attr == NULL)
36511d1e34ddSDavid S. Miller goto out_free_skb;
3652c0144beaSThomas Graf
3653c0144beaSThomas Graf p = nla_data(attr);
36540603eac0SHerbert Xu }
365526b15dadSJamal Hadi Salim
365626b15dadSJamal Hadi Salim copy_to_user_policy(xp, p, dir);
36571d1e34ddSDavid S. Miller err = copy_to_user_tmpl(xp, skb);
36581d1e34ddSDavid S. Miller if (!err)
36591d1e34ddSDavid S. Miller err = copy_to_user_policy_type(xp->type, skb);
36601d1e34ddSDavid S. Miller if (!err)
36611d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &xp->mark);
36627e652640SSteffen Klassert if (!err)
36637e652640SSteffen Klassert err = xfrm_if_id_put(skb, xp->if_id);
3664919e43faSLeon Romanovsky if (!err && xp->xdo.dev)
3665919e43faSLeon Romanovsky err = copy_user_offload(&xp->xdo, skb);
36661d1e34ddSDavid S. Miller if (err)
36671d1e34ddSDavid S. Miller goto out_free_skb;
3668295fae56SJamal Hadi Salim
36699825069dSThomas Graf nlmsg_end(skb, nlh);
367026b15dadSJamal Hadi Salim
367121ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_POLICY);
367226b15dadSJamal Hadi Salim
36731d1e34ddSDavid S. Miller out_free_skb:
367426b15dadSJamal Hadi Salim kfree_skb(skb);
36751d1e34ddSDavid S. Miller return err;
367626b15dadSJamal Hadi Salim }
367726b15dadSJamal Hadi Salim
xfrm_notify_policy_flush(const struct km_event * c)3678214e005bSDavid S. Miller static int xfrm_notify_policy_flush(const struct km_event *c)
367926b15dadSJamal Hadi Salim {
36807067802eSAlexey Dobriyan struct net *net = c->net;
368126b15dadSJamal Hadi Salim struct nlmsghdr *nlh;
368226b15dadSJamal Hadi Salim struct sk_buff *skb;
36831d1e34ddSDavid S. Miller int err;
368426b15dadSJamal Hadi Salim
36857deb2264SThomas Graf skb = nlmsg_new(userpolicy_type_attrsize(), GFP_ATOMIC);
368626b15dadSJamal Hadi Salim if (skb == NULL)
368726b15dadSJamal Hadi Salim return -ENOMEM;
368826b15dadSJamal Hadi Salim
368915e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, c->seq, XFRM_MSG_FLUSHPOLICY, 0, 0);
36901d1e34ddSDavid S. Miller err = -EMSGSIZE;
369179b8b7f4SThomas Graf if (nlh == NULL)
36921d1e34ddSDavid S. Miller goto out_free_skb;
36931d1e34ddSDavid S. Miller err = copy_to_user_policy_type(c->data.type, skb);
36941d1e34ddSDavid S. Miller if (err)
36951d1e34ddSDavid S. Miller goto out_free_skb;
369626b15dadSJamal Hadi Salim
36979825069dSThomas Graf nlmsg_end(skb, nlh);
369826b15dadSJamal Hadi Salim
369921ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_POLICY);
370026b15dadSJamal Hadi Salim
37011d1e34ddSDavid S. Miller out_free_skb:
370226b15dadSJamal Hadi Salim kfree_skb(skb);
37031d1e34ddSDavid S. Miller return err;
370426b15dadSJamal Hadi Salim }
370526b15dadSJamal Hadi Salim
xfrm_send_policy_notify(struct xfrm_policy * xp,int dir,const struct km_event * c)3706214e005bSDavid S. Miller static int xfrm_send_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c)
370726b15dadSJamal Hadi Salim {
370826b15dadSJamal Hadi Salim
370926b15dadSJamal Hadi Salim switch (c->event) {
3710f60f6b8fSHerbert Xu case XFRM_MSG_NEWPOLICY:
3711f60f6b8fSHerbert Xu case XFRM_MSG_UPDPOLICY:
3712f60f6b8fSHerbert Xu case XFRM_MSG_DELPOLICY:
371326b15dadSJamal Hadi Salim return xfrm_notify_policy(xp, dir, c);
3714f60f6b8fSHerbert Xu case XFRM_MSG_FLUSHPOLICY:
371526b15dadSJamal Hadi Salim return xfrm_notify_policy_flush(c);
3716f60f6b8fSHerbert Xu case XFRM_MSG_POLEXPIRE:
371726b15dadSJamal Hadi Salim return xfrm_exp_policy_notify(xp, dir, c);
371826b15dadSJamal Hadi Salim default:
371962db5cfdSstephen hemminger printk(KERN_NOTICE "xfrm_user: Unknown Policy event %d\n",
372062db5cfdSstephen hemminger c->event);
372126b15dadSJamal Hadi Salim }
372226b15dadSJamal Hadi Salim
372326b15dadSJamal Hadi Salim return 0;
372426b15dadSJamal Hadi Salim
372526b15dadSJamal Hadi Salim }
372626b15dadSJamal Hadi Salim
xfrm_report_msgsize(void)3727a1b831f2SAlexey Dobriyan static inline unsigned int xfrm_report_msgsize(void)
37287deb2264SThomas Graf {
37297deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_report));
37307deb2264SThomas Graf }
37317deb2264SThomas Graf
build_report(struct sk_buff * skb,u8 proto,struct xfrm_selector * sel,xfrm_address_t * addr)373297a64b45SMasahide NAKAMURA static int build_report(struct sk_buff *skb, u8 proto,
373397a64b45SMasahide NAKAMURA struct xfrm_selector *sel, xfrm_address_t *addr)
373497a64b45SMasahide NAKAMURA {
373597a64b45SMasahide NAKAMURA struct xfrm_user_report *ur;
373697a64b45SMasahide NAKAMURA struct nlmsghdr *nlh;
373797a64b45SMasahide NAKAMURA
373879b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_REPORT, sizeof(*ur), 0);
373979b8b7f4SThomas Graf if (nlh == NULL)
374079b8b7f4SThomas Graf return -EMSGSIZE;
374197a64b45SMasahide NAKAMURA
37427b67c857SThomas Graf ur = nlmsg_data(nlh);
374397a64b45SMasahide NAKAMURA ur->proto = proto;
374497a64b45SMasahide NAKAMURA memcpy(&ur->sel, sel, sizeof(ur->sel));
374597a64b45SMasahide NAKAMURA
37461d1e34ddSDavid S. Miller if (addr) {
37471d1e34ddSDavid S. Miller int err = nla_put(skb, XFRMA_COADDR, sizeof(*addr), addr);
37481d1e34ddSDavid S. Miller if (err) {
37499825069dSThomas Graf nlmsg_cancel(skb, nlh);
37501d1e34ddSDavid S. Miller return err;
37511d1e34ddSDavid S. Miller }
37521d1e34ddSDavid S. Miller }
3753053c095aSJohannes Berg nlmsg_end(skb, nlh);
3754053c095aSJohannes Berg return 0;
375597a64b45SMasahide NAKAMURA }
375697a64b45SMasahide NAKAMURA
xfrm_send_report(struct net * net,u8 proto,struct xfrm_selector * sel,xfrm_address_t * addr)3757db983c11SAlexey Dobriyan static int xfrm_send_report(struct net *net, u8 proto,
3758db983c11SAlexey Dobriyan struct xfrm_selector *sel, xfrm_address_t *addr)
375997a64b45SMasahide NAKAMURA {
376097a64b45SMasahide NAKAMURA struct sk_buff *skb;
37612fc5f83bSGustavo A. R. Silva int err;
376297a64b45SMasahide NAKAMURA
37637deb2264SThomas Graf skb = nlmsg_new(xfrm_report_msgsize(), GFP_ATOMIC);
376497a64b45SMasahide NAKAMURA if (skb == NULL)
376597a64b45SMasahide NAKAMURA return -ENOMEM;
376697a64b45SMasahide NAKAMURA
37672fc5f83bSGustavo A. R. Silva err = build_report(skb, proto, sel, addr);
37682fc5f83bSGustavo A. R. Silva BUG_ON(err < 0);
376997a64b45SMasahide NAKAMURA
377021ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_REPORT);
377197a64b45SMasahide NAKAMURA }
377297a64b45SMasahide NAKAMURA
xfrm_mapping_msgsize(void)3773a1b831f2SAlexey Dobriyan static inline unsigned int xfrm_mapping_msgsize(void)
37743a2dfbe8SMartin Willi {
37753a2dfbe8SMartin Willi return NLMSG_ALIGN(sizeof(struct xfrm_user_mapping));
37763a2dfbe8SMartin Willi }
37773a2dfbe8SMartin Willi
build_mapping(struct sk_buff * skb,struct xfrm_state * x,xfrm_address_t * new_saddr,__be16 new_sport)37783a2dfbe8SMartin Willi static int build_mapping(struct sk_buff *skb, struct xfrm_state *x,
37793a2dfbe8SMartin Willi xfrm_address_t *new_saddr, __be16 new_sport)
37803a2dfbe8SMartin Willi {
37813a2dfbe8SMartin Willi struct xfrm_user_mapping *um;
37823a2dfbe8SMartin Willi struct nlmsghdr *nlh;
37833a2dfbe8SMartin Willi
37843a2dfbe8SMartin Willi nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_MAPPING, sizeof(*um), 0);
37853a2dfbe8SMartin Willi if (nlh == NULL)
37863a2dfbe8SMartin Willi return -EMSGSIZE;
37873a2dfbe8SMartin Willi
37883a2dfbe8SMartin Willi um = nlmsg_data(nlh);
37893a2dfbe8SMartin Willi
37903a2dfbe8SMartin Willi memcpy(&um->id.daddr, &x->id.daddr, sizeof(um->id.daddr));
37913a2dfbe8SMartin Willi um->id.spi = x->id.spi;
37923a2dfbe8SMartin Willi um->id.family = x->props.family;
37933a2dfbe8SMartin Willi um->id.proto = x->id.proto;
37943a2dfbe8SMartin Willi memcpy(&um->new_saddr, new_saddr, sizeof(um->new_saddr));
37953a2dfbe8SMartin Willi memcpy(&um->old_saddr, &x->props.saddr, sizeof(um->old_saddr));
37963a2dfbe8SMartin Willi um->new_sport = new_sport;
37973a2dfbe8SMartin Willi um->old_sport = x->encap->encap_sport;
37983a2dfbe8SMartin Willi um->reqid = x->props.reqid;
37993a2dfbe8SMartin Willi
3800053c095aSJohannes Berg nlmsg_end(skb, nlh);
3801053c095aSJohannes Berg return 0;
38023a2dfbe8SMartin Willi }
38033a2dfbe8SMartin Willi
xfrm_send_mapping(struct xfrm_state * x,xfrm_address_t * ipaddr,__be16 sport)38043a2dfbe8SMartin Willi static int xfrm_send_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr,
38053a2dfbe8SMartin Willi __be16 sport)
38063a2dfbe8SMartin Willi {
3807a6483b79SAlexey Dobriyan struct net *net = xs_net(x);
38083a2dfbe8SMartin Willi struct sk_buff *skb;
38092fc5f83bSGustavo A. R. Silva int err;
38103a2dfbe8SMartin Willi
38113a2dfbe8SMartin Willi if (x->id.proto != IPPROTO_ESP)
38123a2dfbe8SMartin Willi return -EINVAL;
38133a2dfbe8SMartin Willi
38143a2dfbe8SMartin Willi if (!x->encap)
38153a2dfbe8SMartin Willi return -EINVAL;
38163a2dfbe8SMartin Willi
38173a2dfbe8SMartin Willi skb = nlmsg_new(xfrm_mapping_msgsize(), GFP_ATOMIC);
38183a2dfbe8SMartin Willi if (skb == NULL)
38193a2dfbe8SMartin Willi return -ENOMEM;
38203a2dfbe8SMartin Willi
38212fc5f83bSGustavo A. R. Silva err = build_mapping(skb, x, ipaddr, sport);
38222fc5f83bSGustavo A. R. Silva BUG_ON(err < 0);
38233a2dfbe8SMartin Willi
382421ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_MAPPING);
38253a2dfbe8SMartin Willi }
38263a2dfbe8SMartin Willi
xfrm_is_alive(const struct km_event * c)38270f24558eSHoria Geanta static bool xfrm_is_alive(const struct km_event *c)
38280f24558eSHoria Geanta {
38290f24558eSHoria Geanta return (bool)xfrm_acquire_is_on(c->net);
38300f24558eSHoria Geanta }
38310f24558eSHoria Geanta
38321da177e4SLinus Torvalds static struct xfrm_mgr netlink_mgr = {
38331da177e4SLinus Torvalds .notify = xfrm_send_state_notify,
38341da177e4SLinus Torvalds .acquire = xfrm_send_acquire,
38351da177e4SLinus Torvalds .compile_policy = xfrm_compile_policy,
38361da177e4SLinus Torvalds .notify_policy = xfrm_send_policy_notify,
383797a64b45SMasahide NAKAMURA .report = xfrm_send_report,
38385c79de6eSShinta Sugimoto .migrate = xfrm_send_migrate,
38393a2dfbe8SMartin Willi .new_mapping = xfrm_send_mapping,
38400f24558eSHoria Geanta .is_alive = xfrm_is_alive,
38411da177e4SLinus Torvalds };
38421da177e4SLinus Torvalds
xfrm_user_net_init(struct net * net)3843a6483b79SAlexey Dobriyan static int __net_init xfrm_user_net_init(struct net *net)
38441da177e4SLinus Torvalds {
3845be33690dSPatrick McHardy struct sock *nlsk;
3846a31f2d17SPablo Neira Ayuso struct netlink_kernel_cfg cfg = {
3847a31f2d17SPablo Neira Ayuso .groups = XFRMNLGRP_MAX,
3848a31f2d17SPablo Neira Ayuso .input = xfrm_netlink_rcv,
3849a31f2d17SPablo Neira Ayuso };
3850be33690dSPatrick McHardy
38519f00d977SPablo Neira Ayuso nlsk = netlink_kernel_create(net, NETLINK_XFRM, &cfg);
3852be33690dSPatrick McHardy if (nlsk == NULL)
38531da177e4SLinus Torvalds return -ENOMEM;
3854d79d792eSEric W. Biederman net->xfrm.nlsk_stash = nlsk; /* Don't set to NULL */
3855cf778b00SEric Dumazet rcu_assign_pointer(net->xfrm.nlsk, nlsk);
38561da177e4SLinus Torvalds return 0;
38571da177e4SLinus Torvalds }
38581da177e4SLinus Torvalds
xfrm_user_net_pre_exit(struct net * net)38596218fe18SFlorian Westphal static void __net_exit xfrm_user_net_pre_exit(struct net *net)
38606218fe18SFlorian Westphal {
38616218fe18SFlorian Westphal RCU_INIT_POINTER(net->xfrm.nlsk, NULL);
38626218fe18SFlorian Westphal }
38636218fe18SFlorian Westphal
xfrm_user_net_exit(struct list_head * net_exit_list)3864d79d792eSEric W. Biederman static void __net_exit xfrm_user_net_exit(struct list_head *net_exit_list)
3865a6483b79SAlexey Dobriyan {
3866d79d792eSEric W. Biederman struct net *net;
38676218fe18SFlorian Westphal
3868d79d792eSEric W. Biederman list_for_each_entry(net, net_exit_list, exit_list)
3869d79d792eSEric W. Biederman netlink_kernel_release(net->xfrm.nlsk_stash);
3870a6483b79SAlexey Dobriyan }
3871a6483b79SAlexey Dobriyan
3872a6483b79SAlexey Dobriyan static struct pernet_operations xfrm_user_net_ops = {
3873a6483b79SAlexey Dobriyan .init = xfrm_user_net_init,
38746218fe18SFlorian Westphal .pre_exit = xfrm_user_net_pre_exit,
3875d79d792eSEric W. Biederman .exit_batch = xfrm_user_net_exit,
3876a6483b79SAlexey Dobriyan };
3877a6483b79SAlexey Dobriyan
xfrm_user_init(void)3878a6483b79SAlexey Dobriyan static int __init xfrm_user_init(void)
3879a6483b79SAlexey Dobriyan {
3880a6483b79SAlexey Dobriyan int rv;
3881a6483b79SAlexey Dobriyan
3882a6483b79SAlexey Dobriyan printk(KERN_INFO "Initializing XFRM netlink socket\n");
3883a6483b79SAlexey Dobriyan
3884a6483b79SAlexey Dobriyan rv = register_pernet_subsys(&xfrm_user_net_ops);
3885a6483b79SAlexey Dobriyan if (rv < 0)
3886a6483b79SAlexey Dobriyan return rv;
3887f41b284aSZhengchao Shao xfrm_register_km(&netlink_mgr);
3888f41b284aSZhengchao Shao return 0;
3889a6483b79SAlexey Dobriyan }
3890a6483b79SAlexey Dobriyan
xfrm_user_exit(void)38911da177e4SLinus Torvalds static void __exit xfrm_user_exit(void)
38921da177e4SLinus Torvalds {
38931da177e4SLinus Torvalds xfrm_unregister_km(&netlink_mgr);
3894a6483b79SAlexey Dobriyan unregister_pernet_subsys(&xfrm_user_net_ops);
38951da177e4SLinus Torvalds }
38961da177e4SLinus Torvalds
38971da177e4SLinus Torvalds module_init(xfrm_user_init);
38981da177e4SLinus Torvalds module_exit(xfrm_user_exit);
38991da177e4SLinus Torvalds MODULE_LICENSE("GPL");
39004fdb3bb7SHarald Welte MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_XFRM);
3901