diff options
-rw-r--r-- | include/linux/lwtunnel.h | 6 | ||||
-rw-r--r-- | include/net/lwtunnel.h | 132 | ||||
-rw-r--r-- | include/uapi/linux/lwtunnel.h | 15 | ||||
-rw-r--r-- | net/Kconfig | 7 | ||||
-rw-r--r-- | net/core/Makefile | 1 | ||||
-rw-r--r-- | net/core/lwtunnel.c | 179 |
6 files changed, 340 insertions, 0 deletions
diff --git a/include/linux/lwtunnel.h b/include/linux/lwtunnel.h new file mode 100644 index 000000000000..97f32f8b4ae1 --- /dev/null +++ b/include/linux/lwtunnel.h | |||
@@ -0,0 +1,6 @@ | |||
1 | #ifndef _LINUX_LWTUNNEL_H_ | ||
2 | #define _LINUX_LWTUNNEL_H_ | ||
3 | |||
4 | #include <uapi/linux/lwtunnel.h> | ||
5 | |||
6 | #endif /* _LINUX_LWTUNNEL_H_ */ | ||
diff --git a/include/net/lwtunnel.h b/include/net/lwtunnel.h new file mode 100644 index 000000000000..df24b3611ff4 --- /dev/null +++ b/include/net/lwtunnel.h | |||
@@ -0,0 +1,132 @@ | |||
1 | #ifndef __NET_LWTUNNEL_H | ||
2 | #define __NET_LWTUNNEL_H 1 | ||
3 | |||
4 | #include <linux/lwtunnel.h> | ||
5 | #include <linux/netdevice.h> | ||
6 | #include <linux/skbuff.h> | ||
7 | #include <linux/types.h> | ||
8 | #include <net/route.h> | ||
9 | |||
10 | #define LWTUNNEL_HASH_BITS 7 | ||
11 | #define LWTUNNEL_HASH_SIZE (1 << LWTUNNEL_HASH_BITS) | ||
12 | |||
13 | /* lw tunnel state flags */ | ||
14 | #define LWTUNNEL_STATE_OUTPUT_REDIRECT 0x1 | ||
15 | |||
16 | struct lwtunnel_state { | ||
17 | __u16 type; | ||
18 | __u16 flags; | ||
19 | atomic_t refcnt; | ||
20 | int len; | ||
21 | __u8 data[0]; | ||
22 | }; | ||
23 | |||
24 | struct lwtunnel_encap_ops { | ||
25 | int (*build_state)(struct net_device *dev, struct nlattr *encap, | ||
26 | struct lwtunnel_state **ts); | ||
27 | int (*output)(struct sock *sk, struct sk_buff *skb); | ||
28 | int (*fill_encap)(struct sk_buff *skb, | ||
29 | struct lwtunnel_state *lwtstate); | ||
30 | int (*get_encap_size)(struct lwtunnel_state *lwtstate); | ||
31 | int (*cmp_encap)(struct lwtunnel_state *a, struct lwtunnel_state *b); | ||
32 | }; | ||
33 | |||
34 | extern const struct lwtunnel_encap_ops __rcu * | ||
35 | lwtun_encaps[LWTUNNEL_ENCAP_MAX+1]; | ||
36 | |||
37 | #ifdef CONFIG_LWTUNNEL | ||
38 | static inline void lwtunnel_state_get(struct lwtunnel_state *lws) | ||
39 | { | ||
40 | atomic_inc(&lws->refcnt); | ||
41 | } | ||
42 | |||
43 | static inline void lwtunnel_state_put(struct lwtunnel_state *lws) | ||
44 | { | ||
45 | if (!lws) | ||
46 | return; | ||
47 | |||
48 | if (atomic_dec_and_test(&lws->refcnt)) | ||
49 | kfree(lws); | ||
50 | } | ||
51 | |||
52 | static inline bool lwtunnel_output_redirect(struct lwtunnel_state *lwtstate) | ||
53 | { | ||
54 | if (lwtstate && (lwtstate->flags & LWTUNNEL_STATE_OUTPUT_REDIRECT)) | ||
55 | return true; | ||
56 | |||
57 | return false; | ||
58 | } | ||
59 | |||
60 | int lwtunnel_encap_add_ops(const struct lwtunnel_encap_ops *op, | ||
61 | unsigned int num); | ||
62 | int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *op, | ||
63 | unsigned int num); | ||
64 | int lwtunnel_build_state(struct net_device *dev, u16 encap_type, | ||
65 | struct nlattr *encap, | ||
66 | struct lwtunnel_state **lws); | ||
67 | int lwtunnel_fill_encap(struct sk_buff *skb, | ||
68 | struct lwtunnel_state *lwtstate); | ||
69 | int lwtunnel_get_encap_size(struct lwtunnel_state *lwtstate); | ||
70 | struct lwtunnel_state *lwtunnel_state_alloc(int hdr_len); | ||
71 | int lwtunnel_cmp_encap(struct lwtunnel_state *a, struct lwtunnel_state *b); | ||
72 | |||
73 | #else | ||
74 | |||
75 | static inline void lwtunnel_state_get(struct lwtunnel_state *lws) | ||
76 | { | ||
77 | } | ||
78 | |||
79 | static inline void lwtunnel_state_put(struct lwtunnel_state *lws) | ||
80 | { | ||
81 | } | ||
82 | |||
83 | static inline bool lwtunnel_output_redirect(struct lwtunnel_state *lwtstate) | ||
84 | { | ||
85 | return false; | ||
86 | } | ||
87 | |||
88 | static inline int lwtunnel_encap_add_ops(const struct lwtunnel_encap_ops *op, | ||
89 | unsigned int num) | ||
90 | { | ||
91 | return -EOPNOTSUPP; | ||
92 | |||
93 | } | ||
94 | |||
95 | static inline int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *op, | ||
96 | unsigned int num) | ||
97 | { | ||
98 | return -EOPNOTSUPP; | ||
99 | } | ||
100 | |||
101 | static inline int lwtunnel_build_state(struct net_device *dev, u16 encap_type, | ||
102 | struct nlattr *encap, | ||
103 | struct lwtunnel_state **lws) | ||
104 | { | ||
105 | return -EOPNOTSUPP; | ||
106 | } | ||
107 | |||
108 | static inline int lwtunnel_fill_encap(struct sk_buff *skb, | ||
109 | struct lwtunnel_state *lwtstate) | ||
110 | { | ||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | static inline int lwtunnel_get_encap_size(struct lwtunnel_state *lwtstate) | ||
115 | { | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | static inline struct lwtunnel_state *lwtunnel_state_alloc(int hdr_len) | ||
120 | { | ||
121 | return NULL; | ||
122 | } | ||
123 | |||
124 | static inline int lwtunnel_cmp_encap(struct lwtunnel_state *a, | ||
125 | struct lwtunnel_state *b) | ||
126 | { | ||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | #endif | ||
131 | |||
132 | #endif /* __NET_LWTUNNEL_H */ | ||
diff --git a/include/uapi/linux/lwtunnel.h b/include/uapi/linux/lwtunnel.h new file mode 100644 index 000000000000..aa611d931a31 --- /dev/null +++ b/include/uapi/linux/lwtunnel.h | |||
@@ -0,0 +1,15 @@ | |||
1 | #ifndef _UAPI_LWTUNNEL_H_ | ||
2 | #define _UAPI_LWTUNNEL_H_ | ||
3 | |||
4 | #include <linux/types.h> | ||
5 | |||
6 | enum lwtunnel_encap_types { | ||
7 | LWTUNNEL_ENCAP_NONE, | ||
8 | LWTUNNEL_ENCAP_MPLS, | ||
9 | __LWTUNNEL_ENCAP_MAX, | ||
10 | }; | ||
11 | |||
12 | #define LWTUNNEL_ENCAP_MAX (__LWTUNNEL_ENCAP_MAX - 1) | ||
13 | |||
14 | |||
15 | #endif /* _UAPI_LWTUNNEL_H_ */ | ||
diff --git a/net/Kconfig b/net/Kconfig index 57a7c5af3175..7021c1bf44d6 100644 --- a/net/Kconfig +++ b/net/Kconfig | |||
@@ -374,6 +374,13 @@ source "net/caif/Kconfig" | |||
374 | source "net/ceph/Kconfig" | 374 | source "net/ceph/Kconfig" |
375 | source "net/nfc/Kconfig" | 375 | source "net/nfc/Kconfig" |
376 | 376 | ||
377 | config LWTUNNEL | ||
378 | bool "Network light weight tunnels" | ||
379 | ---help--- | ||
380 | This feature provides an infrastructure to support light weight | ||
381 | tunnels like mpls. There is no netdevice associated with a light | ||
382 | weight tunnel endpoint. Tunnel encapsulation parameters are stored | ||
383 | with light weight tunnel state associated with fib routes. | ||
377 | 384 | ||
378 | endif # if NET | 385 | endif # if NET |
379 | 386 | ||
diff --git a/net/core/Makefile b/net/core/Makefile index fec0856dd6c0..086b01fbe1bd 100644 --- a/net/core/Makefile +++ b/net/core/Makefile | |||
@@ -23,3 +23,4 @@ obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += timestamping.o | |||
23 | obj-$(CONFIG_NET_PTP_CLASSIFY) += ptp_classifier.o | 23 | obj-$(CONFIG_NET_PTP_CLASSIFY) += ptp_classifier.o |
24 | obj-$(CONFIG_CGROUP_NET_PRIO) += netprio_cgroup.o | 24 | obj-$(CONFIG_CGROUP_NET_PRIO) += netprio_cgroup.o |
25 | obj-$(CONFIG_CGROUP_NET_CLASSID) += netclassid_cgroup.o | 25 | obj-$(CONFIG_CGROUP_NET_CLASSID) += netclassid_cgroup.o |
26 | obj-$(CONFIG_LWTUNNEL) += lwtunnel.o | ||
diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c new file mode 100644 index 000000000000..d7ae3a235b4b --- /dev/null +++ b/net/core/lwtunnel.c | |||
@@ -0,0 +1,179 @@ | |||
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); | ||