11da177e4SLinus Torvalds /* xfrm_user.c: User interface to configure xfrm engine. 21da177e4SLinus Torvalds * 31da177e4SLinus Torvalds * Copyright (C) 2002 David S. Miller (davem@redhat.com) 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Changes: 61da177e4SLinus Torvalds * Mitsuru KANDA @USAGI 71da177e4SLinus Torvalds * Kazunori MIYAZAWA @USAGI 81da177e4SLinus Torvalds * Kunihiro Ishiguro <kunihiro@ipinfusion.com> 91da177e4SLinus Torvalds * IPv6 support 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds */ 121da177e4SLinus Torvalds 139409f38aSHerbert Xu #include <linux/crypto.h> 141da177e4SLinus Torvalds #include <linux/module.h> 151da177e4SLinus Torvalds #include <linux/kernel.h> 161da177e4SLinus Torvalds #include <linux/types.h> 171da177e4SLinus Torvalds #include <linux/slab.h> 181da177e4SLinus Torvalds #include <linux/socket.h> 191da177e4SLinus Torvalds #include <linux/string.h> 201da177e4SLinus Torvalds #include <linux/net.h> 211da177e4SLinus Torvalds #include <linux/skbuff.h> 221da177e4SLinus Torvalds #include <linux/pfkeyv2.h> 231da177e4SLinus Torvalds #include <linux/ipsec.h> 241da177e4SLinus Torvalds #include <linux/init.h> 251da177e4SLinus Torvalds #include <linux/security.h> 261da177e4SLinus Torvalds #include <net/sock.h> 271da177e4SLinus Torvalds #include <net/xfrm.h> 2888fc2c84SThomas Graf #include <net/netlink.h> 29fa6dd8a2SNicolas Dichtel #include <net/ah.h> 307c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 31dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 32e23c7194SMasahide NAKAMURA #include <linux/in6.h> 33e23c7194SMasahide NAKAMURA #endif 34e33d4f13SSowmini Varadhan #include <asm/unaligned.h> 351da177e4SLinus Torvalds 365424f32eSThomas Graf static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type) 371da177e4SLinus Torvalds { 385424f32eSThomas Graf struct nlattr *rt = attrs[type]; 391da177e4SLinus Torvalds struct xfrm_algo *algp; 401da177e4SLinus Torvalds 411da177e4SLinus Torvalds if (!rt) 421da177e4SLinus Torvalds return 0; 431da177e4SLinus Torvalds 445424f32eSThomas Graf algp = nla_data(rt); 450f99be0dSEric Dumazet if (nla_len(rt) < xfrm_alg_len(algp)) 4631c26852SHerbert Xu return -EINVAL; 4731c26852SHerbert Xu 481da177e4SLinus Torvalds switch (type) { 491da177e4SLinus Torvalds case XFRMA_ALG_AUTH: 501da177e4SLinus Torvalds case XFRMA_ALG_CRYPT: 511da177e4SLinus Torvalds case XFRMA_ALG_COMP: 521da177e4SLinus Torvalds break; 531da177e4SLinus Torvalds 541da177e4SLinus Torvalds default: 551da177e4SLinus Torvalds return -EINVAL; 563ff50b79SStephen Hemminger } 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0'; 591da177e4SLinus Torvalds return 0; 601da177e4SLinus Torvalds } 611da177e4SLinus Torvalds 624447bb33SMartin Willi static int verify_auth_trunc(struct nlattr **attrs) 634447bb33SMartin Willi { 644447bb33SMartin Willi struct nlattr *rt = attrs[XFRMA_ALG_AUTH_TRUNC]; 654447bb33SMartin Willi struct xfrm_algo_auth *algp; 664447bb33SMartin Willi 674447bb33SMartin Willi if (!rt) 684447bb33SMartin Willi return 0; 694447bb33SMartin Willi 704447bb33SMartin Willi algp = nla_data(rt); 714447bb33SMartin Willi if (nla_len(rt) < xfrm_alg_auth_len(algp)) 724447bb33SMartin Willi return -EINVAL; 734447bb33SMartin Willi 744447bb33SMartin Willi algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0'; 754447bb33SMartin Willi return 0; 764447bb33SMartin Willi } 774447bb33SMartin Willi 781a6509d9SHerbert Xu static int verify_aead(struct nlattr **attrs) 791a6509d9SHerbert Xu { 801a6509d9SHerbert Xu struct nlattr *rt = attrs[XFRMA_ALG_AEAD]; 811a6509d9SHerbert Xu struct xfrm_algo_aead *algp; 821a6509d9SHerbert Xu 831a6509d9SHerbert Xu if (!rt) 841a6509d9SHerbert Xu return 0; 851a6509d9SHerbert Xu 861a6509d9SHerbert Xu algp = nla_data(rt); 871a6509d9SHerbert Xu if (nla_len(rt) < aead_len(algp)) 881a6509d9SHerbert Xu return -EINVAL; 891a6509d9SHerbert Xu 901a6509d9SHerbert Xu algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0'; 911a6509d9SHerbert Xu return 0; 921a6509d9SHerbert Xu } 931a6509d9SHerbert Xu 945424f32eSThomas Graf static void verify_one_addr(struct nlattr **attrs, enum xfrm_attr_type_t type, 95eb2971b6SMasahide NAKAMURA xfrm_address_t **addrp) 96eb2971b6SMasahide NAKAMURA { 975424f32eSThomas Graf struct nlattr *rt = attrs[type]; 98eb2971b6SMasahide NAKAMURA 99cf5cb79fSThomas Graf if (rt && addrp) 1005424f32eSThomas Graf *addrp = nla_data(rt); 101eb2971b6SMasahide NAKAMURA } 102df71837dSTrent Jaeger 1035424f32eSThomas Graf static inline int verify_sec_ctx_len(struct nlattr **attrs) 104df71837dSTrent Jaeger { 1055424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 106df71837dSTrent Jaeger struct xfrm_user_sec_ctx *uctx; 107df71837dSTrent Jaeger 108df71837dSTrent Jaeger if (!rt) 109df71837dSTrent Jaeger return 0; 110df71837dSTrent Jaeger 1115424f32eSThomas Graf uctx = nla_data(rt); 112cf5cb79fSThomas Graf if (uctx->len != (sizeof(struct xfrm_user_sec_ctx) + uctx->ctx_len)) 113df71837dSTrent Jaeger return -EINVAL; 114df71837dSTrent Jaeger 115df71837dSTrent Jaeger return 0; 116df71837dSTrent Jaeger } 117df71837dSTrent Jaeger 118d8647b79SSteffen Klassert static inline int verify_replay(struct xfrm_usersa_info *p, 119d8647b79SSteffen Klassert struct nlattr **attrs) 120d8647b79SSteffen Klassert { 121d8647b79SSteffen Klassert struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL]; 122ecd79187SMathias Krause struct xfrm_replay_state_esn *rs; 123d8647b79SSteffen Klassert 124ecd79187SMathias Krause if (p->flags & XFRM_STATE_ESN) { 125ecd79187SMathias Krause if (!rt) 1267833aa05SSteffen Klassert return -EINVAL; 1277833aa05SSteffen Klassert 128ecd79187SMathias Krause rs = nla_data(rt); 129ecd79187SMathias Krause 130ecd79187SMathias Krause if (rs->bmp_len > XFRMA_REPLAY_ESN_MAX / sizeof(rs->bmp[0]) / 8) 131ecd79187SMathias Krause return -EINVAL; 132ecd79187SMathias Krause 133ecd79187SMathias Krause if (nla_len(rt) < xfrm_replay_state_esn_len(rs) && 134ecd79187SMathias Krause nla_len(rt) != sizeof(*rs)) 135ecd79187SMathias Krause return -EINVAL; 136ecd79187SMathias Krause } 137ecd79187SMathias Krause 138d8647b79SSteffen Klassert if (!rt) 139d8647b79SSteffen Klassert return 0; 140d8647b79SSteffen Klassert 14101714109SFan Du /* As only ESP and AH support ESN feature. */ 14201714109SFan Du if ((p->id.proto != IPPROTO_ESP) && (p->id.proto != IPPROTO_AH)) 14302aadf72SSteffen Klassert return -EINVAL; 14402aadf72SSteffen Klassert 145d8647b79SSteffen Klassert if (p->replay_window != 0) 146d8647b79SSteffen Klassert return -EINVAL; 147d8647b79SSteffen Klassert 148d8647b79SSteffen Klassert return 0; 149d8647b79SSteffen Klassert } 150df71837dSTrent Jaeger 1511da177e4SLinus Torvalds static int verify_newsa_info(struct xfrm_usersa_info *p, 1525424f32eSThomas Graf struct nlattr **attrs) 1531da177e4SLinus Torvalds { 1541da177e4SLinus Torvalds int err; 1551da177e4SLinus Torvalds 1561da177e4SLinus Torvalds err = -EINVAL; 1571da177e4SLinus Torvalds switch (p->family) { 1581da177e4SLinus Torvalds case AF_INET: 1591da177e4SLinus Torvalds break; 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds case AF_INET6: 162dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 1631da177e4SLinus Torvalds break; 1641da177e4SLinus Torvalds #else 1651da177e4SLinus Torvalds err = -EAFNOSUPPORT; 1661da177e4SLinus Torvalds goto out; 1671da177e4SLinus Torvalds #endif 1681da177e4SLinus Torvalds 1691da177e4SLinus Torvalds default: 1701da177e4SLinus Torvalds goto out; 1713ff50b79SStephen Hemminger } 1721da177e4SLinus Torvalds 1731da177e4SLinus Torvalds err = -EINVAL; 1741da177e4SLinus Torvalds switch (p->id.proto) { 1751da177e4SLinus Torvalds case IPPROTO_AH: 1764447bb33SMartin Willi if ((!attrs[XFRMA_ALG_AUTH] && 1774447bb33SMartin Willi !attrs[XFRMA_ALG_AUTH_TRUNC]) || 1781a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] || 17935a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT] || 18035d2856bSMartin Willi attrs[XFRMA_ALG_COMP] || 181a0e5ef53STobias Brunner attrs[XFRMA_TFCPAD]) 1821da177e4SLinus Torvalds goto out; 1831da177e4SLinus Torvalds break; 1841da177e4SLinus Torvalds 1851da177e4SLinus Torvalds case IPPROTO_ESP: 1861a6509d9SHerbert Xu if (attrs[XFRMA_ALG_COMP]) 1871a6509d9SHerbert Xu goto out; 1881a6509d9SHerbert Xu if (!attrs[XFRMA_ALG_AUTH] && 1894447bb33SMartin Willi !attrs[XFRMA_ALG_AUTH_TRUNC] && 1901a6509d9SHerbert Xu !attrs[XFRMA_ALG_CRYPT] && 1911a6509d9SHerbert Xu !attrs[XFRMA_ALG_AEAD]) 1921a6509d9SHerbert Xu goto out; 1931a6509d9SHerbert Xu if ((attrs[XFRMA_ALG_AUTH] || 1944447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC] || 1951a6509d9SHerbert Xu attrs[XFRMA_ALG_CRYPT]) && 1961a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD]) 1971da177e4SLinus Torvalds goto out; 19835d2856bSMartin Willi if (attrs[XFRMA_TFCPAD] && 19935d2856bSMartin Willi p->mode != XFRM_MODE_TUNNEL) 20035d2856bSMartin Willi goto out; 2011da177e4SLinus Torvalds break; 2021da177e4SLinus Torvalds 2031da177e4SLinus Torvalds case IPPROTO_COMP: 20435a7aa08SThomas Graf if (!attrs[XFRMA_ALG_COMP] || 2051a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] || 20635a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH] || 2074447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC] || 20835d2856bSMartin Willi attrs[XFRMA_ALG_CRYPT] || 209a0e5ef53STobias Brunner attrs[XFRMA_TFCPAD] || 210a0e5ef53STobias Brunner (ntohl(p->id.spi) >= 0x10000)) 2111da177e4SLinus Torvalds goto out; 2121da177e4SLinus Torvalds break; 2131da177e4SLinus Torvalds 214dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 215e23c7194SMasahide NAKAMURA case IPPROTO_DSTOPTS: 216e23c7194SMasahide NAKAMURA case IPPROTO_ROUTING: 21735a7aa08SThomas Graf if (attrs[XFRMA_ALG_COMP] || 21835a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH] || 2194447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC] || 2201a6509d9SHerbert Xu attrs[XFRMA_ALG_AEAD] || 22135a7aa08SThomas Graf attrs[XFRMA_ALG_CRYPT] || 22235a7aa08SThomas Graf attrs[XFRMA_ENCAP] || 22335a7aa08SThomas Graf attrs[XFRMA_SEC_CTX] || 22435d2856bSMartin Willi attrs[XFRMA_TFCPAD] || 22535a7aa08SThomas Graf !attrs[XFRMA_COADDR]) 226e23c7194SMasahide NAKAMURA goto out; 227e23c7194SMasahide NAKAMURA break; 228e23c7194SMasahide NAKAMURA #endif 229e23c7194SMasahide NAKAMURA 2301da177e4SLinus Torvalds default: 2311da177e4SLinus Torvalds goto out; 2323ff50b79SStephen Hemminger } 2331da177e4SLinus Torvalds 2341a6509d9SHerbert Xu if ((err = verify_aead(attrs))) 2351a6509d9SHerbert Xu goto out; 2364447bb33SMartin Willi if ((err = verify_auth_trunc(attrs))) 2374447bb33SMartin Willi goto out; 23835a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_AUTH))) 2391da177e4SLinus Torvalds goto out; 24035a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_CRYPT))) 2411da177e4SLinus Torvalds goto out; 24235a7aa08SThomas Graf if ((err = verify_one_alg(attrs, XFRMA_ALG_COMP))) 2431da177e4SLinus Torvalds goto out; 24435a7aa08SThomas Graf if ((err = verify_sec_ctx_len(attrs))) 245df71837dSTrent Jaeger goto out; 246d8647b79SSteffen Klassert if ((err = verify_replay(p, attrs))) 247d8647b79SSteffen Klassert goto out; 2481da177e4SLinus Torvalds 2491da177e4SLinus Torvalds err = -EINVAL; 2501da177e4SLinus Torvalds switch (p->mode) { 2517e49e6deSMasahide NAKAMURA case XFRM_MODE_TRANSPORT: 2527e49e6deSMasahide NAKAMURA case XFRM_MODE_TUNNEL: 253060f02a3SNoriaki TAKAMIYA case XFRM_MODE_ROUTEOPTIMIZATION: 2540a69452cSDiego Beltrami case XFRM_MODE_BEET: 2551da177e4SLinus Torvalds break; 2561da177e4SLinus Torvalds 2571da177e4SLinus Torvalds default: 2581da177e4SLinus Torvalds goto out; 2593ff50b79SStephen Hemminger } 2601da177e4SLinus Torvalds 2611da177e4SLinus Torvalds err = 0; 2621da177e4SLinus Torvalds 2631da177e4SLinus Torvalds out: 2641da177e4SLinus Torvalds return err; 2651da177e4SLinus Torvalds } 2661da177e4SLinus Torvalds 2671da177e4SLinus Torvalds static int attach_one_algo(struct xfrm_algo **algpp, u8 *props, 2686f2f19edSDavid S. Miller struct xfrm_algo_desc *(*get_byname)(const char *, int), 2695424f32eSThomas Graf struct nlattr *rta) 2701da177e4SLinus Torvalds { 2711da177e4SLinus Torvalds struct xfrm_algo *p, *ualg; 2721da177e4SLinus Torvalds struct xfrm_algo_desc *algo; 2731da177e4SLinus Torvalds 2741da177e4SLinus Torvalds if (!rta) 2751da177e4SLinus Torvalds return 0; 2761da177e4SLinus Torvalds 2775424f32eSThomas Graf ualg = nla_data(rta); 2781da177e4SLinus Torvalds 2791da177e4SLinus Torvalds algo = get_byname(ualg->alg_name, 1); 2801da177e4SLinus Torvalds if (!algo) 2811da177e4SLinus Torvalds return -ENOSYS; 2821da177e4SLinus Torvalds *props = algo->desc.sadb_alg_id; 2831da177e4SLinus Torvalds 2840f99be0dSEric Dumazet p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL); 2851da177e4SLinus Torvalds if (!p) 2861da177e4SLinus Torvalds return -ENOMEM; 2871da177e4SLinus Torvalds 28804ff1260SHerbert Xu strcpy(p->alg_name, algo->name); 2891da177e4SLinus Torvalds *algpp = p; 2901da177e4SLinus Torvalds return 0; 2911da177e4SLinus Torvalds } 2921da177e4SLinus Torvalds 29369b0137fSHerbert Xu static int attach_crypt(struct xfrm_state *x, struct nlattr *rta) 29469b0137fSHerbert Xu { 29569b0137fSHerbert Xu struct xfrm_algo *p, *ualg; 29669b0137fSHerbert Xu struct xfrm_algo_desc *algo; 29769b0137fSHerbert Xu 29869b0137fSHerbert Xu if (!rta) 29969b0137fSHerbert Xu return 0; 30069b0137fSHerbert Xu 30169b0137fSHerbert Xu ualg = nla_data(rta); 30269b0137fSHerbert Xu 30369b0137fSHerbert Xu algo = xfrm_ealg_get_byname(ualg->alg_name, 1); 30469b0137fSHerbert Xu if (!algo) 30569b0137fSHerbert Xu return -ENOSYS; 30669b0137fSHerbert Xu x->props.ealgo = algo->desc.sadb_alg_id; 30769b0137fSHerbert Xu 30869b0137fSHerbert Xu p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL); 30969b0137fSHerbert Xu if (!p) 31069b0137fSHerbert Xu return -ENOMEM; 31169b0137fSHerbert Xu 31269b0137fSHerbert Xu strcpy(p->alg_name, algo->name); 31369b0137fSHerbert Xu x->ealg = p; 31469b0137fSHerbert Xu x->geniv = algo->uinfo.encr.geniv; 31569b0137fSHerbert Xu return 0; 31669b0137fSHerbert Xu } 31769b0137fSHerbert Xu 3184447bb33SMartin Willi static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props, 3194447bb33SMartin Willi struct nlattr *rta) 3204447bb33SMartin Willi { 3214447bb33SMartin Willi struct xfrm_algo *ualg; 3224447bb33SMartin Willi struct xfrm_algo_auth *p; 3234447bb33SMartin Willi struct xfrm_algo_desc *algo; 3244447bb33SMartin Willi 3254447bb33SMartin Willi if (!rta) 3264447bb33SMartin Willi return 0; 3274447bb33SMartin Willi 3284447bb33SMartin Willi ualg = nla_data(rta); 3294447bb33SMartin Willi 3304447bb33SMartin Willi algo = xfrm_aalg_get_byname(ualg->alg_name, 1); 3314447bb33SMartin Willi if (!algo) 3324447bb33SMartin Willi return -ENOSYS; 3334447bb33SMartin Willi *props = algo->desc.sadb_alg_id; 3344447bb33SMartin Willi 3354447bb33SMartin Willi p = kmalloc(sizeof(*p) + (ualg->alg_key_len + 7) / 8, GFP_KERNEL); 3364447bb33SMartin Willi if (!p) 3374447bb33SMartin Willi return -ENOMEM; 3384447bb33SMartin Willi 3394447bb33SMartin Willi strcpy(p->alg_name, algo->name); 3404447bb33SMartin Willi p->alg_key_len = ualg->alg_key_len; 3414447bb33SMartin Willi p->alg_trunc_len = algo->uinfo.auth.icv_truncbits; 3424447bb33SMartin Willi memcpy(p->alg_key, ualg->alg_key, (ualg->alg_key_len + 7) / 8); 3434447bb33SMartin Willi 3444447bb33SMartin Willi *algpp = p; 3454447bb33SMartin Willi return 0; 3464447bb33SMartin Willi } 3474447bb33SMartin Willi 3484447bb33SMartin Willi static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props, 3494447bb33SMartin Willi struct nlattr *rta) 3504447bb33SMartin Willi { 3514447bb33SMartin Willi struct xfrm_algo_auth *p, *ualg; 3524447bb33SMartin Willi struct xfrm_algo_desc *algo; 3534447bb33SMartin Willi 3544447bb33SMartin Willi if (!rta) 3554447bb33SMartin Willi return 0; 3564447bb33SMartin Willi 3574447bb33SMartin Willi ualg = nla_data(rta); 3584447bb33SMartin Willi 3594447bb33SMartin Willi algo = xfrm_aalg_get_byname(ualg->alg_name, 1); 3604447bb33SMartin Willi if (!algo) 3614447bb33SMartin Willi return -ENOSYS; 362689f1c9dSHerbert Xu if (ualg->alg_trunc_len > algo->uinfo.auth.icv_fullbits) 3634447bb33SMartin Willi return -EINVAL; 3644447bb33SMartin Willi *props = algo->desc.sadb_alg_id; 3654447bb33SMartin Willi 3664447bb33SMartin Willi p = kmemdup(ualg, xfrm_alg_auth_len(ualg), GFP_KERNEL); 3674447bb33SMartin Willi if (!p) 3684447bb33SMartin Willi return -ENOMEM; 3694447bb33SMartin Willi 3704447bb33SMartin Willi strcpy(p->alg_name, algo->name); 3714447bb33SMartin Willi if (!p->alg_trunc_len) 3724447bb33SMartin Willi p->alg_trunc_len = algo->uinfo.auth.icv_truncbits; 3734447bb33SMartin Willi 3744447bb33SMartin Willi *algpp = p; 3754447bb33SMartin Willi return 0; 3764447bb33SMartin Willi } 3774447bb33SMartin Willi 37869b0137fSHerbert Xu static int attach_aead(struct xfrm_state *x, struct nlattr *rta) 3791a6509d9SHerbert Xu { 3801a6509d9SHerbert Xu struct xfrm_algo_aead *p, *ualg; 3811a6509d9SHerbert Xu struct xfrm_algo_desc *algo; 3821a6509d9SHerbert Xu 3831a6509d9SHerbert Xu if (!rta) 3841a6509d9SHerbert Xu return 0; 3851a6509d9SHerbert Xu 3861a6509d9SHerbert Xu ualg = nla_data(rta); 3871a6509d9SHerbert Xu 3881a6509d9SHerbert Xu algo = xfrm_aead_get_byname(ualg->alg_name, ualg->alg_icv_len, 1); 3891a6509d9SHerbert Xu if (!algo) 3901a6509d9SHerbert Xu return -ENOSYS; 39169b0137fSHerbert Xu x->props.ealgo = algo->desc.sadb_alg_id; 3921a6509d9SHerbert Xu 3931a6509d9SHerbert Xu p = kmemdup(ualg, aead_len(ualg), GFP_KERNEL); 3941a6509d9SHerbert Xu if (!p) 3951a6509d9SHerbert Xu return -ENOMEM; 3961a6509d9SHerbert Xu 3971a6509d9SHerbert Xu strcpy(p->alg_name, algo->name); 39869b0137fSHerbert Xu x->aead = p; 39969b0137fSHerbert Xu x->geniv = algo->uinfo.aead.geniv; 4001a6509d9SHerbert Xu return 0; 4011a6509d9SHerbert Xu } 4021a6509d9SHerbert Xu 403e2b19125SSteffen Klassert static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_esn, 404e2b19125SSteffen Klassert struct nlattr *rp) 405e2b19125SSteffen Klassert { 406e2b19125SSteffen Klassert struct xfrm_replay_state_esn *up; 407ecd79187SMathias Krause int ulen; 408e2b19125SSteffen Klassert 409e2b19125SSteffen Klassert if (!replay_esn || !rp) 410e2b19125SSteffen Klassert return 0; 411e2b19125SSteffen Klassert 412e2b19125SSteffen Klassert up = nla_data(rp); 413ecd79187SMathias Krause ulen = xfrm_replay_state_esn_len(up); 414e2b19125SSteffen Klassert 415ecd79187SMathias Krause if (nla_len(rp) < ulen || xfrm_replay_state_esn_len(replay_esn) != ulen) 416e2b19125SSteffen Klassert return -EINVAL; 417e2b19125SSteffen Klassert 418*677e806dSAndy Whitcroft if (up->replay_window > up->bmp_len * sizeof(__u32) * 8) 419*677e806dSAndy Whitcroft return -EINVAL; 420*677e806dSAndy Whitcroft 421e2b19125SSteffen Klassert return 0; 422e2b19125SSteffen Klassert } 423e2b19125SSteffen Klassert 424d8647b79SSteffen Klassert static int xfrm_alloc_replay_state_esn(struct xfrm_replay_state_esn **replay_esn, 425d8647b79SSteffen Klassert struct xfrm_replay_state_esn **preplay_esn, 426d8647b79SSteffen Klassert struct nlattr *rta) 427d8647b79SSteffen Klassert { 428d8647b79SSteffen Klassert struct xfrm_replay_state_esn *p, *pp, *up; 429ecd79187SMathias Krause int klen, ulen; 430d8647b79SSteffen Klassert 431d8647b79SSteffen Klassert if (!rta) 432d8647b79SSteffen Klassert return 0; 433d8647b79SSteffen Klassert 434d8647b79SSteffen Klassert up = nla_data(rta); 435ecd79187SMathias Krause klen = xfrm_replay_state_esn_len(up); 436ecd79187SMathias Krause ulen = nla_len(rta) >= klen ? klen : sizeof(*up); 437d8647b79SSteffen Klassert 438ecd79187SMathias Krause p = kzalloc(klen, GFP_KERNEL); 439d8647b79SSteffen Klassert if (!p) 440d8647b79SSteffen Klassert return -ENOMEM; 441d8647b79SSteffen Klassert 442ecd79187SMathias Krause pp = kzalloc(klen, GFP_KERNEL); 443d8647b79SSteffen Klassert if (!pp) { 444d8647b79SSteffen Klassert kfree(p); 445d8647b79SSteffen Klassert return -ENOMEM; 446d8647b79SSteffen Klassert } 447d8647b79SSteffen Klassert 448ecd79187SMathias Krause memcpy(p, up, ulen); 449ecd79187SMathias Krause memcpy(pp, up, ulen); 450ecd79187SMathias Krause 451d8647b79SSteffen Klassert *replay_esn = p; 452d8647b79SSteffen Klassert *preplay_esn = pp; 453d8647b79SSteffen Klassert 454d8647b79SSteffen Klassert return 0; 455d8647b79SSteffen Klassert } 456d8647b79SSteffen Klassert 457661697f7SJoy Latten static inline int xfrm_user_sec_ctx_size(struct xfrm_sec_ctx *xfrm_ctx) 458df71837dSTrent Jaeger { 459df71837dSTrent Jaeger int len = 0; 460df71837dSTrent Jaeger 461df71837dSTrent Jaeger if (xfrm_ctx) { 462df71837dSTrent Jaeger len += sizeof(struct xfrm_user_sec_ctx); 463df71837dSTrent Jaeger len += xfrm_ctx->ctx_len; 464df71837dSTrent Jaeger } 465df71837dSTrent Jaeger return len; 466df71837dSTrent Jaeger } 467df71837dSTrent Jaeger 4681da177e4SLinus Torvalds static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p) 4691da177e4SLinus Torvalds { 4701da177e4SLinus Torvalds memcpy(&x->id, &p->id, sizeof(x->id)); 4711da177e4SLinus Torvalds memcpy(&x->sel, &p->sel, sizeof(x->sel)); 4721da177e4SLinus Torvalds memcpy(&x->lft, &p->lft, sizeof(x->lft)); 4731da177e4SLinus Torvalds x->props.mode = p->mode; 47433fce60dSFan Du x->props.replay_window = min_t(unsigned int, p->replay_window, 47533fce60dSFan Du sizeof(x->replay.bitmap) * 8); 4761da177e4SLinus Torvalds x->props.reqid = p->reqid; 4771da177e4SLinus Torvalds x->props.family = p->family; 47854489c14SDavid S. Miller memcpy(&x->props.saddr, &p->saddr, sizeof(x->props.saddr)); 4791da177e4SLinus Torvalds x->props.flags = p->flags; 480196b0036SHerbert Xu 481ccf9b3b8SSteffen Klassert if (!x->sel.family && !(p->flags & XFRM_STATE_AF_UNSPEC)) 482196b0036SHerbert Xu x->sel.family = p->family; 4831da177e4SLinus Torvalds } 4841da177e4SLinus Torvalds 485d51d081dSJamal Hadi Salim /* 486d51d081dSJamal Hadi Salim * someday when pfkey also has support, we could have the code 487d51d081dSJamal Hadi Salim * somehow made shareable and move it to xfrm_state.c - JHS 488d51d081dSJamal Hadi Salim * 489d51d081dSJamal Hadi Salim */ 490e3ac104dSMathias Krause static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs, 491e3ac104dSMathias Krause int update_esn) 492d51d081dSJamal Hadi Salim { 4935424f32eSThomas Graf struct nlattr *rp = attrs[XFRMA_REPLAY_VAL]; 494e3ac104dSMathias Krause struct nlattr *re = update_esn ? attrs[XFRMA_REPLAY_ESN_VAL] : NULL; 4955424f32eSThomas Graf struct nlattr *lt = attrs[XFRMA_LTIME_VAL]; 4965424f32eSThomas Graf struct nlattr *et = attrs[XFRMA_ETIMER_THRESH]; 4975424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH]; 498d51d081dSJamal Hadi Salim 499d8647b79SSteffen Klassert if (re) { 500d8647b79SSteffen Klassert struct xfrm_replay_state_esn *replay_esn; 501d8647b79SSteffen Klassert replay_esn = nla_data(re); 502d8647b79SSteffen Klassert memcpy(x->replay_esn, replay_esn, 503d8647b79SSteffen Klassert xfrm_replay_state_esn_len(replay_esn)); 504d8647b79SSteffen Klassert memcpy(x->preplay_esn, replay_esn, 505d8647b79SSteffen Klassert xfrm_replay_state_esn_len(replay_esn)); 506d8647b79SSteffen Klassert } 507d8647b79SSteffen Klassert 508d51d081dSJamal Hadi Salim if (rp) { 509d51d081dSJamal Hadi Salim struct xfrm_replay_state *replay; 5105424f32eSThomas Graf replay = nla_data(rp); 511d51d081dSJamal Hadi Salim memcpy(&x->replay, replay, sizeof(*replay)); 512d51d081dSJamal Hadi Salim memcpy(&x->preplay, replay, sizeof(*replay)); 513d51d081dSJamal Hadi Salim } 514d51d081dSJamal Hadi Salim 515d51d081dSJamal Hadi Salim if (lt) { 516d51d081dSJamal Hadi Salim struct xfrm_lifetime_cur *ltime; 5175424f32eSThomas Graf ltime = nla_data(lt); 518d51d081dSJamal Hadi Salim x->curlft.bytes = ltime->bytes; 519d51d081dSJamal Hadi Salim x->curlft.packets = ltime->packets; 520d51d081dSJamal Hadi Salim x->curlft.add_time = ltime->add_time; 521d51d081dSJamal Hadi Salim x->curlft.use_time = ltime->use_time; 522d51d081dSJamal Hadi Salim } 523d51d081dSJamal Hadi Salim 524cf5cb79fSThomas Graf if (et) 5255424f32eSThomas Graf x->replay_maxage = nla_get_u32(et); 526d51d081dSJamal Hadi Salim 527cf5cb79fSThomas Graf if (rt) 5285424f32eSThomas Graf x->replay_maxdiff = nla_get_u32(rt); 529d51d081dSJamal Hadi Salim } 530d51d081dSJamal Hadi Salim 531fc34acd3SAlexey Dobriyan static struct xfrm_state *xfrm_state_construct(struct net *net, 532fc34acd3SAlexey Dobriyan struct xfrm_usersa_info *p, 5335424f32eSThomas Graf struct nlattr **attrs, 5341da177e4SLinus Torvalds int *errp) 5351da177e4SLinus Torvalds { 536fc34acd3SAlexey Dobriyan struct xfrm_state *x = xfrm_state_alloc(net); 5371da177e4SLinus Torvalds int err = -ENOMEM; 5381da177e4SLinus Torvalds 5391da177e4SLinus Torvalds if (!x) 5401da177e4SLinus Torvalds goto error_no_put; 5411da177e4SLinus Torvalds 5421da177e4SLinus Torvalds copy_from_user_state(x, p); 5431da177e4SLinus Torvalds 544a947b0a9SNicolas Dichtel if (attrs[XFRMA_SA_EXTRA_FLAGS]) 545a947b0a9SNicolas Dichtel x->props.extra_flags = nla_get_u32(attrs[XFRMA_SA_EXTRA_FLAGS]); 546a947b0a9SNicolas Dichtel 54769b0137fSHerbert Xu if ((err = attach_aead(x, attrs[XFRMA_ALG_AEAD]))) 5481a6509d9SHerbert Xu goto error; 5494447bb33SMartin Willi if ((err = attach_auth_trunc(&x->aalg, &x->props.aalgo, 5504447bb33SMartin Willi attrs[XFRMA_ALG_AUTH_TRUNC]))) 5514447bb33SMartin Willi goto error; 5524447bb33SMartin Willi if (!x->props.aalgo) { 5534447bb33SMartin Willi if ((err = attach_auth(&x->aalg, &x->props.aalgo, 55435a7aa08SThomas Graf attrs[XFRMA_ALG_AUTH]))) 5551da177e4SLinus Torvalds goto error; 5564447bb33SMartin Willi } 55769b0137fSHerbert Xu if ((err = attach_crypt(x, attrs[XFRMA_ALG_CRYPT]))) 5581da177e4SLinus Torvalds goto error; 5591da177e4SLinus Torvalds if ((err = attach_one_algo(&x->calg, &x->props.calgo, 5601da177e4SLinus Torvalds xfrm_calg_get_byname, 56135a7aa08SThomas Graf attrs[XFRMA_ALG_COMP]))) 5621da177e4SLinus Torvalds goto error; 563fd21150aSThomas Graf 564fd21150aSThomas Graf if (attrs[XFRMA_ENCAP]) { 565fd21150aSThomas Graf x->encap = kmemdup(nla_data(attrs[XFRMA_ENCAP]), 566fd21150aSThomas Graf sizeof(*x->encap), GFP_KERNEL); 567fd21150aSThomas Graf if (x->encap == NULL) 5681da177e4SLinus Torvalds goto error; 569fd21150aSThomas Graf } 570fd21150aSThomas Graf 57135d2856bSMartin Willi if (attrs[XFRMA_TFCPAD]) 57235d2856bSMartin Willi x->tfcpad = nla_get_u32(attrs[XFRMA_TFCPAD]); 57335d2856bSMartin Willi 574fd21150aSThomas Graf if (attrs[XFRMA_COADDR]) { 575fd21150aSThomas Graf x->coaddr = kmemdup(nla_data(attrs[XFRMA_COADDR]), 576fd21150aSThomas Graf sizeof(*x->coaddr), GFP_KERNEL); 577fd21150aSThomas Graf if (x->coaddr == NULL) 578060f02a3SNoriaki TAKAMIYA goto error; 579fd21150aSThomas Graf } 580fd21150aSThomas Graf 5816f26b61eSJamal Hadi Salim xfrm_mark_get(attrs, &x->mark); 5826f26b61eSJamal Hadi Salim 583a454f0ccSWei Yongjun err = __xfrm_init_state(x, false); 5841da177e4SLinus Torvalds if (err) 5851da177e4SLinus Torvalds goto error; 5861da177e4SLinus Torvalds 5872f30ea50SMathias Krause if (attrs[XFRMA_SEC_CTX]) { 5882f30ea50SMathias Krause err = security_xfrm_state_alloc(x, 5892f30ea50SMathias Krause nla_data(attrs[XFRMA_SEC_CTX])); 5902f30ea50SMathias Krause if (err) 591df71837dSTrent Jaeger goto error; 5922f30ea50SMathias Krause } 593df71837dSTrent Jaeger 594d8647b79SSteffen Klassert if ((err = xfrm_alloc_replay_state_esn(&x->replay_esn, &x->preplay_esn, 595d8647b79SSteffen Klassert attrs[XFRMA_REPLAY_ESN_VAL]))) 596d8647b79SSteffen Klassert goto error; 597d8647b79SSteffen Klassert 5981da177e4SLinus Torvalds x->km.seq = p->seq; 599b27aeadbSAlexey Dobriyan x->replay_maxdiff = net->xfrm.sysctl_aevent_rseqth; 600d51d081dSJamal Hadi Salim /* sysctl_xfrm_aevent_etime is in 100ms units */ 601b27aeadbSAlexey Dobriyan x->replay_maxage = (net->xfrm.sysctl_aevent_etime*HZ)/XFRM_AE_ETH_M; 602d51d081dSJamal Hadi Salim 6039fdc4883SSteffen Klassert if ((err = xfrm_init_replay(x))) 6049fdc4883SSteffen Klassert goto error; 605d51d081dSJamal Hadi Salim 6069fdc4883SSteffen Klassert /* override default values from above */ 607e3ac104dSMathias Krause xfrm_update_ae_params(x, attrs, 0); 6081da177e4SLinus Torvalds 6091da177e4SLinus Torvalds return x; 6101da177e4SLinus Torvalds 6111da177e4SLinus Torvalds error: 6121da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 6131da177e4SLinus Torvalds xfrm_state_put(x); 6141da177e4SLinus Torvalds error_no_put: 6151da177e4SLinus Torvalds *errp = err; 6161da177e4SLinus Torvalds return NULL; 6171da177e4SLinus Torvalds } 6181da177e4SLinus Torvalds 61922e70050SChristoph Hellwig static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 6205424f32eSThomas Graf struct nlattr **attrs) 6211da177e4SLinus Torvalds { 622fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 6237b67c857SThomas Graf struct xfrm_usersa_info *p = nlmsg_data(nlh); 6241da177e4SLinus Torvalds struct xfrm_state *x; 6251da177e4SLinus Torvalds int err; 62626b15dadSJamal Hadi Salim struct km_event c; 6271da177e4SLinus Torvalds 62835a7aa08SThomas Graf err = verify_newsa_info(p, attrs); 6291da177e4SLinus Torvalds if (err) 6301da177e4SLinus Torvalds return err; 6311da177e4SLinus Torvalds 632fc34acd3SAlexey Dobriyan x = xfrm_state_construct(net, p, attrs, &err); 6331da177e4SLinus Torvalds if (!x) 6341da177e4SLinus Torvalds return err; 6351da177e4SLinus Torvalds 63626b15dadSJamal Hadi Salim xfrm_state_hold(x); 6371da177e4SLinus Torvalds if (nlh->nlmsg_type == XFRM_MSG_NEWSA) 6381da177e4SLinus Torvalds err = xfrm_state_add(x); 6391da177e4SLinus Torvalds else 6401da177e4SLinus Torvalds err = xfrm_state_update(x); 6411da177e4SLinus Torvalds 6422e71029eSTetsuo Handa xfrm_audit_state_add(x, err ? 0 : 1, true); 643161a09e7SJoy Latten 6441da177e4SLinus Torvalds if (err < 0) { 6451da177e4SLinus Torvalds x->km.state = XFRM_STATE_DEAD; 64621380b81SHerbert Xu __xfrm_state_put(x); 6477d6dfe1fSPatrick McHardy goto out; 6481da177e4SLinus Torvalds } 6491da177e4SLinus Torvalds 65026b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 65115e47304SEric W. Biederman c.portid = nlh->nlmsg_pid; 652f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 65326b15dadSJamal Hadi Salim 65426b15dadSJamal Hadi Salim km_state_notify(x, &c); 6557d6dfe1fSPatrick McHardy out: 65626b15dadSJamal Hadi Salim xfrm_state_put(x); 6571da177e4SLinus Torvalds return err; 6581da177e4SLinus Torvalds } 6591da177e4SLinus Torvalds 660fc34acd3SAlexey Dobriyan static struct xfrm_state *xfrm_user_state_lookup(struct net *net, 661fc34acd3SAlexey Dobriyan struct xfrm_usersa_id *p, 6625424f32eSThomas Graf struct nlattr **attrs, 663eb2971b6SMasahide NAKAMURA int *errp) 664eb2971b6SMasahide NAKAMURA { 665eb2971b6SMasahide NAKAMURA struct xfrm_state *x = NULL; 6666f26b61eSJamal Hadi Salim struct xfrm_mark m; 667eb2971b6SMasahide NAKAMURA int err; 6686f26b61eSJamal Hadi Salim u32 mark = xfrm_mark_get(attrs, &m); 669eb2971b6SMasahide NAKAMURA 670eb2971b6SMasahide NAKAMURA if (xfrm_id_proto_match(p->proto, IPSEC_PROTO_ANY)) { 671eb2971b6SMasahide NAKAMURA err = -ESRCH; 6726f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &p->daddr, p->spi, p->proto, p->family); 673eb2971b6SMasahide NAKAMURA } else { 674eb2971b6SMasahide NAKAMURA xfrm_address_t *saddr = NULL; 675eb2971b6SMasahide NAKAMURA 67635a7aa08SThomas Graf verify_one_addr(attrs, XFRMA_SRCADDR, &saddr); 677eb2971b6SMasahide NAKAMURA if (!saddr) { 678eb2971b6SMasahide NAKAMURA err = -EINVAL; 679eb2971b6SMasahide NAKAMURA goto out; 680eb2971b6SMasahide NAKAMURA } 681eb2971b6SMasahide NAKAMURA 6829abbffeeSMasahide NAKAMURA err = -ESRCH; 6836f26b61eSJamal Hadi Salim x = xfrm_state_lookup_byaddr(net, mark, 6846f26b61eSJamal Hadi Salim &p->daddr, saddr, 685221df1edSAlexey Dobriyan p->proto, p->family); 686eb2971b6SMasahide NAKAMURA } 687eb2971b6SMasahide NAKAMURA 688eb2971b6SMasahide NAKAMURA out: 689eb2971b6SMasahide NAKAMURA if (!x && errp) 690eb2971b6SMasahide NAKAMURA *errp = err; 691eb2971b6SMasahide NAKAMURA return x; 692eb2971b6SMasahide NAKAMURA } 693eb2971b6SMasahide NAKAMURA 69422e70050SChristoph Hellwig static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 6955424f32eSThomas Graf struct nlattr **attrs) 6961da177e4SLinus Torvalds { 697fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 6981da177e4SLinus Torvalds struct xfrm_state *x; 699eb2971b6SMasahide NAKAMURA int err = -ESRCH; 70026b15dadSJamal Hadi Salim struct km_event c; 7017b67c857SThomas Graf struct xfrm_usersa_id *p = nlmsg_data(nlh); 7021da177e4SLinus Torvalds 703fc34acd3SAlexey Dobriyan x = xfrm_user_state_lookup(net, p, attrs, &err); 7041da177e4SLinus Torvalds if (x == NULL) 705eb2971b6SMasahide NAKAMURA return err; 7061da177e4SLinus Torvalds 7076f68dc37SDavid S. Miller if ((err = security_xfrm_state_delete(x)) != 0) 708c8c05a8eSCatherine Zhang goto out; 709c8c05a8eSCatherine Zhang 7101da177e4SLinus Torvalds if (xfrm_state_kern(x)) { 711c8c05a8eSCatherine Zhang err = -EPERM; 712c8c05a8eSCatherine Zhang goto out; 7131da177e4SLinus Torvalds } 7141da177e4SLinus Torvalds 71526b15dadSJamal Hadi Salim err = xfrm_state_delete(x); 716161a09e7SJoy Latten 717c8c05a8eSCatherine Zhang if (err < 0) 718c8c05a8eSCatherine Zhang goto out; 71926b15dadSJamal Hadi Salim 72026b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 72115e47304SEric W. Biederman c.portid = nlh->nlmsg_pid; 722f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 72326b15dadSJamal Hadi Salim km_state_notify(x, &c); 7241da177e4SLinus Torvalds 725c8c05a8eSCatherine Zhang out: 7262e71029eSTetsuo Handa xfrm_audit_state_delete(x, err ? 0 : 1, true); 727c8c05a8eSCatherine Zhang xfrm_state_put(x); 72826b15dadSJamal Hadi Salim return err; 7291da177e4SLinus Torvalds } 7301da177e4SLinus Torvalds 7311da177e4SLinus Torvalds static void copy_to_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p) 7321da177e4SLinus Torvalds { 733f778a636SMathias Krause memset(p, 0, sizeof(*p)); 7341da177e4SLinus Torvalds memcpy(&p->id, &x->id, sizeof(p->id)); 7351da177e4SLinus Torvalds memcpy(&p->sel, &x->sel, sizeof(p->sel)); 7361da177e4SLinus Torvalds memcpy(&p->lft, &x->lft, sizeof(p->lft)); 7371da177e4SLinus Torvalds memcpy(&p->curlft, &x->curlft, sizeof(p->curlft)); 738e33d4f13SSowmini Varadhan put_unaligned(x->stats.replay_window, &p->stats.replay_window); 739e33d4f13SSowmini Varadhan put_unaligned(x->stats.replay, &p->stats.replay); 740e33d4f13SSowmini Varadhan put_unaligned(x->stats.integrity_failed, &p->stats.integrity_failed); 74154489c14SDavid S. Miller memcpy(&p->saddr, &x->props.saddr, sizeof(p->saddr)); 7421da177e4SLinus Torvalds p->mode = x->props.mode; 7431da177e4SLinus Torvalds p->replay_window = x->props.replay_window; 7441da177e4SLinus Torvalds p->reqid = x->props.reqid; 7451da177e4SLinus Torvalds p->family = x->props.family; 7461da177e4SLinus Torvalds p->flags = x->props.flags; 7471da177e4SLinus Torvalds p->seq = x->km.seq; 7481da177e4SLinus Torvalds } 7491da177e4SLinus Torvalds 7501da177e4SLinus Torvalds struct xfrm_dump_info { 7511da177e4SLinus Torvalds struct sk_buff *in_skb; 7521da177e4SLinus Torvalds struct sk_buff *out_skb; 7531da177e4SLinus Torvalds u32 nlmsg_seq; 7541da177e4SLinus Torvalds u16 nlmsg_flags; 7551da177e4SLinus Torvalds }; 7561da177e4SLinus Torvalds 757c0144beaSThomas Graf static int copy_sec_ctx(struct xfrm_sec_ctx *s, struct sk_buff *skb) 758c0144beaSThomas Graf { 759c0144beaSThomas Graf struct xfrm_user_sec_ctx *uctx; 760c0144beaSThomas Graf struct nlattr *attr; 76168325d3bSHerbert Xu int ctx_size = sizeof(*uctx) + s->ctx_len; 762c0144beaSThomas Graf 763c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_SEC_CTX, ctx_size); 764c0144beaSThomas Graf if (attr == NULL) 765c0144beaSThomas Graf return -EMSGSIZE; 766c0144beaSThomas Graf 767c0144beaSThomas Graf uctx = nla_data(attr); 768c0144beaSThomas Graf uctx->exttype = XFRMA_SEC_CTX; 769c0144beaSThomas Graf uctx->len = ctx_size; 770c0144beaSThomas Graf uctx->ctx_doi = s->ctx_doi; 771c0144beaSThomas Graf uctx->ctx_alg = s->ctx_alg; 772c0144beaSThomas Graf uctx->ctx_len = s->ctx_len; 773c0144beaSThomas Graf memcpy(uctx + 1, s->ctx_str, s->ctx_len); 774c0144beaSThomas Graf 775c0144beaSThomas Graf return 0; 776c0144beaSThomas Graf } 777c0144beaSThomas Graf 7784447bb33SMartin Willi static int copy_to_user_auth(struct xfrm_algo_auth *auth, struct sk_buff *skb) 7794447bb33SMartin Willi { 7804447bb33SMartin Willi struct xfrm_algo *algo; 7814447bb33SMartin Willi struct nlattr *nla; 7824447bb33SMartin Willi 7834447bb33SMartin Willi nla = nla_reserve(skb, XFRMA_ALG_AUTH, 7844447bb33SMartin Willi sizeof(*algo) + (auth->alg_key_len + 7) / 8); 7854447bb33SMartin Willi if (!nla) 7864447bb33SMartin Willi return -EMSGSIZE; 7874447bb33SMartin Willi 7884447bb33SMartin Willi algo = nla_data(nla); 7894c87308bSMathias Krause strncpy(algo->alg_name, auth->alg_name, sizeof(algo->alg_name)); 7904447bb33SMartin Willi memcpy(algo->alg_key, auth->alg_key, (auth->alg_key_len + 7) / 8); 7914447bb33SMartin Willi algo->alg_key_len = auth->alg_key_len; 7924447bb33SMartin Willi 7934447bb33SMartin Willi return 0; 7944447bb33SMartin Willi } 7954447bb33SMartin Willi 79668325d3bSHerbert Xu /* Don't change this without updating xfrm_sa_len! */ 79768325d3bSHerbert Xu static int copy_to_user_state_extra(struct xfrm_state *x, 79868325d3bSHerbert Xu struct xfrm_usersa_info *p, 79968325d3bSHerbert Xu struct sk_buff *skb) 8001da177e4SLinus Torvalds { 8011d1e34ddSDavid S. Miller int ret = 0; 8021d1e34ddSDavid S. Miller 8031da177e4SLinus Torvalds copy_to_user_state(x, p); 8041da177e4SLinus Torvalds 805a947b0a9SNicolas Dichtel if (x->props.extra_flags) { 806a947b0a9SNicolas Dichtel ret = nla_put_u32(skb, XFRMA_SA_EXTRA_FLAGS, 807a947b0a9SNicolas Dichtel x->props.extra_flags); 808a947b0a9SNicolas Dichtel if (ret) 809a947b0a9SNicolas Dichtel goto out; 810a947b0a9SNicolas Dichtel } 811a947b0a9SNicolas Dichtel 8121d1e34ddSDavid S. Miller if (x->coaddr) { 8131d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_COADDR, sizeof(*x->coaddr), x->coaddr); 8141d1e34ddSDavid S. Miller if (ret) 8151d1e34ddSDavid S. Miller goto out; 8161d1e34ddSDavid S. Miller } 8171d1e34ddSDavid S. Miller if (x->lastused) { 818de95c4a4SNicolas Dichtel ret = nla_put_u64_64bit(skb, XFRMA_LASTUSED, x->lastused, 819de95c4a4SNicolas Dichtel XFRMA_PAD); 8201d1e34ddSDavid S. Miller if (ret) 8211d1e34ddSDavid S. Miller goto out; 8221d1e34ddSDavid S. Miller } 8231d1e34ddSDavid S. Miller if (x->aead) { 8241d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_ALG_AEAD, aead_len(x->aead), x->aead); 8251d1e34ddSDavid S. Miller if (ret) 8261d1e34ddSDavid S. Miller goto out; 8271d1e34ddSDavid S. Miller } 8281d1e34ddSDavid S. Miller if (x->aalg) { 8291d1e34ddSDavid S. Miller ret = copy_to_user_auth(x->aalg, skb); 8301d1e34ddSDavid S. Miller if (!ret) 8311d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_ALG_AUTH_TRUNC, 8321d1e34ddSDavid S. Miller xfrm_alg_auth_len(x->aalg), x->aalg); 8331d1e34ddSDavid S. Miller if (ret) 8341d1e34ddSDavid S. Miller goto out; 8351d1e34ddSDavid S. Miller } 8361d1e34ddSDavid S. Miller if (x->ealg) { 8371d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_ALG_CRYPT, xfrm_alg_len(x->ealg), x->ealg); 8381d1e34ddSDavid S. Miller if (ret) 8391d1e34ddSDavid S. Miller goto out; 8401d1e34ddSDavid S. Miller } 8411d1e34ddSDavid S. Miller if (x->calg) { 8421d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_ALG_COMP, sizeof(*(x->calg)), x->calg); 8431d1e34ddSDavid S. Miller if (ret) 8441d1e34ddSDavid S. Miller goto out; 8451d1e34ddSDavid S. Miller } 8461d1e34ddSDavid S. Miller if (x->encap) { 8471d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_ENCAP, sizeof(*x->encap), x->encap); 8481d1e34ddSDavid S. Miller if (ret) 8491d1e34ddSDavid S. Miller goto out; 8501d1e34ddSDavid S. Miller } 8511d1e34ddSDavid S. Miller if (x->tfcpad) { 8521d1e34ddSDavid S. Miller ret = nla_put_u32(skb, XFRMA_TFCPAD, x->tfcpad); 8531d1e34ddSDavid S. Miller if (ret) 8541d1e34ddSDavid S. Miller goto out; 8551d1e34ddSDavid S. Miller } 8561d1e34ddSDavid S. Miller ret = xfrm_mark_put(skb, &x->mark); 8571d1e34ddSDavid S. Miller if (ret) 8581d1e34ddSDavid S. Miller goto out; 859f293a5e3Sdingzhi if (x->replay_esn) 8601d1e34ddSDavid S. Miller ret = nla_put(skb, XFRMA_REPLAY_ESN_VAL, 861d0fde795SDavid S. Miller xfrm_replay_state_esn_len(x->replay_esn), 8621d1e34ddSDavid S. Miller x->replay_esn); 863f293a5e3Sdingzhi else 864f293a5e3Sdingzhi ret = nla_put(skb, XFRMA_REPLAY_VAL, sizeof(x->replay), 865f293a5e3Sdingzhi &x->replay); 8661d1e34ddSDavid S. Miller if (ret) 8671d1e34ddSDavid S. Miller goto out; 8681d1e34ddSDavid S. Miller if (x->security) 8691d1e34ddSDavid S. Miller ret = copy_sec_ctx(x->security, skb); 8701d1e34ddSDavid S. Miller out: 8711d1e34ddSDavid S. Miller return ret; 87268325d3bSHerbert Xu } 87368325d3bSHerbert Xu 87468325d3bSHerbert Xu static int dump_one_state(struct xfrm_state *x, int count, void *ptr) 87568325d3bSHerbert Xu { 87668325d3bSHerbert Xu struct xfrm_dump_info *sp = ptr; 87768325d3bSHerbert Xu struct sk_buff *in_skb = sp->in_skb; 87868325d3bSHerbert Xu struct sk_buff *skb = sp->out_skb; 87968325d3bSHerbert Xu struct xfrm_usersa_info *p; 88068325d3bSHerbert Xu struct nlmsghdr *nlh; 88168325d3bSHerbert Xu int err; 88268325d3bSHerbert Xu 88315e47304SEric W. Biederman nlh = nlmsg_put(skb, NETLINK_CB(in_skb).portid, sp->nlmsg_seq, 88468325d3bSHerbert Xu XFRM_MSG_NEWSA, sizeof(*p), sp->nlmsg_flags); 88568325d3bSHerbert Xu if (nlh == NULL) 88668325d3bSHerbert Xu return -EMSGSIZE; 88768325d3bSHerbert Xu 88868325d3bSHerbert Xu p = nlmsg_data(nlh); 88968325d3bSHerbert Xu 89068325d3bSHerbert Xu err = copy_to_user_state_extra(x, p, skb); 8911d1e34ddSDavid S. Miller if (err) { 8929825069dSThomas Graf nlmsg_cancel(skb, nlh); 89368325d3bSHerbert Xu return err; 8941da177e4SLinus Torvalds } 8951d1e34ddSDavid S. Miller nlmsg_end(skb, nlh); 8961d1e34ddSDavid S. Miller return 0; 8971d1e34ddSDavid S. Miller } 8981da177e4SLinus Torvalds 8994c563f76STimo Teras static int xfrm_dump_sa_done(struct netlink_callback *cb) 9004c563f76STimo Teras { 9014c563f76STimo Teras struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1]; 902283bc9f3SFan Du struct sock *sk = cb->skb->sk; 903283bc9f3SFan Du struct net *net = sock_net(sk); 904283bc9f3SFan Du 9051ba5bf99SVegard Nossum if (cb->args[0]) 906283bc9f3SFan Du xfrm_state_walk_done(walk, net); 9074c563f76STimo Teras return 0; 9084c563f76STimo Teras } 9094c563f76STimo Teras 910d3623099SNicolas Dichtel static const struct nla_policy xfrma_policy[XFRMA_MAX+1]; 9111da177e4SLinus Torvalds static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb) 9121da177e4SLinus Torvalds { 913fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 9144c563f76STimo Teras struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1]; 9151da177e4SLinus Torvalds struct xfrm_dump_info info; 9161da177e4SLinus Torvalds 9174c563f76STimo Teras BUILD_BUG_ON(sizeof(struct xfrm_state_walk) > 9184c563f76STimo Teras sizeof(cb->args) - sizeof(cb->args[0])); 9194c563f76STimo Teras 9201da177e4SLinus Torvalds info.in_skb = cb->skb; 9211da177e4SLinus Torvalds info.out_skb = skb; 9221da177e4SLinus Torvalds info.nlmsg_seq = cb->nlh->nlmsg_seq; 9231da177e4SLinus Torvalds info.nlmsg_flags = NLM_F_MULTI; 9244c563f76STimo Teras 9254c563f76STimo Teras if (!cb->args[0]) { 926d3623099SNicolas Dichtel struct nlattr *attrs[XFRMA_MAX+1]; 927870a2df4SNicolas Dichtel struct xfrm_address_filter *filter = NULL; 928d3623099SNicolas Dichtel u8 proto = 0; 929d3623099SNicolas Dichtel int err; 930d3623099SNicolas Dichtel 931d3623099SNicolas Dichtel err = nlmsg_parse(cb->nlh, 0, attrs, XFRMA_MAX, 932d3623099SNicolas Dichtel xfrma_policy); 933d3623099SNicolas Dichtel if (err < 0) 934d3623099SNicolas Dichtel return err; 935d3623099SNicolas Dichtel 936870a2df4SNicolas Dichtel if (attrs[XFRMA_ADDRESS_FILTER]) { 937df367561SAndrzej Hajda filter = kmemdup(nla_data(attrs[XFRMA_ADDRESS_FILTER]), 938df367561SAndrzej Hajda sizeof(*filter), GFP_KERNEL); 939d3623099SNicolas Dichtel if (filter == NULL) 940d3623099SNicolas Dichtel return -ENOMEM; 941d3623099SNicolas Dichtel } 942d3623099SNicolas Dichtel 943d3623099SNicolas Dichtel if (attrs[XFRMA_PROTO]) 944d3623099SNicolas Dichtel proto = nla_get_u8(attrs[XFRMA_PROTO]); 945d3623099SNicolas Dichtel 946d3623099SNicolas Dichtel xfrm_state_walk_init(walk, proto, filter); 9471ba5bf99SVegard Nossum cb->args[0] = 1; 9484c563f76STimo Teras } 9494c563f76STimo Teras 950fc34acd3SAlexey Dobriyan (void) xfrm_state_walk(net, walk, dump_one_state, &info); 9511da177e4SLinus Torvalds 9521da177e4SLinus Torvalds return skb->len; 9531da177e4SLinus Torvalds } 9541da177e4SLinus Torvalds 9551da177e4SLinus Torvalds static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb, 9561da177e4SLinus Torvalds struct xfrm_state *x, u32 seq) 9571da177e4SLinus Torvalds { 9581da177e4SLinus Torvalds struct xfrm_dump_info info; 9591da177e4SLinus Torvalds struct sk_buff *skb; 960864745d2SMathias Krause int err; 9611da177e4SLinus Torvalds 9627deb2264SThomas Graf skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 9631da177e4SLinus Torvalds if (!skb) 9641da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 9651da177e4SLinus Torvalds 9661da177e4SLinus Torvalds info.in_skb = in_skb; 9671da177e4SLinus Torvalds info.out_skb = skb; 9681da177e4SLinus Torvalds info.nlmsg_seq = seq; 9691da177e4SLinus Torvalds info.nlmsg_flags = 0; 9701da177e4SLinus Torvalds 971864745d2SMathias Krause err = dump_one_state(x, 0, &info); 972864745d2SMathias Krause if (err) { 9731da177e4SLinus Torvalds kfree_skb(skb); 974864745d2SMathias Krause return ERR_PTR(err); 9751da177e4SLinus Torvalds } 9761da177e4SLinus Torvalds 9771da177e4SLinus Torvalds return skb; 9781da177e4SLinus Torvalds } 9791da177e4SLinus Torvalds 98021ee543eSMichal Kubecek /* A wrapper for nlmsg_multicast() checking that nlsk is still available. 98121ee543eSMichal Kubecek * Must be called with RCU read lock. 98221ee543eSMichal Kubecek */ 98321ee543eSMichal Kubecek static inline int xfrm_nlmsg_multicast(struct net *net, struct sk_buff *skb, 98421ee543eSMichal Kubecek u32 pid, unsigned int group) 98521ee543eSMichal Kubecek { 98621ee543eSMichal Kubecek struct sock *nlsk = rcu_dereference(net->xfrm.nlsk); 98721ee543eSMichal Kubecek 98821ee543eSMichal Kubecek if (nlsk) 98921ee543eSMichal Kubecek return nlmsg_multicast(nlsk, skb, pid, group, GFP_ATOMIC); 99021ee543eSMichal Kubecek else 99121ee543eSMichal Kubecek return -1; 99221ee543eSMichal Kubecek } 99321ee543eSMichal Kubecek 9947deb2264SThomas Graf static inline size_t xfrm_spdinfo_msgsize(void) 9957deb2264SThomas Graf { 9967deb2264SThomas Graf return NLMSG_ALIGN(4) 9977deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_spdinfo)) 998880a6fabSChristophe Gouault + nla_total_size(sizeof(struct xfrmu_spdhinfo)) 999880a6fabSChristophe Gouault + nla_total_size(sizeof(struct xfrmu_spdhthresh)) 1000880a6fabSChristophe Gouault + nla_total_size(sizeof(struct xfrmu_spdhthresh)); 10017deb2264SThomas Graf } 10027deb2264SThomas Graf 1003e071041bSAlexey Dobriyan static int build_spdinfo(struct sk_buff *skb, struct net *net, 100415e47304SEric W. Biederman u32 portid, u32 seq, u32 flags) 1005ecfd6b18SJamal Hadi Salim { 10065a6d3416SJamal Hadi Salim struct xfrmk_spdinfo si; 10075a6d3416SJamal Hadi Salim struct xfrmu_spdinfo spc; 10085a6d3416SJamal Hadi Salim struct xfrmu_spdhinfo sph; 1009880a6fabSChristophe Gouault struct xfrmu_spdhthresh spt4, spt6; 1010ecfd6b18SJamal Hadi Salim struct nlmsghdr *nlh; 10111d1e34ddSDavid S. Miller int err; 1012ecfd6b18SJamal Hadi Salim u32 *f; 1013880a6fabSChristophe Gouault unsigned lseq; 1014ecfd6b18SJamal Hadi Salim 101515e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0); 101625985edcSLucas De Marchi if (nlh == NULL) /* shouldn't really happen ... */ 1017ecfd6b18SJamal Hadi Salim return -EMSGSIZE; 1018ecfd6b18SJamal Hadi Salim 1019ecfd6b18SJamal Hadi Salim f = nlmsg_data(nlh); 1020ecfd6b18SJamal Hadi Salim *f = flags; 1021e071041bSAlexey Dobriyan xfrm_spd_getinfo(net, &si); 10225a6d3416SJamal Hadi Salim spc.incnt = si.incnt; 10235a6d3416SJamal Hadi Salim spc.outcnt = si.outcnt; 10245a6d3416SJamal Hadi Salim spc.fwdcnt = si.fwdcnt; 10255a6d3416SJamal Hadi Salim spc.inscnt = si.inscnt; 10265a6d3416SJamal Hadi Salim spc.outscnt = si.outscnt; 10275a6d3416SJamal Hadi Salim spc.fwdscnt = si.fwdscnt; 10285a6d3416SJamal Hadi Salim sph.spdhcnt = si.spdhcnt; 10295a6d3416SJamal Hadi Salim sph.spdhmcnt = si.spdhmcnt; 1030ecfd6b18SJamal Hadi Salim 1031880a6fabSChristophe Gouault do { 1032880a6fabSChristophe Gouault lseq = read_seqbegin(&net->xfrm.policy_hthresh.lock); 1033880a6fabSChristophe Gouault 1034880a6fabSChristophe Gouault spt4.lbits = net->xfrm.policy_hthresh.lbits4; 1035880a6fabSChristophe Gouault spt4.rbits = net->xfrm.policy_hthresh.rbits4; 1036880a6fabSChristophe Gouault spt6.lbits = net->xfrm.policy_hthresh.lbits6; 1037880a6fabSChristophe Gouault spt6.rbits = net->xfrm.policy_hthresh.rbits6; 1038880a6fabSChristophe Gouault } while (read_seqretry(&net->xfrm.policy_hthresh.lock, lseq)); 1039880a6fabSChristophe Gouault 10401d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_SPD_INFO, sizeof(spc), &spc); 10411d1e34ddSDavid S. Miller if (!err) 10421d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_SPD_HINFO, sizeof(sph), &sph); 1043880a6fabSChristophe Gouault if (!err) 1044880a6fabSChristophe Gouault err = nla_put(skb, XFRMA_SPD_IPV4_HTHRESH, sizeof(spt4), &spt4); 1045880a6fabSChristophe Gouault if (!err) 1046880a6fabSChristophe Gouault err = nla_put(skb, XFRMA_SPD_IPV6_HTHRESH, sizeof(spt6), &spt6); 10471d1e34ddSDavid S. Miller if (err) { 10481d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh); 10491d1e34ddSDavid S. Miller return err; 10501d1e34ddSDavid S. Miller } 1051ecfd6b18SJamal Hadi Salim 1052053c095aSJohannes Berg nlmsg_end(skb, nlh); 1053053c095aSJohannes Berg return 0; 1054ecfd6b18SJamal Hadi Salim } 1055ecfd6b18SJamal Hadi Salim 1056880a6fabSChristophe Gouault static int xfrm_set_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, 1057880a6fabSChristophe Gouault struct nlattr **attrs) 1058880a6fabSChristophe Gouault { 1059880a6fabSChristophe Gouault struct net *net = sock_net(skb->sk); 1060880a6fabSChristophe Gouault struct xfrmu_spdhthresh *thresh4 = NULL; 1061880a6fabSChristophe Gouault struct xfrmu_spdhthresh *thresh6 = NULL; 1062880a6fabSChristophe Gouault 1063880a6fabSChristophe Gouault /* selector prefixlen thresholds to hash policies */ 1064880a6fabSChristophe Gouault if (attrs[XFRMA_SPD_IPV4_HTHRESH]) { 1065880a6fabSChristophe Gouault struct nlattr *rta = attrs[XFRMA_SPD_IPV4_HTHRESH]; 1066880a6fabSChristophe Gouault 1067880a6fabSChristophe Gouault if (nla_len(rta) < sizeof(*thresh4)) 1068880a6fabSChristophe Gouault return -EINVAL; 1069880a6fabSChristophe Gouault thresh4 = nla_data(rta); 1070880a6fabSChristophe Gouault if (thresh4->lbits > 32 || thresh4->rbits > 32) 1071880a6fabSChristophe Gouault return -EINVAL; 1072880a6fabSChristophe Gouault } 1073880a6fabSChristophe Gouault if (attrs[XFRMA_SPD_IPV6_HTHRESH]) { 1074880a6fabSChristophe Gouault struct nlattr *rta = attrs[XFRMA_SPD_IPV6_HTHRESH]; 1075880a6fabSChristophe Gouault 1076880a6fabSChristophe Gouault if (nla_len(rta) < sizeof(*thresh6)) 1077880a6fabSChristophe Gouault return -EINVAL; 1078880a6fabSChristophe Gouault thresh6 = nla_data(rta); 1079880a6fabSChristophe Gouault if (thresh6->lbits > 128 || thresh6->rbits > 128) 1080880a6fabSChristophe Gouault return -EINVAL; 1081880a6fabSChristophe Gouault } 1082880a6fabSChristophe Gouault 1083880a6fabSChristophe Gouault if (thresh4 || thresh6) { 1084880a6fabSChristophe Gouault write_seqlock(&net->xfrm.policy_hthresh.lock); 1085880a6fabSChristophe Gouault if (thresh4) { 1086880a6fabSChristophe Gouault net->xfrm.policy_hthresh.lbits4 = thresh4->lbits; 1087880a6fabSChristophe Gouault net->xfrm.policy_hthresh.rbits4 = thresh4->rbits; 1088880a6fabSChristophe Gouault } 1089880a6fabSChristophe Gouault if (thresh6) { 1090880a6fabSChristophe Gouault net->xfrm.policy_hthresh.lbits6 = thresh6->lbits; 1091880a6fabSChristophe Gouault net->xfrm.policy_hthresh.rbits6 = thresh6->rbits; 1092880a6fabSChristophe Gouault } 1093880a6fabSChristophe Gouault write_sequnlock(&net->xfrm.policy_hthresh.lock); 1094880a6fabSChristophe Gouault 1095880a6fabSChristophe Gouault xfrm_policy_hash_rebuild(net); 1096880a6fabSChristophe Gouault } 1097880a6fabSChristophe Gouault 1098880a6fabSChristophe Gouault return 0; 1099880a6fabSChristophe Gouault } 1100880a6fabSChristophe Gouault 1101ecfd6b18SJamal Hadi Salim static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, 11025424f32eSThomas Graf struct nlattr **attrs) 1103ecfd6b18SJamal Hadi Salim { 1104a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk); 1105ecfd6b18SJamal Hadi Salim struct sk_buff *r_skb; 11067b67c857SThomas Graf u32 *flags = nlmsg_data(nlh); 110715e47304SEric W. Biederman u32 sportid = NETLINK_CB(skb).portid; 1108ecfd6b18SJamal Hadi Salim u32 seq = nlh->nlmsg_seq; 1109ecfd6b18SJamal Hadi Salim 11107deb2264SThomas Graf r_skb = nlmsg_new(xfrm_spdinfo_msgsize(), GFP_ATOMIC); 1111ecfd6b18SJamal Hadi Salim if (r_skb == NULL) 1112ecfd6b18SJamal Hadi Salim return -ENOMEM; 1113ecfd6b18SJamal Hadi Salim 111415e47304SEric W. Biederman if (build_spdinfo(r_skb, net, sportid, seq, *flags) < 0) 1115ecfd6b18SJamal Hadi Salim BUG(); 1116ecfd6b18SJamal Hadi Salim 111715e47304SEric W. Biederman return nlmsg_unicast(net->xfrm.nlsk, r_skb, sportid); 1118ecfd6b18SJamal Hadi Salim } 1119ecfd6b18SJamal Hadi Salim 11207deb2264SThomas Graf static inline size_t xfrm_sadinfo_msgsize(void) 11217deb2264SThomas Graf { 11227deb2264SThomas Graf return NLMSG_ALIGN(4) 11237deb2264SThomas Graf + nla_total_size(sizeof(struct xfrmu_sadhinfo)) 11247deb2264SThomas Graf + nla_total_size(4); /* XFRMA_SAD_CNT */ 11257deb2264SThomas Graf } 11267deb2264SThomas Graf 1127e071041bSAlexey Dobriyan static int build_sadinfo(struct sk_buff *skb, struct net *net, 112815e47304SEric W. Biederman u32 portid, u32 seq, u32 flags) 112928d8909bSJamal Hadi Salim { 1130af11e316SJamal Hadi Salim struct xfrmk_sadinfo si; 1131af11e316SJamal Hadi Salim struct xfrmu_sadhinfo sh; 113228d8909bSJamal Hadi Salim struct nlmsghdr *nlh; 11331d1e34ddSDavid S. Miller int err; 113428d8909bSJamal Hadi Salim u32 *f; 113528d8909bSJamal Hadi Salim 113615e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, XFRM_MSG_NEWSADINFO, sizeof(u32), 0); 113725985edcSLucas De Marchi if (nlh == NULL) /* shouldn't really happen ... */ 113828d8909bSJamal Hadi Salim return -EMSGSIZE; 113928d8909bSJamal Hadi Salim 114028d8909bSJamal Hadi Salim f = nlmsg_data(nlh); 114128d8909bSJamal Hadi Salim *f = flags; 1142e071041bSAlexey Dobriyan xfrm_sad_getinfo(net, &si); 114328d8909bSJamal Hadi Salim 1144af11e316SJamal Hadi Salim sh.sadhmcnt = si.sadhmcnt; 1145af11e316SJamal Hadi Salim sh.sadhcnt = si.sadhcnt; 1146af11e316SJamal Hadi Salim 11471d1e34ddSDavid S. Miller err = nla_put_u32(skb, XFRMA_SAD_CNT, si.sadcnt); 11481d1e34ddSDavid S. Miller if (!err) 11491d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_SAD_HINFO, sizeof(sh), &sh); 11501d1e34ddSDavid S. Miller if (err) { 11511d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh); 11521d1e34ddSDavid S. Miller return err; 11531d1e34ddSDavid S. Miller } 115428d8909bSJamal Hadi Salim 1155053c095aSJohannes Berg nlmsg_end(skb, nlh); 1156053c095aSJohannes Berg return 0; 115728d8909bSJamal Hadi Salim } 115828d8909bSJamal Hadi Salim 115928d8909bSJamal Hadi Salim static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh, 11605424f32eSThomas Graf struct nlattr **attrs) 116128d8909bSJamal Hadi Salim { 1162a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk); 116328d8909bSJamal Hadi Salim struct sk_buff *r_skb; 11647b67c857SThomas Graf u32 *flags = nlmsg_data(nlh); 116515e47304SEric W. Biederman u32 sportid = NETLINK_CB(skb).portid; 116628d8909bSJamal Hadi Salim u32 seq = nlh->nlmsg_seq; 116728d8909bSJamal Hadi Salim 11687deb2264SThomas Graf r_skb = nlmsg_new(xfrm_sadinfo_msgsize(), GFP_ATOMIC); 116928d8909bSJamal Hadi Salim if (r_skb == NULL) 117028d8909bSJamal Hadi Salim return -ENOMEM; 117128d8909bSJamal Hadi Salim 117215e47304SEric W. Biederman if (build_sadinfo(r_skb, net, sportid, seq, *flags) < 0) 117328d8909bSJamal Hadi Salim BUG(); 117428d8909bSJamal Hadi Salim 117515e47304SEric W. Biederman return nlmsg_unicast(net->xfrm.nlsk, r_skb, sportid); 117628d8909bSJamal Hadi Salim } 117728d8909bSJamal Hadi Salim 117822e70050SChristoph Hellwig static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 11795424f32eSThomas Graf struct nlattr **attrs) 11801da177e4SLinus Torvalds { 1181fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 11827b67c857SThomas Graf struct xfrm_usersa_id *p = nlmsg_data(nlh); 11831da177e4SLinus Torvalds struct xfrm_state *x; 11841da177e4SLinus Torvalds struct sk_buff *resp_skb; 1185eb2971b6SMasahide NAKAMURA int err = -ESRCH; 11861da177e4SLinus Torvalds 1187fc34acd3SAlexey Dobriyan x = xfrm_user_state_lookup(net, p, attrs, &err); 11881da177e4SLinus Torvalds if (x == NULL) 11891da177e4SLinus Torvalds goto out_noput; 11901da177e4SLinus Torvalds 11911da177e4SLinus Torvalds resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq); 11921da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 11931da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 11941da177e4SLinus Torvalds } else { 119515e47304SEric W. Biederman err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).portid); 11961da177e4SLinus Torvalds } 11971da177e4SLinus Torvalds xfrm_state_put(x); 11981da177e4SLinus Torvalds out_noput: 11991da177e4SLinus Torvalds return err; 12001da177e4SLinus Torvalds } 12011da177e4SLinus Torvalds 120222e70050SChristoph Hellwig static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, 12035424f32eSThomas Graf struct nlattr **attrs) 12041da177e4SLinus Torvalds { 1205fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 12061da177e4SLinus Torvalds struct xfrm_state *x; 12071da177e4SLinus Torvalds struct xfrm_userspi_info *p; 12081da177e4SLinus Torvalds struct sk_buff *resp_skb; 12091da177e4SLinus Torvalds xfrm_address_t *daddr; 12101da177e4SLinus Torvalds int family; 12111da177e4SLinus Torvalds int err; 12126f26b61eSJamal Hadi Salim u32 mark; 12136f26b61eSJamal Hadi Salim struct xfrm_mark m; 12141da177e4SLinus Torvalds 12157b67c857SThomas Graf p = nlmsg_data(nlh); 1216776e9dd9SFan Du err = verify_spi_info(p->info.id.proto, p->min, p->max); 12171da177e4SLinus Torvalds if (err) 12181da177e4SLinus Torvalds goto out_noput; 12191da177e4SLinus Torvalds 12201da177e4SLinus Torvalds family = p->info.family; 12211da177e4SLinus Torvalds daddr = &p->info.id.daddr; 12221da177e4SLinus Torvalds 12231da177e4SLinus Torvalds x = NULL; 12246f26b61eSJamal Hadi Salim 12256f26b61eSJamal Hadi Salim mark = xfrm_mark_get(attrs, &m); 12261da177e4SLinus Torvalds if (p->info.seq) { 12276f26b61eSJamal Hadi Salim x = xfrm_find_acq_byseq(net, mark, p->info.seq); 122870e94e66SYOSHIFUJI Hideaki / 吉藤英明 if (x && !xfrm_addr_equal(&x->id.daddr, daddr, family)) { 12291da177e4SLinus Torvalds xfrm_state_put(x); 12301da177e4SLinus Torvalds x = NULL; 12311da177e4SLinus Torvalds } 12321da177e4SLinus Torvalds } 12331da177e4SLinus Torvalds 12341da177e4SLinus Torvalds if (!x) 12356f26b61eSJamal Hadi Salim x = xfrm_find_acq(net, &m, p->info.mode, p->info.reqid, 12361da177e4SLinus Torvalds p->info.id.proto, daddr, 12371da177e4SLinus Torvalds &p->info.saddr, 1, 12381da177e4SLinus Torvalds family); 12391da177e4SLinus Torvalds err = -ENOENT; 12401da177e4SLinus Torvalds if (x == NULL) 12411da177e4SLinus Torvalds goto out_noput; 12421da177e4SLinus Torvalds 1243658b219eSHerbert Xu err = xfrm_alloc_spi(x, p->min, p->max); 1244658b219eSHerbert Xu if (err) 1245658b219eSHerbert Xu goto out; 12461da177e4SLinus Torvalds 12471da177e4SLinus Torvalds resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq); 12481da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 12491da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 12501da177e4SLinus Torvalds goto out; 12511da177e4SLinus Torvalds } 12521da177e4SLinus Torvalds 125315e47304SEric W. Biederman err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).portid); 12541da177e4SLinus Torvalds 12551da177e4SLinus Torvalds out: 12561da177e4SLinus Torvalds xfrm_state_put(x); 12571da177e4SLinus Torvalds out_noput: 12581da177e4SLinus Torvalds return err; 12591da177e4SLinus Torvalds } 12601da177e4SLinus Torvalds 1261b798a9edSJamal Hadi Salim static int verify_policy_dir(u8 dir) 12621da177e4SLinus Torvalds { 12631da177e4SLinus Torvalds switch (dir) { 12641da177e4SLinus Torvalds case XFRM_POLICY_IN: 12651da177e4SLinus Torvalds case XFRM_POLICY_OUT: 12661da177e4SLinus Torvalds case XFRM_POLICY_FWD: 12671da177e4SLinus Torvalds break; 12681da177e4SLinus Torvalds 12691da177e4SLinus Torvalds default: 12701da177e4SLinus Torvalds return -EINVAL; 12713ff50b79SStephen Hemminger } 12721da177e4SLinus Torvalds 12731da177e4SLinus Torvalds return 0; 12741da177e4SLinus Torvalds } 12751da177e4SLinus Torvalds 1276b798a9edSJamal Hadi Salim static int verify_policy_type(u8 type) 1277f7b6983fSMasahide NAKAMURA { 1278f7b6983fSMasahide NAKAMURA switch (type) { 1279f7b6983fSMasahide NAKAMURA case XFRM_POLICY_TYPE_MAIN: 1280f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 1281f7b6983fSMasahide NAKAMURA case XFRM_POLICY_TYPE_SUB: 1282f7b6983fSMasahide NAKAMURA #endif 1283f7b6983fSMasahide NAKAMURA break; 1284f7b6983fSMasahide NAKAMURA 1285f7b6983fSMasahide NAKAMURA default: 1286f7b6983fSMasahide NAKAMURA return -EINVAL; 12873ff50b79SStephen Hemminger } 1288f7b6983fSMasahide NAKAMURA 1289f7b6983fSMasahide NAKAMURA return 0; 1290f7b6983fSMasahide NAKAMURA } 1291f7b6983fSMasahide NAKAMURA 12921da177e4SLinus Torvalds static int verify_newpolicy_info(struct xfrm_userpolicy_info *p) 12931da177e4SLinus Torvalds { 1294e682adf0SFan Du int ret; 1295e682adf0SFan Du 12961da177e4SLinus Torvalds switch (p->share) { 12971da177e4SLinus Torvalds case XFRM_SHARE_ANY: 12981da177e4SLinus Torvalds case XFRM_SHARE_SESSION: 12991da177e4SLinus Torvalds case XFRM_SHARE_USER: 13001da177e4SLinus Torvalds case XFRM_SHARE_UNIQUE: 13011da177e4SLinus Torvalds break; 13021da177e4SLinus Torvalds 13031da177e4SLinus Torvalds default: 13041da177e4SLinus Torvalds return -EINVAL; 13053ff50b79SStephen Hemminger } 13061da177e4SLinus Torvalds 13071da177e4SLinus Torvalds switch (p->action) { 13081da177e4SLinus Torvalds case XFRM_POLICY_ALLOW: 13091da177e4SLinus Torvalds case XFRM_POLICY_BLOCK: 13101da177e4SLinus Torvalds break; 13111da177e4SLinus Torvalds 13121da177e4SLinus Torvalds default: 13131da177e4SLinus Torvalds return -EINVAL; 13143ff50b79SStephen Hemminger } 13151da177e4SLinus Torvalds 13161da177e4SLinus Torvalds switch (p->sel.family) { 13171da177e4SLinus Torvalds case AF_INET: 13181da177e4SLinus Torvalds break; 13191da177e4SLinus Torvalds 13201da177e4SLinus Torvalds case AF_INET6: 1321dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 13221da177e4SLinus Torvalds break; 13231da177e4SLinus Torvalds #else 13241da177e4SLinus Torvalds return -EAFNOSUPPORT; 13251da177e4SLinus Torvalds #endif 13261da177e4SLinus Torvalds 13271da177e4SLinus Torvalds default: 13281da177e4SLinus Torvalds return -EINVAL; 13293ff50b79SStephen Hemminger } 13301da177e4SLinus Torvalds 1331e682adf0SFan Du ret = verify_policy_dir(p->dir); 1332e682adf0SFan Du if (ret) 1333e682adf0SFan Du return ret; 1334e682adf0SFan Du if (p->index && ((p->index & XFRM_POLICY_MAX) != p->dir)) 1335e682adf0SFan Du return -EINVAL; 1336e682adf0SFan Du 1337e682adf0SFan Du return 0; 13381da177e4SLinus Torvalds } 13391da177e4SLinus Torvalds 13405424f32eSThomas Graf static int copy_from_user_sec_ctx(struct xfrm_policy *pol, struct nlattr **attrs) 1341df71837dSTrent Jaeger { 13425424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 1343df71837dSTrent Jaeger struct xfrm_user_sec_ctx *uctx; 1344df71837dSTrent Jaeger 1345df71837dSTrent Jaeger if (!rt) 1346df71837dSTrent Jaeger return 0; 1347df71837dSTrent Jaeger 13485424f32eSThomas Graf uctx = nla_data(rt); 134952a4c640SNikolay Aleksandrov return security_xfrm_policy_alloc(&pol->security, uctx, GFP_KERNEL); 1350df71837dSTrent Jaeger } 1351df71837dSTrent Jaeger 13521da177e4SLinus Torvalds static void copy_templates(struct xfrm_policy *xp, struct xfrm_user_tmpl *ut, 13531da177e4SLinus Torvalds int nr) 13541da177e4SLinus Torvalds { 13551da177e4SLinus Torvalds int i; 13561da177e4SLinus Torvalds 13571da177e4SLinus Torvalds xp->xfrm_nr = nr; 13581da177e4SLinus Torvalds for (i = 0; i < nr; i++, ut++) { 13591da177e4SLinus Torvalds struct xfrm_tmpl *t = &xp->xfrm_vec[i]; 13601da177e4SLinus Torvalds 13611da177e4SLinus Torvalds memcpy(&t->id, &ut->id, sizeof(struct xfrm_id)); 13621da177e4SLinus Torvalds memcpy(&t->saddr, &ut->saddr, 13631da177e4SLinus Torvalds sizeof(xfrm_address_t)); 13641da177e4SLinus Torvalds t->reqid = ut->reqid; 13651da177e4SLinus Torvalds t->mode = ut->mode; 13661da177e4SLinus Torvalds t->share = ut->share; 13671da177e4SLinus Torvalds t->optional = ut->optional; 13681da177e4SLinus Torvalds t->aalgos = ut->aalgos; 13691da177e4SLinus Torvalds t->ealgos = ut->ealgos; 13701da177e4SLinus Torvalds t->calgos = ut->calgos; 1371c5d18e98SHerbert Xu /* If all masks are ~0, then we allow all algorithms. */ 1372c5d18e98SHerbert Xu t->allalgs = !~(t->aalgos & t->ealgos & t->calgos); 13738511d01dSMiika Komu t->encap_family = ut->family; 13741da177e4SLinus Torvalds } 13751da177e4SLinus Torvalds } 13761da177e4SLinus Torvalds 1377b4ad86bfSDavid S. Miller static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family) 1378b4ad86bfSDavid S. Miller { 1379b4ad86bfSDavid S. Miller int i; 1380b4ad86bfSDavid S. Miller 1381b4ad86bfSDavid S. Miller if (nr > XFRM_MAX_DEPTH) 1382b4ad86bfSDavid S. Miller return -EINVAL; 1383b4ad86bfSDavid S. Miller 1384b4ad86bfSDavid S. Miller for (i = 0; i < nr; i++) { 1385b4ad86bfSDavid S. Miller /* We never validated the ut->family value, so many 1386b4ad86bfSDavid S. Miller * applications simply leave it at zero. The check was 1387b4ad86bfSDavid S. Miller * never made and ut->family was ignored because all 1388b4ad86bfSDavid S. Miller * templates could be assumed to have the same family as 1389b4ad86bfSDavid S. Miller * the policy itself. Now that we will have ipv4-in-ipv6 1390b4ad86bfSDavid S. Miller * and ipv6-in-ipv4 tunnels, this is no longer true. 1391b4ad86bfSDavid S. Miller */ 1392b4ad86bfSDavid S. Miller if (!ut[i].family) 1393b4ad86bfSDavid S. Miller ut[i].family = family; 1394b4ad86bfSDavid S. Miller 1395b4ad86bfSDavid S. Miller switch (ut[i].family) { 1396b4ad86bfSDavid S. Miller case AF_INET: 1397b4ad86bfSDavid S. Miller break; 1398dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 1399b4ad86bfSDavid S. Miller case AF_INET6: 1400b4ad86bfSDavid S. Miller break; 1401b4ad86bfSDavid S. Miller #endif 1402b4ad86bfSDavid S. Miller default: 1403b4ad86bfSDavid S. Miller return -EINVAL; 14043ff50b79SStephen Hemminger } 1405b4ad86bfSDavid S. Miller } 1406b4ad86bfSDavid S. Miller 1407b4ad86bfSDavid S. Miller return 0; 1408b4ad86bfSDavid S. Miller } 1409b4ad86bfSDavid S. Miller 14105424f32eSThomas Graf static int copy_from_user_tmpl(struct xfrm_policy *pol, struct nlattr **attrs) 14111da177e4SLinus Torvalds { 14125424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_TMPL]; 14131da177e4SLinus Torvalds 14141da177e4SLinus Torvalds if (!rt) { 14151da177e4SLinus Torvalds pol->xfrm_nr = 0; 14161da177e4SLinus Torvalds } else { 14175424f32eSThomas Graf struct xfrm_user_tmpl *utmpl = nla_data(rt); 14185424f32eSThomas Graf int nr = nla_len(rt) / sizeof(*utmpl); 1419b4ad86bfSDavid S. Miller int err; 14201da177e4SLinus Torvalds 1421b4ad86bfSDavid S. Miller err = validate_tmpl(nr, utmpl, pol->family); 1422b4ad86bfSDavid S. Miller if (err) 1423b4ad86bfSDavid S. Miller return err; 14241da177e4SLinus Torvalds 14255424f32eSThomas Graf copy_templates(pol, utmpl, nr); 14261da177e4SLinus Torvalds } 14271da177e4SLinus Torvalds return 0; 14281da177e4SLinus Torvalds } 14291da177e4SLinus Torvalds 14305424f32eSThomas Graf static int copy_from_user_policy_type(u8 *tp, struct nlattr **attrs) 1431f7b6983fSMasahide NAKAMURA { 14325424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_POLICY_TYPE]; 1433f7b6983fSMasahide NAKAMURA struct xfrm_userpolicy_type *upt; 1434b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 1435f7b6983fSMasahide NAKAMURA int err; 1436f7b6983fSMasahide NAKAMURA 1437f7b6983fSMasahide NAKAMURA if (rt) { 14385424f32eSThomas Graf upt = nla_data(rt); 1439f7b6983fSMasahide NAKAMURA type = upt->type; 1440f7b6983fSMasahide NAKAMURA } 1441f7b6983fSMasahide NAKAMURA 1442f7b6983fSMasahide NAKAMURA err = verify_policy_type(type); 1443f7b6983fSMasahide NAKAMURA if (err) 1444f7b6983fSMasahide NAKAMURA return err; 1445f7b6983fSMasahide NAKAMURA 1446f7b6983fSMasahide NAKAMURA *tp = type; 1447f7b6983fSMasahide NAKAMURA return 0; 1448f7b6983fSMasahide NAKAMURA } 1449f7b6983fSMasahide NAKAMURA 14501da177e4SLinus Torvalds static void copy_from_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p) 14511da177e4SLinus Torvalds { 14521da177e4SLinus Torvalds xp->priority = p->priority; 14531da177e4SLinus Torvalds xp->index = p->index; 14541da177e4SLinus Torvalds memcpy(&xp->selector, &p->sel, sizeof(xp->selector)); 14551da177e4SLinus Torvalds memcpy(&xp->lft, &p->lft, sizeof(xp->lft)); 14561da177e4SLinus Torvalds xp->action = p->action; 14571da177e4SLinus Torvalds xp->flags = p->flags; 14581da177e4SLinus Torvalds xp->family = p->sel.family; 14591da177e4SLinus Torvalds /* XXX xp->share = p->share; */ 14601da177e4SLinus Torvalds } 14611da177e4SLinus Torvalds 14621da177e4SLinus Torvalds static void copy_to_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p, int dir) 14631da177e4SLinus Torvalds { 14647b789836SMathias Krause memset(p, 0, sizeof(*p)); 14651da177e4SLinus Torvalds memcpy(&p->sel, &xp->selector, sizeof(p->sel)); 14661da177e4SLinus Torvalds memcpy(&p->lft, &xp->lft, sizeof(p->lft)); 14671da177e4SLinus Torvalds memcpy(&p->curlft, &xp->curlft, sizeof(p->curlft)); 14681da177e4SLinus Torvalds p->priority = xp->priority; 14691da177e4SLinus Torvalds p->index = xp->index; 14701da177e4SLinus Torvalds p->sel.family = xp->family; 14711da177e4SLinus Torvalds p->dir = dir; 14721da177e4SLinus Torvalds p->action = xp->action; 14731da177e4SLinus Torvalds p->flags = xp->flags; 14741da177e4SLinus Torvalds p->share = XFRM_SHARE_ANY; /* XXX xp->share */ 14751da177e4SLinus Torvalds } 14761da177e4SLinus Torvalds 1477fc34acd3SAlexey Dobriyan static struct xfrm_policy *xfrm_policy_construct(struct net *net, struct xfrm_userpolicy_info *p, struct nlattr **attrs, int *errp) 14781da177e4SLinus Torvalds { 1479fc34acd3SAlexey Dobriyan struct xfrm_policy *xp = xfrm_policy_alloc(net, GFP_KERNEL); 14801da177e4SLinus Torvalds int err; 14811da177e4SLinus Torvalds 14821da177e4SLinus Torvalds if (!xp) { 14831da177e4SLinus Torvalds *errp = -ENOMEM; 14841da177e4SLinus Torvalds return NULL; 14851da177e4SLinus Torvalds } 14861da177e4SLinus Torvalds 14871da177e4SLinus Torvalds copy_from_user_policy(xp, p); 1488df71837dSTrent Jaeger 148935a7aa08SThomas Graf err = copy_from_user_policy_type(&xp->type, attrs); 1490f7b6983fSMasahide NAKAMURA if (err) 1491f7b6983fSMasahide NAKAMURA goto error; 1492f7b6983fSMasahide NAKAMURA 149335a7aa08SThomas Graf if (!(err = copy_from_user_tmpl(xp, attrs))) 149435a7aa08SThomas Graf err = copy_from_user_sec_ctx(xp, attrs); 1495f7b6983fSMasahide NAKAMURA if (err) 1496f7b6983fSMasahide NAKAMURA goto error; 14971da177e4SLinus Torvalds 1498295fae56SJamal Hadi Salim xfrm_mark_get(attrs, &xp->mark); 1499295fae56SJamal Hadi Salim 15001da177e4SLinus Torvalds return xp; 1501f7b6983fSMasahide NAKAMURA error: 1502f7b6983fSMasahide NAKAMURA *errp = err; 150312a169e7SHerbert Xu xp->walk.dead = 1; 150464c31b3fSWANG Cong xfrm_policy_destroy(xp); 1505f7b6983fSMasahide NAKAMURA return NULL; 15061da177e4SLinus Torvalds } 15071da177e4SLinus Torvalds 150822e70050SChristoph Hellwig static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 15095424f32eSThomas Graf struct nlattr **attrs) 15101da177e4SLinus Torvalds { 1511fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 15127b67c857SThomas Graf struct xfrm_userpolicy_info *p = nlmsg_data(nlh); 15131da177e4SLinus Torvalds struct xfrm_policy *xp; 151426b15dadSJamal Hadi Salim struct km_event c; 15151da177e4SLinus Torvalds int err; 15161da177e4SLinus Torvalds int excl; 15171da177e4SLinus Torvalds 15181da177e4SLinus Torvalds err = verify_newpolicy_info(p); 15191da177e4SLinus Torvalds if (err) 15201da177e4SLinus Torvalds return err; 152135a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 1522df71837dSTrent Jaeger if (err) 1523df71837dSTrent Jaeger return err; 15241da177e4SLinus Torvalds 1525fc34acd3SAlexey Dobriyan xp = xfrm_policy_construct(net, p, attrs, &err); 15261da177e4SLinus Torvalds if (!xp) 15271da177e4SLinus Torvalds return err; 15281da177e4SLinus Torvalds 152925985edcSLucas De Marchi /* shouldn't excl be based on nlh flags?? 153026b15dadSJamal Hadi Salim * Aha! this is anti-netlink really i.e more pfkey derived 153126b15dadSJamal Hadi Salim * in netlink excl is a flag and you wouldnt need 153226b15dadSJamal Hadi Salim * a type XFRM_MSG_UPDPOLICY - JHS */ 15331da177e4SLinus Torvalds excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY; 15341da177e4SLinus Torvalds err = xfrm_policy_insert(p->dir, xp, excl); 15352e71029eSTetsuo Handa xfrm_audit_policy_add(xp, err ? 0 : 1, true); 1536161a09e7SJoy Latten 15371da177e4SLinus Torvalds if (err) { 153803e1ad7bSPaul Moore security_xfrm_policy_free(xp->security); 15391da177e4SLinus Torvalds kfree(xp); 15401da177e4SLinus Torvalds return err; 15411da177e4SLinus Torvalds } 15421da177e4SLinus Torvalds 1543f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 154426b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 154515e47304SEric W. Biederman c.portid = nlh->nlmsg_pid; 154626b15dadSJamal Hadi Salim km_policy_notify(xp, p->dir, &c); 154726b15dadSJamal Hadi Salim 15481da177e4SLinus Torvalds xfrm_pol_put(xp); 15491da177e4SLinus Torvalds 15501da177e4SLinus Torvalds return 0; 15511da177e4SLinus Torvalds } 15521da177e4SLinus Torvalds 15531da177e4SLinus Torvalds static int copy_to_user_tmpl(struct xfrm_policy *xp, struct sk_buff *skb) 15541da177e4SLinus Torvalds { 15551da177e4SLinus Torvalds struct xfrm_user_tmpl vec[XFRM_MAX_DEPTH]; 15561da177e4SLinus Torvalds int i; 15571da177e4SLinus Torvalds 15581da177e4SLinus Torvalds if (xp->xfrm_nr == 0) 15591da177e4SLinus Torvalds return 0; 15601da177e4SLinus Torvalds 15611da177e4SLinus Torvalds for (i = 0; i < xp->xfrm_nr; i++) { 15621da177e4SLinus Torvalds struct xfrm_user_tmpl *up = &vec[i]; 15631da177e4SLinus Torvalds struct xfrm_tmpl *kp = &xp->xfrm_vec[i]; 15641da177e4SLinus Torvalds 15651f86840fSMathias Krause memset(up, 0, sizeof(*up)); 15661da177e4SLinus Torvalds memcpy(&up->id, &kp->id, sizeof(up->id)); 15678511d01dSMiika Komu up->family = kp->encap_family; 15681da177e4SLinus Torvalds memcpy(&up->saddr, &kp->saddr, sizeof(up->saddr)); 15691da177e4SLinus Torvalds up->reqid = kp->reqid; 15701da177e4SLinus Torvalds up->mode = kp->mode; 15711da177e4SLinus Torvalds up->share = kp->share; 15721da177e4SLinus Torvalds up->optional = kp->optional; 15731da177e4SLinus Torvalds up->aalgos = kp->aalgos; 15741da177e4SLinus Torvalds up->ealgos = kp->ealgos; 15751da177e4SLinus Torvalds up->calgos = kp->calgos; 15761da177e4SLinus Torvalds } 15771da177e4SLinus Torvalds 1578c0144beaSThomas Graf return nla_put(skb, XFRMA_TMPL, 1579c0144beaSThomas Graf sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr, vec); 1580df71837dSTrent Jaeger } 1581df71837dSTrent Jaeger 15820d681623SSerge Hallyn static inline int copy_to_user_state_sec_ctx(struct xfrm_state *x, struct sk_buff *skb) 15830d681623SSerge Hallyn { 15840d681623SSerge Hallyn if (x->security) { 15850d681623SSerge Hallyn return copy_sec_ctx(x->security, skb); 15860d681623SSerge Hallyn } 15870d681623SSerge Hallyn return 0; 15880d681623SSerge Hallyn } 15890d681623SSerge Hallyn 15900d681623SSerge Hallyn static inline int copy_to_user_sec_ctx(struct xfrm_policy *xp, struct sk_buff *skb) 15910d681623SSerge Hallyn { 15921d1e34ddSDavid S. Miller if (xp->security) 15930d681623SSerge Hallyn return copy_sec_ctx(xp->security, skb); 15940d681623SSerge Hallyn return 0; 15950d681623SSerge Hallyn } 1596cfbfd45aSThomas Graf static inline size_t userpolicy_type_attrsize(void) 1597cfbfd45aSThomas Graf { 1598cfbfd45aSThomas Graf #ifdef CONFIG_XFRM_SUB_POLICY 1599cfbfd45aSThomas Graf return nla_total_size(sizeof(struct xfrm_userpolicy_type)); 1600cfbfd45aSThomas Graf #else 1601cfbfd45aSThomas Graf return 0; 1602cfbfd45aSThomas Graf #endif 1603cfbfd45aSThomas Graf } 16040d681623SSerge Hallyn 1605f7b6983fSMasahide NAKAMURA #ifdef CONFIG_XFRM_SUB_POLICY 1606b798a9edSJamal Hadi Salim static int copy_to_user_policy_type(u8 type, struct sk_buff *skb) 1607f7b6983fSMasahide NAKAMURA { 1608c0144beaSThomas Graf struct xfrm_userpolicy_type upt = { 1609c0144beaSThomas Graf .type = type, 1610c0144beaSThomas Graf }; 1611f7b6983fSMasahide NAKAMURA 1612c0144beaSThomas Graf return nla_put(skb, XFRMA_POLICY_TYPE, sizeof(upt), &upt); 1613f7b6983fSMasahide NAKAMURA } 1614f7b6983fSMasahide NAKAMURA 1615f7b6983fSMasahide NAKAMURA #else 1616b798a9edSJamal Hadi Salim static inline int copy_to_user_policy_type(u8 type, struct sk_buff *skb) 1617f7b6983fSMasahide NAKAMURA { 1618f7b6983fSMasahide NAKAMURA return 0; 1619f7b6983fSMasahide NAKAMURA } 1620f7b6983fSMasahide NAKAMURA #endif 1621f7b6983fSMasahide NAKAMURA 16221da177e4SLinus Torvalds static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr) 16231da177e4SLinus Torvalds { 16241da177e4SLinus Torvalds struct xfrm_dump_info *sp = ptr; 16251da177e4SLinus Torvalds struct xfrm_userpolicy_info *p; 16261da177e4SLinus Torvalds struct sk_buff *in_skb = sp->in_skb; 16271da177e4SLinus Torvalds struct sk_buff *skb = sp->out_skb; 16281da177e4SLinus Torvalds struct nlmsghdr *nlh; 16291d1e34ddSDavid S. Miller int err; 16301da177e4SLinus Torvalds 163115e47304SEric W. Biederman nlh = nlmsg_put(skb, NETLINK_CB(in_skb).portid, sp->nlmsg_seq, 163279b8b7f4SThomas Graf XFRM_MSG_NEWPOLICY, sizeof(*p), sp->nlmsg_flags); 163379b8b7f4SThomas Graf if (nlh == NULL) 163479b8b7f4SThomas Graf return -EMSGSIZE; 16351da177e4SLinus Torvalds 16367b67c857SThomas Graf p = nlmsg_data(nlh); 16371da177e4SLinus Torvalds copy_to_user_policy(xp, p, dir); 16381d1e34ddSDavid S. Miller err = copy_to_user_tmpl(xp, skb); 16391d1e34ddSDavid S. Miller if (!err) 16401d1e34ddSDavid S. Miller err = copy_to_user_sec_ctx(xp, skb); 16411d1e34ddSDavid S. Miller if (!err) 16421d1e34ddSDavid S. Miller err = copy_to_user_policy_type(xp->type, skb); 16431d1e34ddSDavid S. Miller if (!err) 16441d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &xp->mark); 16451d1e34ddSDavid S. Miller if (err) { 16461d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh); 16471d1e34ddSDavid S. Miller return err; 16481d1e34ddSDavid S. Miller } 16499825069dSThomas Graf nlmsg_end(skb, nlh); 16501da177e4SLinus Torvalds return 0; 16511da177e4SLinus Torvalds } 16521da177e4SLinus Torvalds 16534c563f76STimo Teras static int xfrm_dump_policy_done(struct netlink_callback *cb) 16544c563f76STimo Teras { 16554c563f76STimo Teras struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1]; 1656283bc9f3SFan Du struct net *net = sock_net(cb->skb->sk); 16574c563f76STimo Teras 1658283bc9f3SFan Du xfrm_policy_walk_done(walk, net); 16594c563f76STimo Teras return 0; 16604c563f76STimo Teras } 16614c563f76STimo Teras 16621da177e4SLinus Torvalds static int xfrm_dump_policy(struct sk_buff *skb, struct netlink_callback *cb) 16631da177e4SLinus Torvalds { 1664fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 16654c563f76STimo Teras struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1]; 16661da177e4SLinus Torvalds struct xfrm_dump_info info; 16671da177e4SLinus Torvalds 16684c563f76STimo Teras BUILD_BUG_ON(sizeof(struct xfrm_policy_walk) > 16694c563f76STimo Teras sizeof(cb->args) - sizeof(cb->args[0])); 16704c563f76STimo Teras 16711da177e4SLinus Torvalds info.in_skb = cb->skb; 16721da177e4SLinus Torvalds info.out_skb = skb; 16731da177e4SLinus Torvalds info.nlmsg_seq = cb->nlh->nlmsg_seq; 16741da177e4SLinus Torvalds info.nlmsg_flags = NLM_F_MULTI; 16754c563f76STimo Teras 16764c563f76STimo Teras if (!cb->args[0]) { 16774c563f76STimo Teras cb->args[0] = 1; 16784c563f76STimo Teras xfrm_policy_walk_init(walk, XFRM_POLICY_TYPE_ANY); 16794c563f76STimo Teras } 16804c563f76STimo Teras 1681fc34acd3SAlexey Dobriyan (void) xfrm_policy_walk(net, walk, dump_one_policy, &info); 16821da177e4SLinus Torvalds 16831da177e4SLinus Torvalds return skb->len; 16841da177e4SLinus Torvalds } 16851da177e4SLinus Torvalds 16861da177e4SLinus Torvalds static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb, 16871da177e4SLinus Torvalds struct xfrm_policy *xp, 16881da177e4SLinus Torvalds int dir, u32 seq) 16891da177e4SLinus Torvalds { 16901da177e4SLinus Torvalds struct xfrm_dump_info info; 16911da177e4SLinus Torvalds struct sk_buff *skb; 1692c2546372SMathias Krause int err; 16931da177e4SLinus Torvalds 16947deb2264SThomas Graf skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 16951da177e4SLinus Torvalds if (!skb) 16961da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 16971da177e4SLinus Torvalds 16981da177e4SLinus Torvalds info.in_skb = in_skb; 16991da177e4SLinus Torvalds info.out_skb = skb; 17001da177e4SLinus Torvalds info.nlmsg_seq = seq; 17011da177e4SLinus Torvalds info.nlmsg_flags = 0; 17021da177e4SLinus Torvalds 1703c2546372SMathias Krause err = dump_one_policy(xp, dir, 0, &info); 1704c2546372SMathias Krause if (err) { 17051da177e4SLinus Torvalds kfree_skb(skb); 1706c2546372SMathias Krause return ERR_PTR(err); 17071da177e4SLinus Torvalds } 17081da177e4SLinus Torvalds 17091da177e4SLinus Torvalds return skb; 17101da177e4SLinus Torvalds } 17111da177e4SLinus Torvalds 171222e70050SChristoph Hellwig static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 17135424f32eSThomas Graf struct nlattr **attrs) 17141da177e4SLinus Torvalds { 1715fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 17161da177e4SLinus Torvalds struct xfrm_policy *xp; 17171da177e4SLinus Torvalds struct xfrm_userpolicy_id *p; 1718b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 17191da177e4SLinus Torvalds int err; 172026b15dadSJamal Hadi Salim struct km_event c; 17211da177e4SLinus Torvalds int delete; 1722295fae56SJamal Hadi Salim struct xfrm_mark m; 1723295fae56SJamal Hadi Salim u32 mark = xfrm_mark_get(attrs, &m); 17241da177e4SLinus Torvalds 17257b67c857SThomas Graf p = nlmsg_data(nlh); 17261da177e4SLinus Torvalds delete = nlh->nlmsg_type == XFRM_MSG_DELPOLICY; 17271da177e4SLinus Torvalds 172835a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1729f7b6983fSMasahide NAKAMURA if (err) 1730f7b6983fSMasahide NAKAMURA return err; 1731f7b6983fSMasahide NAKAMURA 17321da177e4SLinus Torvalds err = verify_policy_dir(p->dir); 17331da177e4SLinus Torvalds if (err) 17341da177e4SLinus Torvalds return err; 17351da177e4SLinus Torvalds 17361da177e4SLinus Torvalds if (p->index) 1737295fae56SJamal Hadi Salim xp = xfrm_policy_byid(net, mark, type, p->dir, p->index, delete, &err); 1738df71837dSTrent Jaeger else { 17395424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 174003e1ad7bSPaul Moore struct xfrm_sec_ctx *ctx; 1741df71837dSTrent Jaeger 174235a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 1743df71837dSTrent Jaeger if (err) 1744df71837dSTrent Jaeger return err; 1745df71837dSTrent Jaeger 17462c8dd116SDenis V. Lunev ctx = NULL; 1747df71837dSTrent Jaeger if (rt) { 17485424f32eSThomas Graf struct xfrm_user_sec_ctx *uctx = nla_data(rt); 1749df71837dSTrent Jaeger 175052a4c640SNikolay Aleksandrov err = security_xfrm_policy_alloc(&ctx, uctx, GFP_KERNEL); 175103e1ad7bSPaul Moore if (err) 1752df71837dSTrent Jaeger return err; 17532c8dd116SDenis V. Lunev } 1754295fae56SJamal Hadi Salim xp = xfrm_policy_bysel_ctx(net, mark, type, p->dir, &p->sel, 17556f26b61eSJamal Hadi Salim ctx, delete, &err); 175603e1ad7bSPaul Moore security_xfrm_policy_free(ctx); 1757df71837dSTrent Jaeger } 17581da177e4SLinus Torvalds if (xp == NULL) 17591da177e4SLinus Torvalds return -ENOENT; 17601da177e4SLinus Torvalds 17611da177e4SLinus Torvalds if (!delete) { 17621da177e4SLinus Torvalds struct sk_buff *resp_skb; 17631da177e4SLinus Torvalds 17641da177e4SLinus Torvalds resp_skb = xfrm_policy_netlink(skb, xp, p->dir, nlh->nlmsg_seq); 17651da177e4SLinus Torvalds if (IS_ERR(resp_skb)) { 17661da177e4SLinus Torvalds err = PTR_ERR(resp_skb); 17671da177e4SLinus Torvalds } else { 1768a6483b79SAlexey Dobriyan err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, 176915e47304SEric W. Biederman NETLINK_CB(skb).portid); 17701da177e4SLinus Torvalds } 177126b15dadSJamal Hadi Salim } else { 17722e71029eSTetsuo Handa xfrm_audit_policy_delete(xp, err ? 0 : 1, true); 177313fcfbb0SDavid S. Miller 177413fcfbb0SDavid S. Miller if (err != 0) 1775c8c05a8eSCatherine Zhang goto out; 177613fcfbb0SDavid S. Miller 1777e7443892SHerbert Xu c.data.byid = p->index; 1778f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 177926b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 178015e47304SEric W. Biederman c.portid = nlh->nlmsg_pid; 178126b15dadSJamal Hadi Salim km_policy_notify(xp, p->dir, &c); 17821da177e4SLinus Torvalds } 17831da177e4SLinus Torvalds 1784c8c05a8eSCatherine Zhang out: 1785ef41aaa0SEric Paris xfrm_pol_put(xp); 1786e4c17216SPaul Moore if (delete && err == 0) 1787e4c17216SPaul Moore xfrm_garbage_collect(net); 17881da177e4SLinus Torvalds return err; 17891da177e4SLinus Torvalds } 17901da177e4SLinus Torvalds 179122e70050SChristoph Hellwig static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh, 17925424f32eSThomas Graf struct nlattr **attrs) 17931da177e4SLinus Torvalds { 1794fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 179526b15dadSJamal Hadi Salim struct km_event c; 17967b67c857SThomas Graf struct xfrm_usersa_flush *p = nlmsg_data(nlh); 17974aa2e62cSJoy Latten int err; 17981da177e4SLinus Torvalds 17992e71029eSTetsuo Handa err = xfrm_state_flush(net, p->proto, true); 18009e64cc95SJamal Hadi Salim if (err) { 18019e64cc95SJamal Hadi Salim if (err == -ESRCH) /* empty table */ 18029e64cc95SJamal Hadi Salim return 0; 1803069c474eSDavid S. Miller return err; 18049e64cc95SJamal Hadi Salim } 1805bf08867fSHerbert Xu c.data.proto = p->proto; 1806f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 180726b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 180815e47304SEric W. Biederman c.portid = nlh->nlmsg_pid; 18097067802eSAlexey Dobriyan c.net = net; 181026b15dadSJamal Hadi Salim km_state_notify(NULL, &c); 181126b15dadSJamal Hadi Salim 18121da177e4SLinus Torvalds return 0; 18131da177e4SLinus Torvalds } 18141da177e4SLinus Torvalds 1815d8647b79SSteffen Klassert static inline size_t xfrm_aevent_msgsize(struct xfrm_state *x) 18167deb2264SThomas Graf { 1817d8647b79SSteffen Klassert size_t replay_size = x->replay_esn ? 1818d8647b79SSteffen Klassert xfrm_replay_state_esn_len(x->replay_esn) : 1819d8647b79SSteffen Klassert sizeof(struct xfrm_replay_state); 1820d8647b79SSteffen Klassert 18217deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_aevent_id)) 1822d8647b79SSteffen Klassert + nla_total_size(replay_size) 1823de95c4a4SNicolas Dichtel + nla_total_size_64bit(sizeof(struct xfrm_lifetime_cur)) 18246f26b61eSJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark)) 18257deb2264SThomas Graf + nla_total_size(4) /* XFRM_AE_RTHR */ 18267deb2264SThomas Graf + nla_total_size(4); /* XFRM_AE_ETHR */ 18277deb2264SThomas Graf } 1828d51d081dSJamal Hadi Salim 1829214e005bSDavid S. Miller static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c) 1830d51d081dSJamal Hadi Salim { 1831d51d081dSJamal Hadi Salim struct xfrm_aevent_id *id; 1832d51d081dSJamal Hadi Salim struct nlmsghdr *nlh; 18331d1e34ddSDavid S. Miller int err; 1834d51d081dSJamal Hadi Salim 183515e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, c->seq, XFRM_MSG_NEWAE, sizeof(*id), 0); 183679b8b7f4SThomas Graf if (nlh == NULL) 183779b8b7f4SThomas Graf return -EMSGSIZE; 1838d51d081dSJamal Hadi Salim 18397b67c857SThomas Graf id = nlmsg_data(nlh); 18402b5f6dccSJamal Hadi Salim memcpy(&id->sa_id.daddr, &x->id.daddr, sizeof(x->id.daddr)); 1841d51d081dSJamal Hadi Salim id->sa_id.spi = x->id.spi; 1842d51d081dSJamal Hadi Salim id->sa_id.family = x->props.family; 1843d51d081dSJamal Hadi Salim id->sa_id.proto = x->id.proto; 18442b5f6dccSJamal Hadi Salim memcpy(&id->saddr, &x->props.saddr, sizeof(x->props.saddr)); 18452b5f6dccSJamal Hadi Salim id->reqid = x->props.reqid; 1846d51d081dSJamal Hadi Salim id->flags = c->data.aevent; 1847d51d081dSJamal Hadi Salim 1848d0fde795SDavid S. Miller if (x->replay_esn) { 18491d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_REPLAY_ESN_VAL, 1850d8647b79SSteffen Klassert xfrm_replay_state_esn_len(x->replay_esn), 18511d1e34ddSDavid S. Miller x->replay_esn); 1852d0fde795SDavid S. Miller } else { 18531d1e34ddSDavid S. Miller err = nla_put(skb, XFRMA_REPLAY_VAL, sizeof(x->replay), 18541d1e34ddSDavid S. Miller &x->replay); 1855d0fde795SDavid S. Miller } 18561d1e34ddSDavid S. Miller if (err) 18571d1e34ddSDavid S. Miller goto out_cancel; 1858de95c4a4SNicolas Dichtel err = nla_put_64bit(skb, XFRMA_LTIME_VAL, sizeof(x->curlft), &x->curlft, 1859de95c4a4SNicolas Dichtel XFRMA_PAD); 18601d1e34ddSDavid S. Miller if (err) 18611d1e34ddSDavid S. Miller goto out_cancel; 1862d8647b79SSteffen Klassert 18631d1e34ddSDavid S. Miller if (id->flags & XFRM_AE_RTHR) { 18641d1e34ddSDavid S. Miller err = nla_put_u32(skb, XFRMA_REPLAY_THRESH, x->replay_maxdiff); 18651d1e34ddSDavid S. Miller if (err) 18661d1e34ddSDavid S. Miller goto out_cancel; 18671d1e34ddSDavid S. Miller } 18681d1e34ddSDavid S. Miller if (id->flags & XFRM_AE_ETHR) { 18691d1e34ddSDavid S. Miller err = nla_put_u32(skb, XFRMA_ETIMER_THRESH, 18701d1e34ddSDavid S. Miller x->replay_maxage * 10 / HZ); 18711d1e34ddSDavid S. Miller if (err) 18721d1e34ddSDavid S. Miller goto out_cancel; 18731d1e34ddSDavid S. Miller } 18741d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &x->mark); 18751d1e34ddSDavid S. Miller if (err) 18761d1e34ddSDavid S. Miller goto out_cancel; 18776f26b61eSJamal Hadi Salim 1878053c095aSJohannes Berg nlmsg_end(skb, nlh); 1879053c095aSJohannes Berg return 0; 1880d51d081dSJamal Hadi Salim 18811d1e34ddSDavid S. Miller out_cancel: 18829825069dSThomas Graf nlmsg_cancel(skb, nlh); 18831d1e34ddSDavid S. Miller return err; 1884d51d081dSJamal Hadi Salim } 1885d51d081dSJamal Hadi Salim 188622e70050SChristoph Hellwig static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh, 18875424f32eSThomas Graf struct nlattr **attrs) 1888d51d081dSJamal Hadi Salim { 1889fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 1890d51d081dSJamal Hadi Salim struct xfrm_state *x; 1891d51d081dSJamal Hadi Salim struct sk_buff *r_skb; 1892d51d081dSJamal Hadi Salim int err; 1893d51d081dSJamal Hadi Salim struct km_event c; 18946f26b61eSJamal Hadi Salim u32 mark; 18956f26b61eSJamal Hadi Salim struct xfrm_mark m; 18967b67c857SThomas Graf struct xfrm_aevent_id *p = nlmsg_data(nlh); 1897d51d081dSJamal Hadi Salim struct xfrm_usersa_id *id = &p->sa_id; 1898d51d081dSJamal Hadi Salim 18996f26b61eSJamal Hadi Salim mark = xfrm_mark_get(attrs, &m); 19006f26b61eSJamal Hadi Salim 19016f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &id->daddr, id->spi, id->proto, id->family); 1902d8647b79SSteffen Klassert if (x == NULL) 1903d51d081dSJamal Hadi Salim return -ESRCH; 1904d8647b79SSteffen Klassert 1905d8647b79SSteffen Klassert r_skb = nlmsg_new(xfrm_aevent_msgsize(x), GFP_ATOMIC); 1906d8647b79SSteffen Klassert if (r_skb == NULL) { 1907d8647b79SSteffen Klassert xfrm_state_put(x); 1908d8647b79SSteffen Klassert return -ENOMEM; 1909d51d081dSJamal Hadi Salim } 1910d51d081dSJamal Hadi Salim 1911d51d081dSJamal Hadi Salim /* 1912d51d081dSJamal Hadi Salim * XXX: is this lock really needed - none of the other 1913d51d081dSJamal Hadi Salim * gets lock (the concern is things getting updated 1914d51d081dSJamal Hadi Salim * while we are still reading) - jhs 1915d51d081dSJamal Hadi Salim */ 1916d51d081dSJamal Hadi Salim spin_lock_bh(&x->lock); 1917d51d081dSJamal Hadi Salim c.data.aevent = p->flags; 1918d51d081dSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 191915e47304SEric W. Biederman c.portid = nlh->nlmsg_pid; 1920d51d081dSJamal Hadi Salim 1921d51d081dSJamal Hadi Salim if (build_aevent(r_skb, x, &c) < 0) 1922d51d081dSJamal Hadi Salim BUG(); 192315e47304SEric W. Biederman err = nlmsg_unicast(net->xfrm.nlsk, r_skb, NETLINK_CB(skb).portid); 1924d51d081dSJamal Hadi Salim spin_unlock_bh(&x->lock); 1925d51d081dSJamal Hadi Salim xfrm_state_put(x); 1926d51d081dSJamal Hadi Salim return err; 1927d51d081dSJamal Hadi Salim } 1928d51d081dSJamal Hadi Salim 192922e70050SChristoph Hellwig static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh, 19305424f32eSThomas Graf struct nlattr **attrs) 1931d51d081dSJamal Hadi Salim { 1932fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 1933d51d081dSJamal Hadi Salim struct xfrm_state *x; 1934d51d081dSJamal Hadi Salim struct km_event c; 1935d51d081dSJamal Hadi Salim int err = -EINVAL; 19366f26b61eSJamal Hadi Salim u32 mark = 0; 19376f26b61eSJamal Hadi Salim struct xfrm_mark m; 19387b67c857SThomas Graf struct xfrm_aevent_id *p = nlmsg_data(nlh); 19395424f32eSThomas Graf struct nlattr *rp = attrs[XFRMA_REPLAY_VAL]; 1940d8647b79SSteffen Klassert struct nlattr *re = attrs[XFRMA_REPLAY_ESN_VAL]; 19415424f32eSThomas Graf struct nlattr *lt = attrs[XFRMA_LTIME_VAL]; 19424e077237SMichael Rossberg struct nlattr *et = attrs[XFRMA_ETIMER_THRESH]; 19434e077237SMichael Rossberg struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH]; 1944d51d081dSJamal Hadi Salim 19454e077237SMichael Rossberg if (!lt && !rp && !re && !et && !rt) 1946d51d081dSJamal Hadi Salim return err; 1947d51d081dSJamal Hadi Salim 1948d51d081dSJamal Hadi Salim /* pedantic mode - thou shalt sayeth replaceth */ 1949d51d081dSJamal Hadi Salim if (!(nlh->nlmsg_flags&NLM_F_REPLACE)) 1950d51d081dSJamal Hadi Salim return err; 1951d51d081dSJamal Hadi Salim 19526f26b61eSJamal Hadi Salim mark = xfrm_mark_get(attrs, &m); 19536f26b61eSJamal Hadi Salim 19546f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &p->sa_id.daddr, p->sa_id.spi, p->sa_id.proto, p->sa_id.family); 1955d51d081dSJamal Hadi Salim if (x == NULL) 1956d51d081dSJamal Hadi Salim return -ESRCH; 1957d51d081dSJamal Hadi Salim 1958d51d081dSJamal Hadi Salim if (x->km.state != XFRM_STATE_VALID) 1959d51d081dSJamal Hadi Salim goto out; 1960d51d081dSJamal Hadi Salim 19614479ff76SSteffen Klassert err = xfrm_replay_verify_len(x->replay_esn, re); 1962e2b19125SSteffen Klassert if (err) 1963e2b19125SSteffen Klassert goto out; 1964e2b19125SSteffen Klassert 1965d51d081dSJamal Hadi Salim spin_lock_bh(&x->lock); 1966e3ac104dSMathias Krause xfrm_update_ae_params(x, attrs, 1); 1967d51d081dSJamal Hadi Salim spin_unlock_bh(&x->lock); 1968d51d081dSJamal Hadi Salim 1969d51d081dSJamal Hadi Salim c.event = nlh->nlmsg_type; 1970d51d081dSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 197115e47304SEric W. Biederman c.portid = nlh->nlmsg_pid; 1972d51d081dSJamal Hadi Salim c.data.aevent = XFRM_AE_CU; 1973d51d081dSJamal Hadi Salim km_state_notify(x, &c); 1974d51d081dSJamal Hadi Salim err = 0; 1975d51d081dSJamal Hadi Salim out: 1976d51d081dSJamal Hadi Salim xfrm_state_put(x); 1977d51d081dSJamal Hadi Salim return err; 1978d51d081dSJamal Hadi Salim } 1979d51d081dSJamal Hadi Salim 198022e70050SChristoph Hellwig static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh, 19815424f32eSThomas Graf struct nlattr **attrs) 19821da177e4SLinus Torvalds { 1983fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 198426b15dadSJamal Hadi Salim struct km_event c; 1985b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 1986f7b6983fSMasahide NAKAMURA int err; 198726b15dadSJamal Hadi Salim 198835a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 1989f7b6983fSMasahide NAKAMURA if (err) 1990f7b6983fSMasahide NAKAMURA return err; 1991f7b6983fSMasahide NAKAMURA 19922e71029eSTetsuo Handa err = xfrm_policy_flush(net, type, true); 19932f1eb65fSJamal Hadi Salim if (err) { 19942f1eb65fSJamal Hadi Salim if (err == -ESRCH) /* empty table */ 19952f1eb65fSJamal Hadi Salim return 0; 1996069c474eSDavid S. Miller return err; 19972f1eb65fSJamal Hadi Salim } 19982f1eb65fSJamal Hadi Salim 1999f7b6983fSMasahide NAKAMURA c.data.type = type; 2000f60f6b8fSHerbert Xu c.event = nlh->nlmsg_type; 200126b15dadSJamal Hadi Salim c.seq = nlh->nlmsg_seq; 200215e47304SEric W. Biederman c.portid = nlh->nlmsg_pid; 20037067802eSAlexey Dobriyan c.net = net; 200426b15dadSJamal Hadi Salim km_policy_notify(NULL, 0, &c); 20051da177e4SLinus Torvalds return 0; 20061da177e4SLinus Torvalds } 20071da177e4SLinus Torvalds 200822e70050SChristoph Hellwig static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh, 20095424f32eSThomas Graf struct nlattr **attrs) 20106c5c8ca7SJamal Hadi Salim { 2011fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 20126c5c8ca7SJamal Hadi Salim struct xfrm_policy *xp; 20137b67c857SThomas Graf struct xfrm_user_polexpire *up = nlmsg_data(nlh); 20146c5c8ca7SJamal Hadi Salim struct xfrm_userpolicy_info *p = &up->pol; 2015b798a9edSJamal Hadi Salim u8 type = XFRM_POLICY_TYPE_MAIN; 20166c5c8ca7SJamal Hadi Salim int err = -ENOENT; 2017295fae56SJamal Hadi Salim struct xfrm_mark m; 2018295fae56SJamal Hadi Salim u32 mark = xfrm_mark_get(attrs, &m); 20196c5c8ca7SJamal Hadi Salim 202035a7aa08SThomas Graf err = copy_from_user_policy_type(&type, attrs); 2021f7b6983fSMasahide NAKAMURA if (err) 2022f7b6983fSMasahide NAKAMURA return err; 2023f7b6983fSMasahide NAKAMURA 2024c8bf4d04STimo Teräs err = verify_policy_dir(p->dir); 2025c8bf4d04STimo Teräs if (err) 2026c8bf4d04STimo Teräs return err; 2027c8bf4d04STimo Teräs 20286c5c8ca7SJamal Hadi Salim if (p->index) 2029295fae56SJamal Hadi Salim xp = xfrm_policy_byid(net, mark, type, p->dir, p->index, 0, &err); 20306c5c8ca7SJamal Hadi Salim else { 20315424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_SEC_CTX]; 203203e1ad7bSPaul Moore struct xfrm_sec_ctx *ctx; 20336c5c8ca7SJamal Hadi Salim 203435a7aa08SThomas Graf err = verify_sec_ctx_len(attrs); 20356c5c8ca7SJamal Hadi Salim if (err) 20366c5c8ca7SJamal Hadi Salim return err; 20376c5c8ca7SJamal Hadi Salim 20382c8dd116SDenis V. Lunev ctx = NULL; 20396c5c8ca7SJamal Hadi Salim if (rt) { 20405424f32eSThomas Graf struct xfrm_user_sec_ctx *uctx = nla_data(rt); 20416c5c8ca7SJamal Hadi Salim 204252a4c640SNikolay Aleksandrov err = security_xfrm_policy_alloc(&ctx, uctx, GFP_KERNEL); 204303e1ad7bSPaul Moore if (err) 20446c5c8ca7SJamal Hadi Salim return err; 20452c8dd116SDenis V. Lunev } 2046295fae56SJamal Hadi Salim xp = xfrm_policy_bysel_ctx(net, mark, type, p->dir, 20476f26b61eSJamal Hadi Salim &p->sel, ctx, 0, &err); 204803e1ad7bSPaul Moore security_xfrm_policy_free(ctx); 20496c5c8ca7SJamal Hadi Salim } 20506c5c8ca7SJamal Hadi Salim if (xp == NULL) 2051ef41aaa0SEric Paris return -ENOENT; 205203e1ad7bSPaul Moore 2053ea2dea9dSTimo Teräs if (unlikely(xp->walk.dead)) 20546c5c8ca7SJamal Hadi Salim goto out; 20556c5c8ca7SJamal Hadi Salim 20566c5c8ca7SJamal Hadi Salim err = 0; 20576c5c8ca7SJamal Hadi Salim if (up->hard) { 20586c5c8ca7SJamal Hadi Salim xfrm_policy_delete(xp, p->dir); 20592e71029eSTetsuo Handa xfrm_audit_policy_delete(xp, 1, true); 20606c5c8ca7SJamal Hadi Salim } 2061c6bb8136SEric W. Biederman km_policy_expired(xp, p->dir, up->hard, nlh->nlmsg_pid); 20626c5c8ca7SJamal Hadi Salim 20636c5c8ca7SJamal Hadi Salim out: 20646c5c8ca7SJamal Hadi Salim xfrm_pol_put(xp); 20656c5c8ca7SJamal Hadi Salim return err; 20666c5c8ca7SJamal Hadi Salim } 20676c5c8ca7SJamal Hadi Salim 206822e70050SChristoph Hellwig static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh, 20695424f32eSThomas Graf struct nlattr **attrs) 207053bc6b4dSJamal Hadi Salim { 2071fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 207253bc6b4dSJamal Hadi Salim struct xfrm_state *x; 207353bc6b4dSJamal Hadi Salim int err; 20747b67c857SThomas Graf struct xfrm_user_expire *ue = nlmsg_data(nlh); 207553bc6b4dSJamal Hadi Salim struct xfrm_usersa_info *p = &ue->state; 20766f26b61eSJamal Hadi Salim struct xfrm_mark m; 2077928497f0SNicolas Dichtel u32 mark = xfrm_mark_get(attrs, &m); 207853bc6b4dSJamal Hadi Salim 20796f26b61eSJamal Hadi Salim x = xfrm_state_lookup(net, mark, &p->id.daddr, p->id.spi, p->id.proto, p->family); 208053bc6b4dSJamal Hadi Salim 20813a765aa5SDavid S. Miller err = -ENOENT; 208253bc6b4dSJamal Hadi Salim if (x == NULL) 208353bc6b4dSJamal Hadi Salim return err; 208453bc6b4dSJamal Hadi Salim 208553bc6b4dSJamal Hadi Salim spin_lock_bh(&x->lock); 20863a765aa5SDavid S. Miller err = -EINVAL; 208753bc6b4dSJamal Hadi Salim if (x->km.state != XFRM_STATE_VALID) 208853bc6b4dSJamal Hadi Salim goto out; 2089c6bb8136SEric W. Biederman km_state_expired(x, ue->hard, nlh->nlmsg_pid); 209053bc6b4dSJamal Hadi Salim 2091161a09e7SJoy Latten if (ue->hard) { 209253bc6b4dSJamal Hadi Salim __xfrm_state_delete(x); 20932e71029eSTetsuo Handa xfrm_audit_state_delete(x, 1, true); 2094161a09e7SJoy Latten } 20953a765aa5SDavid S. Miller err = 0; 209653bc6b4dSJamal Hadi Salim out: 209753bc6b4dSJamal Hadi Salim spin_unlock_bh(&x->lock); 209853bc6b4dSJamal Hadi Salim xfrm_state_put(x); 209953bc6b4dSJamal Hadi Salim return err; 210053bc6b4dSJamal Hadi Salim } 210153bc6b4dSJamal Hadi Salim 210222e70050SChristoph Hellwig static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh, 21035424f32eSThomas Graf struct nlattr **attrs) 2104980ebd25SJamal Hadi Salim { 2105fc34acd3SAlexey Dobriyan struct net *net = sock_net(skb->sk); 2106980ebd25SJamal Hadi Salim struct xfrm_policy *xp; 2107980ebd25SJamal Hadi Salim struct xfrm_user_tmpl *ut; 2108980ebd25SJamal Hadi Salim int i; 21095424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_TMPL]; 21106f26b61eSJamal Hadi Salim struct xfrm_mark mark; 2111980ebd25SJamal Hadi Salim 21127b67c857SThomas Graf struct xfrm_user_acquire *ua = nlmsg_data(nlh); 2113fc34acd3SAlexey Dobriyan struct xfrm_state *x = xfrm_state_alloc(net); 2114980ebd25SJamal Hadi Salim int err = -ENOMEM; 2115980ebd25SJamal Hadi Salim 2116980ebd25SJamal Hadi Salim if (!x) 2117d8eb9307SIlpo Järvinen goto nomem; 2118980ebd25SJamal Hadi Salim 21196f26b61eSJamal Hadi Salim xfrm_mark_get(attrs, &mark); 21206f26b61eSJamal Hadi Salim 2121980ebd25SJamal Hadi Salim err = verify_newpolicy_info(&ua->policy); 2122d8eb9307SIlpo Järvinen if (err) 212373efc324SVegard Nossum goto free_state; 2124980ebd25SJamal Hadi Salim 2125980ebd25SJamal Hadi Salim /* build an XP */ 2126fc34acd3SAlexey Dobriyan xp = xfrm_policy_construct(net, &ua->policy, attrs, &err); 2127d8eb9307SIlpo Järvinen if (!xp) 2128d8eb9307SIlpo Järvinen goto free_state; 2129980ebd25SJamal Hadi Salim 2130980ebd25SJamal Hadi Salim memcpy(&x->id, &ua->id, sizeof(ua->id)); 2131980ebd25SJamal Hadi Salim memcpy(&x->props.saddr, &ua->saddr, sizeof(ua->saddr)); 2132980ebd25SJamal Hadi Salim memcpy(&x->sel, &ua->sel, sizeof(ua->sel)); 21336f26b61eSJamal Hadi Salim xp->mark.m = x->mark.m = mark.m; 21346f26b61eSJamal Hadi Salim xp->mark.v = x->mark.v = mark.v; 21355424f32eSThomas Graf ut = nla_data(rt); 2136980ebd25SJamal Hadi Salim /* extract the templates and for each call km_key */ 2137980ebd25SJamal Hadi Salim for (i = 0; i < xp->xfrm_nr; i++, ut++) { 2138980ebd25SJamal Hadi Salim struct xfrm_tmpl *t = &xp->xfrm_vec[i]; 2139980ebd25SJamal Hadi Salim memcpy(&x->id, &t->id, sizeof(x->id)); 2140980ebd25SJamal Hadi Salim x->props.mode = t->mode; 2141980ebd25SJamal Hadi Salim x->props.reqid = t->reqid; 2142980ebd25SJamal Hadi Salim x->props.family = ut->family; 2143980ebd25SJamal Hadi Salim t->aalgos = ua->aalgos; 2144980ebd25SJamal Hadi Salim t->ealgos = ua->ealgos; 2145980ebd25SJamal Hadi Salim t->calgos = ua->calgos; 2146980ebd25SJamal Hadi Salim err = km_query(x, t, xp); 2147980ebd25SJamal Hadi Salim 2148980ebd25SJamal Hadi Salim } 2149980ebd25SJamal Hadi Salim 2150980ebd25SJamal Hadi Salim kfree(x); 2151980ebd25SJamal Hadi Salim kfree(xp); 2152980ebd25SJamal Hadi Salim 2153980ebd25SJamal Hadi Salim return 0; 2154d8eb9307SIlpo Järvinen 2155d8eb9307SIlpo Järvinen free_state: 2156d8eb9307SIlpo Järvinen kfree(x); 2157d8eb9307SIlpo Järvinen nomem: 2158d8eb9307SIlpo Järvinen return err; 2159980ebd25SJamal Hadi Salim } 2160980ebd25SJamal Hadi Salim 21615c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE 21625c79de6eSShinta Sugimoto static int copy_from_user_migrate(struct xfrm_migrate *ma, 216313c1d189SArnaud Ebalard struct xfrm_kmaddress *k, 21645424f32eSThomas Graf struct nlattr **attrs, int *num) 21655c79de6eSShinta Sugimoto { 21665424f32eSThomas Graf struct nlattr *rt = attrs[XFRMA_MIGRATE]; 21675c79de6eSShinta Sugimoto struct xfrm_user_migrate *um; 21685c79de6eSShinta Sugimoto int i, num_migrate; 21695c79de6eSShinta Sugimoto 217013c1d189SArnaud Ebalard if (k != NULL) { 217113c1d189SArnaud Ebalard struct xfrm_user_kmaddress *uk; 217213c1d189SArnaud Ebalard 217313c1d189SArnaud Ebalard uk = nla_data(attrs[XFRMA_KMADDRESS]); 217413c1d189SArnaud Ebalard memcpy(&k->local, &uk->local, sizeof(k->local)); 217513c1d189SArnaud Ebalard memcpy(&k->remote, &uk->remote, sizeof(k->remote)); 217613c1d189SArnaud Ebalard k->family = uk->family; 217713c1d189SArnaud Ebalard k->reserved = uk->reserved; 217813c1d189SArnaud Ebalard } 217913c1d189SArnaud Ebalard 21805424f32eSThomas Graf um = nla_data(rt); 21815424f32eSThomas Graf num_migrate = nla_len(rt) / sizeof(*um); 21825c79de6eSShinta Sugimoto 21835c79de6eSShinta Sugimoto if (num_migrate <= 0 || num_migrate > XFRM_MAX_DEPTH) 21845c79de6eSShinta Sugimoto return -EINVAL; 21855c79de6eSShinta Sugimoto 21865c79de6eSShinta Sugimoto for (i = 0; i < num_migrate; i++, um++, ma++) { 21875c79de6eSShinta Sugimoto memcpy(&ma->old_daddr, &um->old_daddr, sizeof(ma->old_daddr)); 21885c79de6eSShinta Sugimoto memcpy(&ma->old_saddr, &um->old_saddr, sizeof(ma->old_saddr)); 21895c79de6eSShinta Sugimoto memcpy(&ma->new_daddr, &um->new_daddr, sizeof(ma->new_daddr)); 21905c79de6eSShinta Sugimoto memcpy(&ma->new_saddr, &um->new_saddr, sizeof(ma->new_saddr)); 21915c79de6eSShinta Sugimoto 21925c79de6eSShinta Sugimoto ma->proto = um->proto; 21935c79de6eSShinta Sugimoto ma->mode = um->mode; 21945c79de6eSShinta Sugimoto ma->reqid = um->reqid; 21955c79de6eSShinta Sugimoto 21965c79de6eSShinta Sugimoto ma->old_family = um->old_family; 21975c79de6eSShinta Sugimoto ma->new_family = um->new_family; 21985c79de6eSShinta Sugimoto } 21995c79de6eSShinta Sugimoto 22005c79de6eSShinta Sugimoto *num = i; 22015c79de6eSShinta Sugimoto return 0; 22025c79de6eSShinta Sugimoto } 22035c79de6eSShinta Sugimoto 22045c79de6eSShinta Sugimoto static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, 22055424f32eSThomas Graf struct nlattr **attrs) 22065c79de6eSShinta Sugimoto { 22077b67c857SThomas Graf struct xfrm_userpolicy_id *pi = nlmsg_data(nlh); 22085c79de6eSShinta Sugimoto struct xfrm_migrate m[XFRM_MAX_DEPTH]; 220913c1d189SArnaud Ebalard struct xfrm_kmaddress km, *kmp; 22105c79de6eSShinta Sugimoto u8 type; 22115c79de6eSShinta Sugimoto int err; 22125c79de6eSShinta Sugimoto int n = 0; 22138d549c4fSFan Du struct net *net = sock_net(skb->sk); 22145c79de6eSShinta Sugimoto 221535a7aa08SThomas Graf if (attrs[XFRMA_MIGRATE] == NULL) 2216cf5cb79fSThomas Graf return -EINVAL; 22175c79de6eSShinta Sugimoto 221813c1d189SArnaud Ebalard kmp = attrs[XFRMA_KMADDRESS] ? &km : NULL; 221913c1d189SArnaud Ebalard 22205424f32eSThomas Graf err = copy_from_user_policy_type(&type, attrs); 22215c79de6eSShinta Sugimoto if (err) 22225c79de6eSShinta Sugimoto return err; 22235c79de6eSShinta Sugimoto 222413c1d189SArnaud Ebalard err = copy_from_user_migrate((struct xfrm_migrate *)m, kmp, attrs, &n); 22255c79de6eSShinta Sugimoto if (err) 22265c79de6eSShinta Sugimoto return err; 22275c79de6eSShinta Sugimoto 22285c79de6eSShinta Sugimoto if (!n) 22295c79de6eSShinta Sugimoto return 0; 22305c79de6eSShinta Sugimoto 22318d549c4fSFan Du xfrm_migrate(&pi->sel, pi->dir, type, m, n, kmp, net); 22325c79de6eSShinta Sugimoto 22335c79de6eSShinta Sugimoto return 0; 22345c79de6eSShinta Sugimoto } 22355c79de6eSShinta Sugimoto #else 22365c79de6eSShinta Sugimoto static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, 22375424f32eSThomas Graf struct nlattr **attrs) 22385c79de6eSShinta Sugimoto { 22395c79de6eSShinta Sugimoto return -ENOPROTOOPT; 22405c79de6eSShinta Sugimoto } 22415c79de6eSShinta Sugimoto #endif 22425c79de6eSShinta Sugimoto 22435c79de6eSShinta Sugimoto #ifdef CONFIG_XFRM_MIGRATE 2244183cad12SDavid S. Miller static int copy_to_user_migrate(const struct xfrm_migrate *m, struct sk_buff *skb) 22455c79de6eSShinta Sugimoto { 22465c79de6eSShinta Sugimoto struct xfrm_user_migrate um; 22475c79de6eSShinta Sugimoto 22485c79de6eSShinta Sugimoto memset(&um, 0, sizeof(um)); 22495c79de6eSShinta Sugimoto um.proto = m->proto; 22505c79de6eSShinta Sugimoto um.mode = m->mode; 22515c79de6eSShinta Sugimoto um.reqid = m->reqid; 22525c79de6eSShinta Sugimoto um.old_family = m->old_family; 22535c79de6eSShinta Sugimoto memcpy(&um.old_daddr, &m->old_daddr, sizeof(um.old_daddr)); 22545c79de6eSShinta Sugimoto memcpy(&um.old_saddr, &m->old_saddr, sizeof(um.old_saddr)); 22555c79de6eSShinta Sugimoto um.new_family = m->new_family; 22565c79de6eSShinta Sugimoto memcpy(&um.new_daddr, &m->new_daddr, sizeof(um.new_daddr)); 22575c79de6eSShinta Sugimoto memcpy(&um.new_saddr, &m->new_saddr, sizeof(um.new_saddr)); 22585c79de6eSShinta Sugimoto 2259c0144beaSThomas Graf return nla_put(skb, XFRMA_MIGRATE, sizeof(um), &um); 22605c79de6eSShinta Sugimoto } 22615c79de6eSShinta Sugimoto 2262183cad12SDavid S. Miller static int copy_to_user_kmaddress(const struct xfrm_kmaddress *k, struct sk_buff *skb) 226313c1d189SArnaud Ebalard { 226413c1d189SArnaud Ebalard struct xfrm_user_kmaddress uk; 226513c1d189SArnaud Ebalard 226613c1d189SArnaud Ebalard memset(&uk, 0, sizeof(uk)); 226713c1d189SArnaud Ebalard uk.family = k->family; 226813c1d189SArnaud Ebalard uk.reserved = k->reserved; 226913c1d189SArnaud Ebalard memcpy(&uk.local, &k->local, sizeof(uk.local)); 2270a1caa322SArnaud Ebalard memcpy(&uk.remote, &k->remote, sizeof(uk.remote)); 227113c1d189SArnaud Ebalard 227213c1d189SArnaud Ebalard return nla_put(skb, XFRMA_KMADDRESS, sizeof(uk), &uk); 227313c1d189SArnaud Ebalard } 227413c1d189SArnaud Ebalard 227513c1d189SArnaud Ebalard static inline size_t xfrm_migrate_msgsize(int num_migrate, int with_kma) 22767deb2264SThomas Graf { 22777deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_userpolicy_id)) 227813c1d189SArnaud Ebalard + (with_kma ? nla_total_size(sizeof(struct xfrm_kmaddress)) : 0) 22797deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_migrate) * num_migrate) 22807deb2264SThomas Graf + userpolicy_type_attrsize(); 22817deb2264SThomas Graf } 22827deb2264SThomas Graf 2283183cad12SDavid S. Miller static int build_migrate(struct sk_buff *skb, const struct xfrm_migrate *m, 2284183cad12SDavid S. Miller int num_migrate, const struct xfrm_kmaddress *k, 2285183cad12SDavid S. Miller const struct xfrm_selector *sel, u8 dir, u8 type) 22865c79de6eSShinta Sugimoto { 2287183cad12SDavid S. Miller const struct xfrm_migrate *mp; 22885c79de6eSShinta Sugimoto struct xfrm_userpolicy_id *pol_id; 22895c79de6eSShinta Sugimoto struct nlmsghdr *nlh; 22901d1e34ddSDavid S. Miller int i, err; 22915c79de6eSShinta Sugimoto 229279b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_MIGRATE, sizeof(*pol_id), 0); 229379b8b7f4SThomas Graf if (nlh == NULL) 229479b8b7f4SThomas Graf return -EMSGSIZE; 22955c79de6eSShinta Sugimoto 22967b67c857SThomas Graf pol_id = nlmsg_data(nlh); 22975c79de6eSShinta Sugimoto /* copy data from selector, dir, and type to the pol_id */ 22985c79de6eSShinta Sugimoto memset(pol_id, 0, sizeof(*pol_id)); 22995c79de6eSShinta Sugimoto memcpy(&pol_id->sel, sel, sizeof(pol_id->sel)); 23005c79de6eSShinta Sugimoto pol_id->dir = dir; 23015c79de6eSShinta Sugimoto 23021d1e34ddSDavid S. Miller if (k != NULL) { 23031d1e34ddSDavid S. Miller err = copy_to_user_kmaddress(k, skb); 23041d1e34ddSDavid S. Miller if (err) 23051d1e34ddSDavid S. Miller goto out_cancel; 23061d1e34ddSDavid S. Miller } 23071d1e34ddSDavid S. Miller err = copy_to_user_policy_type(type, skb); 23081d1e34ddSDavid S. Miller if (err) 23091d1e34ddSDavid S. Miller goto out_cancel; 23105c79de6eSShinta Sugimoto for (i = 0, mp = m ; i < num_migrate; i++, mp++) { 23111d1e34ddSDavid S. Miller err = copy_to_user_migrate(mp, skb); 23121d1e34ddSDavid S. Miller if (err) 23131d1e34ddSDavid S. Miller goto out_cancel; 23145c79de6eSShinta Sugimoto } 23155c79de6eSShinta Sugimoto 2316053c095aSJohannes Berg nlmsg_end(skb, nlh); 2317053c095aSJohannes Berg return 0; 23181d1e34ddSDavid S. Miller 23191d1e34ddSDavid S. Miller out_cancel: 23209825069dSThomas Graf nlmsg_cancel(skb, nlh); 23211d1e34ddSDavid S. Miller return err; 23225c79de6eSShinta Sugimoto } 23235c79de6eSShinta Sugimoto 2324183cad12SDavid S. Miller static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, 2325183cad12SDavid S. Miller const struct xfrm_migrate *m, int num_migrate, 2326183cad12SDavid S. Miller const struct xfrm_kmaddress *k) 23275c79de6eSShinta Sugimoto { 2328a6483b79SAlexey Dobriyan struct net *net = &init_net; 23295c79de6eSShinta Sugimoto struct sk_buff *skb; 23305c79de6eSShinta Sugimoto 233113c1d189SArnaud Ebalard skb = nlmsg_new(xfrm_migrate_msgsize(num_migrate, !!k), GFP_ATOMIC); 23325c79de6eSShinta Sugimoto if (skb == NULL) 23335c79de6eSShinta Sugimoto return -ENOMEM; 23345c79de6eSShinta Sugimoto 23355c79de6eSShinta Sugimoto /* build migrate */ 233613c1d189SArnaud Ebalard if (build_migrate(skb, m, num_migrate, k, sel, dir, type) < 0) 23375c79de6eSShinta Sugimoto BUG(); 23385c79de6eSShinta Sugimoto 233921ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_MIGRATE); 23405c79de6eSShinta Sugimoto } 23415c79de6eSShinta Sugimoto #else 2342183cad12SDavid S. Miller static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, 2343183cad12SDavid S. Miller const struct xfrm_migrate *m, int num_migrate, 2344183cad12SDavid S. Miller const struct xfrm_kmaddress *k) 23455c79de6eSShinta Sugimoto { 23465c79de6eSShinta Sugimoto return -ENOPROTOOPT; 23475c79de6eSShinta Sugimoto } 23485c79de6eSShinta Sugimoto #endif 2349d51d081dSJamal Hadi Salim 2350a7bd9a45SThomas Graf #define XMSGSIZE(type) sizeof(struct type) 2351492b558bSThomas Graf 2352492b558bSThomas Graf static const int xfrm_msg_min[XFRM_NR_MSGTYPES] = { 235366f9a259SDavid S. Miller [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_info), 2354492b558bSThomas Graf [XFRM_MSG_DELSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 2355492b558bSThomas Graf [XFRM_MSG_GETSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 2356492b558bSThomas Graf [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_info), 2357492b558bSThomas Graf [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 2358492b558bSThomas Graf [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 2359492b558bSThomas Graf [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userspi_info), 2360980ebd25SJamal Hadi Salim [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_acquire), 236153bc6b4dSJamal Hadi Salim [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_expire), 2362492b558bSThomas Graf [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_info), 236366f9a259SDavid S. Miller [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_info), 23646c5c8ca7SJamal Hadi Salim [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_polexpire), 2365492b558bSThomas Graf [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_flush), 2366a7bd9a45SThomas Graf [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = 0, 2367d51d081dSJamal Hadi Salim [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 2368d51d081dSJamal Hadi Salim [XFRM_MSG_GETAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 236997a64b45SMasahide NAKAMURA [XFRM_MSG_REPORT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report), 23705c79de6eSShinta Sugimoto [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 2371a7bd9a45SThomas Graf [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = sizeof(u32), 2372880a6fabSChristophe Gouault [XFRM_MSG_NEWSPDINFO - XFRM_MSG_BASE] = sizeof(u32), 2373a7bd9a45SThomas Graf [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32), 23741da177e4SLinus Torvalds }; 23751da177e4SLinus Torvalds 2376492b558bSThomas Graf #undef XMSGSIZE 2377492b558bSThomas Graf 2378cf5cb79fSThomas Graf static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = { 2379c28e9304Sjamal [XFRMA_SA] = { .len = sizeof(struct xfrm_usersa_info)}, 2380c28e9304Sjamal [XFRMA_POLICY] = { .len = sizeof(struct xfrm_userpolicy_info)}, 2381c28e9304Sjamal [XFRMA_LASTUSED] = { .type = NLA_U64}, 2382c28e9304Sjamal [XFRMA_ALG_AUTH_TRUNC] = { .len = sizeof(struct xfrm_algo_auth)}, 23831a6509d9SHerbert Xu [XFRMA_ALG_AEAD] = { .len = sizeof(struct xfrm_algo_aead) }, 2384cf5cb79fSThomas Graf [XFRMA_ALG_AUTH] = { .len = sizeof(struct xfrm_algo) }, 2385cf5cb79fSThomas Graf [XFRMA_ALG_CRYPT] = { .len = sizeof(struct xfrm_algo) }, 2386cf5cb79fSThomas Graf [XFRMA_ALG_COMP] = { .len = sizeof(struct xfrm_algo) }, 2387cf5cb79fSThomas Graf [XFRMA_ENCAP] = { .len = sizeof(struct xfrm_encap_tmpl) }, 2388cf5cb79fSThomas Graf [XFRMA_TMPL] = { .len = sizeof(struct xfrm_user_tmpl) }, 2389cf5cb79fSThomas Graf [XFRMA_SEC_CTX] = { .len = sizeof(struct xfrm_sec_ctx) }, 2390cf5cb79fSThomas Graf [XFRMA_LTIME_VAL] = { .len = sizeof(struct xfrm_lifetime_cur) }, 2391cf5cb79fSThomas Graf [XFRMA_REPLAY_VAL] = { .len = sizeof(struct xfrm_replay_state) }, 2392cf5cb79fSThomas Graf [XFRMA_REPLAY_THRESH] = { .type = NLA_U32 }, 2393cf5cb79fSThomas Graf [XFRMA_ETIMER_THRESH] = { .type = NLA_U32 }, 2394cf5cb79fSThomas Graf [XFRMA_SRCADDR] = { .len = sizeof(xfrm_address_t) }, 2395cf5cb79fSThomas Graf [XFRMA_COADDR] = { .len = sizeof(xfrm_address_t) }, 2396cf5cb79fSThomas Graf [XFRMA_POLICY_TYPE] = { .len = sizeof(struct xfrm_userpolicy_type)}, 2397cf5cb79fSThomas Graf [XFRMA_MIGRATE] = { .len = sizeof(struct xfrm_user_migrate) }, 239813c1d189SArnaud Ebalard [XFRMA_KMADDRESS] = { .len = sizeof(struct xfrm_user_kmaddress) }, 23996f26b61eSJamal Hadi Salim [XFRMA_MARK] = { .len = sizeof(struct xfrm_mark) }, 240035d2856bSMartin Willi [XFRMA_TFCPAD] = { .type = NLA_U32 }, 2401d8647b79SSteffen Klassert [XFRMA_REPLAY_ESN_VAL] = { .len = sizeof(struct xfrm_replay_state_esn) }, 2402a947b0a9SNicolas Dichtel [XFRMA_SA_EXTRA_FLAGS] = { .type = NLA_U32 }, 2403d3623099SNicolas Dichtel [XFRMA_PROTO] = { .type = NLA_U8 }, 2404870a2df4SNicolas Dichtel [XFRMA_ADDRESS_FILTER] = { .len = sizeof(struct xfrm_address_filter) }, 2405cf5cb79fSThomas Graf }; 2406cf5cb79fSThomas Graf 2407880a6fabSChristophe Gouault static const struct nla_policy xfrma_spd_policy[XFRMA_SPD_MAX+1] = { 2408880a6fabSChristophe Gouault [XFRMA_SPD_IPV4_HTHRESH] = { .len = sizeof(struct xfrmu_spdhthresh) }, 2409880a6fabSChristophe Gouault [XFRMA_SPD_IPV6_HTHRESH] = { .len = sizeof(struct xfrmu_spdhthresh) }, 2410880a6fabSChristophe Gouault }; 2411880a6fabSChristophe Gouault 241205600a79SMathias Krause static const struct xfrm_link { 24135424f32eSThomas Graf int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **); 24141da177e4SLinus Torvalds int (*dump)(struct sk_buff *, struct netlink_callback *); 24154c563f76STimo Teras int (*done)(struct netlink_callback *); 2416880a6fabSChristophe Gouault const struct nla_policy *nla_pol; 2417880a6fabSChristophe Gouault int nla_max; 2418492b558bSThomas Graf } xfrm_dispatch[XFRM_NR_MSGTYPES] = { 2419492b558bSThomas Graf [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa }, 2420492b558bSThomas Graf [XFRM_MSG_DELSA - XFRM_MSG_BASE] = { .doit = xfrm_del_sa }, 2421492b558bSThomas Graf [XFRM_MSG_GETSA - XFRM_MSG_BASE] = { .doit = xfrm_get_sa, 24224c563f76STimo Teras .dump = xfrm_dump_sa, 24234c563f76STimo Teras .done = xfrm_dump_sa_done }, 2424492b558bSThomas Graf [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy }, 2425492b558bSThomas Graf [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy }, 2426492b558bSThomas Graf [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy, 24274c563f76STimo Teras .dump = xfrm_dump_policy, 24284c563f76STimo Teras .done = xfrm_dump_policy_done }, 2429492b558bSThomas Graf [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = { .doit = xfrm_alloc_userspi }, 2430980ebd25SJamal Hadi Salim [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_acquire }, 243153bc6b4dSJamal Hadi Salim [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_sa_expire }, 2432492b558bSThomas Graf [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy }, 2433492b558bSThomas Graf [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa }, 24346c5c8ca7SJamal Hadi Salim [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = { .doit = xfrm_add_pol_expire}, 2435492b558bSThomas Graf [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = { .doit = xfrm_flush_sa }, 2436492b558bSThomas Graf [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_flush_policy }, 2437d51d081dSJamal Hadi Salim [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = { .doit = xfrm_new_ae }, 2438d51d081dSJamal Hadi Salim [XFRM_MSG_GETAE - XFRM_MSG_BASE] = { .doit = xfrm_get_ae }, 24395c79de6eSShinta Sugimoto [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate }, 244028d8909bSJamal Hadi Salim [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_sadinfo }, 2441880a6fabSChristophe Gouault [XFRM_MSG_NEWSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_set_spdinfo, 2442880a6fabSChristophe Gouault .nla_pol = xfrma_spd_policy, 2443880a6fabSChristophe Gouault .nla_max = XFRMA_SPD_MAX }, 2444ecfd6b18SJamal Hadi Salim [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo }, 24451da177e4SLinus Torvalds }; 24461da177e4SLinus Torvalds 24471d00a4ebSThomas Graf static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) 24481da177e4SLinus Torvalds { 2449a6483b79SAlexey Dobriyan struct net *net = sock_net(skb->sk); 245035a7aa08SThomas Graf struct nlattr *attrs[XFRMA_MAX+1]; 245105600a79SMathias Krause const struct xfrm_link *link; 2452a7bd9a45SThomas Graf int type, err; 24531da177e4SLinus Torvalds 245474005991SFan Du #ifdef CONFIG_COMPAT 24552bf8c476SAndy Lutomirski if (in_compat_syscall()) 245683e2d058SYi Zhao return -EOPNOTSUPP; 245774005991SFan Du #endif 245874005991SFan Du 24591da177e4SLinus Torvalds type = nlh->nlmsg_type; 24601da177e4SLinus Torvalds if (type > XFRM_MSG_MAX) 24611d00a4ebSThomas Graf return -EINVAL; 24621da177e4SLinus Torvalds 24631da177e4SLinus Torvalds type -= XFRM_MSG_BASE; 24641da177e4SLinus Torvalds link = &xfrm_dispatch[type]; 24651da177e4SLinus Torvalds 24661da177e4SLinus Torvalds /* All operations require privileges, even GET */ 246790f62cf3SEric W. Biederman if (!netlink_net_capable(skb, CAP_NET_ADMIN)) 24681d00a4ebSThomas Graf return -EPERM; 24691da177e4SLinus Torvalds 2470492b558bSThomas Graf if ((type == (XFRM_MSG_GETSA - XFRM_MSG_BASE) || 2471492b558bSThomas Graf type == (XFRM_MSG_GETPOLICY - XFRM_MSG_BASE)) && 2472b8f3ab42SDavid S. Miller (nlh->nlmsg_flags & NLM_F_DUMP)) { 24731da177e4SLinus Torvalds if (link->dump == NULL) 24741d00a4ebSThomas Graf return -EINVAL; 24751da177e4SLinus Torvalds 247680d326faSPablo Neira Ayuso { 247780d326faSPablo Neira Ayuso struct netlink_dump_control c = { 247880d326faSPablo Neira Ayuso .dump = link->dump, 247980d326faSPablo Neira Ayuso .done = link->done, 248080d326faSPablo Neira Ayuso }; 248180d326faSPablo Neira Ayuso return netlink_dump_start(net->xfrm.nlsk, skb, nlh, &c); 248280d326faSPablo Neira Ayuso } 24831da177e4SLinus Torvalds } 24841da177e4SLinus Torvalds 2485880a6fabSChristophe Gouault err = nlmsg_parse(nlh, xfrm_msg_min[type], attrs, 2486880a6fabSChristophe Gouault link->nla_max ? : XFRMA_MAX, 2487880a6fabSChristophe Gouault link->nla_pol ? : xfrma_policy); 2488a7bd9a45SThomas Graf if (err < 0) 2489a7bd9a45SThomas Graf return err; 24901da177e4SLinus Torvalds 24911da177e4SLinus Torvalds if (link->doit == NULL) 24921d00a4ebSThomas Graf return -EINVAL; 24931da177e4SLinus Torvalds 24945424f32eSThomas Graf return link->doit(skb, nlh, attrs); 24951da177e4SLinus Torvalds } 24961da177e4SLinus Torvalds 2497cd40b7d3SDenis V. Lunev static void xfrm_netlink_rcv(struct sk_buff *skb) 24981da177e4SLinus Torvalds { 2499283bc9f3SFan Du struct net *net = sock_net(skb->sk); 2500283bc9f3SFan Du 2501283bc9f3SFan Du mutex_lock(&net->xfrm.xfrm_cfg_mutex); 2502cd40b7d3SDenis V. Lunev netlink_rcv_skb(skb, &xfrm_user_rcv_msg); 2503283bc9f3SFan Du mutex_unlock(&net->xfrm.xfrm_cfg_mutex); 25041da177e4SLinus Torvalds } 25051da177e4SLinus Torvalds 25067deb2264SThomas Graf static inline size_t xfrm_expire_msgsize(void) 25077deb2264SThomas Graf { 25086f26b61eSJamal Hadi Salim return NLMSG_ALIGN(sizeof(struct xfrm_user_expire)) 25096f26b61eSJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark)); 25107deb2264SThomas Graf } 25117deb2264SThomas Graf 2512214e005bSDavid S. Miller static int build_expire(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c) 25131da177e4SLinus Torvalds { 25141da177e4SLinus Torvalds struct xfrm_user_expire *ue; 25151da177e4SLinus Torvalds struct nlmsghdr *nlh; 25161d1e34ddSDavid S. Miller int err; 25171da177e4SLinus Torvalds 251815e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, 0, XFRM_MSG_EXPIRE, sizeof(*ue), 0); 251979b8b7f4SThomas Graf if (nlh == NULL) 252079b8b7f4SThomas Graf return -EMSGSIZE; 25211da177e4SLinus Torvalds 25227b67c857SThomas Graf ue = nlmsg_data(nlh); 25231da177e4SLinus Torvalds copy_to_user_state(x, &ue->state); 2524d51d081dSJamal Hadi Salim ue->hard = (c->data.hard != 0) ? 1 : 0; 25251da177e4SLinus Torvalds 25261d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &x->mark); 25271d1e34ddSDavid S. Miller if (err) 25281d1e34ddSDavid S. Miller return err; 25296f26b61eSJamal Hadi Salim 2530053c095aSJohannes Berg nlmsg_end(skb, nlh); 2531053c095aSJohannes Berg return 0; 25321da177e4SLinus Torvalds } 25331da177e4SLinus Torvalds 2534214e005bSDavid S. Miller static int xfrm_exp_state_notify(struct xfrm_state *x, const struct km_event *c) 25351da177e4SLinus Torvalds { 2536fc34acd3SAlexey Dobriyan struct net *net = xs_net(x); 25371da177e4SLinus Torvalds struct sk_buff *skb; 25381da177e4SLinus Torvalds 25397deb2264SThomas Graf skb = nlmsg_new(xfrm_expire_msgsize(), GFP_ATOMIC); 25401da177e4SLinus Torvalds if (skb == NULL) 25411da177e4SLinus Torvalds return -ENOMEM; 25421da177e4SLinus Torvalds 25436f26b61eSJamal Hadi Salim if (build_expire(skb, x, c) < 0) { 25446f26b61eSJamal Hadi Salim kfree_skb(skb); 25456f26b61eSJamal Hadi Salim return -EMSGSIZE; 25466f26b61eSJamal Hadi Salim } 25471da177e4SLinus Torvalds 254821ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE); 25491da177e4SLinus Torvalds } 25501da177e4SLinus Torvalds 2551214e005bSDavid S. Miller static int xfrm_aevent_state_notify(struct xfrm_state *x, const struct km_event *c) 2552d51d081dSJamal Hadi Salim { 2553fc34acd3SAlexey Dobriyan struct net *net = xs_net(x); 2554d51d081dSJamal Hadi Salim struct sk_buff *skb; 2555d51d081dSJamal Hadi Salim 2556d8647b79SSteffen Klassert skb = nlmsg_new(xfrm_aevent_msgsize(x), GFP_ATOMIC); 2557d51d081dSJamal Hadi Salim if (skb == NULL) 2558d51d081dSJamal Hadi Salim return -ENOMEM; 2559d51d081dSJamal Hadi Salim 2560d51d081dSJamal Hadi Salim if (build_aevent(skb, x, c) < 0) 2561d51d081dSJamal Hadi Salim BUG(); 2562d51d081dSJamal Hadi Salim 256321ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_AEVENTS); 2564d51d081dSJamal Hadi Salim } 2565d51d081dSJamal Hadi Salim 2566214e005bSDavid S. Miller static int xfrm_notify_sa_flush(const struct km_event *c) 256726b15dadSJamal Hadi Salim { 25687067802eSAlexey Dobriyan struct net *net = c->net; 256926b15dadSJamal Hadi Salim struct xfrm_usersa_flush *p; 257026b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 257126b15dadSJamal Hadi Salim struct sk_buff *skb; 25727deb2264SThomas Graf int len = NLMSG_ALIGN(sizeof(struct xfrm_usersa_flush)); 257326b15dadSJamal Hadi Salim 25747deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 257526b15dadSJamal Hadi Salim if (skb == NULL) 257626b15dadSJamal Hadi Salim return -ENOMEM; 257726b15dadSJamal Hadi Salim 257815e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, c->seq, XFRM_MSG_FLUSHSA, sizeof(*p), 0); 257979b8b7f4SThomas Graf if (nlh == NULL) { 258079b8b7f4SThomas Graf kfree_skb(skb); 258179b8b7f4SThomas Graf return -EMSGSIZE; 258279b8b7f4SThomas Graf } 258326b15dadSJamal Hadi Salim 25847b67c857SThomas Graf p = nlmsg_data(nlh); 2585bf08867fSHerbert Xu p->proto = c->data.proto; 258626b15dadSJamal Hadi Salim 25879825069dSThomas Graf nlmsg_end(skb, nlh); 258826b15dadSJamal Hadi Salim 258921ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_SA); 259026b15dadSJamal Hadi Salim } 259126b15dadSJamal Hadi Salim 25927deb2264SThomas Graf static inline size_t xfrm_sa_len(struct xfrm_state *x) 259326b15dadSJamal Hadi Salim { 25947deb2264SThomas Graf size_t l = 0; 25951a6509d9SHerbert Xu if (x->aead) 25961a6509d9SHerbert Xu l += nla_total_size(aead_len(x->aead)); 25974447bb33SMartin Willi if (x->aalg) { 25984447bb33SMartin Willi l += nla_total_size(sizeof(struct xfrm_algo) + 25994447bb33SMartin Willi (x->aalg->alg_key_len + 7) / 8); 26004447bb33SMartin Willi l += nla_total_size(xfrm_alg_auth_len(x->aalg)); 26014447bb33SMartin Willi } 260226b15dadSJamal Hadi Salim if (x->ealg) 26030f99be0dSEric Dumazet l += nla_total_size(xfrm_alg_len(x->ealg)); 260426b15dadSJamal Hadi Salim if (x->calg) 26057deb2264SThomas Graf l += nla_total_size(sizeof(*x->calg)); 260626b15dadSJamal Hadi Salim if (x->encap) 26077deb2264SThomas Graf l += nla_total_size(sizeof(*x->encap)); 260835d2856bSMartin Willi if (x->tfcpad) 260935d2856bSMartin Willi l += nla_total_size(sizeof(x->tfcpad)); 2610d8647b79SSteffen Klassert if (x->replay_esn) 2611d8647b79SSteffen Klassert l += nla_total_size(xfrm_replay_state_esn_len(x->replay_esn)); 2612f293a5e3Sdingzhi else 2613f293a5e3Sdingzhi l += nla_total_size(sizeof(struct xfrm_replay_state)); 261468325d3bSHerbert Xu if (x->security) 261568325d3bSHerbert Xu l += nla_total_size(sizeof(struct xfrm_user_sec_ctx) + 261668325d3bSHerbert Xu x->security->ctx_len); 261768325d3bSHerbert Xu if (x->coaddr) 261868325d3bSHerbert Xu l += nla_total_size(sizeof(*x->coaddr)); 2619a947b0a9SNicolas Dichtel if (x->props.extra_flags) 2620a947b0a9SNicolas Dichtel l += nla_total_size(sizeof(x->props.extra_flags)); 262168325d3bSHerbert Xu 2622d26f3984SHerbert Xu /* Must count x->lastused as it may become non-zero behind our back. */ 2623de95c4a4SNicolas Dichtel l += nla_total_size_64bit(sizeof(u64)); 262426b15dadSJamal Hadi Salim 262526b15dadSJamal Hadi Salim return l; 262626b15dadSJamal Hadi Salim } 262726b15dadSJamal Hadi Salim 2628214e005bSDavid S. Miller static int xfrm_notify_sa(struct xfrm_state *x, const struct km_event *c) 262926b15dadSJamal Hadi Salim { 2630fc34acd3SAlexey Dobriyan struct net *net = xs_net(x); 263126b15dadSJamal Hadi Salim struct xfrm_usersa_info *p; 26320603eac0SHerbert Xu struct xfrm_usersa_id *id; 263326b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 263426b15dadSJamal Hadi Salim struct sk_buff *skb; 263526b15dadSJamal Hadi Salim int len = xfrm_sa_len(x); 26361d1e34ddSDavid S. Miller int headlen, err; 26370603eac0SHerbert Xu 26380603eac0SHerbert Xu headlen = sizeof(*p); 26390603eac0SHerbert Xu if (c->event == XFRM_MSG_DELSA) { 26407deb2264SThomas Graf len += nla_total_size(headlen); 26410603eac0SHerbert Xu headlen = sizeof(*id); 26426f26b61eSJamal Hadi Salim len += nla_total_size(sizeof(struct xfrm_mark)); 26430603eac0SHerbert Xu } 26447deb2264SThomas Graf len += NLMSG_ALIGN(headlen); 264526b15dadSJamal Hadi Salim 26467deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 264726b15dadSJamal Hadi Salim if (skb == NULL) 264826b15dadSJamal Hadi Salim return -ENOMEM; 264926b15dadSJamal Hadi Salim 265015e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, c->seq, c->event, headlen, 0); 26511d1e34ddSDavid S. Miller err = -EMSGSIZE; 265279b8b7f4SThomas Graf if (nlh == NULL) 26531d1e34ddSDavid S. Miller goto out_free_skb; 265426b15dadSJamal Hadi Salim 26557b67c857SThomas Graf p = nlmsg_data(nlh); 26560603eac0SHerbert Xu if (c->event == XFRM_MSG_DELSA) { 2657c0144beaSThomas Graf struct nlattr *attr; 2658c0144beaSThomas Graf 26597b67c857SThomas Graf id = nlmsg_data(nlh); 26600603eac0SHerbert Xu memcpy(&id->daddr, &x->id.daddr, sizeof(id->daddr)); 26610603eac0SHerbert Xu id->spi = x->id.spi; 26620603eac0SHerbert Xu id->family = x->props.family; 26630603eac0SHerbert Xu id->proto = x->id.proto; 26640603eac0SHerbert Xu 2665c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_SA, sizeof(*p)); 26661d1e34ddSDavid S. Miller err = -EMSGSIZE; 2667c0144beaSThomas Graf if (attr == NULL) 26681d1e34ddSDavid S. Miller goto out_free_skb; 2669c0144beaSThomas Graf 2670c0144beaSThomas Graf p = nla_data(attr); 26710603eac0SHerbert Xu } 26721d1e34ddSDavid S. Miller err = copy_to_user_state_extra(x, p, skb); 26731d1e34ddSDavid S. Miller if (err) 26741d1e34ddSDavid S. Miller goto out_free_skb; 267526b15dadSJamal Hadi Salim 26769825069dSThomas Graf nlmsg_end(skb, nlh); 267726b15dadSJamal Hadi Salim 267821ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_SA); 267926b15dadSJamal Hadi Salim 26801d1e34ddSDavid S. Miller out_free_skb: 268126b15dadSJamal Hadi Salim kfree_skb(skb); 26821d1e34ddSDavid S. Miller return err; 268326b15dadSJamal Hadi Salim } 268426b15dadSJamal Hadi Salim 2685214e005bSDavid S. Miller static int xfrm_send_state_notify(struct xfrm_state *x, const struct km_event *c) 268626b15dadSJamal Hadi Salim { 268726b15dadSJamal Hadi Salim 268826b15dadSJamal Hadi Salim switch (c->event) { 2689f60f6b8fSHerbert Xu case XFRM_MSG_EXPIRE: 269026b15dadSJamal Hadi Salim return xfrm_exp_state_notify(x, c); 2691d51d081dSJamal Hadi Salim case XFRM_MSG_NEWAE: 2692d51d081dSJamal Hadi Salim return xfrm_aevent_state_notify(x, c); 2693f60f6b8fSHerbert Xu case XFRM_MSG_DELSA: 2694f60f6b8fSHerbert Xu case XFRM_MSG_UPDSA: 2695f60f6b8fSHerbert Xu case XFRM_MSG_NEWSA: 269626b15dadSJamal Hadi Salim return xfrm_notify_sa(x, c); 2697f60f6b8fSHerbert Xu case XFRM_MSG_FLUSHSA: 269826b15dadSJamal Hadi Salim return xfrm_notify_sa_flush(c); 269926b15dadSJamal Hadi Salim default: 270062db5cfdSstephen hemminger printk(KERN_NOTICE "xfrm_user: Unknown SA event %d\n", 270162db5cfdSstephen hemminger c->event); 270226b15dadSJamal Hadi Salim break; 270326b15dadSJamal Hadi Salim } 270426b15dadSJamal Hadi Salim 270526b15dadSJamal Hadi Salim return 0; 270626b15dadSJamal Hadi Salim 270726b15dadSJamal Hadi Salim } 270826b15dadSJamal Hadi Salim 27097deb2264SThomas Graf static inline size_t xfrm_acquire_msgsize(struct xfrm_state *x, 27107deb2264SThomas Graf struct xfrm_policy *xp) 27117deb2264SThomas Graf { 27127deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_acquire)) 27137deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr) 27146f26b61eSJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark)) 27157deb2264SThomas Graf + nla_total_size(xfrm_user_sec_ctx_size(x->security)) 27167deb2264SThomas Graf + userpolicy_type_attrsize(); 27177deb2264SThomas Graf } 27187deb2264SThomas Graf 27191da177e4SLinus Torvalds static int build_acquire(struct sk_buff *skb, struct xfrm_state *x, 272065e0736bSFan Du struct xfrm_tmpl *xt, struct xfrm_policy *xp) 27211da177e4SLinus Torvalds { 27221d1e34ddSDavid S. Miller __u32 seq = xfrm_get_acqseq(); 27231da177e4SLinus Torvalds struct xfrm_user_acquire *ua; 27241da177e4SLinus Torvalds struct nlmsghdr *nlh; 27251d1e34ddSDavid S. Miller int err; 27261da177e4SLinus Torvalds 272779b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_ACQUIRE, sizeof(*ua), 0); 272879b8b7f4SThomas Graf if (nlh == NULL) 272979b8b7f4SThomas Graf return -EMSGSIZE; 27301da177e4SLinus Torvalds 27317b67c857SThomas Graf ua = nlmsg_data(nlh); 27321da177e4SLinus Torvalds memcpy(&ua->id, &x->id, sizeof(ua->id)); 27331da177e4SLinus Torvalds memcpy(&ua->saddr, &x->props.saddr, sizeof(ua->saddr)); 27341da177e4SLinus Torvalds memcpy(&ua->sel, &x->sel, sizeof(ua->sel)); 273565e0736bSFan Du copy_to_user_policy(xp, &ua->policy, XFRM_POLICY_OUT); 27361da177e4SLinus Torvalds ua->aalgos = xt->aalgos; 27371da177e4SLinus Torvalds ua->ealgos = xt->ealgos; 27381da177e4SLinus Torvalds ua->calgos = xt->calgos; 27391da177e4SLinus Torvalds ua->seq = x->km.seq = seq; 27401da177e4SLinus Torvalds 27411d1e34ddSDavid S. Miller err = copy_to_user_tmpl(xp, skb); 27421d1e34ddSDavid S. Miller if (!err) 27431d1e34ddSDavid S. Miller err = copy_to_user_state_sec_ctx(x, skb); 27441d1e34ddSDavid S. Miller if (!err) 27451d1e34ddSDavid S. Miller err = copy_to_user_policy_type(xp->type, skb); 27461d1e34ddSDavid S. Miller if (!err) 27471d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &xp->mark); 27481d1e34ddSDavid S. Miller if (err) { 27491d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh); 27501d1e34ddSDavid S. Miller return err; 27511d1e34ddSDavid S. Miller } 27521da177e4SLinus Torvalds 2753053c095aSJohannes Berg nlmsg_end(skb, nlh); 2754053c095aSJohannes Berg return 0; 27551da177e4SLinus Torvalds } 27561da177e4SLinus Torvalds 27571da177e4SLinus Torvalds static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt, 275865e0736bSFan Du struct xfrm_policy *xp) 27591da177e4SLinus Torvalds { 2760a6483b79SAlexey Dobriyan struct net *net = xs_net(x); 27611da177e4SLinus Torvalds struct sk_buff *skb; 27621da177e4SLinus Torvalds 27637deb2264SThomas Graf skb = nlmsg_new(xfrm_acquire_msgsize(x, xp), GFP_ATOMIC); 27641da177e4SLinus Torvalds if (skb == NULL) 27651da177e4SLinus Torvalds return -ENOMEM; 27661da177e4SLinus Torvalds 276765e0736bSFan Du if (build_acquire(skb, x, xt, xp) < 0) 27681da177e4SLinus Torvalds BUG(); 27691da177e4SLinus Torvalds 277021ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_ACQUIRE); 27711da177e4SLinus Torvalds } 27721da177e4SLinus Torvalds 27731da177e4SLinus Torvalds /* User gives us xfrm_user_policy_info followed by an array of 0 27741da177e4SLinus Torvalds * or more templates. 27751da177e4SLinus Torvalds */ 2776cb969f07SVenkat Yekkirala static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt, 27771da177e4SLinus Torvalds u8 *data, int len, int *dir) 27781da177e4SLinus Torvalds { 2779fc34acd3SAlexey Dobriyan struct net *net = sock_net(sk); 27801da177e4SLinus Torvalds struct xfrm_userpolicy_info *p = (struct xfrm_userpolicy_info *)data; 27811da177e4SLinus Torvalds struct xfrm_user_tmpl *ut = (struct xfrm_user_tmpl *) (p + 1); 27821da177e4SLinus Torvalds struct xfrm_policy *xp; 27831da177e4SLinus Torvalds int nr; 27841da177e4SLinus Torvalds 2785cb969f07SVenkat Yekkirala switch (sk->sk_family) { 27861da177e4SLinus Torvalds case AF_INET: 27871da177e4SLinus Torvalds if (opt != IP_XFRM_POLICY) { 27881da177e4SLinus Torvalds *dir = -EOPNOTSUPP; 27891da177e4SLinus Torvalds return NULL; 27901da177e4SLinus Torvalds } 27911da177e4SLinus Torvalds break; 2792dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 27931da177e4SLinus Torvalds case AF_INET6: 27941da177e4SLinus Torvalds if (opt != IPV6_XFRM_POLICY) { 27951da177e4SLinus Torvalds *dir = -EOPNOTSUPP; 27961da177e4SLinus Torvalds return NULL; 27971da177e4SLinus Torvalds } 27981da177e4SLinus Torvalds break; 27991da177e4SLinus Torvalds #endif 28001da177e4SLinus Torvalds default: 28011da177e4SLinus Torvalds *dir = -EINVAL; 28021da177e4SLinus Torvalds return NULL; 28031da177e4SLinus Torvalds } 28041da177e4SLinus Torvalds 28051da177e4SLinus Torvalds *dir = -EINVAL; 28061da177e4SLinus Torvalds 28071da177e4SLinus Torvalds if (len < sizeof(*p) || 28081da177e4SLinus Torvalds verify_newpolicy_info(p)) 28091da177e4SLinus Torvalds return NULL; 28101da177e4SLinus Torvalds 28111da177e4SLinus Torvalds nr = ((len - sizeof(*p)) / sizeof(*ut)); 2812b4ad86bfSDavid S. Miller if (validate_tmpl(nr, ut, p->sel.family)) 28131da177e4SLinus Torvalds return NULL; 28141da177e4SLinus Torvalds 2815a4f1bac6SHerbert Xu if (p->dir > XFRM_POLICY_OUT) 2816a4f1bac6SHerbert Xu return NULL; 2817a4f1bac6SHerbert Xu 28182f09a4d5SHerbert Xu xp = xfrm_policy_alloc(net, GFP_ATOMIC); 28191da177e4SLinus Torvalds if (xp == NULL) { 28201da177e4SLinus Torvalds *dir = -ENOBUFS; 28211da177e4SLinus Torvalds return NULL; 28221da177e4SLinus Torvalds } 28231da177e4SLinus Torvalds 28241da177e4SLinus Torvalds copy_from_user_policy(xp, p); 2825f7b6983fSMasahide NAKAMURA xp->type = XFRM_POLICY_TYPE_MAIN; 28261da177e4SLinus Torvalds copy_templates(xp, ut, nr); 28271da177e4SLinus Torvalds 28281da177e4SLinus Torvalds *dir = p->dir; 28291da177e4SLinus Torvalds 28301da177e4SLinus Torvalds return xp; 28311da177e4SLinus Torvalds } 28321da177e4SLinus Torvalds 28337deb2264SThomas Graf static inline size_t xfrm_polexpire_msgsize(struct xfrm_policy *xp) 28347deb2264SThomas Graf { 28357deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_polexpire)) 28367deb2264SThomas Graf + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr) 28377deb2264SThomas Graf + nla_total_size(xfrm_user_sec_ctx_size(xp->security)) 2838295fae56SJamal Hadi Salim + nla_total_size(sizeof(struct xfrm_mark)) 28397deb2264SThomas Graf + userpolicy_type_attrsize(); 28407deb2264SThomas Graf } 28417deb2264SThomas Graf 28421da177e4SLinus Torvalds static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp, 2843214e005bSDavid S. Miller int dir, const struct km_event *c) 28441da177e4SLinus Torvalds { 28451da177e4SLinus Torvalds struct xfrm_user_polexpire *upe; 2846d51d081dSJamal Hadi Salim int hard = c->data.hard; 28471d1e34ddSDavid S. Miller struct nlmsghdr *nlh; 28481d1e34ddSDavid S. Miller int err; 28491da177e4SLinus Torvalds 285015e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, 0, XFRM_MSG_POLEXPIRE, sizeof(*upe), 0); 285179b8b7f4SThomas Graf if (nlh == NULL) 285279b8b7f4SThomas Graf return -EMSGSIZE; 28531da177e4SLinus Torvalds 28547b67c857SThomas Graf upe = nlmsg_data(nlh); 28551da177e4SLinus Torvalds copy_to_user_policy(xp, &upe->pol, dir); 28561d1e34ddSDavid S. Miller err = copy_to_user_tmpl(xp, skb); 28571d1e34ddSDavid S. Miller if (!err) 28581d1e34ddSDavid S. Miller err = copy_to_user_sec_ctx(xp, skb); 28591d1e34ddSDavid S. Miller if (!err) 28601d1e34ddSDavid S. Miller err = copy_to_user_policy_type(xp->type, skb); 28611d1e34ddSDavid S. Miller if (!err) 28621d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &xp->mark); 28631d1e34ddSDavid S. Miller if (err) { 28641d1e34ddSDavid S. Miller nlmsg_cancel(skb, nlh); 28651d1e34ddSDavid S. Miller return err; 28661d1e34ddSDavid S. Miller } 28671da177e4SLinus Torvalds upe->hard = !!hard; 28681da177e4SLinus Torvalds 2869053c095aSJohannes Berg nlmsg_end(skb, nlh); 2870053c095aSJohannes Berg return 0; 28711da177e4SLinus Torvalds } 28721da177e4SLinus Torvalds 2873214e005bSDavid S. Miller static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c) 28741da177e4SLinus Torvalds { 2875fc34acd3SAlexey Dobriyan struct net *net = xp_net(xp); 28761da177e4SLinus Torvalds struct sk_buff *skb; 28771da177e4SLinus Torvalds 28787deb2264SThomas Graf skb = nlmsg_new(xfrm_polexpire_msgsize(xp), GFP_ATOMIC); 28791da177e4SLinus Torvalds if (skb == NULL) 28801da177e4SLinus Torvalds return -ENOMEM; 28811da177e4SLinus Torvalds 2882d51d081dSJamal Hadi Salim if (build_polexpire(skb, xp, dir, c) < 0) 28831da177e4SLinus Torvalds BUG(); 28841da177e4SLinus Torvalds 288521ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE); 28861da177e4SLinus Torvalds } 28871da177e4SLinus Torvalds 2888214e005bSDavid S. Miller static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_event *c) 288926b15dadSJamal Hadi Salim { 28901d1e34ddSDavid S. Miller int len = nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr); 2891fc34acd3SAlexey Dobriyan struct net *net = xp_net(xp); 289226b15dadSJamal Hadi Salim struct xfrm_userpolicy_info *p; 28930603eac0SHerbert Xu struct xfrm_userpolicy_id *id; 289426b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 289526b15dadSJamal Hadi Salim struct sk_buff *skb; 28961d1e34ddSDavid S. Miller int headlen, err; 28970603eac0SHerbert Xu 28980603eac0SHerbert Xu headlen = sizeof(*p); 28990603eac0SHerbert Xu if (c->event == XFRM_MSG_DELPOLICY) { 29007deb2264SThomas Graf len += nla_total_size(headlen); 29010603eac0SHerbert Xu headlen = sizeof(*id); 29020603eac0SHerbert Xu } 2903cfbfd45aSThomas Graf len += userpolicy_type_attrsize(); 2904295fae56SJamal Hadi Salim len += nla_total_size(sizeof(struct xfrm_mark)); 29057deb2264SThomas Graf len += NLMSG_ALIGN(headlen); 290626b15dadSJamal Hadi Salim 29077deb2264SThomas Graf skb = nlmsg_new(len, GFP_ATOMIC); 290826b15dadSJamal Hadi Salim if (skb == NULL) 290926b15dadSJamal Hadi Salim return -ENOMEM; 291026b15dadSJamal Hadi Salim 291115e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, c->seq, c->event, headlen, 0); 29121d1e34ddSDavid S. Miller err = -EMSGSIZE; 291379b8b7f4SThomas Graf if (nlh == NULL) 29141d1e34ddSDavid S. Miller goto out_free_skb; 291526b15dadSJamal Hadi Salim 29167b67c857SThomas Graf p = nlmsg_data(nlh); 29170603eac0SHerbert Xu if (c->event == XFRM_MSG_DELPOLICY) { 2918c0144beaSThomas Graf struct nlattr *attr; 2919c0144beaSThomas Graf 29207b67c857SThomas Graf id = nlmsg_data(nlh); 29210603eac0SHerbert Xu memset(id, 0, sizeof(*id)); 29220603eac0SHerbert Xu id->dir = dir; 29230603eac0SHerbert Xu if (c->data.byid) 29240603eac0SHerbert Xu id->index = xp->index; 29250603eac0SHerbert Xu else 29260603eac0SHerbert Xu memcpy(&id->sel, &xp->selector, sizeof(id->sel)); 29270603eac0SHerbert Xu 2928c0144beaSThomas Graf attr = nla_reserve(skb, XFRMA_POLICY, sizeof(*p)); 29291d1e34ddSDavid S. Miller err = -EMSGSIZE; 2930c0144beaSThomas Graf if (attr == NULL) 29311d1e34ddSDavid S. Miller goto out_free_skb; 2932c0144beaSThomas Graf 2933c0144beaSThomas Graf p = nla_data(attr); 29340603eac0SHerbert Xu } 293526b15dadSJamal Hadi Salim 293626b15dadSJamal Hadi Salim copy_to_user_policy(xp, p, dir); 29371d1e34ddSDavid S. Miller err = copy_to_user_tmpl(xp, skb); 29381d1e34ddSDavid S. Miller if (!err) 29391d1e34ddSDavid S. Miller err = copy_to_user_policy_type(xp->type, skb); 29401d1e34ddSDavid S. Miller if (!err) 29411d1e34ddSDavid S. Miller err = xfrm_mark_put(skb, &xp->mark); 29421d1e34ddSDavid S. Miller if (err) 29431d1e34ddSDavid S. Miller goto out_free_skb; 2944295fae56SJamal Hadi Salim 29459825069dSThomas Graf nlmsg_end(skb, nlh); 294626b15dadSJamal Hadi Salim 294721ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_POLICY); 294826b15dadSJamal Hadi Salim 29491d1e34ddSDavid S. Miller out_free_skb: 295026b15dadSJamal Hadi Salim kfree_skb(skb); 29511d1e34ddSDavid S. Miller return err; 295226b15dadSJamal Hadi Salim } 295326b15dadSJamal Hadi Salim 2954214e005bSDavid S. Miller static int xfrm_notify_policy_flush(const struct km_event *c) 295526b15dadSJamal Hadi Salim { 29567067802eSAlexey Dobriyan struct net *net = c->net; 295726b15dadSJamal Hadi Salim struct nlmsghdr *nlh; 295826b15dadSJamal Hadi Salim struct sk_buff *skb; 29591d1e34ddSDavid S. Miller int err; 296026b15dadSJamal Hadi Salim 29617deb2264SThomas Graf skb = nlmsg_new(userpolicy_type_attrsize(), GFP_ATOMIC); 296226b15dadSJamal Hadi Salim if (skb == NULL) 296326b15dadSJamal Hadi Salim return -ENOMEM; 296426b15dadSJamal Hadi Salim 296515e47304SEric W. Biederman nlh = nlmsg_put(skb, c->portid, c->seq, XFRM_MSG_FLUSHPOLICY, 0, 0); 29661d1e34ddSDavid S. Miller err = -EMSGSIZE; 296779b8b7f4SThomas Graf if (nlh == NULL) 29681d1e34ddSDavid S. Miller goto out_free_skb; 29691d1e34ddSDavid S. Miller err = copy_to_user_policy_type(c->data.type, skb); 29701d1e34ddSDavid S. Miller if (err) 29711d1e34ddSDavid S. Miller goto out_free_skb; 297226b15dadSJamal Hadi Salim 29739825069dSThomas Graf nlmsg_end(skb, nlh); 297426b15dadSJamal Hadi Salim 297521ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_POLICY); 297626b15dadSJamal Hadi Salim 29771d1e34ddSDavid S. Miller out_free_skb: 297826b15dadSJamal Hadi Salim kfree_skb(skb); 29791d1e34ddSDavid S. Miller return err; 298026b15dadSJamal Hadi Salim } 298126b15dadSJamal Hadi Salim 2982214e005bSDavid S. Miller static int xfrm_send_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c) 298326b15dadSJamal Hadi Salim { 298426b15dadSJamal Hadi Salim 298526b15dadSJamal Hadi Salim switch (c->event) { 2986f60f6b8fSHerbert Xu case XFRM_MSG_NEWPOLICY: 2987f60f6b8fSHerbert Xu case XFRM_MSG_UPDPOLICY: 2988f60f6b8fSHerbert Xu case XFRM_MSG_DELPOLICY: 298926b15dadSJamal Hadi Salim return xfrm_notify_policy(xp, dir, c); 2990f60f6b8fSHerbert Xu case XFRM_MSG_FLUSHPOLICY: 299126b15dadSJamal Hadi Salim return xfrm_notify_policy_flush(c); 2992f60f6b8fSHerbert Xu case XFRM_MSG_POLEXPIRE: 299326b15dadSJamal Hadi Salim return xfrm_exp_policy_notify(xp, dir, c); 299426b15dadSJamal Hadi Salim default: 299562db5cfdSstephen hemminger printk(KERN_NOTICE "xfrm_user: Unknown Policy event %d\n", 299662db5cfdSstephen hemminger c->event); 299726b15dadSJamal Hadi Salim } 299826b15dadSJamal Hadi Salim 299926b15dadSJamal Hadi Salim return 0; 300026b15dadSJamal Hadi Salim 300126b15dadSJamal Hadi Salim } 300226b15dadSJamal Hadi Salim 30037deb2264SThomas Graf static inline size_t xfrm_report_msgsize(void) 30047deb2264SThomas Graf { 30057deb2264SThomas Graf return NLMSG_ALIGN(sizeof(struct xfrm_user_report)); 30067deb2264SThomas Graf } 30077deb2264SThomas Graf 300897a64b45SMasahide NAKAMURA static int build_report(struct sk_buff *skb, u8 proto, 300997a64b45SMasahide NAKAMURA struct xfrm_selector *sel, xfrm_address_t *addr) 301097a64b45SMasahide NAKAMURA { 301197a64b45SMasahide NAKAMURA struct xfrm_user_report *ur; 301297a64b45SMasahide NAKAMURA struct nlmsghdr *nlh; 301397a64b45SMasahide NAKAMURA 301479b8b7f4SThomas Graf nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_REPORT, sizeof(*ur), 0); 301579b8b7f4SThomas Graf if (nlh == NULL) 301679b8b7f4SThomas Graf return -EMSGSIZE; 301797a64b45SMasahide NAKAMURA 30187b67c857SThomas Graf ur = nlmsg_data(nlh); 301997a64b45SMasahide NAKAMURA ur->proto = proto; 302097a64b45SMasahide NAKAMURA memcpy(&ur->sel, sel, sizeof(ur->sel)); 302197a64b45SMasahide NAKAMURA 30221d1e34ddSDavid S. Miller if (addr) { 30231d1e34ddSDavid S. Miller int err = nla_put(skb, XFRMA_COADDR, sizeof(*addr), addr); 30241d1e34ddSDavid S. Miller if (err) { 30259825069dSThomas Graf nlmsg_cancel(skb, nlh); 30261d1e34ddSDavid S. Miller return err; 30271d1e34ddSDavid S. Miller } 30281d1e34ddSDavid S. Miller } 3029053c095aSJohannes Berg nlmsg_end(skb, nlh); 3030053c095aSJohannes Berg return 0; 303197a64b45SMasahide NAKAMURA } 303297a64b45SMasahide NAKAMURA 3033db983c11SAlexey Dobriyan static int xfrm_send_report(struct net *net, u8 proto, 3034db983c11SAlexey Dobriyan struct xfrm_selector *sel, xfrm_address_t *addr) 303597a64b45SMasahide NAKAMURA { 303697a64b45SMasahide NAKAMURA struct sk_buff *skb; 303797a64b45SMasahide NAKAMURA 30387deb2264SThomas Graf skb = nlmsg_new(xfrm_report_msgsize(), GFP_ATOMIC); 303997a64b45SMasahide NAKAMURA if (skb == NULL) 304097a64b45SMasahide NAKAMURA return -ENOMEM; 304197a64b45SMasahide NAKAMURA 304297a64b45SMasahide NAKAMURA if (build_report(skb, proto, sel, addr) < 0) 304397a64b45SMasahide NAKAMURA BUG(); 304497a64b45SMasahide NAKAMURA 304521ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_REPORT); 304697a64b45SMasahide NAKAMURA } 304797a64b45SMasahide NAKAMURA 30483a2dfbe8SMartin Willi static inline size_t xfrm_mapping_msgsize(void) 30493a2dfbe8SMartin Willi { 30503a2dfbe8SMartin Willi return NLMSG_ALIGN(sizeof(struct xfrm_user_mapping)); 30513a2dfbe8SMartin Willi } 30523a2dfbe8SMartin Willi 30533a2dfbe8SMartin Willi static int build_mapping(struct sk_buff *skb, struct xfrm_state *x, 30543a2dfbe8SMartin Willi xfrm_address_t *new_saddr, __be16 new_sport) 30553a2dfbe8SMartin Willi { 30563a2dfbe8SMartin Willi struct xfrm_user_mapping *um; 30573a2dfbe8SMartin Willi struct nlmsghdr *nlh; 30583a2dfbe8SMartin Willi 30593a2dfbe8SMartin Willi nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_MAPPING, sizeof(*um), 0); 30603a2dfbe8SMartin Willi if (nlh == NULL) 30613a2dfbe8SMartin Willi return -EMSGSIZE; 30623a2dfbe8SMartin Willi 30633a2dfbe8SMartin Willi um = nlmsg_data(nlh); 30643a2dfbe8SMartin Willi 30653a2dfbe8SMartin Willi memcpy(&um->id.daddr, &x->id.daddr, sizeof(um->id.daddr)); 30663a2dfbe8SMartin Willi um->id.spi = x->id.spi; 30673a2dfbe8SMartin Willi um->id.family = x->props.family; 30683a2dfbe8SMartin Willi um->id.proto = x->id.proto; 30693a2dfbe8SMartin Willi memcpy(&um->new_saddr, new_saddr, sizeof(um->new_saddr)); 30703a2dfbe8SMartin Willi memcpy(&um->old_saddr, &x->props.saddr, sizeof(um->old_saddr)); 30713a2dfbe8SMartin Willi um->new_sport = new_sport; 30723a2dfbe8SMartin Willi um->old_sport = x->encap->encap_sport; 30733a2dfbe8SMartin Willi um->reqid = x->props.reqid; 30743a2dfbe8SMartin Willi 3075053c095aSJohannes Berg nlmsg_end(skb, nlh); 3076053c095aSJohannes Berg return 0; 30773a2dfbe8SMartin Willi } 30783a2dfbe8SMartin Willi 30793a2dfbe8SMartin Willi static int xfrm_send_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, 30803a2dfbe8SMartin Willi __be16 sport) 30813a2dfbe8SMartin Willi { 3082a6483b79SAlexey Dobriyan struct net *net = xs_net(x); 30833a2dfbe8SMartin Willi struct sk_buff *skb; 30843a2dfbe8SMartin Willi 30853a2dfbe8SMartin Willi if (x->id.proto != IPPROTO_ESP) 30863a2dfbe8SMartin Willi return -EINVAL; 30873a2dfbe8SMartin Willi 30883a2dfbe8SMartin Willi if (!x->encap) 30893a2dfbe8SMartin Willi return -EINVAL; 30903a2dfbe8SMartin Willi 30913a2dfbe8SMartin Willi skb = nlmsg_new(xfrm_mapping_msgsize(), GFP_ATOMIC); 30923a2dfbe8SMartin Willi if (skb == NULL) 30933a2dfbe8SMartin Willi return -ENOMEM; 30943a2dfbe8SMartin Willi 30953a2dfbe8SMartin Willi if (build_mapping(skb, x, ipaddr, sport) < 0) 30963a2dfbe8SMartin Willi BUG(); 30973a2dfbe8SMartin Willi 309821ee543eSMichal Kubecek return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_MAPPING); 30993a2dfbe8SMartin Willi } 31003a2dfbe8SMartin Willi 31010f24558eSHoria Geanta static bool xfrm_is_alive(const struct km_event *c) 31020f24558eSHoria Geanta { 31030f24558eSHoria Geanta return (bool)xfrm_acquire_is_on(c->net); 31040f24558eSHoria Geanta } 31050f24558eSHoria Geanta 31061da177e4SLinus Torvalds static struct xfrm_mgr netlink_mgr = { 31071da177e4SLinus Torvalds .id = "netlink", 31081da177e4SLinus Torvalds .notify = xfrm_send_state_notify, 31091da177e4SLinus Torvalds .acquire = xfrm_send_acquire, 31101da177e4SLinus Torvalds .compile_policy = xfrm_compile_policy, 31111da177e4SLinus Torvalds .notify_policy = xfrm_send_policy_notify, 311297a64b45SMasahide NAKAMURA .report = xfrm_send_report, 31135c79de6eSShinta Sugimoto .migrate = xfrm_send_migrate, 31143a2dfbe8SMartin Willi .new_mapping = xfrm_send_mapping, 31150f24558eSHoria Geanta .is_alive = xfrm_is_alive, 31161da177e4SLinus Torvalds }; 31171da177e4SLinus Torvalds 3118a6483b79SAlexey Dobriyan static int __net_init xfrm_user_net_init(struct net *net) 31191da177e4SLinus Torvalds { 3120be33690dSPatrick McHardy struct sock *nlsk; 3121a31f2d17SPablo Neira Ayuso struct netlink_kernel_cfg cfg = { 3122a31f2d17SPablo Neira Ayuso .groups = XFRMNLGRP_MAX, 3123a31f2d17SPablo Neira Ayuso .input = xfrm_netlink_rcv, 3124a31f2d17SPablo Neira Ayuso }; 3125be33690dSPatrick McHardy 31269f00d977SPablo Neira Ayuso nlsk = netlink_kernel_create(net, NETLINK_XFRM, &cfg); 3127be33690dSPatrick McHardy if (nlsk == NULL) 31281da177e4SLinus Torvalds return -ENOMEM; 3129d79d792eSEric W. Biederman net->xfrm.nlsk_stash = nlsk; /* Don't set to NULL */ 3130cf778b00SEric Dumazet rcu_assign_pointer(net->xfrm.nlsk, nlsk); 31311da177e4SLinus Torvalds return 0; 31321da177e4SLinus Torvalds } 31331da177e4SLinus Torvalds 3134d79d792eSEric W. Biederman static void __net_exit xfrm_user_net_exit(struct list_head *net_exit_list) 3135a6483b79SAlexey Dobriyan { 3136d79d792eSEric W. Biederman struct net *net; 3137d79d792eSEric W. Biederman list_for_each_entry(net, net_exit_list, exit_list) 3138a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(net->xfrm.nlsk, NULL); 3139d79d792eSEric W. Biederman synchronize_net(); 3140d79d792eSEric W. Biederman list_for_each_entry(net, net_exit_list, exit_list) 3141d79d792eSEric W. Biederman netlink_kernel_release(net->xfrm.nlsk_stash); 3142a6483b79SAlexey Dobriyan } 3143a6483b79SAlexey Dobriyan 3144a6483b79SAlexey Dobriyan static struct pernet_operations xfrm_user_net_ops = { 3145a6483b79SAlexey Dobriyan .init = xfrm_user_net_init, 3146d79d792eSEric W. Biederman .exit_batch = xfrm_user_net_exit, 3147a6483b79SAlexey Dobriyan }; 3148a6483b79SAlexey Dobriyan 3149a6483b79SAlexey Dobriyan static int __init xfrm_user_init(void) 3150a6483b79SAlexey Dobriyan { 3151a6483b79SAlexey Dobriyan int rv; 3152a6483b79SAlexey Dobriyan 3153a6483b79SAlexey Dobriyan printk(KERN_INFO "Initializing XFRM netlink socket\n"); 3154a6483b79SAlexey Dobriyan 3155a6483b79SAlexey Dobriyan rv = register_pernet_subsys(&xfrm_user_net_ops); 3156a6483b79SAlexey Dobriyan if (rv < 0) 3157a6483b79SAlexey Dobriyan return rv; 3158a6483b79SAlexey Dobriyan rv = xfrm_register_km(&netlink_mgr); 3159a6483b79SAlexey Dobriyan if (rv < 0) 3160a6483b79SAlexey Dobriyan unregister_pernet_subsys(&xfrm_user_net_ops); 3161a6483b79SAlexey Dobriyan return rv; 3162a6483b79SAlexey Dobriyan } 3163a6483b79SAlexey Dobriyan 31641da177e4SLinus Torvalds static void __exit xfrm_user_exit(void) 31651da177e4SLinus Torvalds { 31661da177e4SLinus Torvalds xfrm_unregister_km(&netlink_mgr); 3167a6483b79SAlexey Dobriyan unregister_pernet_subsys(&xfrm_user_net_ops); 31681da177e4SLinus Torvalds } 31691da177e4SLinus Torvalds 31701da177e4SLinus Torvalds module_init(xfrm_user_init); 31711da177e4SLinus Torvalds module_exit(xfrm_user_exit); 31721da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 31734fdb3bb7SHarald Welte MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_XFRM); 3174f8cd5488SJamal Hadi Salim 3175