Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * IP Payload Compression Protocol (IPComp) for IPv6 - RFC3173
0004  *
0005  * Copyright (C)2003 USAGI/WIDE Project
0006  *
0007  * Author   Mitsuru KANDA  <mk@linux-ipv6.org>
0008  */
0009 /*
0010  * [Memo]
0011  *
0012  * Outbound:
0013  *  The compression of IP datagram MUST be done before AH/ESP processing,
0014  *  fragmentation, and the addition of Hop-by-Hop/Routing header.
0015  *
0016  * Inbound:
0017  *  The decompression of IP datagram MUST be done after the reassembly,
0018  *  AH/ESP processing.
0019  */
0020 
0021 #define pr_fmt(fmt) "IPv6: " fmt
0022 
0023 #include <linux/module.h>
0024 #include <net/ip.h>
0025 #include <net/xfrm.h>
0026 #include <net/ipcomp.h>
0027 #include <linux/crypto.h>
0028 #include <linux/err.h>
0029 #include <linux/pfkeyv2.h>
0030 #include <linux/random.h>
0031 #include <linux/percpu.h>
0032 #include <linux/smp.h>
0033 #include <linux/list.h>
0034 #include <linux/vmalloc.h>
0035 #include <linux/rtnetlink.h>
0036 #include <net/ip6_route.h>
0037 #include <net/icmp.h>
0038 #include <net/ipv6.h>
0039 #include <net/protocol.h>
0040 #include <linux/ipv6.h>
0041 #include <linux/icmpv6.h>
0042 #include <linux/mutex.h>
0043 
0044 static int ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
0045                 u8 type, u8 code, int offset, __be32 info)
0046 {
0047     struct net *net = dev_net(skb->dev);
0048     __be32 spi;
0049     const struct ipv6hdr *iph = (const struct ipv6hdr *)skb->data;
0050     struct ip_comp_hdr *ipcomph =
0051         (struct ip_comp_hdr *)(skb->data + offset);
0052     struct xfrm_state *x;
0053 
0054     if (type != ICMPV6_PKT_TOOBIG &&
0055         type != NDISC_REDIRECT)
0056         return 0;
0057 
0058     spi = htonl(ntohs(ipcomph->cpi));
0059     x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr,
0060                   spi, IPPROTO_COMP, AF_INET6);
0061     if (!x)
0062         return 0;
0063 
0064     if (type == NDISC_REDIRECT)
0065         ip6_redirect(skb, net, skb->dev->ifindex, 0,
0066                  sock_net_uid(net, NULL));
0067     else
0068         ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
0069     xfrm_state_put(x);
0070 
0071     return 0;
0072 }
0073 
0074 static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x)
0075 {
0076     struct net *net = xs_net(x);
0077     struct xfrm_state *t = NULL;
0078 
0079     t = xfrm_state_alloc(net);
0080     if (!t)
0081         goto out;
0082 
0083     t->id.proto = IPPROTO_IPV6;
0084     t->id.spi = xfrm6_tunnel_alloc_spi(net, (xfrm_address_t *)&x->props.saddr);
0085     if (!t->id.spi)
0086         goto error;
0087 
0088     memcpy(t->id.daddr.a6, x->id.daddr.a6, sizeof(struct in6_addr));
0089     memcpy(&t->sel, &x->sel, sizeof(t->sel));
0090     t->props.family = AF_INET6;
0091     t->props.mode = x->props.mode;
0092     memcpy(t->props.saddr.a6, x->props.saddr.a6, sizeof(struct in6_addr));
0093     memcpy(&t->mark, &x->mark, sizeof(t->mark));
0094     t->if_id = x->if_id;
0095 
0096     if (xfrm_init_state(t))
0097         goto error;
0098 
0099     atomic_set(&t->tunnel_users, 1);
0100 
0101 out:
0102     return t;
0103 
0104 error:
0105     t->km.state = XFRM_STATE_DEAD;
0106     xfrm_state_put(t);
0107     t = NULL;
0108     goto out;
0109 }
0110 
0111 static int ipcomp6_tunnel_attach(struct xfrm_state *x)
0112 {
0113     struct net *net = xs_net(x);
0114     int err = 0;
0115     struct xfrm_state *t = NULL;
0116     __be32 spi;
0117     u32 mark = x->mark.m & x->mark.v;
0118 
0119     spi = xfrm6_tunnel_spi_lookup(net, (xfrm_address_t *)&x->props.saddr);
0120     if (spi)
0121         t = xfrm_state_lookup(net, mark, (xfrm_address_t *)&x->id.daddr,
0122                           spi, IPPROTO_IPV6, AF_INET6);
0123     if (!t) {
0124         t = ipcomp6_tunnel_create(x);
0125         if (!t) {
0126             err = -EINVAL;
0127             goto out;
0128         }
0129         xfrm_state_insert(t);
0130         xfrm_state_hold(t);
0131     }
0132     x->tunnel = t;
0133     atomic_inc(&t->tunnel_users);
0134 
0135 out:
0136     return err;
0137 }
0138 
0139 static int ipcomp6_init_state(struct xfrm_state *x)
0140 {
0141     int err = -EINVAL;
0142 
0143     x->props.header_len = 0;
0144     switch (x->props.mode) {
0145     case XFRM_MODE_TRANSPORT:
0146         break;
0147     case XFRM_MODE_TUNNEL:
0148         x->props.header_len += sizeof(struct ipv6hdr);
0149         break;
0150     default:
0151         goto out;
0152     }
0153 
0154     err = ipcomp_init_state(x);
0155     if (err)
0156         goto out;
0157 
0158     if (x->props.mode == XFRM_MODE_TUNNEL) {
0159         err = ipcomp6_tunnel_attach(x);
0160         if (err)
0161             goto out;
0162     }
0163 
0164     err = 0;
0165 out:
0166     return err;
0167 }
0168 
0169 static int ipcomp6_rcv_cb(struct sk_buff *skb, int err)
0170 {
0171     return 0;
0172 }
0173 
0174 static const struct xfrm_type ipcomp6_type = {
0175     .owner      = THIS_MODULE,
0176     .proto      = IPPROTO_COMP,
0177     .init_state = ipcomp6_init_state,
0178     .destructor = ipcomp_destroy,
0179     .input      = ipcomp_input,
0180     .output     = ipcomp_output,
0181 };
0182 
0183 static struct xfrm6_protocol ipcomp6_protocol = {
0184     .handler    = xfrm6_rcv,
0185     .input_handler  = xfrm_input,
0186     .cb_handler = ipcomp6_rcv_cb,
0187     .err_handler    = ipcomp6_err,
0188     .priority   = 0,
0189 };
0190 
0191 static int __init ipcomp6_init(void)
0192 {
0193     if (xfrm_register_type(&ipcomp6_type, AF_INET6) < 0) {
0194         pr_info("%s: can't add xfrm type\n", __func__);
0195         return -EAGAIN;
0196     }
0197     if (xfrm6_protocol_register(&ipcomp6_protocol, IPPROTO_COMP) < 0) {
0198         pr_info("%s: can't add protocol\n", __func__);
0199         xfrm_unregister_type(&ipcomp6_type, AF_INET6);
0200         return -EAGAIN;
0201     }
0202     return 0;
0203 }
0204 
0205 static void __exit ipcomp6_fini(void)
0206 {
0207     if (xfrm6_protocol_deregister(&ipcomp6_protocol, IPPROTO_COMP) < 0)
0208         pr_info("%s: can't remove protocol\n", __func__);
0209     xfrm_unregister_type(&ipcomp6_type, AF_INET6);
0210 }
0211 
0212 module_init(ipcomp6_init);
0213 module_exit(ipcomp6_fini);
0214 MODULE_LICENSE("GPL");
0215 MODULE_DESCRIPTION("IP Payload Compression Protocol (IPComp) for IPv6 - RFC3173");
0216 MODULE_AUTHOR("Mitsuru KANDA <mk@linux-ipv6.org>");
0217 
0218 MODULE_ALIAS_XFRM_TYPE(AF_INET6, XFRM_PROTO_COMP);