Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

xfrm: iptfs: add new iptfs xfrm mode impl

Add a new xfrm mode implementing AggFrag/IP-TFS from RFC9347.

This utilizes the new xfrm_mode_cbs to implement demand-driven IP-TFS
functionality. This functionality can be used to increase bandwidth
utilization through small packet aggregation, as well as help solve PMTU
issues through it's efficient use of fragmentation.

Link: https://www.rfc-editor.org/rfc/rfc9347.txt

Multiple commits follow to build the functionality into xfrm_iptfs.c

Signed-off-by: Christian Hopps <chopps@labn.net>
Tested-by: Antony Antony <antony.antony@secunet.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>

authored by

Christian Hopps and committed by
Steffen Klassert
4b3faf61 d1716d5a

+217
+1
net/xfrm/Makefile
··· 21 21 obj-$(CONFIG_XFRM_USER_COMPAT) += xfrm_compat.o 22 22 obj-$(CONFIG_XFRM_IPCOMP) += xfrm_ipcomp.o 23 23 obj-$(CONFIG_XFRM_INTERFACE) += xfrm_interface.o 24 + obj-$(CONFIG_XFRM_IPTFS) += xfrm_iptfs.o 24 25 obj-$(CONFIG_XFRM_ESPINTCP) += espintcp.o 25 26 obj-$(CONFIG_DEBUG_INFO_BTF) += xfrm_state_bpf.o
+216
net/xfrm/xfrm_iptfs.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* xfrm_iptfs: IPTFS encapsulation support 3 + * 4 + * April 21 2022, Christian Hopps <chopps@labn.net> 5 + * 6 + * Copyright (c) 2022, LabN Consulting, L.L.C. 7 + * 8 + */ 9 + 10 + #include <linux/kernel.h> 11 + #include <linux/icmpv6.h> 12 + #include <net/gro.h> 13 + #include <net/icmp.h> 14 + #include <net/ip6_route.h> 15 + #include <net/inet_ecn.h> 16 + #include <net/xfrm.h> 17 + 18 + #include <crypto/aead.h> 19 + 20 + #include "xfrm_inout.h" 21 + 22 + /** 23 + * struct xfrm_iptfs_config - configuration for the IPTFS tunnel. 24 + * @pkt_size: size of the outer IP packet. 0 to use interface and MTU discovery, 25 + * otherwise the user specified value. 26 + */ 27 + struct xfrm_iptfs_config { 28 + u32 pkt_size; /* outer_packet_size or 0 */ 29 + }; 30 + 31 + /** 32 + * struct xfrm_iptfs_data - mode specific xfrm state. 33 + * @cfg: IPTFS tunnel config. 34 + * @x: owning SA (xfrm_state). 35 + * @payload_mtu: max payload size. 36 + */ 37 + struct xfrm_iptfs_data { 38 + struct xfrm_iptfs_config cfg; 39 + 40 + /* Ingress User Input */ 41 + struct xfrm_state *x; /* owning state */ 42 + u32 payload_mtu; /* max payload size */ 43 + }; 44 + 45 + /* ========================== */ 46 + /* State Management Functions */ 47 + /* ========================== */ 48 + 49 + /** 50 + * iptfs_get_inner_mtu() - return inner MTU with no fragmentation. 51 + * @x: xfrm state. 52 + * @outer_mtu: the outer mtu 53 + */ 54 + static u32 iptfs_get_inner_mtu(struct xfrm_state *x, int outer_mtu) 55 + { 56 + struct crypto_aead *aead; 57 + u32 blksize; 58 + 59 + aead = x->data; 60 + blksize = ALIGN(crypto_aead_blocksize(aead), 4); 61 + return ((outer_mtu - x->props.header_len - crypto_aead_authsize(aead)) & 62 + ~(blksize - 1)) - 2; 63 + } 64 + 65 + /** 66 + * iptfs_user_init() - initialize the SA with IPTFS options from netlink. 67 + * @net: the net data 68 + * @x: xfrm state 69 + * @attrs: netlink attributes 70 + * @extack: extack return data 71 + * 72 + * Return: 0 on success or a negative error code on failure 73 + */ 74 + static int iptfs_user_init(struct net *net, struct xfrm_state *x, 75 + struct nlattr **attrs, 76 + struct netlink_ext_ack *extack) 77 + { 78 + struct xfrm_iptfs_data *xtfs = x->mode_data; 79 + struct xfrm_iptfs_config *xc; 80 + 81 + xc = &xtfs->cfg; 82 + 83 + if (attrs[XFRMA_IPTFS_PKT_SIZE]) { 84 + xc->pkt_size = nla_get_u32(attrs[XFRMA_IPTFS_PKT_SIZE]); 85 + if (!xc->pkt_size) { 86 + xtfs->payload_mtu = 0; 87 + } else if (xc->pkt_size > x->props.header_len) { 88 + xtfs->payload_mtu = xc->pkt_size - x->props.header_len; 89 + } else { 90 + NL_SET_ERR_MSG(extack, 91 + "Packet size must be 0 or greater than IPTFS/ESP header length"); 92 + return -EINVAL; 93 + } 94 + } 95 + return 0; 96 + } 97 + 98 + static unsigned int iptfs_sa_len(const struct xfrm_state *x) 99 + { 100 + struct xfrm_iptfs_data *xtfs = x->mode_data; 101 + struct xfrm_iptfs_config *xc = &xtfs->cfg; 102 + unsigned int l = 0; 103 + 104 + if (x->dir == XFRM_SA_DIR_OUT) 105 + l += nla_total_size(sizeof(xc->pkt_size)); 106 + 107 + return l; 108 + } 109 + 110 + static int iptfs_copy_to_user(struct xfrm_state *x, struct sk_buff *skb) 111 + { 112 + struct xfrm_iptfs_data *xtfs = x->mode_data; 113 + struct xfrm_iptfs_config *xc = &xtfs->cfg; 114 + int ret = 0; 115 + 116 + if (x->dir == XFRM_SA_DIR_OUT) 117 + ret = nla_put_u32(skb, XFRMA_IPTFS_PKT_SIZE, xc->pkt_size); 118 + 119 + return ret; 120 + } 121 + 122 + static void __iptfs_init_state(struct xfrm_state *x, 123 + struct xfrm_iptfs_data *xtfs) 124 + { 125 + /* Modify type (esp) adjustment values */ 126 + 127 + if (x->props.family == AF_INET) 128 + x->props.header_len += sizeof(struct iphdr) + sizeof(struct ip_iptfs_hdr); 129 + else if (x->props.family == AF_INET6) 130 + x->props.header_len += sizeof(struct ipv6hdr) + sizeof(struct ip_iptfs_hdr); 131 + x->props.enc_hdr_len = sizeof(struct ip_iptfs_hdr); 132 + 133 + /* Always keep a module reference when x->mode_data is set */ 134 + __module_get(x->mode_cbs->owner); 135 + 136 + x->mode_data = xtfs; 137 + xtfs->x = x; 138 + } 139 + 140 + static int iptfs_clone_state(struct xfrm_state *x, struct xfrm_state *orig) 141 + { 142 + struct xfrm_iptfs_data *xtfs; 143 + 144 + xtfs = kmemdup(orig->mode_data, sizeof(*xtfs), GFP_KERNEL); 145 + if (!xtfs) 146 + return -ENOMEM; 147 + 148 + x->mode_data = xtfs; 149 + xtfs->x = x; 150 + 151 + return 0; 152 + } 153 + 154 + static int iptfs_init_state(struct xfrm_state *x) 155 + { 156 + struct xfrm_iptfs_data *xtfs; 157 + 158 + if (x->mode_data) { 159 + /* We have arrived here from xfrm_state_clone() */ 160 + xtfs = x->mode_data; 161 + } else { 162 + xtfs = kzalloc(sizeof(*xtfs), GFP_KERNEL); 163 + if (!xtfs) 164 + return -ENOMEM; 165 + } 166 + 167 + __iptfs_init_state(x, xtfs); 168 + 169 + return 0; 170 + } 171 + 172 + static void iptfs_destroy_state(struct xfrm_state *x) 173 + { 174 + struct xfrm_iptfs_data *xtfs = x->mode_data; 175 + 176 + if (!xtfs) 177 + return; 178 + 179 + kfree_sensitive(xtfs); 180 + 181 + module_put(x->mode_cbs->owner); 182 + } 183 + 184 + static const struct xfrm_mode_cbs iptfs_mode_cbs = { 185 + .owner = THIS_MODULE, 186 + .init_state = iptfs_init_state, 187 + .clone_state = iptfs_clone_state, 188 + .destroy_state = iptfs_destroy_state, 189 + .user_init = iptfs_user_init, 190 + .copy_to_user = iptfs_copy_to_user, 191 + .sa_len = iptfs_sa_len, 192 + .get_inner_mtu = iptfs_get_inner_mtu, 193 + }; 194 + 195 + static int __init xfrm_iptfs_init(void) 196 + { 197 + int err; 198 + 199 + pr_info("xfrm_iptfs: IPsec IP-TFS tunnel mode module\n"); 200 + 201 + err = xfrm_register_mode_cbs(XFRM_MODE_IPTFS, &iptfs_mode_cbs); 202 + if (err < 0) 203 + pr_info("%s: can't register IP-TFS\n", __func__); 204 + 205 + return err; 206 + } 207 + 208 + static void __exit xfrm_iptfs_fini(void) 209 + { 210 + xfrm_unregister_mode_cbs(XFRM_MODE_IPTFS); 211 + } 212 + 213 + module_init(xfrm_iptfs_init); 214 + module_exit(xfrm_iptfs_fini); 215 + MODULE_LICENSE("GPL"); 216 + MODULE_DESCRIPTION("IP-TFS support for xfrm ipsec tunnels");