1c9422999SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2dcc38c03SThomas Graf /*
3dcc38c03SThomas Graf * Copyright (c) 2014 Nicira, Inc.
4dcc38c03SThomas Graf * Copyright (c) 2013 Cisco Systems, Inc.
5dcc38c03SThomas Graf */
6dcc38c03SThomas Graf
7dcc38c03SThomas Graf #include <linux/kernel.h>
8dcc38c03SThomas Graf #include <linux/skbuff.h>
9dcc38c03SThomas Graf #include <linux/openvswitch.h>
10dcc38c03SThomas Graf #include <linux/module.h>
11dcc38c03SThomas Graf #include <net/udp.h>
12dcc38c03SThomas Graf #include <net/ip_tunnels.h>
13dcc38c03SThomas Graf #include <net/rtnetlink.h>
14dcc38c03SThomas Graf #include <net/vxlan.h>
15dcc38c03SThomas Graf
16dcc38c03SThomas Graf #include "datapath.h"
17dcc38c03SThomas Graf #include "vport.h"
18dcc38c03SThomas Graf #include "vport-netdev.h"
19dcc38c03SThomas Graf
20dcc38c03SThomas Graf static struct vport_ops ovs_vxlan_netdev_vport_ops;
21dcc38c03SThomas Graf
vxlan_get_options(const struct vport * vport,struct sk_buff * skb)22dcc38c03SThomas Graf static int vxlan_get_options(const struct vport *vport, struct sk_buff *skb)
23dcc38c03SThomas Graf {
24dcc38c03SThomas Graf struct vxlan_dev *vxlan = netdev_priv(vport->dev);
25dcc38c03SThomas Graf __be16 dst_port = vxlan->cfg.dst_port;
26dcc38c03SThomas Graf
27dcc38c03SThomas Graf if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, ntohs(dst_port)))
28dcc38c03SThomas Graf return -EMSGSIZE;
29dcc38c03SThomas Graf
30dc5321d7SMatthias Schiffer if (vxlan->cfg.flags & VXLAN_F_GBP) {
31dcc38c03SThomas Graf struct nlattr *exts;
32dcc38c03SThomas Graf
33ae0be8deSMichal Kubecek exts = nla_nest_start_noflag(skb, OVS_TUNNEL_ATTR_EXTENSION);
34dcc38c03SThomas Graf if (!exts)
35dcc38c03SThomas Graf return -EMSGSIZE;
36dcc38c03SThomas Graf
37dc5321d7SMatthias Schiffer if (vxlan->cfg.flags & VXLAN_F_GBP &&
38dcc38c03SThomas Graf nla_put_flag(skb, OVS_VXLAN_EXT_GBP))
39dcc38c03SThomas Graf return -EMSGSIZE;
40dcc38c03SThomas Graf
41dcc38c03SThomas Graf nla_nest_end(skb, exts);
42dcc38c03SThomas Graf }
43dcc38c03SThomas Graf
44dcc38c03SThomas Graf return 0;
45dcc38c03SThomas Graf }
46dcc38c03SThomas Graf
47dcc38c03SThomas Graf static const struct nla_policy exts_policy[OVS_VXLAN_EXT_MAX + 1] = {
48dcc38c03SThomas Graf [OVS_VXLAN_EXT_GBP] = { .type = NLA_FLAG, },
49dcc38c03SThomas Graf };
50dcc38c03SThomas Graf
vxlan_configure_exts(struct vport * vport,struct nlattr * attr,struct vxlan_config * conf)51dcc38c03SThomas Graf static int vxlan_configure_exts(struct vport *vport, struct nlattr *attr,
52dcc38c03SThomas Graf struct vxlan_config *conf)
53dcc38c03SThomas Graf {
54dcc38c03SThomas Graf struct nlattr *exts[OVS_VXLAN_EXT_MAX + 1];
55dcc38c03SThomas Graf int err;
56dcc38c03SThomas Graf
57dcc38c03SThomas Graf if (nla_len(attr) < sizeof(struct nlattr))
58dcc38c03SThomas Graf return -EINVAL;
59dcc38c03SThomas Graf
608cb08174SJohannes Berg err = nla_parse_nested_deprecated(exts, OVS_VXLAN_EXT_MAX, attr,
618cb08174SJohannes Berg exts_policy, NULL);
62dcc38c03SThomas Graf if (err < 0)
63dcc38c03SThomas Graf return err;
64dcc38c03SThomas Graf
65dcc38c03SThomas Graf if (exts[OVS_VXLAN_EXT_GBP])
66dcc38c03SThomas Graf conf->flags |= VXLAN_F_GBP;
67dcc38c03SThomas Graf
68dcc38c03SThomas Graf return 0;
69dcc38c03SThomas Graf }
70dcc38c03SThomas Graf
vxlan_tnl_create(const struct vport_parms * parms)71dcc38c03SThomas Graf static struct vport *vxlan_tnl_create(const struct vport_parms *parms)
72dcc38c03SThomas Graf {
73dcc38c03SThomas Graf struct net *net = ovs_dp_get_net(parms->dp);
74dcc38c03SThomas Graf struct nlattr *options = parms->options;
75dcc38c03SThomas Graf struct net_device *dev;
76dcc38c03SThomas Graf struct vport *vport;
77dcc38c03SThomas Graf struct nlattr *a;
78dcc38c03SThomas Graf int err;
79dcc38c03SThomas Graf struct vxlan_config conf = {
80dcc38c03SThomas Graf .no_share = true,
81c868ee70SPaolo Abeni .flags = VXLAN_F_COLLECT_METADATA | VXLAN_F_UDP_ZERO_CSUM6_RX,
827e059158SDavid Wragg /* Don't restrict the packets that can be sent by MTU */
837e059158SDavid Wragg .mtu = IP_MAX_MTU,
84dcc38c03SThomas Graf };
85dcc38c03SThomas Graf
86dcc38c03SThomas Graf if (!options) {
87dcc38c03SThomas Graf err = -EINVAL;
88dcc38c03SThomas Graf goto error;
89dcc38c03SThomas Graf }
90dcc38c03SThomas Graf
91dcc38c03SThomas Graf a = nla_find_nested(options, OVS_TUNNEL_ATTR_DST_PORT);
92dcc38c03SThomas Graf if (a && nla_len(a) == sizeof(u16)) {
93dcc38c03SThomas Graf conf.dst_port = htons(nla_get_u16(a));
94dcc38c03SThomas Graf } else {
95dcc38c03SThomas Graf /* Require destination port from userspace. */
96dcc38c03SThomas Graf err = -EINVAL;
97dcc38c03SThomas Graf goto error;
98dcc38c03SThomas Graf }
99dcc38c03SThomas Graf
100dcc38c03SThomas Graf vport = ovs_vport_alloc(0, &ovs_vxlan_netdev_vport_ops, parms);
101dcc38c03SThomas Graf if (IS_ERR(vport))
102dcc38c03SThomas Graf return vport;
103dcc38c03SThomas Graf
104dcc38c03SThomas Graf a = nla_find_nested(options, OVS_TUNNEL_ATTR_EXTENSION);
105dcc38c03SThomas Graf if (a) {
106dcc38c03SThomas Graf err = vxlan_configure_exts(vport, a, &conf);
107dcc38c03SThomas Graf if (err) {
108dcc38c03SThomas Graf ovs_vport_free(vport);
109dcc38c03SThomas Graf goto error;
110dcc38c03SThomas Graf }
111dcc38c03SThomas Graf }
112dcc38c03SThomas Graf
113dcc38c03SThomas Graf rtnl_lock();
114dcc38c03SThomas Graf dev = vxlan_dev_create(net, parms->name, NET_NAME_USER, &conf);
115dcc38c03SThomas Graf if (IS_ERR(dev)) {
116dcc38c03SThomas Graf rtnl_unlock();
117dcc38c03SThomas Graf ovs_vport_free(vport);
118dcc38c03SThomas Graf return ERR_CAST(dev);
119dcc38c03SThomas Graf }
120dcc38c03SThomas Graf
121567c5e13SPetr Machata err = dev_change_flags(dev, dev->flags | IFF_UP, NULL);
1224b5b9ba5SMartynas Pumputis if (err < 0) {
123*f3a63cceSHangbin Liu rtnl_delete_link(dev, 0, NULL);
1244b5b9ba5SMartynas Pumputis rtnl_unlock();
1254b5b9ba5SMartynas Pumputis ovs_vport_free(vport);
1264b5b9ba5SMartynas Pumputis goto error;
1274b5b9ba5SMartynas Pumputis }
1284b5b9ba5SMartynas Pumputis
129dcc38c03SThomas Graf rtnl_unlock();
130dcc38c03SThomas Graf return vport;
131dcc38c03SThomas Graf error:
132dcc38c03SThomas Graf return ERR_PTR(err);
133dcc38c03SThomas Graf }
134dcc38c03SThomas Graf
vxlan_create(const struct vport_parms * parms)135dcc38c03SThomas Graf static struct vport *vxlan_create(const struct vport_parms *parms)
136dcc38c03SThomas Graf {
137dcc38c03SThomas Graf struct vport *vport;
138dcc38c03SThomas Graf
139dcc38c03SThomas Graf vport = vxlan_tnl_create(parms);
140dcc38c03SThomas Graf if (IS_ERR(vport))
141dcc38c03SThomas Graf return vport;
142dcc38c03SThomas Graf
143dcc38c03SThomas Graf return ovs_netdev_link(vport, parms->name);
144dcc38c03SThomas Graf }
145dcc38c03SThomas Graf
146dcc38c03SThomas Graf static struct vport_ops ovs_vxlan_netdev_vport_ops = {
147dcc38c03SThomas Graf .type = OVS_VPORT_TYPE_VXLAN,
148dcc38c03SThomas Graf .create = vxlan_create,
149a9020fdeSPravin B Shelar .destroy = ovs_netdev_tunnel_destroy,
150dcc38c03SThomas Graf .get_options = vxlan_get_options,
151aec15924SPravin B Shelar .send = dev_queue_xmit,
152dcc38c03SThomas Graf };
153dcc38c03SThomas Graf
ovs_vxlan_tnl_init(void)154dcc38c03SThomas Graf static int __init ovs_vxlan_tnl_init(void)
155dcc38c03SThomas Graf {
156dcc38c03SThomas Graf return ovs_vport_ops_register(&ovs_vxlan_netdev_vport_ops);
157dcc38c03SThomas Graf }
158dcc38c03SThomas Graf
ovs_vxlan_tnl_exit(void)159dcc38c03SThomas Graf static void __exit ovs_vxlan_tnl_exit(void)
160dcc38c03SThomas Graf {
161dcc38c03SThomas Graf ovs_vport_ops_unregister(&ovs_vxlan_netdev_vport_ops);
162dcc38c03SThomas Graf }
163dcc38c03SThomas Graf
164dcc38c03SThomas Graf module_init(ovs_vxlan_tnl_init);
165dcc38c03SThomas Graf module_exit(ovs_vxlan_tnl_exit);
166dcc38c03SThomas Graf
167dcc38c03SThomas Graf MODULE_DESCRIPTION("OVS: VXLAN switching port");
168dcc38c03SThomas Graf MODULE_LICENSE("GPL");
169dcc38c03SThomas Graf MODULE_ALIAS("vport-type-4");
170