datapath: Support VXLAN Group Policy extension
[cascardo/ovs.git] / datapath / vport-vxlan.c
1 /*
2  * Copyright (c) 2013 Nicira, Inc.
3  * Copyright (c) 2013 Cisco Systems, Inc.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of version 2 of the GNU General Public
7  * License as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA
18  */
19
20 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
21
22 #include <linux/version.h>
23
24 #include <linux/in.h>
25 #include <linux/ip.h>
26 #include <linux/net.h>
27 #include <linux/rculist.h>
28 #include <linux/udp.h>
29
30 #include <net/icmp.h>
31 #include <net/ip.h>
32 #include <net/udp.h>
33 #include <net/ip_tunnels.h>
34 #include <net/rtnetlink.h>
35 #include <net/route.h>
36 #include <net/dsfield.h>
37 #include <net/inet_ecn.h>
38 #include <net/net_namespace.h>
39 #include <net/netns/generic.h>
40 #include <net/vxlan.h>
41
42 #include "datapath.h"
43 #include "vport.h"
44 #include "vport-vxlan.h"
45
46 /**
47  * struct vxlan_port - Keeps track of open UDP ports
48  * @vs: vxlan_sock created for the port.
49  * @name: vport name.
50  */
51 struct vxlan_port {
52         struct vxlan_sock *vs;
53         char name[IFNAMSIZ];
54         u32 exts; /* VXLAN_F_* in <net/vxlan.h> */
55 };
56
57 static inline struct vxlan_port *vxlan_vport(const struct vport *vport)
58 {
59         return vport_priv(vport);
60 }
61
62 static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb,
63                       struct vxlan_metadata *md)
64 {
65         struct ovs_tunnel_info tun_info;
66         struct vxlan_port *vxlan_port;
67         struct vport *vport = vs->data;
68         struct iphdr *iph;
69         struct ovs_vxlan_opts opts = {
70                 .gbp = md->gbp,
71         };
72         __be64 key;
73         __be16 flags;
74
75         flags = TUNNEL_KEY;
76         vxlan_port = vxlan_vport(vport);
77         if (vxlan_port->exts & VXLAN_F_GBP && md->gbp)
78                 flags |= TUNNEL_VXLAN_OPT;
79
80         /* Save outer tunnel values */
81         iph = ip_hdr(skb);
82         key = cpu_to_be64(ntohl(md->vni) >> 8);
83         ovs_flow_tun_info_init(&tun_info, iph,
84                                udp_hdr(skb)->source, udp_hdr(skb)->dest,
85                                key, flags, &opts, sizeof(opts));
86
87         ovs_vport_receive(vport, skb, &tun_info);
88 }
89
90 static int vxlan_get_options(const struct vport *vport, struct sk_buff *skb)
91 {
92         struct vxlan_port *vxlan_port = vxlan_vport(vport);
93         __be16 dst_port = inet_sport(vxlan_port->vs->sock->sk);
94
95         if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, ntohs(dst_port)))
96                 return -EMSGSIZE;
97
98         if (vxlan_port->exts) {
99                 struct nlattr *exts;
100
101                 exts = nla_nest_start(skb, OVS_TUNNEL_ATTR_EXTENSION);
102                 if (!exts)
103                         return -EMSGSIZE;
104
105                 if (vxlan_port->exts & VXLAN_F_GBP &&
106                     nla_put_flag(skb, OVS_VXLAN_EXT_GBP))
107                         return -EMSGSIZE;
108
109                 nla_nest_end(skb, exts);
110         }
111
112         return 0;
113 }
114
115 static void vxlan_tnl_destroy(struct vport *vport)
116 {
117         struct vxlan_port *vxlan_port = vxlan_vport(vport);
118
119         vxlan_sock_release(vxlan_port->vs);
120
121         ovs_vport_deferred_free(vport);
122 }
123
124 static const struct nla_policy exts_policy[OVS_VXLAN_EXT_MAX+1] = {
125         [OVS_VXLAN_EXT_GBP]     = { .type = NLA_FLAG, },
126 };
127
128 static int vxlan_configure_exts(struct vport *vport, struct nlattr *attr)
129 {
130         struct nlattr *exts[OVS_VXLAN_EXT_MAX+1];
131         struct vxlan_port *vxlan_port;
132         int err;
133
134         if (nla_len(attr) < sizeof(struct nlattr))
135                 return -EINVAL;
136
137         err = nla_parse_nested(exts, OVS_VXLAN_EXT_MAX, attr, exts_policy);
138         if (err < 0)
139                 return err;
140
141         vxlan_port = vxlan_vport(vport);
142
143         if (exts[OVS_VXLAN_EXT_GBP])
144                 vxlan_port->exts |= VXLAN_F_GBP;
145
146         return 0;
147 }
148
149 static struct vport *vxlan_tnl_create(const struct vport_parms *parms)
150 {
151         struct net *net = ovs_dp_get_net(parms->dp);
152         struct nlattr *options = parms->options;
153         struct vxlan_port *vxlan_port;
154         struct vxlan_sock *vs;
155         struct vport *vport;
156         struct nlattr *a;
157         u16 dst_port;
158         int err;
159
160         if (!options) {
161                 err = -EINVAL;
162                 goto error;
163         }
164         a = nla_find_nested(options, OVS_TUNNEL_ATTR_DST_PORT);
165         if (a && nla_len(a) == sizeof(u16)) {
166                 dst_port = nla_get_u16(a);
167         } else {
168                 /* Require destination port from userspace. */
169                 err = -EINVAL;
170                 goto error;
171         }
172
173         vport = ovs_vport_alloc(sizeof(struct vxlan_port),
174                                 &ovs_vxlan_vport_ops, parms);
175         if (IS_ERR(vport))
176                 return vport;
177
178         vxlan_port = vxlan_vport(vport);
179         strncpy(vxlan_port->name, parms->name, IFNAMSIZ);
180
181         a = nla_find_nested(options, OVS_TUNNEL_ATTR_EXTENSION);
182         if (a) {
183                 err = vxlan_configure_exts(vport, a);
184                 if (err) {
185                         ovs_vport_free(vport);
186                         goto error;
187                 }
188         }
189
190         vs = vxlan_sock_add(net, htons(dst_port), vxlan_rcv, vport, true,
191                             vxlan_port->exts);
192         if (IS_ERR(vs)) {
193                 ovs_vport_free(vport);
194                 return (void *)vs;
195         }
196         vxlan_port->vs = vs;
197
198         return vport;
199
200 error:
201         return ERR_PTR(err);
202 }
203
204 static int vxlan_ext_gbp(struct sk_buff *skb)
205 {
206         const struct ovs_tunnel_info *tun_info;
207         const struct ovs_vxlan_opts *opts;
208
209         tun_info = OVS_CB(skb)->egress_tun_info;
210         opts = tun_info->options;
211
212         if (tun_info->tunnel.tun_flags & TUNNEL_VXLAN_OPT &&
213             tun_info->options_len >= sizeof(*opts))
214                 return opts->gbp;
215         else
216                 return 0;
217 }
218
219 static int vxlan_tnl_send(struct vport *vport, struct sk_buff *skb)
220 {
221         struct ovs_key_ipv4_tunnel *tun_key;
222         struct net *net = ovs_dp_get_net(vport->dp);
223         struct vxlan_port *vxlan_port = vxlan_vport(vport);
224         __be16 dst_port = inet_sport(vxlan_port->vs->sock->sk);
225         struct vxlan_metadata md = {0};
226         struct rtable *rt;
227         __be16 src_port;
228         __be32 saddr;
229         __be16 df;
230         int err;
231
232         if (unlikely(!OVS_CB(skb)->egress_tun_info)) {
233                 err = -EINVAL;
234                 goto error;
235         }
236
237         tun_key = &OVS_CB(skb)->egress_tun_info->tunnel;
238
239         /* Route lookup */
240         saddr = tun_key->ipv4_src;
241         rt = find_route(ovs_dp_get_net(vport->dp),
242                         &saddr, tun_key->ipv4_dst,
243                         IPPROTO_UDP, tun_key->ipv4_tos,
244                         skb->mark);
245         if (IS_ERR(rt)) {
246                 err = PTR_ERR(rt);
247                 goto error;
248         }
249
250         df = tun_key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0;
251         skb->ignore_df = 1;
252
253         src_port = udp_flow_src_port(net, skb, 0, 0, true);
254         md.vni = htonl(be64_to_cpu(tun_key->tun_id) << 8);
255         md.gbp = vxlan_ext_gbp(skb);
256
257         err = vxlan_xmit_skb(vxlan_port->vs, rt, skb,
258                              saddr, tun_key->ipv4_dst,
259                              tun_key->ipv4_tos,
260                              tun_key->ipv4_ttl, df,
261                              src_port, dst_port,
262                              &md, false);
263         if (err < 0)
264                 ip_rt_put(rt);
265         return err;
266 error:
267         kfree_skb(skb);
268         return err;
269 }
270
271 static int vxlan_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
272                                      struct ovs_tunnel_info *egress_tun_info)
273 {
274         struct net *net = ovs_dp_get_net(vport->dp);
275         struct vxlan_port *vxlan_port = vxlan_vport(vport);
276         __be16 dst_port = inet_sport(vxlan_port->vs->sock->sk);
277         __be16 src_port;
278
279         src_port = udp_flow_src_port(net, skb, 0, 0, true);
280
281         return ovs_tunnel_get_egress_info(egress_tun_info, net,
282                                           OVS_CB(skb)->egress_tun_info,
283                                           IPPROTO_UDP, skb->mark,
284                                           src_port, dst_port);
285 }
286
287 static const char *vxlan_get_name(const struct vport *vport)
288 {
289         struct vxlan_port *vxlan_port = vxlan_vport(vport);
290         return vxlan_port->name;
291 }
292
293 const struct vport_ops ovs_vxlan_vport_ops = {
294         .type                   = OVS_VPORT_TYPE_VXLAN,
295         .create                 = vxlan_tnl_create,
296         .destroy                = vxlan_tnl_destroy,
297         .get_name               = vxlan_get_name,
298         .get_options            = vxlan_get_options,
299         .send                   = vxlan_tnl_send,
300         .get_egress_tun_info    = vxlan_get_egress_tun_info,
301 };