1c9e7c76dSDmitry Safonov // SPDX-License-Identifier: GPL-2.0 2c9e7c76dSDmitry Safonov /* 3c9e7c76dSDmitry Safonov * XFRM compat layer 4c9e7c76dSDmitry Safonov * Author: Dmitry Safonov <dima@arista.com> 5c9e7c76dSDmitry Safonov * Based on code and translator idea by: Florian Westphal <fw@strlen.de> 6c9e7c76dSDmitry Safonov */ 7c9e7c76dSDmitry Safonov #include <linux/compat.h> 8c9e7c76dSDmitry Safonov #include <linux/xfrm.h> 9c9e7c76dSDmitry Safonov #include <net/xfrm.h> 10c9e7c76dSDmitry Safonov 115461fc0cSDmitry Safonov struct compat_xfrm_lifetime_cfg { 125461fc0cSDmitry Safonov compat_u64 soft_byte_limit, hard_byte_limit; 135461fc0cSDmitry Safonov compat_u64 soft_packet_limit, hard_packet_limit; 145461fc0cSDmitry Safonov compat_u64 soft_add_expires_seconds, hard_add_expires_seconds; 155461fc0cSDmitry Safonov compat_u64 soft_use_expires_seconds, hard_use_expires_seconds; 165461fc0cSDmitry Safonov }; /* same size on 32bit, but only 4 byte alignment required */ 175461fc0cSDmitry Safonov 185461fc0cSDmitry Safonov struct compat_xfrm_lifetime_cur { 195461fc0cSDmitry Safonov compat_u64 bytes, packets, add_time, use_time; 205461fc0cSDmitry Safonov }; /* same size on 32bit, but only 4 byte alignment required */ 215461fc0cSDmitry Safonov 225461fc0cSDmitry Safonov struct compat_xfrm_userpolicy_info { 235461fc0cSDmitry Safonov struct xfrm_selector sel; 245461fc0cSDmitry Safonov struct compat_xfrm_lifetime_cfg lft; 255461fc0cSDmitry Safonov struct compat_xfrm_lifetime_cur curlft; 265461fc0cSDmitry Safonov __u32 priority, index; 275461fc0cSDmitry Safonov u8 dir, action, flags, share; 285461fc0cSDmitry Safonov /* 4 bytes additional padding on 64bit */ 295461fc0cSDmitry Safonov }; 305461fc0cSDmitry Safonov 315461fc0cSDmitry Safonov struct compat_xfrm_usersa_info { 325461fc0cSDmitry Safonov struct xfrm_selector sel; 335461fc0cSDmitry Safonov struct xfrm_id id; 345461fc0cSDmitry Safonov xfrm_address_t saddr; 355461fc0cSDmitry Safonov struct compat_xfrm_lifetime_cfg lft; 365461fc0cSDmitry Safonov struct compat_xfrm_lifetime_cur curlft; 375461fc0cSDmitry Safonov struct xfrm_stats stats; 385461fc0cSDmitry Safonov __u32 seq, reqid; 395461fc0cSDmitry Safonov u16 family; 405461fc0cSDmitry Safonov u8 mode, replay_window, flags; 415461fc0cSDmitry Safonov /* 4 bytes additional padding on 64bit */ 425461fc0cSDmitry Safonov }; 435461fc0cSDmitry Safonov 445461fc0cSDmitry Safonov struct compat_xfrm_user_acquire { 455461fc0cSDmitry Safonov struct xfrm_id id; 465461fc0cSDmitry Safonov xfrm_address_t saddr; 475461fc0cSDmitry Safonov struct xfrm_selector sel; 485461fc0cSDmitry Safonov struct compat_xfrm_userpolicy_info policy; 495461fc0cSDmitry Safonov /* 4 bytes additional padding on 64bit */ 505461fc0cSDmitry Safonov __u32 aalgos, ealgos, calgos, seq; 515461fc0cSDmitry Safonov }; 525461fc0cSDmitry Safonov 535461fc0cSDmitry Safonov struct compat_xfrm_userspi_info { 545461fc0cSDmitry Safonov struct compat_xfrm_usersa_info info; 555461fc0cSDmitry Safonov /* 4 bytes additional padding on 64bit */ 565461fc0cSDmitry Safonov __u32 min, max; 575461fc0cSDmitry Safonov }; 585461fc0cSDmitry Safonov 595461fc0cSDmitry Safonov struct compat_xfrm_user_expire { 605461fc0cSDmitry Safonov struct compat_xfrm_usersa_info state; 615461fc0cSDmitry Safonov /* 8 bytes additional padding on 64bit */ 625461fc0cSDmitry Safonov u8 hard; 635461fc0cSDmitry Safonov }; 645461fc0cSDmitry Safonov 655461fc0cSDmitry Safonov struct compat_xfrm_user_polexpire { 665461fc0cSDmitry Safonov struct compat_xfrm_userpolicy_info pol; 675461fc0cSDmitry Safonov /* 8 bytes additional padding on 64bit */ 685461fc0cSDmitry Safonov u8 hard; 695461fc0cSDmitry Safonov }; 705461fc0cSDmitry Safonov 715461fc0cSDmitry Safonov #define XMSGSIZE(type) sizeof(struct type) 725461fc0cSDmitry Safonov 735461fc0cSDmitry Safonov static const int compat_msg_min[XFRM_NR_MSGTYPES] = { 745461fc0cSDmitry Safonov [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_usersa_info), 755461fc0cSDmitry Safonov [XFRM_MSG_DELSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 765461fc0cSDmitry Safonov [XFRM_MSG_GETSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_id), 775461fc0cSDmitry Safonov [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_userpolicy_info), 785461fc0cSDmitry Safonov [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 795461fc0cSDmitry Safonov [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 805461fc0cSDmitry Safonov [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_userspi_info), 815461fc0cSDmitry Safonov [XFRM_MSG_ACQUIRE - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_user_acquire), 825461fc0cSDmitry Safonov [XFRM_MSG_EXPIRE - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_user_expire), 835461fc0cSDmitry Safonov [XFRM_MSG_UPDPOLICY - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_userpolicy_info), 845461fc0cSDmitry Safonov [XFRM_MSG_UPDSA - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_usersa_info), 855461fc0cSDmitry Safonov [XFRM_MSG_POLEXPIRE - XFRM_MSG_BASE] = XMSGSIZE(compat_xfrm_user_polexpire), 865461fc0cSDmitry Safonov [XFRM_MSG_FLUSHSA - XFRM_MSG_BASE] = XMSGSIZE(xfrm_usersa_flush), 875461fc0cSDmitry Safonov [XFRM_MSG_FLUSHPOLICY - XFRM_MSG_BASE] = 0, 885461fc0cSDmitry Safonov [XFRM_MSG_NEWAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 895461fc0cSDmitry Safonov [XFRM_MSG_GETAE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_aevent_id), 905461fc0cSDmitry Safonov [XFRM_MSG_REPORT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report), 915461fc0cSDmitry Safonov [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 925461fc0cSDmitry Safonov [XFRM_MSG_NEWSADINFO - XFRM_MSG_BASE] = sizeof(u32), 935461fc0cSDmitry Safonov [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = sizeof(u32), 945461fc0cSDmitry Safonov [XFRM_MSG_NEWSPDINFO - XFRM_MSG_BASE] = sizeof(u32), 955461fc0cSDmitry Safonov [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32), 965461fc0cSDmitry Safonov [XFRM_MSG_MAPPING - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_mapping) 975461fc0cSDmitry Safonov }; 985461fc0cSDmitry Safonov 995106f4a8SDmitry Safonov static const struct nla_policy compat_policy[XFRMA_MAX+1] = { 1005106f4a8SDmitry Safonov [XFRMA_SA] = { .len = XMSGSIZE(compat_xfrm_usersa_info)}, 1015106f4a8SDmitry Safonov [XFRMA_POLICY] = { .len = XMSGSIZE(compat_xfrm_userpolicy_info)}, 1025106f4a8SDmitry Safonov [XFRMA_LASTUSED] = { .type = NLA_U64}, 1035106f4a8SDmitry Safonov [XFRMA_ALG_AUTH_TRUNC] = { .len = sizeof(struct xfrm_algo_auth)}, 1045106f4a8SDmitry Safonov [XFRMA_ALG_AEAD] = { .len = sizeof(struct xfrm_algo_aead) }, 1055106f4a8SDmitry Safonov [XFRMA_ALG_AUTH] = { .len = sizeof(struct xfrm_algo) }, 1065106f4a8SDmitry Safonov [XFRMA_ALG_CRYPT] = { .len = sizeof(struct xfrm_algo) }, 1075106f4a8SDmitry Safonov [XFRMA_ALG_COMP] = { .len = sizeof(struct xfrm_algo) }, 1085106f4a8SDmitry Safonov [XFRMA_ENCAP] = { .len = sizeof(struct xfrm_encap_tmpl) }, 1095106f4a8SDmitry Safonov [XFRMA_TMPL] = { .len = sizeof(struct xfrm_user_tmpl) }, 1105106f4a8SDmitry Safonov [XFRMA_SEC_CTX] = { .len = sizeof(struct xfrm_sec_ctx) }, 1115106f4a8SDmitry Safonov [XFRMA_LTIME_VAL] = { .len = sizeof(struct xfrm_lifetime_cur) }, 1125106f4a8SDmitry Safonov [XFRMA_REPLAY_VAL] = { .len = sizeof(struct xfrm_replay_state) }, 1135106f4a8SDmitry Safonov [XFRMA_REPLAY_THRESH] = { .type = NLA_U32 }, 1145106f4a8SDmitry Safonov [XFRMA_ETIMER_THRESH] = { .type = NLA_U32 }, 1155106f4a8SDmitry Safonov [XFRMA_SRCADDR] = { .len = sizeof(xfrm_address_t) }, 1165106f4a8SDmitry Safonov [XFRMA_COADDR] = { .len = sizeof(xfrm_address_t) }, 1175106f4a8SDmitry Safonov [XFRMA_POLICY_TYPE] = { .len = sizeof(struct xfrm_userpolicy_type)}, 1185106f4a8SDmitry Safonov [XFRMA_MIGRATE] = { .len = sizeof(struct xfrm_user_migrate) }, 1195106f4a8SDmitry Safonov [XFRMA_KMADDRESS] = { .len = sizeof(struct xfrm_user_kmaddress) }, 1205106f4a8SDmitry Safonov [XFRMA_MARK] = { .len = sizeof(struct xfrm_mark) }, 1215106f4a8SDmitry Safonov [XFRMA_TFCPAD] = { .type = NLA_U32 }, 1225106f4a8SDmitry Safonov [XFRMA_REPLAY_ESN_VAL] = { .len = sizeof(struct xfrm_replay_state_esn) }, 1235106f4a8SDmitry Safonov [XFRMA_SA_EXTRA_FLAGS] = { .type = NLA_U32 }, 1245106f4a8SDmitry Safonov [XFRMA_PROTO] = { .type = NLA_U8 }, 1255106f4a8SDmitry Safonov [XFRMA_ADDRESS_FILTER] = { .len = sizeof(struct xfrm_address_filter) }, 1265106f4a8SDmitry Safonov [XFRMA_OFFLOAD_DEV] = { .len = sizeof(struct xfrm_user_offload) }, 1275106f4a8SDmitry Safonov [XFRMA_SET_MARK] = { .type = NLA_U32 }, 1285106f4a8SDmitry Safonov [XFRMA_SET_MARK_MASK] = { .type = NLA_U32 }, 1295106f4a8SDmitry Safonov [XFRMA_IF_ID] = { .type = NLA_U32 }, 1305106f4a8SDmitry Safonov }; 1315106f4a8SDmitry Safonov 1325461fc0cSDmitry Safonov static struct nlmsghdr *xfrm_nlmsg_put_compat(struct sk_buff *skb, 1335461fc0cSDmitry Safonov const struct nlmsghdr *nlh_src, u16 type) 1345461fc0cSDmitry Safonov { 1355461fc0cSDmitry Safonov int payload = compat_msg_min[type]; 1365461fc0cSDmitry Safonov int src_len = xfrm_msg_min[type]; 1375461fc0cSDmitry Safonov struct nlmsghdr *nlh_dst; 1385461fc0cSDmitry Safonov 1395461fc0cSDmitry Safonov /* Compat messages are shorter or equal to native (+padding) */ 1405461fc0cSDmitry Safonov if (WARN_ON_ONCE(src_len < payload)) 1415461fc0cSDmitry Safonov return ERR_PTR(-EMSGSIZE); 1425461fc0cSDmitry Safonov 1435461fc0cSDmitry Safonov nlh_dst = nlmsg_put(skb, nlh_src->nlmsg_pid, nlh_src->nlmsg_seq, 1445461fc0cSDmitry Safonov nlh_src->nlmsg_type, payload, nlh_src->nlmsg_flags); 1455461fc0cSDmitry Safonov if (!nlh_dst) 1465461fc0cSDmitry Safonov return ERR_PTR(-EMSGSIZE); 1475461fc0cSDmitry Safonov 1485461fc0cSDmitry Safonov memset(nlmsg_data(nlh_dst), 0, payload); 1495461fc0cSDmitry Safonov 1505461fc0cSDmitry Safonov switch (nlh_src->nlmsg_type) { 1515461fc0cSDmitry Safonov /* Compat message has the same layout as native */ 1525461fc0cSDmitry Safonov case XFRM_MSG_DELSA: 1535461fc0cSDmitry Safonov case XFRM_MSG_DELPOLICY: 1545461fc0cSDmitry Safonov case XFRM_MSG_FLUSHSA: 1555461fc0cSDmitry Safonov case XFRM_MSG_FLUSHPOLICY: 1565461fc0cSDmitry Safonov case XFRM_MSG_NEWAE: 1575461fc0cSDmitry Safonov case XFRM_MSG_REPORT: 1585461fc0cSDmitry Safonov case XFRM_MSG_MIGRATE: 1595461fc0cSDmitry Safonov case XFRM_MSG_NEWSADINFO: 1605461fc0cSDmitry Safonov case XFRM_MSG_NEWSPDINFO: 1615461fc0cSDmitry Safonov case XFRM_MSG_MAPPING: 1625461fc0cSDmitry Safonov WARN_ON_ONCE(src_len != payload); 1635461fc0cSDmitry Safonov memcpy(nlmsg_data(nlh_dst), nlmsg_data(nlh_src), src_len); 1645461fc0cSDmitry Safonov break; 1655461fc0cSDmitry Safonov /* 4 byte alignment for trailing u64 on native, but not on compat */ 1665461fc0cSDmitry Safonov case XFRM_MSG_NEWSA: 1675461fc0cSDmitry Safonov case XFRM_MSG_NEWPOLICY: 1685461fc0cSDmitry Safonov case XFRM_MSG_UPDSA: 1695461fc0cSDmitry Safonov case XFRM_MSG_UPDPOLICY: 1705461fc0cSDmitry Safonov WARN_ON_ONCE(src_len != payload + 4); 1715461fc0cSDmitry Safonov memcpy(nlmsg_data(nlh_dst), nlmsg_data(nlh_src), payload); 1725461fc0cSDmitry Safonov break; 1735461fc0cSDmitry Safonov case XFRM_MSG_EXPIRE: { 1745461fc0cSDmitry Safonov const struct xfrm_user_expire *src_ue = nlmsg_data(nlh_src); 1755461fc0cSDmitry Safonov struct compat_xfrm_user_expire *dst_ue = nlmsg_data(nlh_dst); 1765461fc0cSDmitry Safonov 1775461fc0cSDmitry Safonov /* compat_xfrm_user_expire has 4-byte smaller state */ 1785461fc0cSDmitry Safonov memcpy(dst_ue, src_ue, sizeof(dst_ue->state)); 1795461fc0cSDmitry Safonov dst_ue->hard = src_ue->hard; 1805461fc0cSDmitry Safonov break; 1815461fc0cSDmitry Safonov } 1825461fc0cSDmitry Safonov case XFRM_MSG_ACQUIRE: { 1835461fc0cSDmitry Safonov const struct xfrm_user_acquire *src_ua = nlmsg_data(nlh_src); 1845461fc0cSDmitry Safonov struct compat_xfrm_user_acquire *dst_ua = nlmsg_data(nlh_dst); 1855461fc0cSDmitry Safonov 1865461fc0cSDmitry Safonov memcpy(dst_ua, src_ua, offsetof(struct compat_xfrm_user_acquire, aalgos)); 1875461fc0cSDmitry Safonov dst_ua->aalgos = src_ua->aalgos; 1885461fc0cSDmitry Safonov dst_ua->ealgos = src_ua->ealgos; 1895461fc0cSDmitry Safonov dst_ua->calgos = src_ua->calgos; 1905461fc0cSDmitry Safonov dst_ua->seq = src_ua->seq; 1915461fc0cSDmitry Safonov break; 1925461fc0cSDmitry Safonov } 1935461fc0cSDmitry Safonov case XFRM_MSG_POLEXPIRE: { 1945461fc0cSDmitry Safonov const struct xfrm_user_polexpire *src_upe = nlmsg_data(nlh_src); 1955461fc0cSDmitry Safonov struct compat_xfrm_user_polexpire *dst_upe = nlmsg_data(nlh_dst); 1965461fc0cSDmitry Safonov 1975461fc0cSDmitry Safonov /* compat_xfrm_user_polexpire has 4-byte smaller state */ 1985461fc0cSDmitry Safonov memcpy(dst_upe, src_upe, sizeof(dst_upe->pol)); 1995461fc0cSDmitry Safonov dst_upe->hard = src_upe->hard; 2005461fc0cSDmitry Safonov break; 2015461fc0cSDmitry Safonov } 2025461fc0cSDmitry Safonov case XFRM_MSG_ALLOCSPI: { 2035461fc0cSDmitry Safonov const struct xfrm_userspi_info *src_usi = nlmsg_data(nlh_src); 2045461fc0cSDmitry Safonov struct compat_xfrm_userspi_info *dst_usi = nlmsg_data(nlh_dst); 2055461fc0cSDmitry Safonov 2065461fc0cSDmitry Safonov /* compat_xfrm_user_polexpire has 4-byte smaller state */ 2075461fc0cSDmitry Safonov memcpy(dst_usi, src_usi, sizeof(src_usi->info)); 2085461fc0cSDmitry Safonov dst_usi->min = src_usi->min; 2095461fc0cSDmitry Safonov dst_usi->max = src_usi->max; 2105461fc0cSDmitry Safonov break; 2115461fc0cSDmitry Safonov } 2125461fc0cSDmitry Safonov /* Not being sent by kernel */ 2135461fc0cSDmitry Safonov case XFRM_MSG_GETSA: 2145461fc0cSDmitry Safonov case XFRM_MSG_GETPOLICY: 2155461fc0cSDmitry Safonov case XFRM_MSG_GETAE: 2165461fc0cSDmitry Safonov case XFRM_MSG_GETSADINFO: 2175461fc0cSDmitry Safonov case XFRM_MSG_GETSPDINFO: 2185461fc0cSDmitry Safonov default: 2195461fc0cSDmitry Safonov WARN_ONCE(1, "unsupported nlmsg_type %d", nlh_src->nlmsg_type); 2205461fc0cSDmitry Safonov return ERR_PTR(-EOPNOTSUPP); 2215461fc0cSDmitry Safonov } 2225461fc0cSDmitry Safonov 2235461fc0cSDmitry Safonov return nlh_dst; 2245461fc0cSDmitry Safonov } 2255461fc0cSDmitry Safonov 2265461fc0cSDmitry Safonov static int xfrm_nla_cpy(struct sk_buff *dst, const struct nlattr *src, int len) 2275461fc0cSDmitry Safonov { 2285461fc0cSDmitry Safonov return nla_put(dst, src->nla_type, len, nla_data(src)); 2295461fc0cSDmitry Safonov } 2305461fc0cSDmitry Safonov 2315461fc0cSDmitry Safonov static int xfrm_xlate64_attr(struct sk_buff *dst, const struct nlattr *src) 2325461fc0cSDmitry Safonov { 2335461fc0cSDmitry Safonov switch (src->nla_type) { 2345461fc0cSDmitry Safonov case XFRMA_PAD: 2355461fc0cSDmitry Safonov /* Ignore */ 2365461fc0cSDmitry Safonov return 0; 237dbd7ae51SDmitry Safonov case XFRMA_UNSPEC: 2385461fc0cSDmitry Safonov case XFRMA_ALG_AUTH: 2395461fc0cSDmitry Safonov case XFRMA_ALG_CRYPT: 2405461fc0cSDmitry Safonov case XFRMA_ALG_COMP: 2415461fc0cSDmitry Safonov case XFRMA_ENCAP: 2425461fc0cSDmitry Safonov case XFRMA_TMPL: 2435461fc0cSDmitry Safonov return xfrm_nla_cpy(dst, src, nla_len(src)); 2445461fc0cSDmitry Safonov case XFRMA_SA: 2455461fc0cSDmitry Safonov return xfrm_nla_cpy(dst, src, XMSGSIZE(compat_xfrm_usersa_info)); 2465461fc0cSDmitry Safonov case XFRMA_POLICY: 2475461fc0cSDmitry Safonov return xfrm_nla_cpy(dst, src, XMSGSIZE(compat_xfrm_userpolicy_info)); 2485461fc0cSDmitry Safonov case XFRMA_SEC_CTX: 2495461fc0cSDmitry Safonov return xfrm_nla_cpy(dst, src, nla_len(src)); 2505461fc0cSDmitry Safonov case XFRMA_LTIME_VAL: 2515461fc0cSDmitry Safonov return nla_put_64bit(dst, src->nla_type, nla_len(src), 2525461fc0cSDmitry Safonov nla_data(src), XFRMA_PAD); 2535461fc0cSDmitry Safonov case XFRMA_REPLAY_VAL: 2545461fc0cSDmitry Safonov case XFRMA_REPLAY_THRESH: 2555461fc0cSDmitry Safonov case XFRMA_ETIMER_THRESH: 2565461fc0cSDmitry Safonov case XFRMA_SRCADDR: 2575461fc0cSDmitry Safonov case XFRMA_COADDR: 2585461fc0cSDmitry Safonov return xfrm_nla_cpy(dst, src, nla_len(src)); 2595461fc0cSDmitry Safonov case XFRMA_LASTUSED: 2605461fc0cSDmitry Safonov return nla_put_64bit(dst, src->nla_type, nla_len(src), 2615461fc0cSDmitry Safonov nla_data(src), XFRMA_PAD); 2625461fc0cSDmitry Safonov case XFRMA_POLICY_TYPE: 2635461fc0cSDmitry Safonov case XFRMA_MIGRATE: 2645461fc0cSDmitry Safonov case XFRMA_ALG_AEAD: 2655461fc0cSDmitry Safonov case XFRMA_KMADDRESS: 2665461fc0cSDmitry Safonov case XFRMA_ALG_AUTH_TRUNC: 2675461fc0cSDmitry Safonov case XFRMA_MARK: 2685461fc0cSDmitry Safonov case XFRMA_TFCPAD: 2695461fc0cSDmitry Safonov case XFRMA_REPLAY_ESN_VAL: 2705461fc0cSDmitry Safonov case XFRMA_SA_EXTRA_FLAGS: 2715461fc0cSDmitry Safonov case XFRMA_PROTO: 2725461fc0cSDmitry Safonov case XFRMA_ADDRESS_FILTER: 2735461fc0cSDmitry Safonov case XFRMA_OFFLOAD_DEV: 2745461fc0cSDmitry Safonov case XFRMA_SET_MARK: 2755461fc0cSDmitry Safonov case XFRMA_SET_MARK_MASK: 2765461fc0cSDmitry Safonov case XFRMA_IF_ID: 2775461fc0cSDmitry Safonov return xfrm_nla_cpy(dst, src, nla_len(src)); 2785461fc0cSDmitry Safonov default: 2795461fc0cSDmitry Safonov BUILD_BUG_ON(XFRMA_MAX != XFRMA_IF_ID); 2805461fc0cSDmitry Safonov WARN_ONCE(1, "unsupported nla_type %d", src->nla_type); 2815461fc0cSDmitry Safonov return -EOPNOTSUPP; 2825461fc0cSDmitry Safonov } 2835461fc0cSDmitry Safonov } 2845461fc0cSDmitry Safonov 2855461fc0cSDmitry Safonov /* Take kernel-built (64bit layout) and create 32bit layout for userspace */ 2865461fc0cSDmitry Safonov static int xfrm_xlate64(struct sk_buff *dst, const struct nlmsghdr *nlh_src) 2875461fc0cSDmitry Safonov { 2885461fc0cSDmitry Safonov u16 type = nlh_src->nlmsg_type - XFRM_MSG_BASE; 2895461fc0cSDmitry Safonov const struct nlattr *nla, *attrs; 2905461fc0cSDmitry Safonov struct nlmsghdr *nlh_dst; 2915461fc0cSDmitry Safonov int len, remaining; 2925461fc0cSDmitry Safonov 2935461fc0cSDmitry Safonov nlh_dst = xfrm_nlmsg_put_compat(dst, nlh_src, type); 2945461fc0cSDmitry Safonov if (IS_ERR(nlh_dst)) 2955461fc0cSDmitry Safonov return PTR_ERR(nlh_dst); 2965461fc0cSDmitry Safonov 2975461fc0cSDmitry Safonov attrs = nlmsg_attrdata(nlh_src, xfrm_msg_min[type]); 2985461fc0cSDmitry Safonov len = nlmsg_attrlen(nlh_src, xfrm_msg_min[type]); 2995461fc0cSDmitry Safonov 3005461fc0cSDmitry Safonov nla_for_each_attr(nla, attrs, len, remaining) { 3015461fc0cSDmitry Safonov int err = xfrm_xlate64_attr(dst, nla); 3025461fc0cSDmitry Safonov 3035461fc0cSDmitry Safonov if (err) 3045461fc0cSDmitry Safonov return err; 3055461fc0cSDmitry Safonov } 3065461fc0cSDmitry Safonov 3075461fc0cSDmitry Safonov nlmsg_end(dst, nlh_dst); 3085461fc0cSDmitry Safonov 3095461fc0cSDmitry Safonov return 0; 3105461fc0cSDmitry Safonov } 3115461fc0cSDmitry Safonov 3125461fc0cSDmitry Safonov static int xfrm_alloc_compat(struct sk_buff *skb, const struct nlmsghdr *nlh_src) 3135461fc0cSDmitry Safonov { 3145461fc0cSDmitry Safonov u16 type = nlh_src->nlmsg_type - XFRM_MSG_BASE; 3155461fc0cSDmitry Safonov struct sk_buff *new = NULL; 3165461fc0cSDmitry Safonov int err; 3175461fc0cSDmitry Safonov 3185461fc0cSDmitry Safonov if (WARN_ON_ONCE(type >= ARRAY_SIZE(xfrm_msg_min))) 3195461fc0cSDmitry Safonov return -EOPNOTSUPP; 3205461fc0cSDmitry Safonov 3215461fc0cSDmitry Safonov if (skb_shinfo(skb)->frag_list == NULL) { 3225461fc0cSDmitry Safonov new = alloc_skb(skb->len + skb_tailroom(skb), GFP_ATOMIC); 3235461fc0cSDmitry Safonov if (!new) 3245461fc0cSDmitry Safonov return -ENOMEM; 3255461fc0cSDmitry Safonov skb_shinfo(skb)->frag_list = new; 3265461fc0cSDmitry Safonov } 3275461fc0cSDmitry Safonov 3285461fc0cSDmitry Safonov err = xfrm_xlate64(skb_shinfo(skb)->frag_list, nlh_src); 3295461fc0cSDmitry Safonov if (err) { 3305461fc0cSDmitry Safonov if (new) { 3315461fc0cSDmitry Safonov kfree_skb(new); 3325461fc0cSDmitry Safonov skb_shinfo(skb)->frag_list = NULL; 3335461fc0cSDmitry Safonov } 3345461fc0cSDmitry Safonov return err; 3355461fc0cSDmitry Safonov } 3365461fc0cSDmitry Safonov 3375461fc0cSDmitry Safonov return 0; 3385461fc0cSDmitry Safonov } 3395461fc0cSDmitry Safonov 3405106f4a8SDmitry Safonov /* Calculates len of translated 64-bit message. */ 3415106f4a8SDmitry Safonov static size_t xfrm_user_rcv_calculate_len64(const struct nlmsghdr *src, 3425106f4a8SDmitry Safonov struct nlattr *attrs[XFRMA_MAX+1]) 3435106f4a8SDmitry Safonov { 3445106f4a8SDmitry Safonov size_t len = nlmsg_len(src); 3455106f4a8SDmitry Safonov 3465106f4a8SDmitry Safonov switch (src->nlmsg_type) { 3475106f4a8SDmitry Safonov case XFRM_MSG_NEWSA: 3485106f4a8SDmitry Safonov case XFRM_MSG_NEWPOLICY: 3495106f4a8SDmitry Safonov case XFRM_MSG_ALLOCSPI: 3505106f4a8SDmitry Safonov case XFRM_MSG_ACQUIRE: 3515106f4a8SDmitry Safonov case XFRM_MSG_UPDPOLICY: 3525106f4a8SDmitry Safonov case XFRM_MSG_UPDSA: 3535106f4a8SDmitry Safonov len += 4; 3545106f4a8SDmitry Safonov break; 3555106f4a8SDmitry Safonov case XFRM_MSG_EXPIRE: 3565106f4a8SDmitry Safonov case XFRM_MSG_POLEXPIRE: 3575106f4a8SDmitry Safonov len += 8; 3585106f4a8SDmitry Safonov break; 3595106f4a8SDmitry Safonov default: 3605106f4a8SDmitry Safonov break; 3615106f4a8SDmitry Safonov } 3625106f4a8SDmitry Safonov 3635106f4a8SDmitry Safonov if (attrs[XFRMA_SA]) 3645106f4a8SDmitry Safonov len += 4; 3655106f4a8SDmitry Safonov if (attrs[XFRMA_POLICY]) 3665106f4a8SDmitry Safonov len += 4; 3675106f4a8SDmitry Safonov 3685106f4a8SDmitry Safonov /* XXX: some attrs may need to be realigned 3695106f4a8SDmitry Safonov * if !CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS 3705106f4a8SDmitry Safonov */ 3715106f4a8SDmitry Safonov 3725106f4a8SDmitry Safonov return len; 3735106f4a8SDmitry Safonov } 3745106f4a8SDmitry Safonov 3755106f4a8SDmitry Safonov static int xfrm_attr_cpy32(void *dst, size_t *pos, const struct nlattr *src, 3765106f4a8SDmitry Safonov size_t size, int copy_len, int payload) 3775106f4a8SDmitry Safonov { 3785106f4a8SDmitry Safonov struct nlmsghdr *nlmsg = dst; 3795106f4a8SDmitry Safonov struct nlattr *nla; 3805106f4a8SDmitry Safonov 3815106f4a8SDmitry Safonov if (WARN_ON_ONCE(copy_len > payload)) 3825106f4a8SDmitry Safonov copy_len = payload; 3835106f4a8SDmitry Safonov 3845106f4a8SDmitry Safonov if (size - *pos < nla_attr_size(payload)) 3855106f4a8SDmitry Safonov return -ENOBUFS; 3865106f4a8SDmitry Safonov 3875106f4a8SDmitry Safonov nla = dst + *pos; 3885106f4a8SDmitry Safonov 3895106f4a8SDmitry Safonov memcpy(nla, src, nla_attr_size(copy_len)); 3905106f4a8SDmitry Safonov nla->nla_len = nla_attr_size(payload); 391*d1949d04SDmitry Safonov *pos += nla_attr_size(copy_len); 3925106f4a8SDmitry Safonov nlmsg->nlmsg_len += nla->nla_len; 3935106f4a8SDmitry Safonov 3945106f4a8SDmitry Safonov memset(dst + *pos, 0, payload - copy_len); 3955106f4a8SDmitry Safonov *pos += payload - copy_len; 3965106f4a8SDmitry Safonov 3975106f4a8SDmitry Safonov return 0; 3985106f4a8SDmitry Safonov } 3995106f4a8SDmitry Safonov 4005106f4a8SDmitry Safonov static int xfrm_xlate32_attr(void *dst, const struct nlattr *nla, 4015106f4a8SDmitry Safonov size_t *pos, size_t size, 4025106f4a8SDmitry Safonov struct netlink_ext_ack *extack) 4035106f4a8SDmitry Safonov { 4045106f4a8SDmitry Safonov int type = nla_type(nla); 4055106f4a8SDmitry Safonov u16 pol_len32, pol_len64; 4065106f4a8SDmitry Safonov int err; 4075106f4a8SDmitry Safonov 4085106f4a8SDmitry Safonov if (type > XFRMA_MAX) { 4095106f4a8SDmitry Safonov BUILD_BUG_ON(XFRMA_MAX != XFRMA_IF_ID); 4105106f4a8SDmitry Safonov NL_SET_ERR_MSG(extack, "Bad attribute"); 4115106f4a8SDmitry Safonov return -EOPNOTSUPP; 4125106f4a8SDmitry Safonov } 4135106f4a8SDmitry Safonov if (nla_len(nla) < compat_policy[type].len) { 4145106f4a8SDmitry Safonov NL_SET_ERR_MSG(extack, "Attribute bad length"); 4155106f4a8SDmitry Safonov return -EOPNOTSUPP; 4165106f4a8SDmitry Safonov } 4175106f4a8SDmitry Safonov 4185106f4a8SDmitry Safonov pol_len32 = compat_policy[type].len; 4195106f4a8SDmitry Safonov pol_len64 = xfrma_policy[type].len; 4205106f4a8SDmitry Safonov 4215106f4a8SDmitry Safonov /* XFRMA_SA and XFRMA_POLICY - need to know how-to translate */ 4225106f4a8SDmitry Safonov if (pol_len32 != pol_len64) { 4235106f4a8SDmitry Safonov if (nla_len(nla) != compat_policy[type].len) { 4245106f4a8SDmitry Safonov NL_SET_ERR_MSG(extack, "Attribute bad length"); 4255106f4a8SDmitry Safonov return -EOPNOTSUPP; 4265106f4a8SDmitry Safonov } 4275106f4a8SDmitry Safonov err = xfrm_attr_cpy32(dst, pos, nla, size, pol_len32, pol_len64); 4285106f4a8SDmitry Safonov if (err) 4295106f4a8SDmitry Safonov return err; 4305106f4a8SDmitry Safonov } 4315106f4a8SDmitry Safonov 4325106f4a8SDmitry Safonov return xfrm_attr_cpy32(dst, pos, nla, size, nla_len(nla), nla_len(nla)); 4335106f4a8SDmitry Safonov } 4345106f4a8SDmitry Safonov 4355106f4a8SDmitry Safonov static int xfrm_xlate32(struct nlmsghdr *dst, const struct nlmsghdr *src, 4365106f4a8SDmitry Safonov struct nlattr *attrs[XFRMA_MAX+1], 4375106f4a8SDmitry Safonov size_t size, u8 type, struct netlink_ext_ack *extack) 4385106f4a8SDmitry Safonov { 4395106f4a8SDmitry Safonov size_t pos; 4405106f4a8SDmitry Safonov int i; 4415106f4a8SDmitry Safonov 4425106f4a8SDmitry Safonov memcpy(dst, src, NLMSG_HDRLEN); 4435106f4a8SDmitry Safonov dst->nlmsg_len = NLMSG_HDRLEN + xfrm_msg_min[type]; 4445106f4a8SDmitry Safonov memset(nlmsg_data(dst), 0, xfrm_msg_min[type]); 4455106f4a8SDmitry Safonov 4465106f4a8SDmitry Safonov switch (src->nlmsg_type) { 4475106f4a8SDmitry Safonov /* Compat message has the same layout as native */ 4485106f4a8SDmitry Safonov case XFRM_MSG_DELSA: 4495106f4a8SDmitry Safonov case XFRM_MSG_GETSA: 4505106f4a8SDmitry Safonov case XFRM_MSG_DELPOLICY: 4515106f4a8SDmitry Safonov case XFRM_MSG_GETPOLICY: 4525106f4a8SDmitry Safonov case XFRM_MSG_FLUSHSA: 4535106f4a8SDmitry Safonov case XFRM_MSG_FLUSHPOLICY: 4545106f4a8SDmitry Safonov case XFRM_MSG_NEWAE: 4555106f4a8SDmitry Safonov case XFRM_MSG_GETAE: 4565106f4a8SDmitry Safonov case XFRM_MSG_REPORT: 4575106f4a8SDmitry Safonov case XFRM_MSG_MIGRATE: 4585106f4a8SDmitry Safonov case XFRM_MSG_NEWSADINFO: 4595106f4a8SDmitry Safonov case XFRM_MSG_GETSADINFO: 4605106f4a8SDmitry Safonov case XFRM_MSG_NEWSPDINFO: 4615106f4a8SDmitry Safonov case XFRM_MSG_GETSPDINFO: 4625106f4a8SDmitry Safonov case XFRM_MSG_MAPPING: 4635106f4a8SDmitry Safonov memcpy(nlmsg_data(dst), nlmsg_data(src), compat_msg_min[type]); 4645106f4a8SDmitry Safonov break; 4655106f4a8SDmitry Safonov /* 4 byte alignment for trailing u64 on native, but not on compat */ 4665106f4a8SDmitry Safonov case XFRM_MSG_NEWSA: 4675106f4a8SDmitry Safonov case XFRM_MSG_NEWPOLICY: 4685106f4a8SDmitry Safonov case XFRM_MSG_UPDSA: 4695106f4a8SDmitry Safonov case XFRM_MSG_UPDPOLICY: 4705106f4a8SDmitry Safonov memcpy(nlmsg_data(dst), nlmsg_data(src), compat_msg_min[type]); 4715106f4a8SDmitry Safonov break; 4725106f4a8SDmitry Safonov case XFRM_MSG_EXPIRE: { 4735106f4a8SDmitry Safonov const struct compat_xfrm_user_expire *src_ue = nlmsg_data(src); 4745106f4a8SDmitry Safonov struct xfrm_user_expire *dst_ue = nlmsg_data(dst); 4755106f4a8SDmitry Safonov 4765106f4a8SDmitry Safonov /* compat_xfrm_user_expire has 4-byte smaller state */ 4775106f4a8SDmitry Safonov memcpy(dst_ue, src_ue, sizeof(src_ue->state)); 4785106f4a8SDmitry Safonov dst_ue->hard = src_ue->hard; 4795106f4a8SDmitry Safonov break; 4805106f4a8SDmitry Safonov } 4815106f4a8SDmitry Safonov case XFRM_MSG_ACQUIRE: { 4825106f4a8SDmitry Safonov const struct compat_xfrm_user_acquire *src_ua = nlmsg_data(src); 4835106f4a8SDmitry Safonov struct xfrm_user_acquire *dst_ua = nlmsg_data(dst); 4845106f4a8SDmitry Safonov 4855106f4a8SDmitry Safonov memcpy(dst_ua, src_ua, offsetof(struct compat_xfrm_user_acquire, aalgos)); 4865106f4a8SDmitry Safonov dst_ua->aalgos = src_ua->aalgos; 4875106f4a8SDmitry Safonov dst_ua->ealgos = src_ua->ealgos; 4885106f4a8SDmitry Safonov dst_ua->calgos = src_ua->calgos; 4895106f4a8SDmitry Safonov dst_ua->seq = src_ua->seq; 4905106f4a8SDmitry Safonov break; 4915106f4a8SDmitry Safonov } 4925106f4a8SDmitry Safonov case XFRM_MSG_POLEXPIRE: { 4935106f4a8SDmitry Safonov const struct compat_xfrm_user_polexpire *src_upe = nlmsg_data(src); 4945106f4a8SDmitry Safonov struct xfrm_user_polexpire *dst_upe = nlmsg_data(dst); 4955106f4a8SDmitry Safonov 4965106f4a8SDmitry Safonov /* compat_xfrm_user_polexpire has 4-byte smaller state */ 4975106f4a8SDmitry Safonov memcpy(dst_upe, src_upe, sizeof(src_upe->pol)); 4985106f4a8SDmitry Safonov dst_upe->hard = src_upe->hard; 4995106f4a8SDmitry Safonov break; 5005106f4a8SDmitry Safonov } 5015106f4a8SDmitry Safonov case XFRM_MSG_ALLOCSPI: { 5025106f4a8SDmitry Safonov const struct compat_xfrm_userspi_info *src_usi = nlmsg_data(src); 5035106f4a8SDmitry Safonov struct xfrm_userspi_info *dst_usi = nlmsg_data(dst); 5045106f4a8SDmitry Safonov 5055106f4a8SDmitry Safonov /* compat_xfrm_user_polexpire has 4-byte smaller state */ 5065106f4a8SDmitry Safonov memcpy(dst_usi, src_usi, sizeof(src_usi->info)); 5075106f4a8SDmitry Safonov dst_usi->min = src_usi->min; 5085106f4a8SDmitry Safonov dst_usi->max = src_usi->max; 5095106f4a8SDmitry Safonov break; 5105106f4a8SDmitry Safonov } 5115106f4a8SDmitry Safonov default: 5125106f4a8SDmitry Safonov NL_SET_ERR_MSG(extack, "Unsupported message type"); 5135106f4a8SDmitry Safonov return -EOPNOTSUPP; 5145106f4a8SDmitry Safonov } 5155106f4a8SDmitry Safonov pos = dst->nlmsg_len; 5165106f4a8SDmitry Safonov 5175106f4a8SDmitry Safonov for (i = 1; i < XFRMA_MAX + 1; i++) { 5185106f4a8SDmitry Safonov int err; 5195106f4a8SDmitry Safonov 5205106f4a8SDmitry Safonov if (i == XFRMA_PAD) 5215106f4a8SDmitry Safonov continue; 5225106f4a8SDmitry Safonov 5235106f4a8SDmitry Safonov if (!attrs[i]) 5245106f4a8SDmitry Safonov continue; 5255106f4a8SDmitry Safonov 5265106f4a8SDmitry Safonov err = xfrm_xlate32_attr(dst, attrs[i], &pos, size, extack); 5275106f4a8SDmitry Safonov if (err) 5285106f4a8SDmitry Safonov return err; 5295106f4a8SDmitry Safonov } 5305106f4a8SDmitry Safonov 5315106f4a8SDmitry Safonov return 0; 5325106f4a8SDmitry Safonov } 5335106f4a8SDmitry Safonov 5345106f4a8SDmitry Safonov static struct nlmsghdr *xfrm_user_rcv_msg_compat(const struct nlmsghdr *h32, 5355106f4a8SDmitry Safonov int maxtype, const struct nla_policy *policy, 5365106f4a8SDmitry Safonov struct netlink_ext_ack *extack) 5375106f4a8SDmitry Safonov { 5385106f4a8SDmitry Safonov /* netlink_rcv_skb() checks if a message has full (struct nlmsghdr) */ 5395106f4a8SDmitry Safonov u16 type = h32->nlmsg_type - XFRM_MSG_BASE; 5405106f4a8SDmitry Safonov struct nlattr *attrs[XFRMA_MAX+1]; 5415106f4a8SDmitry Safonov struct nlmsghdr *h64; 5425106f4a8SDmitry Safonov size_t len; 5435106f4a8SDmitry Safonov int err; 5445106f4a8SDmitry Safonov 5455106f4a8SDmitry Safonov BUILD_BUG_ON(ARRAY_SIZE(xfrm_msg_min) != ARRAY_SIZE(compat_msg_min)); 5465106f4a8SDmitry Safonov 5475106f4a8SDmitry Safonov if (type >= ARRAY_SIZE(xfrm_msg_min)) 5485106f4a8SDmitry Safonov return ERR_PTR(-EINVAL); 5495106f4a8SDmitry Safonov 5505106f4a8SDmitry Safonov /* Don't call parse: the message might have only nlmsg header */ 5515106f4a8SDmitry Safonov if ((h32->nlmsg_type == XFRM_MSG_GETSA || 5525106f4a8SDmitry Safonov h32->nlmsg_type == XFRM_MSG_GETPOLICY) && 5535106f4a8SDmitry Safonov (h32->nlmsg_flags & NLM_F_DUMP)) 5545106f4a8SDmitry Safonov return NULL; 5555106f4a8SDmitry Safonov 5565106f4a8SDmitry Safonov err = nlmsg_parse_deprecated(h32, compat_msg_min[type], attrs, 5575106f4a8SDmitry Safonov maxtype ? : XFRMA_MAX, policy ? : compat_policy, extack); 5585106f4a8SDmitry Safonov if (err < 0) 5595106f4a8SDmitry Safonov return ERR_PTR(err); 5605106f4a8SDmitry Safonov 5615106f4a8SDmitry Safonov len = xfrm_user_rcv_calculate_len64(h32, attrs); 5625106f4a8SDmitry Safonov /* The message doesn't need translation */ 5635106f4a8SDmitry Safonov if (len == nlmsg_len(h32)) 5645106f4a8SDmitry Safonov return NULL; 5655106f4a8SDmitry Safonov 5665106f4a8SDmitry Safonov len += NLMSG_HDRLEN; 5675106f4a8SDmitry Safonov h64 = kvmalloc(len, GFP_KERNEL | __GFP_ZERO); 5685106f4a8SDmitry Safonov if (!h64) 5695106f4a8SDmitry Safonov return ERR_PTR(-ENOMEM); 5705106f4a8SDmitry Safonov 5715106f4a8SDmitry Safonov err = xfrm_xlate32(h64, h32, attrs, len, type, extack); 5725106f4a8SDmitry Safonov if (err < 0) { 5735106f4a8SDmitry Safonov kvfree(h64); 5745106f4a8SDmitry Safonov return ERR_PTR(err); 5755106f4a8SDmitry Safonov } 5765106f4a8SDmitry Safonov 5775106f4a8SDmitry Safonov return h64; 5785106f4a8SDmitry Safonov } 5795106f4a8SDmitry Safonov 58096392ee5SDmitry Safonov static int xfrm_user_policy_compat(u8 **pdata32, int optlen) 58196392ee5SDmitry Safonov { 58296392ee5SDmitry Safonov struct compat_xfrm_userpolicy_info *p = (void *)*pdata32; 58396392ee5SDmitry Safonov u8 *src_templates, *dst_templates; 58496392ee5SDmitry Safonov u8 *data64; 58596392ee5SDmitry Safonov 58696392ee5SDmitry Safonov if (optlen < sizeof(*p)) 58796392ee5SDmitry Safonov return -EINVAL; 58896392ee5SDmitry Safonov 58996392ee5SDmitry Safonov data64 = kmalloc_track_caller(optlen + 4, GFP_USER | __GFP_NOWARN); 59096392ee5SDmitry Safonov if (!data64) 59196392ee5SDmitry Safonov return -ENOMEM; 59296392ee5SDmitry Safonov 59396392ee5SDmitry Safonov memcpy(data64, *pdata32, sizeof(*p)); 59496392ee5SDmitry Safonov memset(data64 + sizeof(*p), 0, 4); 59596392ee5SDmitry Safonov 59696392ee5SDmitry Safonov src_templates = *pdata32 + sizeof(*p); 59796392ee5SDmitry Safonov dst_templates = data64 + sizeof(*p) + 4; 59896392ee5SDmitry Safonov memcpy(dst_templates, src_templates, optlen - sizeof(*p)); 59996392ee5SDmitry Safonov 60096392ee5SDmitry Safonov kfree(*pdata32); 60196392ee5SDmitry Safonov *pdata32 = data64; 60296392ee5SDmitry Safonov return 0; 60396392ee5SDmitry Safonov } 60496392ee5SDmitry Safonov 605c9e7c76dSDmitry Safonov static struct xfrm_translator xfrm_translator = { 606c9e7c76dSDmitry Safonov .owner = THIS_MODULE, 6075461fc0cSDmitry Safonov .alloc_compat = xfrm_alloc_compat, 6085106f4a8SDmitry Safonov .rcv_msg_compat = xfrm_user_rcv_msg_compat, 60996392ee5SDmitry Safonov .xlate_user_policy_sockptr = xfrm_user_policy_compat, 610c9e7c76dSDmitry Safonov }; 611c9e7c76dSDmitry Safonov 612c9e7c76dSDmitry Safonov static int __init xfrm_compat_init(void) 613c9e7c76dSDmitry Safonov { 614c9e7c76dSDmitry Safonov return xfrm_register_translator(&xfrm_translator); 615c9e7c76dSDmitry Safonov } 616c9e7c76dSDmitry Safonov 617c9e7c76dSDmitry Safonov static void __exit xfrm_compat_exit(void) 618c9e7c76dSDmitry Safonov { 619c9e7c76dSDmitry Safonov xfrm_unregister_translator(&xfrm_translator); 620c9e7c76dSDmitry Safonov } 621c9e7c76dSDmitry Safonov 622c9e7c76dSDmitry Safonov module_init(xfrm_compat_init); 623c9e7c76dSDmitry Safonov module_exit(xfrm_compat_exit); 624c9e7c76dSDmitry Safonov MODULE_LICENSE("GPL"); 625c9e7c76dSDmitry Safonov MODULE_AUTHOR("Dmitry Safonov"); 626c9e7c76dSDmitry Safonov MODULE_DESCRIPTION("XFRM 32-bit compatibility layer"); 627