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