12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * IP Payload Compression Protocol (IPComp) - RFC3173. 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Copyright (c) 2003 James Morris <jmorris@intercode.com.au> 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * Todo: 81da177e4SLinus Torvalds * - Tunable compression parameters. 91da177e4SLinus Torvalds * - Compression stats. 101da177e4SLinus Torvalds * - Adaptive compression. 111da177e4SLinus Torvalds */ 121da177e4SLinus Torvalds #include <linux/module.h> 134999f362SHerbert Xu #include <linux/err.h> 141da177e4SLinus Torvalds #include <linux/rtnetlink.h> 151da177e4SLinus Torvalds #include <net/ip.h> 161da177e4SLinus Torvalds #include <net/xfrm.h> 171da177e4SLinus Torvalds #include <net/icmp.h> 181da177e4SLinus Torvalds #include <net/ipcomp.h> 1914c85021SArnaldo Carvalho de Melo #include <net/protocol.h> 206fccab67SHerbert Xu #include <net/sock.h> 211da177e4SLinus Torvalds 22d099160eSSteffen Klassert static int ipcomp4_err(struct sk_buff *skb, u32 info) 231da177e4SLinus Torvalds { 24a92df254SAlexey Dobriyan struct net *net = dev_net(skb->dev); 25a94cfd19SAl Viro __be32 spi; 26b71d1d42SEric Dumazet const struct iphdr *iph = (const struct iphdr *)skb->data; 271da177e4SLinus Torvalds struct ip_comp_hdr *ipch = (struct ip_comp_hdr *)(skb->data+(iph->ihl<<2)); 281da177e4SLinus Torvalds struct xfrm_state *x; 291da177e4SLinus Torvalds 3055be7a9cSDavid S. Miller switch (icmp_hdr(skb)->type) { 3155be7a9cSDavid S. Miller case ICMP_DEST_UNREACH: 3255be7a9cSDavid S. Miller if (icmp_hdr(skb)->code != ICMP_FRAG_NEEDED) 33d099160eSSteffen Klassert return 0; 3479121184SGustavo A. R. Silva break; 3555be7a9cSDavid S. Miller case ICMP_REDIRECT: 3655be7a9cSDavid S. Miller break; 3755be7a9cSDavid S. Miller default: 38d099160eSSteffen Klassert return 0; 3955be7a9cSDavid S. Miller } 401da177e4SLinus Torvalds 414195f814SAlexey Dobriyan spi = htonl(ntohs(ipch->cpi)); 42b71d1d42SEric Dumazet x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, 431da177e4SLinus Torvalds spi, IPPROTO_COMP, AF_INET); 441da177e4SLinus Torvalds if (!x) 45d099160eSSteffen Klassert return 0; 4655be7a9cSDavid S. Miller 47387aa65aSTimo Teräs if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH) 48d888f396SMaciej Żenczykowski ipv4_update_pmtu(skb, net, info, 0, IPPROTO_COMP); 49387aa65aSTimo Teräs else 501042caa7SMaciej Żenczykowski ipv4_redirect(skb, net, 0, IPPROTO_COMP); 511da177e4SLinus Torvalds xfrm_state_put(x); 52d099160eSSteffen Klassert 53d099160eSSteffen Klassert return 0; 541da177e4SLinus Torvalds } 551da177e4SLinus Torvalds 561da177e4SLinus Torvalds /* We always hold one tunnel user reference to indicate a tunnel */ 571da177e4SLinus Torvalds static struct xfrm_state *ipcomp_tunnel_create(struct xfrm_state *x) 581da177e4SLinus Torvalds { 59a92df254SAlexey Dobriyan struct net *net = xs_net(x); 601da177e4SLinus Torvalds struct xfrm_state *t; 611da177e4SLinus Torvalds 62a92df254SAlexey Dobriyan t = xfrm_state_alloc(net); 6351456b29SIan Morris if (!t) 641da177e4SLinus Torvalds goto out; 651da177e4SLinus Torvalds 661da177e4SLinus Torvalds t->id.proto = IPPROTO_IPIP; 671da177e4SLinus Torvalds t->id.spi = x->props.saddr.a4; 681da177e4SLinus Torvalds t->id.daddr.a4 = x->id.daddr.a4; 691da177e4SLinus Torvalds memcpy(&t->sel, &x->sel, sizeof(t->sel)); 701da177e4SLinus Torvalds t->props.family = AF_INET; 71e40b3286SHerbert Xu t->props.mode = x->props.mode; 721da177e4SLinus Torvalds t->props.saddr.a4 = x->props.saddr.a4; 731da177e4SLinus Torvalds t->props.flags = x->props.flags; 74a947b0a9SNicolas Dichtel t->props.extra_flags = x->props.extra_flags; 75bd55775cSJamal Hadi Salim memcpy(&t->mark, &x->mark, sizeof(t->mark)); 76d5a7a505SXin Long t->if_id = x->if_id; 771da177e4SLinus Torvalds 7872cb6962SHerbert Xu if (xfrm_init_state(t)) 791da177e4SLinus Torvalds goto error; 801da177e4SLinus Torvalds 811da177e4SLinus Torvalds atomic_set(&t->tunnel_users, 1); 821da177e4SLinus Torvalds out: 831da177e4SLinus Torvalds return t; 841da177e4SLinus Torvalds 851da177e4SLinus Torvalds error: 861da177e4SLinus Torvalds t->km.state = XFRM_STATE_DEAD; 871da177e4SLinus Torvalds xfrm_state_put(t); 881da177e4SLinus Torvalds t = NULL; 891da177e4SLinus Torvalds goto out; 901da177e4SLinus Torvalds } 911da177e4SLinus Torvalds 921da177e4SLinus Torvalds /* 934a3e2f71SArjan van de Ven * Must be protected by xfrm_cfg_mutex. State and tunnel user references are 941da177e4SLinus Torvalds * always incremented on success. 951da177e4SLinus Torvalds */ 961da177e4SLinus Torvalds static int ipcomp_tunnel_attach(struct xfrm_state *x) 971da177e4SLinus Torvalds { 98a92df254SAlexey Dobriyan struct net *net = xs_net(x); 991da177e4SLinus Torvalds int err = 0; 1001da177e4SLinus Torvalds struct xfrm_state *t; 101bd55775cSJamal Hadi Salim u32 mark = x->mark.v & x->mark.m; 1021da177e4SLinus Torvalds 103bd55775cSJamal Hadi Salim t = xfrm_state_lookup(net, mark, (xfrm_address_t *)&x->id.daddr.a4, 1041da177e4SLinus Torvalds x->props.saddr.a4, IPPROTO_IPIP, AF_INET); 1051da177e4SLinus Torvalds if (!t) { 1061da177e4SLinus Torvalds t = ipcomp_tunnel_create(x); 1071da177e4SLinus Torvalds if (!t) { 1081da177e4SLinus Torvalds err = -EINVAL; 1091da177e4SLinus Torvalds goto out; 1101da177e4SLinus Torvalds } 1111da177e4SLinus Torvalds xfrm_state_insert(t); 1121da177e4SLinus Torvalds xfrm_state_hold(t); 1131da177e4SLinus Torvalds } 1141da177e4SLinus Torvalds x->tunnel = t; 1151da177e4SLinus Torvalds atomic_inc(&t->tunnel_users); 1161da177e4SLinus Torvalds out: 1171da177e4SLinus Torvalds return err; 1181da177e4SLinus Torvalds } 1191da177e4SLinus Torvalds 120*e1e10b44SSabrina Dubroca static int ipcomp4_init_state(struct xfrm_state *x, 121*e1e10b44SSabrina Dubroca struct netlink_ext_ack *extack) 1221da177e4SLinus Torvalds { 1232c3abab7SDavid S. Miller int err = -EINVAL; 1241da177e4SLinus Torvalds 125e40b3286SHerbert Xu x->props.header_len = 0; 126e40b3286SHerbert Xu switch (x->props.mode) { 127e40b3286SHerbert Xu case XFRM_MODE_TRANSPORT: 128e40b3286SHerbert Xu break; 129e40b3286SHerbert Xu case XFRM_MODE_TUNNEL: 130e40b3286SHerbert Xu x->props.header_len += sizeof(struct iphdr); 131e40b3286SHerbert Xu break; 132e40b3286SHerbert Xu default: 133e40b3286SHerbert Xu goto out; 134e40b3286SHerbert Xu } 135e40b3286SHerbert Xu 1366fccab67SHerbert Xu err = ipcomp_init_state(x); 1376fccab67SHerbert Xu if (err) 1381da177e4SLinus Torvalds goto out; 1391da177e4SLinus Torvalds 1407e49e6deSMasahide NAKAMURA if (x->props.mode == XFRM_MODE_TUNNEL) { 1411da177e4SLinus Torvalds err = ipcomp_tunnel_attach(x); 1421da177e4SLinus Torvalds if (err) 14310e7454eSHerbert Xu goto out; 1441da177e4SLinus Torvalds } 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds err = 0; 1471da177e4SLinus Torvalds out: 1481da177e4SLinus Torvalds return err; 1491da177e4SLinus Torvalds } 1501da177e4SLinus Torvalds 151d099160eSSteffen Klassert static int ipcomp4_rcv_cb(struct sk_buff *skb, int err) 152d099160eSSteffen Klassert { 153d099160eSSteffen Klassert return 0; 154d099160eSSteffen Klassert } 155d099160eSSteffen Klassert 156533cb5b0SEric Dumazet static const struct xfrm_type ipcomp_type = { 1571da177e4SLinus Torvalds .owner = THIS_MODULE, 1581da177e4SLinus Torvalds .proto = IPPROTO_COMP, 1596fccab67SHerbert Xu .init_state = ipcomp4_init_state, 1601da177e4SLinus Torvalds .destructor = ipcomp_destroy, 1611da177e4SLinus Torvalds .input = ipcomp_input, 1621da177e4SLinus Torvalds .output = ipcomp_output 1631da177e4SLinus Torvalds }; 1641da177e4SLinus Torvalds 165d099160eSSteffen Klassert static struct xfrm4_protocol ipcomp4_protocol = { 1661da177e4SLinus Torvalds .handler = xfrm4_rcv, 167d099160eSSteffen Klassert .input_handler = xfrm_input, 168d099160eSSteffen Klassert .cb_handler = ipcomp4_rcv_cb, 1691da177e4SLinus Torvalds .err_handler = ipcomp4_err, 170d099160eSSteffen Klassert .priority = 0, 1711da177e4SLinus Torvalds }; 1721da177e4SLinus Torvalds 1731da177e4SLinus Torvalds static int __init ipcomp4_init(void) 1741da177e4SLinus Torvalds { 1751da177e4SLinus Torvalds if (xfrm_register_type(&ipcomp_type, AF_INET) < 0) { 176058bd4d2SJoe Perches pr_info("%s: can't add xfrm type\n", __func__); 1771da177e4SLinus Torvalds return -EAGAIN; 1781da177e4SLinus Torvalds } 179d099160eSSteffen Klassert if (xfrm4_protocol_register(&ipcomp4_protocol, IPPROTO_COMP) < 0) { 180058bd4d2SJoe Perches pr_info("%s: can't add protocol\n", __func__); 1811da177e4SLinus Torvalds xfrm_unregister_type(&ipcomp_type, AF_INET); 1821da177e4SLinus Torvalds return -EAGAIN; 1831da177e4SLinus Torvalds } 1841da177e4SLinus Torvalds return 0; 1851da177e4SLinus Torvalds } 1861da177e4SLinus Torvalds 1871da177e4SLinus Torvalds static void __exit ipcomp4_fini(void) 1881da177e4SLinus Torvalds { 189d099160eSSteffen Klassert if (xfrm4_protocol_deregister(&ipcomp4_protocol, IPPROTO_COMP) < 0) 190058bd4d2SJoe Perches pr_info("%s: can't remove protocol\n", __func__); 1914f518e80SFlorian Westphal xfrm_unregister_type(&ipcomp_type, AF_INET); 1921da177e4SLinus Torvalds } 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds module_init(ipcomp4_init); 1951da177e4SLinus Torvalds module_exit(ipcomp4_fini); 1961da177e4SLinus Torvalds 1971da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 1986fccab67SHerbert Xu MODULE_DESCRIPTION("IP Payload Compression Protocol (IPComp/IPv4) - RFC3173"); 1991da177e4SLinus Torvalds MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>"); 2001da177e4SLinus Torvalds 201d3d6dd3aSMasahide NAKAMURA MODULE_ALIAS_XFRM_TYPE(AF_INET, XFRM_PROTO_COMP); 202