aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/fou.c
diff options
context:
space:
mode:
authorTom Herbert <therbert@google.com>2014-09-17 15:25:56 -0400
committerDavid S. Miller <davem@davemloft.net>2014-09-19 17:15:31 -0400
commit23461551c00628c3f3fe9cf837bf53cf8f212b63 (patch)
treebd19f9ee79a5c2e8e899c6dfb25da38911515777 /net/ipv4/fou.c
parentce3e02867ed8e12c6e6e83a793d273c1f4d929ea (diff)
fou: Support for foo-over-udp RX path
This patch provides a receive path for foo-over-udp. This allows direct encapsulation of IP protocols over UDP. The bound destination port is used to map to an IP protocol, and the XFRM framework (udp_encap_rcv) is used to receive encapsulated packets. Upon reception, the encapsulation header is logically removed (pointer to transport header is advanced) and the packet is reinjected into the receive path with the IP protocol indicated by the mapping. Netlink is used to configure FOU ports. The configuration information includes the port number to bind to and the IP protocol corresponding to that port. This should support GRE/UDP (http://tools.ietf.org/html/draft-yong-tsvwg-gre-in-udp-encap-02), as will as the other IP tunneling protocols (IPIP, SIT). Signed-off-by: Tom Herbert <therbert@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/fou.c')
-rw-r--r--net/ipv4/fou.c279
1 files changed, 279 insertions, 0 deletions
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c
new file mode 100644
index 000000000000..d44f97b79418
--- /dev/null
+++ b/net/ipv4/fou.c
@@ -0,0 +1,279 @@
1#include <linux/module.h>
2#include <linux/errno.h>
3#include <linux/socket.h>
4#include <linux/skbuff.h>
5#include <linux/ip.h>
6#include <linux/udp.h>
7#include <linux/types.h>
8#include <linux/kernel.h>
9#include <net/genetlink.h>
10#include <net/ip.h>
11#include <net/udp.h>
12#include <net/udp_tunnel.h>
13#include <net/xfrm.h>
14#include <uapi/linux/fou.h>
15#include <uapi/linux/genetlink.h>
16
17static DEFINE_SPINLOCK(fou_lock);
18static LIST_HEAD(fou_list);
19
20struct fou {
21 struct socket *sock;
22 u8 protocol;
23 u16 port;
24 struct list_head list;
25};
26
27struct fou_cfg {
28 u8 protocol;
29 struct udp_port_cfg udp_config;
30};
31
32static inline struct fou *fou_from_sock(struct sock *sk)
33{
34 return sk->sk_user_data;
35}
36
37static int fou_udp_encap_recv_deliver(struct sk_buff *skb,
38 u8 protocol, size_t len)
39{
40 struct iphdr *iph = ip_hdr(skb);
41
42 /* Remove 'len' bytes from the packet (UDP header and
43 * FOU header if present), modify the protocol to the one
44 * we found, and then call rcv_encap.
45 */
46 iph->tot_len = htons(ntohs(iph->tot_len) - len);
47 __skb_pull(skb, len);
48 skb_postpull_rcsum(skb, udp_hdr(skb), len);
49 skb_reset_transport_header(skb);
50
51 return -protocol;
52}
53
54static int fou_udp_recv(struct sock *sk, struct sk_buff *skb)
55{
56 struct fou *fou = fou_from_sock(sk);
57
58 if (!fou)
59 return 1;
60
61 return fou_udp_encap_recv_deliver(skb, fou->protocol,
62 sizeof(struct udphdr));
63}
64
65static int fou_add_to_port_list(struct fou *fou)
66{
67 struct fou *fout;
68
69 spin_lock(&fou_lock);
70 list_for_each_entry(fout, &fou_list, list) {
71 if (fou->port == fout->port) {
72 spin_unlock(&fou_lock);
73 return -EALREADY;
74 }
75 }
76
77 list_add(&fou->list, &fou_list);
78 spin_unlock(&fou_lock);
79
80 return 0;
81}
82
83static void fou_release(struct fou *fou)
84{
85 struct socket *sock = fou->sock;
86 struct sock *sk = sock->sk;
87
88 udp_del_offload(&fou->udp_offloads);
89
90 list_del(&fou->list);
91
92 /* Remove hooks into tunnel socket */
93 sk->sk_user_data = NULL;
94
95 sock_release(sock);
96
97 kfree(fou);
98}
99
100static int fou_create(struct net *net, struct fou_cfg *cfg,
101 struct socket **sockp)
102{
103 struct fou *fou = NULL;
104 int err;
105 struct socket *sock = NULL;
106 struct sock *sk;
107
108 /* Open UDP socket */
109 err = udp_sock_create(net, &cfg->udp_config, &sock);
110 if (err < 0)
111 goto error;
112
113 /* Allocate FOU port structure */
114 fou = kzalloc(sizeof(*fou), GFP_KERNEL);
115 if (!fou) {
116 err = -ENOMEM;
117 goto error;
118 }
119
120 sk = sock->sk;
121
122 /* Mark socket as an encapsulation socket. See net/ipv4/udp.c */
123 fou->protocol = cfg->protocol;
124 fou->port = cfg->udp_config.local_udp_port;
125 udp_sk(sk)->encap_rcv = fou_udp_recv;
126
127 udp_sk(sk)->encap_type = 1;
128 udp_encap_enable();
129
130 sk->sk_user_data = fou;
131 fou->sock = sock;
132
133 udp_set_convert_csum(sk, true);
134
135 sk->sk_allocation = GFP_ATOMIC;
136
137 err = fou_add_to_port_list(fou);
138 if (err)
139 goto error;
140
141 if (sockp)
142 *sockp = sock;
143
144 return 0;
145
146error:
147 kfree(fou);
148 if (sock)
149 sock_release(sock);
150
151 return err;
152}
153
154static int fou_destroy(struct net *net, struct fou_cfg *cfg)
155{
156 struct fou *fou;
157 u16 port = cfg->udp_config.local_udp_port;
158 int err = -EINVAL;
159
160 spin_lock(&fou_lock);
161 list_for_each_entry(fou, &fou_list, list) {
162 if (fou->port == port) {
163 fou_release(fou);
164 err = 0;
165 break;
166 }
167 }
168 spin_unlock(&fou_lock);
169
170 return err;
171}
172
173static struct genl_family fou_nl_family = {
174 .id = GENL_ID_GENERATE,
175 .hdrsize = 0,
176 .name = FOU_GENL_NAME,
177 .version = FOU_GENL_VERSION,
178 .maxattr = FOU_ATTR_MAX,
179 .netnsok = true,
180};
181
182static struct nla_policy fou_nl_policy[FOU_ATTR_MAX + 1] = {
183 [FOU_ATTR_PORT] = { .type = NLA_U16, },
184 [FOU_ATTR_AF] = { .type = NLA_U8, },
185 [FOU_ATTR_IPPROTO] = { .type = NLA_U8, },
186};
187
188static int parse_nl_config(struct genl_info *info,
189 struct fou_cfg *cfg)
190{
191 memset(cfg, 0, sizeof(*cfg));
192
193 cfg->udp_config.family = AF_INET;
194
195 if (info->attrs[FOU_ATTR_AF]) {
196 u8 family = nla_get_u8(info->attrs[FOU_ATTR_AF]);
197
198 if (family != AF_INET && family != AF_INET6)
199 return -EINVAL;
200
201 cfg->udp_config.family = family;
202 }
203
204 if (info->attrs[FOU_ATTR_PORT]) {
205 u16 port = nla_get_u16(info->attrs[FOU_ATTR_PORT]);
206
207 cfg->udp_config.local_udp_port = port;
208 }
209
210 if (info->attrs[FOU_ATTR_IPPROTO])
211 cfg->protocol = nla_get_u8(info->attrs[FOU_ATTR_IPPROTO]);
212
213 return 0;
214}
215
216static int fou_nl_cmd_add_port(struct sk_buff *skb, struct genl_info *info)
217{
218 struct fou_cfg cfg;
219 int err;
220
221 err = parse_nl_config(info, &cfg);
222 if (err)
223 return err;
224
225 return fou_create(&init_net, &cfg, NULL);
226}
227
228static int fou_nl_cmd_rm_port(struct sk_buff *skb, struct genl_info *info)
229{
230 struct fou_cfg cfg;
231
232 parse_nl_config(info, &cfg);
233
234 return fou_destroy(&init_net, &cfg);
235}
236
237static const struct genl_ops fou_nl_ops[] = {
238 {
239 .cmd = FOU_CMD_ADD,
240 .doit = fou_nl_cmd_add_port,
241 .policy = fou_nl_policy,
242 .flags = GENL_ADMIN_PERM,
243 },
244 {
245 .cmd = FOU_CMD_DEL,
246 .doit = fou_nl_cmd_rm_port,
247 .policy = fou_nl_policy,
248 .flags = GENL_ADMIN_PERM,
249 },
250};
251
252static int __init fou_init(void)
253{
254 int ret;
255
256 ret = genl_register_family_with_ops(&fou_nl_family,
257 fou_nl_ops);
258
259 return ret;
260}
261
262static void __exit fou_fini(void)
263{
264 struct fou *fou, *next;
265
266 genl_unregister_family(&fou_nl_family);
267
268 /* Close all the FOU sockets */
269
270 spin_lock(&fou_lock);
271 list_for_each_entry_safe(fou, next, &fou_list, list)
272 fou_release(fou);
273 spin_unlock(&fou_lock);
274}
275
276module_init(fou_init);
277module_exit(fou_fini);
278MODULE_AUTHOR("Tom Herbert <therbert@google.com>");
279MODULE_LICENSE("GPL");