]> git.proxmox.com Git - ovs.git/blame - lib/dpif-netlink-rtnl.c
dpif-netlink-rtnl: Support VXLAN creation
[ovs.git] / lib / dpif-netlink-rtnl.c
CommitLineData
c4e08753
EG
1/*
2 * Copyright (c) 2017 Red Hat, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <config.h>
18
19#include "dpif-netlink-rtnl.h"
20
21#include <net/if.h>
22#include <linux/ip.h>
23#include <linux/rtnetlink.h>
24
25#include "dpif-netlink.h"
26#include "netdev-vport.h"
27#include "netlink-socket.h"
28#include "openvswitch/vlog.h"
29
30VLOG_DEFINE_THIS_MODULE(dpif_netlink_rtnl);
31
825e45e0
EG
32/* On some older systems, these enums are not defined. */
33#ifndef IFLA_VXLAN_MAX
34#define IFLA_VXLAN_MAX 0
35#endif
36#if IFLA_VXLAN_MAX < 25
37#define IFLA_VXLAN_LEARNING 7
38#define IFLA_VXLAN_PORT 15
39#define IFLA_VXLAN_UDP_ZERO_CSUM6_RX 20
40#define IFLA_VXLAN_GBP 23
41#define IFLA_VXLAN_COLLECT_METADATA 25
42#endif
43
c4e08753
EG
44static const struct nl_policy rtlink_policy[] = {
45 [IFLA_LINKINFO] = { .type = NL_A_NESTED },
46};
47static const struct nl_policy linkinfo_policy[] = {
48 [IFLA_INFO_KIND] = { .type = NL_A_STRING },
49 [IFLA_INFO_DATA] = { .type = NL_A_NESTED },
50};
825e45e0
EG
51static const struct nl_policy vxlan_policy[] = {
52 [IFLA_VXLAN_COLLECT_METADATA] = { .type = NL_A_U8 },
53 [IFLA_VXLAN_LEARNING] = { .type = NL_A_U8 },
54 [IFLA_VXLAN_UDP_ZERO_CSUM6_RX] = { .type = NL_A_U8 },
55 [IFLA_VXLAN_PORT] = { .type = NL_A_U16 },
56};
c4e08753
EG
57
58static const char *
59vport_type_to_kind(enum ovs_vport_type type)
60{
61 switch (type) {
62 case OVS_VPORT_TYPE_VXLAN:
63 return "vxlan";
64 case OVS_VPORT_TYPE_GRE:
65 return "gretap";
66 case OVS_VPORT_TYPE_GENEVE:
67 return "geneve";
68 case OVS_VPORT_TYPE_NETDEV:
69 case OVS_VPORT_TYPE_INTERNAL:
70 case OVS_VPORT_TYPE_LISP:
71 case OVS_VPORT_TYPE_STT:
72 case OVS_VPORT_TYPE_UNSPEC:
73 case __OVS_VPORT_TYPE_MAX:
74 default:
75 break;
76 }
77
78 return NULL;
79}
80
81static int
82rtnl_transact(uint32_t type, uint32_t flags, const char *name,
83 struct ofpbuf **reply)
84{
85 struct ofpbuf request;
86 int err;
87
88 ofpbuf_init(&request, 0);
89 nl_msg_put_nlmsghdr(&request, 0, type, flags);
90 ofpbuf_put_zeros(&request, sizeof(struct ifinfomsg));
91 nl_msg_put_string(&request, IFLA_IFNAME, name);
92
93 err = nl_transact(NETLINK_ROUTE, &request, reply);
94 ofpbuf_uninit(&request);
95
96 return err;
97}
98
99static int
100dpif_netlink_rtnl_destroy(const char *name)
101{
102 return rtnl_transact(RTM_DELLINK, NLM_F_REQUEST | NLM_F_ACK, name, NULL);
103}
104
825e45e0 105static int
c4e08753
EG
106dpif_netlink_rtnl_getlink(const char *name, struct ofpbuf **reply)
107{
108 return rtnl_transact(RTM_GETLINK, NLM_F_REQUEST, name, reply);
109}
110
825e45e0 111static int
c4e08753
EG
112rtnl_policy_parse(const char *kind, struct ofpbuf *reply,
113 const struct nl_policy *policy,
114 struct nlattr *tnl_info[],
115 size_t policy_size)
116{
117 struct nlattr *linkinfo[ARRAY_SIZE(linkinfo_policy)];
118 struct nlattr *rtlink[ARRAY_SIZE(rtlink_policy)];
119 struct ifinfomsg *ifmsg;
120 int error = 0;
121
122 ifmsg = ofpbuf_at(reply, NLMSG_HDRLEN, sizeof *ifmsg);
123 if (!nl_policy_parse(reply, NLMSG_HDRLEN + sizeof *ifmsg,
124 rtlink_policy, rtlink, ARRAY_SIZE(rtlink_policy))
125 || !nl_parse_nested(rtlink[IFLA_LINKINFO], linkinfo_policy,
126 linkinfo, ARRAY_SIZE(linkinfo_policy))
127 || strcmp(nl_attr_get_string(linkinfo[IFLA_INFO_KIND]), kind)
128 || !nl_parse_nested(linkinfo[IFLA_INFO_DATA], policy,
129 tnl_info, policy_size)) {
130 error = EINVAL;
131 }
132
133 return error;
134}
135
136static int
825e45e0
EG
137dpif_netlink_rtnl_vxlan_verify(const struct netdev_tunnel_config *tnl_cfg,
138 const char *name, const char *kind)
139{
140 struct ofpbuf *reply;
141 int err;
142
143 err = dpif_netlink_rtnl_getlink(name, &reply);
144
145 if (!err) {
146 struct nlattr *vxlan[ARRAY_SIZE(vxlan_policy)];
147
148 err = rtnl_policy_parse(kind, reply, vxlan_policy, vxlan,
149 ARRAY_SIZE(vxlan_policy));
150 if (!err) {
151 if (0 != nl_attr_get_u8(vxlan[IFLA_VXLAN_LEARNING])
152 || 1 != nl_attr_get_u8(vxlan[IFLA_VXLAN_COLLECT_METADATA])
153 || 1 != nl_attr_get_u8(vxlan[IFLA_VXLAN_UDP_ZERO_CSUM6_RX])
154 || (tnl_cfg->dst_port
155 != nl_attr_get_be16(vxlan[IFLA_VXLAN_PORT]))) {
156 err = EINVAL;
157 }
158 }
159 if (!err) {
160 if (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GBP)
161 && !nl_attr_get_flag(vxlan[IFLA_VXLAN_GBP])) {
162 err = EINVAL;
163 }
164 }
165 ofpbuf_delete(reply);
166 }
167
168 return err;
169}
170
171
172static int
173dpif_netlink_rtnl_verify(const struct netdev_tunnel_config *tnl_cfg,
174 enum ovs_vport_type type, const char *name)
c4e08753
EG
175{
176 const char *kind;
177
178 kind = vport_type_to_kind(type);
179 if (!kind) {
180 return EOPNOTSUPP;
181 }
182
183 switch (type) {
184 case OVS_VPORT_TYPE_VXLAN:
825e45e0 185 return dpif_netlink_rtnl_vxlan_verify(tnl_cfg, name, kind);
c4e08753
EG
186 case OVS_VPORT_TYPE_GRE:
187 case OVS_VPORT_TYPE_GENEVE:
188 case OVS_VPORT_TYPE_NETDEV:
189 case OVS_VPORT_TYPE_INTERNAL:
190 case OVS_VPORT_TYPE_LISP:
191 case OVS_VPORT_TYPE_STT:
192 case OVS_VPORT_TYPE_UNSPEC:
193 case __OVS_VPORT_TYPE_MAX:
194 default:
195 return EOPNOTSUPP;
196 }
197
198 return 0;
199}
200
825e45e0
EG
201static int
202dpif_netlink_rtnl_create(const struct netdev_tunnel_config *tnl_cfg,
c4e08753
EG
203 const char *name, enum ovs_vport_type type,
204 const char *kind, uint32_t flags)
205{
206 size_t linkinfo_off, infodata_off;
207 struct ifinfomsg *ifinfo;
208 struct ofpbuf request;
209 int err;
210
211 ofpbuf_init(&request, 0);
212 nl_msg_put_nlmsghdr(&request, 0, RTM_NEWLINK, flags);
213 ifinfo = ofpbuf_put_zeros(&request, sizeof(struct ifinfomsg));
214 ifinfo->ifi_change = ifinfo->ifi_flags = IFF_UP;
215 nl_msg_put_string(&request, IFLA_IFNAME, name);
216 nl_msg_put_u32(&request, IFLA_MTU, UINT16_MAX);
217 linkinfo_off = nl_msg_start_nested(&request, IFLA_LINKINFO);
218 nl_msg_put_string(&request, IFLA_INFO_KIND, kind);
219 infodata_off = nl_msg_start_nested(&request, IFLA_INFO_DATA);
220
221 /* tunnel unique info */
222 switch (type) {
223 case OVS_VPORT_TYPE_VXLAN:
825e45e0
EG
224 nl_msg_put_u8(&request, IFLA_VXLAN_LEARNING, 0);
225 nl_msg_put_u8(&request, IFLA_VXLAN_COLLECT_METADATA, 1);
226 nl_msg_put_u8(&request, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, 1);
227 if (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GBP)) {
228 nl_msg_put_flag(&request, IFLA_VXLAN_GBP);
229 }
230 nl_msg_put_be16(&request, IFLA_VXLAN_PORT, tnl_cfg->dst_port);
231 break;
c4e08753
EG
232 case OVS_VPORT_TYPE_GRE:
233 case OVS_VPORT_TYPE_GENEVE:
234 case OVS_VPORT_TYPE_NETDEV:
235 case OVS_VPORT_TYPE_INTERNAL:
236 case OVS_VPORT_TYPE_LISP:
237 case OVS_VPORT_TYPE_STT:
238 case OVS_VPORT_TYPE_UNSPEC:
239 case __OVS_VPORT_TYPE_MAX:
240 default:
241 err = EOPNOTSUPP;
242 goto exit;
243 }
244
245 nl_msg_end_nested(&request, infodata_off);
246 nl_msg_end_nested(&request, linkinfo_off);
247
248 err = nl_transact(NETLINK_ROUTE, &request, NULL);
249
250exit:
251 ofpbuf_uninit(&request);
252
253 return err;
254}
255
256int
257dpif_netlink_rtnl_port_create(struct netdev *netdev)
258{
259 const struct netdev_tunnel_config *tnl_cfg;
260 char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
261 enum ovs_vport_type type;
262 const char *name;
263 const char *kind;
264 uint32_t flags;
265 int err;
266
267 type = netdev_to_ovs_vport_type(netdev_get_type(netdev));
268 kind = vport_type_to_kind(type);
269 if (!kind) {
270 return EOPNOTSUPP;
271 }
272
273 tnl_cfg = netdev_get_tunnel_config(netdev);
274 if (!tnl_cfg) {
275 return EINVAL;
276 }
277
278 name = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf);
279 flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL;
280
281 err = dpif_netlink_rtnl_create(tnl_cfg, name, type, kind, flags);
282
283 /* If the device exists, validate and/or attempt to recreate it. */
284 if (err == EEXIST) {
285 err = dpif_netlink_rtnl_verify(tnl_cfg, type, name);
286 if (!err) {
287 return 0;
288 } else {
289 err = dpif_netlink_rtnl_destroy(name);
290 if (err) {
291 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
292
293 VLOG_WARN_RL(&rl, "RTNL device %s exists and cannot be "
294 "deleted: %s", name, ovs_strerror(err));
295 return err;
296 }
297 err = dpif_netlink_rtnl_create(tnl_cfg, name, type, kind, flags);
298 }
299 }
300 if (err) {
301 return err;
302 }
303
304 err = dpif_netlink_rtnl_verify(tnl_cfg, type, name);
305 if (err) {
306 int err2 = dpif_netlink_rtnl_destroy(name);
307
308 if (err2) {
309 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
310
311 VLOG_WARN_RL(&rl, "Failed to delete device %s during rtnl port "
312 "creation: %s", name, ovs_strerror(err2));
313 }
314 }
315
316 return err;
317}
318
319int
825e45e0 320dpif_netlink_rtnl_port_destroy(const char *name, const char *type)
c4e08753
EG
321{
322 switch (netdev_to_ovs_vport_type(type)) {
323 case OVS_VPORT_TYPE_VXLAN:
825e45e0 324 return dpif_netlink_rtnl_destroy(name);
c4e08753
EG
325 case OVS_VPORT_TYPE_GRE:
326 case OVS_VPORT_TYPE_GENEVE:
327 case OVS_VPORT_TYPE_NETDEV:
328 case OVS_VPORT_TYPE_INTERNAL:
329 case OVS_VPORT_TYPE_LISP:
330 case OVS_VPORT_TYPE_STT:
331 case OVS_VPORT_TYPE_UNSPEC:
332 case __OVS_VPORT_TYPE_MAX:
333 default:
334 return EOPNOTSUPP;
335 }
336 return 0;
337}