xref: /linux/net/ipv4/ipcomp.c (revision b71d1d426d263b0b6cb5760322efebbfc89d4463)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * IP Payload Compression Protocol (IPComp) - RFC3173.
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Copyright (c) 2003 James Morris <jmorris@intercode.com.au>
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or modify it
71da177e4SLinus Torvalds  * under the terms of the GNU General Public License as published by the Free
81da177e4SLinus Torvalds  * Software Foundation; either version 2 of the License, or (at your option)
91da177e4SLinus Torvalds  * any later version.
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  * Todo:
121da177e4SLinus Torvalds  *   - Tunable compression parameters.
131da177e4SLinus Torvalds  *   - Compression stats.
141da177e4SLinus Torvalds  *   - Adaptive compression.
151da177e4SLinus Torvalds  */
161da177e4SLinus Torvalds #include <linux/module.h>
174999f362SHerbert Xu #include <linux/err.h>
181da177e4SLinus Torvalds #include <linux/rtnetlink.h>
191da177e4SLinus Torvalds #include <net/ip.h>
201da177e4SLinus Torvalds #include <net/xfrm.h>
211da177e4SLinus Torvalds #include <net/icmp.h>
221da177e4SLinus Torvalds #include <net/ipcomp.h>
2314c85021SArnaldo Carvalho de Melo #include <net/protocol.h>
246fccab67SHerbert Xu #include <net/sock.h>
251da177e4SLinus Torvalds 
261da177e4SLinus Torvalds static void ipcomp4_err(struct sk_buff *skb, u32 info)
271da177e4SLinus Torvalds {
28a92df254SAlexey Dobriyan 	struct net *net = dev_net(skb->dev);
29a94cfd19SAl Viro 	__be32 spi;
30*b71d1d42SEric Dumazet 	const struct iphdr *iph = (const struct iphdr *)skb->data;
311da177e4SLinus Torvalds 	struct ip_comp_hdr *ipch = (struct ip_comp_hdr *)(skb->data+(iph->ihl<<2));
321da177e4SLinus Torvalds 	struct xfrm_state *x;
331da177e4SLinus Torvalds 
3488c7664fSArnaldo Carvalho de Melo 	if (icmp_hdr(skb)->type != ICMP_DEST_UNREACH ||
3588c7664fSArnaldo Carvalho de Melo 	    icmp_hdr(skb)->code != ICMP_FRAG_NEEDED)
361da177e4SLinus Torvalds 		return;
371da177e4SLinus Torvalds 
384195f814SAlexey Dobriyan 	spi = htonl(ntohs(ipch->cpi));
39*b71d1d42SEric Dumazet 	x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr,
401da177e4SLinus Torvalds 			      spi, IPPROTO_COMP, AF_INET);
411da177e4SLinus Torvalds 	if (!x)
421da177e4SLinus Torvalds 		return;
43673d57e7SHarvey Harrison 	NETDEBUG(KERN_DEBUG "pmtu discovery on SA IPCOMP/%08x/%pI4\n",
44673d57e7SHarvey Harrison 		 spi, &iph->daddr);
451da177e4SLinus Torvalds 	xfrm_state_put(x);
461da177e4SLinus Torvalds }
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds /* We always hold one tunnel user reference to indicate a tunnel */
491da177e4SLinus Torvalds static struct xfrm_state *ipcomp_tunnel_create(struct xfrm_state *x)
501da177e4SLinus Torvalds {
51a92df254SAlexey Dobriyan 	struct net *net = xs_net(x);
521da177e4SLinus Torvalds 	struct xfrm_state *t;
531da177e4SLinus Torvalds 
54a92df254SAlexey Dobriyan 	t = xfrm_state_alloc(net);
551da177e4SLinus Torvalds 	if (t == NULL)
561da177e4SLinus Torvalds 		goto out;
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds 	t->id.proto = IPPROTO_IPIP;
591da177e4SLinus Torvalds 	t->id.spi = x->props.saddr.a4;
601da177e4SLinus Torvalds 	t->id.daddr.a4 = x->id.daddr.a4;
611da177e4SLinus Torvalds 	memcpy(&t->sel, &x->sel, sizeof(t->sel));
621da177e4SLinus Torvalds 	t->props.family = AF_INET;
63e40b3286SHerbert Xu 	t->props.mode = x->props.mode;
641da177e4SLinus Torvalds 	t->props.saddr.a4 = x->props.saddr.a4;
651da177e4SLinus Torvalds 	t->props.flags = x->props.flags;
66bd55775cSJamal Hadi Salim 	memcpy(&t->mark, &x->mark, sizeof(t->mark));
671da177e4SLinus Torvalds 
6872cb6962SHerbert Xu 	if (xfrm_init_state(t))
691da177e4SLinus Torvalds 		goto error;
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds 	atomic_set(&t->tunnel_users, 1);
721da177e4SLinus Torvalds out:
731da177e4SLinus Torvalds 	return t;
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds error:
761da177e4SLinus Torvalds 	t->km.state = XFRM_STATE_DEAD;
771da177e4SLinus Torvalds 	xfrm_state_put(t);
781da177e4SLinus Torvalds 	t = NULL;
791da177e4SLinus Torvalds 	goto out;
801da177e4SLinus Torvalds }
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds /*
834a3e2f71SArjan van de Ven  * Must be protected by xfrm_cfg_mutex.  State and tunnel user references are
841da177e4SLinus Torvalds  * always incremented on success.
851da177e4SLinus Torvalds  */
861da177e4SLinus Torvalds static int ipcomp_tunnel_attach(struct xfrm_state *x)
871da177e4SLinus Torvalds {
88a92df254SAlexey Dobriyan 	struct net *net = xs_net(x);
891da177e4SLinus Torvalds 	int err = 0;
901da177e4SLinus Torvalds 	struct xfrm_state *t;
91bd55775cSJamal Hadi Salim 	u32 mark = x->mark.v & x->mark.m;
921da177e4SLinus Torvalds 
93bd55775cSJamal Hadi Salim 	t = xfrm_state_lookup(net, mark, (xfrm_address_t *)&x->id.daddr.a4,
941da177e4SLinus Torvalds 			      x->props.saddr.a4, IPPROTO_IPIP, AF_INET);
951da177e4SLinus Torvalds 	if (!t) {
961da177e4SLinus Torvalds 		t = ipcomp_tunnel_create(x);
971da177e4SLinus Torvalds 		if (!t) {
981da177e4SLinus Torvalds 			err = -EINVAL;
991da177e4SLinus Torvalds 			goto out;
1001da177e4SLinus Torvalds 		}
1011da177e4SLinus Torvalds 		xfrm_state_insert(t);
1021da177e4SLinus Torvalds 		xfrm_state_hold(t);
1031da177e4SLinus Torvalds 	}
1041da177e4SLinus Torvalds 	x->tunnel = t;
1051da177e4SLinus Torvalds 	atomic_inc(&t->tunnel_users);
1061da177e4SLinus Torvalds out:
1071da177e4SLinus Torvalds 	return err;
1081da177e4SLinus Torvalds }
1091da177e4SLinus Torvalds 
1106fccab67SHerbert Xu static int ipcomp4_init_state(struct xfrm_state *x)
1111da177e4SLinus Torvalds {
1122c3abab7SDavid S. Miller 	int err = -EINVAL;
1131da177e4SLinus Torvalds 
114e40b3286SHerbert Xu 	x->props.header_len = 0;
115e40b3286SHerbert Xu 	switch (x->props.mode) {
116e40b3286SHerbert Xu 	case XFRM_MODE_TRANSPORT:
117e40b3286SHerbert Xu 		break;
118e40b3286SHerbert Xu 	case XFRM_MODE_TUNNEL:
119e40b3286SHerbert Xu 		x->props.header_len += sizeof(struct iphdr);
120e40b3286SHerbert Xu 		break;
121e40b3286SHerbert Xu 	default:
122e40b3286SHerbert Xu 		goto out;
123e40b3286SHerbert Xu 	}
124e40b3286SHerbert Xu 
1256fccab67SHerbert Xu 	err = ipcomp_init_state(x);
1266fccab67SHerbert Xu 	if (err)
1271da177e4SLinus Torvalds 		goto out;
1281da177e4SLinus Torvalds 
1297e49e6deSMasahide NAKAMURA 	if (x->props.mode == XFRM_MODE_TUNNEL) {
1301da177e4SLinus Torvalds 		err = ipcomp_tunnel_attach(x);
1311da177e4SLinus Torvalds 		if (err)
13210e7454eSHerbert Xu 			goto out;
1331da177e4SLinus Torvalds 	}
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds 	err = 0;
1361da177e4SLinus Torvalds out:
1371da177e4SLinus Torvalds 	return err;
1381da177e4SLinus Torvalds }
1391da177e4SLinus Torvalds 
140533cb5b0SEric Dumazet static const struct xfrm_type ipcomp_type = {
1411da177e4SLinus Torvalds 	.description	= "IPCOMP4",
1421da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
1431da177e4SLinus Torvalds 	.proto	     	= IPPROTO_COMP,
1446fccab67SHerbert Xu 	.init_state	= ipcomp4_init_state,
1451da177e4SLinus Torvalds 	.destructor	= ipcomp_destroy,
1461da177e4SLinus Torvalds 	.input		= ipcomp_input,
1471da177e4SLinus Torvalds 	.output		= ipcomp_output
1481da177e4SLinus Torvalds };
1491da177e4SLinus Torvalds 
15032613090SAlexey Dobriyan static const struct net_protocol ipcomp4_protocol = {
1511da177e4SLinus Torvalds 	.handler	=	xfrm4_rcv,
1521da177e4SLinus Torvalds 	.err_handler	=	ipcomp4_err,
1531da177e4SLinus Torvalds 	.no_policy	=	1,
1541da177e4SLinus Torvalds };
1551da177e4SLinus Torvalds 
1561da177e4SLinus Torvalds static int __init ipcomp4_init(void)
1571da177e4SLinus Torvalds {
1581da177e4SLinus Torvalds 	if (xfrm_register_type(&ipcomp_type, AF_INET) < 0) {
1591da177e4SLinus Torvalds 		printk(KERN_INFO "ipcomp init: can't add xfrm type\n");
1601da177e4SLinus Torvalds 		return -EAGAIN;
1611da177e4SLinus Torvalds 	}
1621da177e4SLinus Torvalds 	if (inet_add_protocol(&ipcomp4_protocol, IPPROTO_COMP) < 0) {
1631da177e4SLinus Torvalds 		printk(KERN_INFO "ipcomp init: can't add protocol\n");
1641da177e4SLinus Torvalds 		xfrm_unregister_type(&ipcomp_type, AF_INET);
1651da177e4SLinus Torvalds 		return -EAGAIN;
1661da177e4SLinus Torvalds 	}
1671da177e4SLinus Torvalds 	return 0;
1681da177e4SLinus Torvalds }
1691da177e4SLinus Torvalds 
1701da177e4SLinus Torvalds static void __exit ipcomp4_fini(void)
1711da177e4SLinus Torvalds {
1721da177e4SLinus Torvalds 	if (inet_del_protocol(&ipcomp4_protocol, IPPROTO_COMP) < 0)
1731da177e4SLinus Torvalds 		printk(KERN_INFO "ip ipcomp close: can't remove protocol\n");
1741da177e4SLinus Torvalds 	if (xfrm_unregister_type(&ipcomp_type, AF_INET) < 0)
1751da177e4SLinus Torvalds 		printk(KERN_INFO "ip ipcomp close: can't remove xfrm type\n");
1761da177e4SLinus Torvalds }
1771da177e4SLinus Torvalds 
1781da177e4SLinus Torvalds module_init(ipcomp4_init);
1791da177e4SLinus Torvalds module_exit(ipcomp4_fini);
1801da177e4SLinus Torvalds 
1811da177e4SLinus Torvalds MODULE_LICENSE("GPL");
1826fccab67SHerbert Xu MODULE_DESCRIPTION("IP Payload Compression Protocol (IPComp/IPv4) - RFC3173");
1831da177e4SLinus Torvalds MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>");
1841da177e4SLinus Torvalds 
185d3d6dd3aSMasahide NAKAMURA MODULE_ALIAS_XFRM_TYPE(AF_INET, XFRM_PROTO_COMP);
186