]> Pileus Git - ~andy/linux/blob - net/ipv4/gre.c
gre: Simplify gre protocol registration locking.
[~andy/linux] / net / ipv4 / gre.c
1 /*
2  *      GRE over IPv4 demultiplexer driver
3  *
4  *      Authors: Dmitry Kozlov (xeb@mail.ru)
5  *
6  *      This program is free software; you can redistribute it and/or
7  *      modify it under the terms of the GNU General Public License
8  *      as published by the Free Software Foundation; either version
9  *      2 of the License, or (at your option) any later version.
10  *
11  */
12
13 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14
15 #include <linux/module.h>
16 #include <linux/kernel.h>
17 #include <linux/kmod.h>
18 #include <linux/skbuff.h>
19 #include <linux/in.h>
20 #include <linux/ip.h>
21 #include <linux/netdevice.h>
22 #include <linux/if_tunnel.h>
23 #include <linux/spinlock.h>
24 #include <net/protocol.h>
25 #include <net/gre.h>
26
27
28 static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly;
29
30 int gre_add_protocol(const struct gre_protocol *proto, u8 version)
31 {
32         if (version >= GREPROTO_MAX)
33                 return -EINVAL;
34
35         return (cmpxchg((const struct gre_protocol **)&gre_proto[version], NULL, proto) == NULL) ?
36                 0 : -EBUSY;
37 }
38 EXPORT_SYMBOL_GPL(gre_add_protocol);
39
40 int gre_del_protocol(const struct gre_protocol *proto, u8 version)
41 {
42         int ret;
43
44         if (version >= GREPROTO_MAX)
45                 return -EINVAL;
46
47         ret = (cmpxchg((const struct gre_protocol **)&gre_proto[version], proto, NULL) == proto) ?
48                 0 : -EBUSY;
49
50         if (ret)
51                 return ret;
52
53         synchronize_rcu();
54         return 0;
55 }
56 EXPORT_SYMBOL_GPL(gre_del_protocol);
57
58 static int gre_rcv(struct sk_buff *skb)
59 {
60         const struct gre_protocol *proto;
61         u8 ver;
62         int ret;
63
64         if (!pskb_may_pull(skb, 12))
65                 goto drop;
66
67         ver = skb->data[1]&0x7f;
68         if (ver >= GREPROTO_MAX)
69                 goto drop;
70
71         rcu_read_lock();
72         proto = rcu_dereference(gre_proto[ver]);
73         if (!proto || !proto->handler)
74                 goto drop_unlock;
75         ret = proto->handler(skb);
76         rcu_read_unlock();
77         return ret;
78
79 drop_unlock:
80         rcu_read_unlock();
81 drop:
82         kfree_skb(skb);
83         return NET_RX_DROP;
84 }
85
86 static void gre_err(struct sk_buff *skb, u32 info)
87 {
88         const struct gre_protocol *proto;
89         const struct iphdr *iph = (const struct iphdr *)skb->data;
90         u8 ver = skb->data[(iph->ihl<<2) + 1]&0x7f;
91
92         if (ver >= GREPROTO_MAX)
93                 return;
94
95         rcu_read_lock();
96         proto = rcu_dereference(gre_proto[ver]);
97         if (proto && proto->err_handler)
98                 proto->err_handler(skb, info);
99         rcu_read_unlock();
100 }
101
102 static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
103                                        netdev_features_t features)
104 {
105         struct sk_buff *segs = ERR_PTR(-EINVAL);
106         netdev_features_t enc_features;
107         int ghl = GRE_HEADER_SECTION;
108         struct gre_base_hdr *greh;
109         int mac_len = skb->mac_len;
110         __be16 protocol = skb->protocol;
111         int tnl_hlen;
112         bool csum;
113
114         if (unlikely(skb_shinfo(skb)->gso_type &
115                                 ~(SKB_GSO_TCPV4 |
116                                   SKB_GSO_TCPV6 |
117                                   SKB_GSO_UDP |
118                                   SKB_GSO_DODGY |
119                                   SKB_GSO_TCP_ECN |
120                                   SKB_GSO_GRE)))
121                 goto out;
122
123         if (unlikely(!pskb_may_pull(skb, sizeof(*greh))))
124                 goto out;
125
126         greh = (struct gre_base_hdr *)skb_transport_header(skb);
127
128         if (greh->flags & GRE_KEY)
129                 ghl += GRE_HEADER_SECTION;
130         if (greh->flags & GRE_SEQ)
131                 ghl += GRE_HEADER_SECTION;
132         if (greh->flags & GRE_CSUM) {
133                 ghl += GRE_HEADER_SECTION;
134                 csum = true;
135         } else
136                 csum = false;
137
138         /* setup inner skb. */
139         skb->protocol = greh->protocol;
140         skb->encapsulation = 0;
141
142         if (unlikely(!pskb_may_pull(skb, ghl)))
143                 goto out;
144         __skb_pull(skb, ghl);
145         skb_reset_mac_header(skb);
146         skb_set_network_header(skb, skb_inner_network_offset(skb));
147         skb->mac_len = skb_inner_network_offset(skb);
148
149         /* segment inner packet. */
150         enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
151         segs = skb_mac_gso_segment(skb, enc_features);
152         if (!segs || IS_ERR(segs))
153                 goto out;
154
155         skb = segs;
156         tnl_hlen = skb_tnl_header_len(skb);
157         do {
158                 __skb_push(skb, ghl);
159                 if (csum) {
160                         __be32 *pcsum;
161
162                         if (skb_has_shared_frag(skb)) {
163                                 int err;
164
165                                 err = __skb_linearize(skb);
166                                 if (err) {
167                                         kfree_skb(segs);
168                                         segs = ERR_PTR(err);
169                                         goto out;
170                                 }
171                         }
172
173                         greh = (struct gre_base_hdr *)(skb->data);
174                         pcsum = (__be32 *)(greh + 1);
175                         *pcsum = 0;
176                         *(__sum16 *)pcsum = csum_fold(skb_checksum(skb, 0, skb->len, 0));
177                 }
178                 __skb_push(skb, tnl_hlen - ghl);
179
180                 skb_reset_mac_header(skb);
181                 skb_set_network_header(skb, mac_len);
182                 skb->mac_len = mac_len;
183                 skb->protocol = protocol;
184         } while ((skb = skb->next));
185 out:
186         return segs;
187 }
188
189 static int gre_gso_send_check(struct sk_buff *skb)
190 {
191         if (!skb->encapsulation)
192                 return -EINVAL;
193         return 0;
194 }
195
196 static const struct net_protocol net_gre_protocol = {
197         .handler     = gre_rcv,
198         .err_handler = gre_err,
199         .netns_ok    = 1,
200 };
201
202 static const struct net_offload gre_offload = {
203         .callbacks = {
204                 .gso_send_check =       gre_gso_send_check,
205                 .gso_segment    =       gre_gso_segment,
206         },
207 };
208
209 static int __init gre_init(void)
210 {
211         pr_info("GRE over IPv4 demultiplexor driver\n");
212
213         if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) {
214                 pr_err("can't add protocol\n");
215                 return -EAGAIN;
216         }
217
218         if (inet_add_offload(&gre_offload, IPPROTO_GRE)) {
219                 pr_err("can't add protocol offload\n");
220                 inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
221                 return -EAGAIN;
222         }
223
224         return 0;
225 }
226
227 static void __exit gre_exit(void)
228 {
229         inet_del_offload(&gre_offload, IPPROTO_GRE);
230         inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
231 }
232
233 module_init(gre_init);
234 module_exit(gre_exit);
235
236 MODULE_DESCRIPTION("GRE over IPv4 demultiplexer driver");
237 MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)");
238 MODULE_LICENSE("GPL");
239