lwtunnel: infrastructure for handling light weight tunnels like mpls
[firefly-linux-kernel-4.4.55.git] / net / core / lwtunnel.c
1 /*
2  * lwtunnel     Infrastructure for light weight tunnels like mpls
3  *
4  * Authors:     Roopa Prabhu, <roopa@cumulusnetworks.com>
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 #include <linux/capability.h>
14 #include <linux/module.h>
15 #include <linux/types.h>
16 #include <linux/kernel.h>
17 #include <linux/slab.h>
18 #include <linux/uaccess.h>
19 #include <linux/skbuff.h>
20 #include <linux/netdevice.h>
21 #include <linux/lwtunnel.h>
22 #include <linux/in.h>
23 #include <linux/init.h>
24 #include <linux/err.h>
25
26 #include <net/lwtunnel.h>
27 #include <net/rtnetlink.h>
28
29 struct lwtunnel_state *lwtunnel_state_alloc(int encap_len)
30 {
31         struct lwtunnel_state *lws;
32
33         lws = kzalloc(sizeof(*lws) + encap_len, GFP_ATOMIC);
34
35         return lws;
36 }
37 EXPORT_SYMBOL(lwtunnel_state_alloc);
38
39 const struct lwtunnel_encap_ops __rcu *
40                 lwtun_encaps[LWTUNNEL_ENCAP_MAX + 1] __read_mostly;
41
42 int lwtunnel_encap_add_ops(const struct lwtunnel_encap_ops *ops,
43                            unsigned int num)
44 {
45         if (num > LWTUNNEL_ENCAP_MAX)
46                 return -ERANGE;
47
48         return !cmpxchg((const struct lwtunnel_encap_ops **)
49                         &lwtun_encaps[num],
50                         NULL, ops) ? 0 : -1;
51 }
52 EXPORT_SYMBOL(lwtunnel_encap_add_ops);
53
54 int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *ops,
55                            unsigned int encap_type)
56 {
57         int ret;
58
59         if (encap_type == LWTUNNEL_ENCAP_NONE ||
60             encap_type > LWTUNNEL_ENCAP_MAX)
61                 return -ERANGE;
62
63         ret = (cmpxchg((const struct lwtunnel_encap_ops **)
64                        &lwtun_encaps[encap_type],
65                        ops, NULL) == ops) ? 0 : -1;
66
67         synchronize_net();
68
69         return ret;
70 }
71 EXPORT_SYMBOL(lwtunnel_encap_del_ops);
72
73 int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
74                          struct nlattr *encap, struct lwtunnel_state **lws)
75 {
76         const struct lwtunnel_encap_ops *ops;
77         int ret = -EINVAL;
78
79         if (encap_type == LWTUNNEL_ENCAP_NONE ||
80             encap_type > LWTUNNEL_ENCAP_MAX)
81                 return ret;
82
83         ret = -EOPNOTSUPP;
84         rcu_read_lock();
85         ops = rcu_dereference(lwtun_encaps[encap_type]);
86         if (likely(ops && ops->build_state))
87                 ret = ops->build_state(dev, encap, lws);
88         rcu_read_unlock();
89
90         return ret;
91 }
92 EXPORT_SYMBOL(lwtunnel_build_state);
93
94 int lwtunnel_fill_encap(struct sk_buff *skb, struct lwtunnel_state *lwtstate)
95 {
96         const struct lwtunnel_encap_ops *ops;
97         struct nlattr *nest;
98         int ret = -EINVAL;
99
100         if (!lwtstate)
101                 return 0;
102
103         if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
104             lwtstate->type > LWTUNNEL_ENCAP_MAX)
105                 return 0;
106
107         ret = -EOPNOTSUPP;
108         nest = nla_nest_start(skb, RTA_ENCAP);
109         rcu_read_lock();
110         ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
111         if (likely(ops && ops->fill_encap))
112                 ret = ops->fill_encap(skb, lwtstate);
113         rcu_read_unlock();
114
115         if (ret)
116                 goto nla_put_failure;
117         nla_nest_end(skb, nest);
118         ret = nla_put_u16(skb, RTA_ENCAP_TYPE, lwtstate->type);
119         if (ret)
120                 goto nla_put_failure;
121
122         return 0;
123
124 nla_put_failure:
125         nla_nest_cancel(skb, nest);
126
127         return (ret == -EOPNOTSUPP ? 0 : ret);
128 }
129 EXPORT_SYMBOL(lwtunnel_fill_encap);
130
131 int lwtunnel_get_encap_size(struct lwtunnel_state *lwtstate)
132 {
133         const struct lwtunnel_encap_ops *ops;
134         int ret = 0;
135
136         if (!lwtstate)
137                 return 0;
138
139         if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
140             lwtstate->type > LWTUNNEL_ENCAP_MAX)
141                 return 0;
142
143         rcu_read_lock();
144         ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
145         if (likely(ops && ops->get_encap_size))
146                 ret = nla_total_size(ops->get_encap_size(lwtstate));
147         rcu_read_unlock();
148
149         return ret;
150 }
151 EXPORT_SYMBOL(lwtunnel_get_encap_size);
152
153 int lwtunnel_cmp_encap(struct lwtunnel_state *a, struct lwtunnel_state *b)
154 {
155         const struct lwtunnel_encap_ops *ops;
156         int ret = 0;
157
158         if (!a && !b)
159                 return 0;
160
161         if (!a || !b)
162                 return 1;
163
164         if (a->type != b->type)
165                 return 1;
166
167         if (a->type == LWTUNNEL_ENCAP_NONE ||
168             a->type > LWTUNNEL_ENCAP_MAX)
169                 return 0;
170
171         rcu_read_lock();
172         ops = rcu_dereference(lwtun_encaps[a->type]);
173         if (likely(ops && ops->cmp_encap))
174                 ret = ops->cmp_encap(a, b);
175         rcu_read_unlock();
176
177         return ret;
178 }
179 EXPORT_SYMBOL(lwtunnel_cmp_encap);