]>
Commit | Line | Data |
---|---|---|
777ece09 JG |
1 | /* |
2 | * Copyright (c) 2010 Nicira Networks. | |
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 | * | |
6fcfff1b | 10 | * Unless required by applicable law or agreed to in writing, software |
777ece09 JG |
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> | |
2b9d6589 BP |
18 | |
19 | #include "netdev-vport.h" | |
20 | ||
777ece09 JG |
21 | #include <errno.h> |
22 | #include <fcntl.h> | |
2b9d6589 | 23 | #include <net/if.h> |
777ece09 JG |
24 | #include <sys/ioctl.h> |
25 | ||
b9298d3f | 26 | #include "byte-order.h" |
777ece09 | 27 | #include "list.h" |
2b9d6589 | 28 | #include "netdev-provider.h" |
777ece09 | 29 | #include "openvswitch/datapath-protocol.h" |
2b9d6589 BP |
30 | #include "openvswitch/tunnel.h" |
31 | #include "packets.h" | |
777ece09 JG |
32 | #include "shash.h" |
33 | #include "socket-util.h" | |
777ece09 JG |
34 | #include "vlog.h" |
35 | ||
d98e6007 | 36 | VLOG_DEFINE_THIS_MODULE(netdev_vport); |
5136ce49 | 37 | |
777ece09 JG |
38 | struct netdev_vport_notifier { |
39 | struct netdev_notifier notifier; | |
40 | struct list list_node; | |
d295e8e9 | 41 | struct shash_node *shash_node; |
777ece09 JG |
42 | }; |
43 | ||
2b9d6589 BP |
44 | struct netdev_dev_vport { |
45 | struct netdev_dev netdev_dev; | |
c3827f61 | 46 | uint64_t config[VPORT_CONFIG_SIZE / 8]; |
2b9d6589 BP |
47 | }; |
48 | ||
49 | struct netdev_vport { | |
50 | struct netdev netdev; | |
51 | }; | |
52 | ||
2b9d6589 | 53 | struct vport_class { |
c3827f61 BP |
54 | struct netdev_class netdev_class; |
55 | int (*parse_config)(const struct netdev_dev *, const struct shash *args, | |
56 | void *config); | |
2b9d6589 BP |
57 | }; |
58 | ||
777ece09 JG |
59 | static struct shash netdev_vport_notifiers = |
60 | SHASH_INITIALIZER(&netdev_vport_notifiers); | |
61 | ||
62 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); | |
63 | ||
2b9d6589 BP |
64 | static int netdev_vport_do_ioctl(int cmd, void *arg); |
65 | static int netdev_vport_create(const struct netdev_class *, const char *, | |
66 | const struct shash *, struct netdev_dev **); | |
67 | static void netdev_vport_poll_notify(const struct netdev *); | |
68 | ||
69 | static bool | |
70 | is_vport_class(const struct netdev_class *class) | |
777ece09 | 71 | { |
2b9d6589 BP |
72 | return class->create == netdev_vport_create; |
73 | } | |
777ece09 | 74 | |
2b9d6589 BP |
75 | static const struct vport_class * |
76 | vport_class_cast(const struct netdev_class *class) | |
77 | { | |
78 | assert(is_vport_class(class)); | |
79 | return CONTAINER_OF(class, struct vport_class, netdev_class); | |
80 | } | |
81 | ||
82 | static struct netdev_dev_vport * | |
83 | netdev_dev_vport_cast(const struct netdev_dev *netdev_dev) | |
84 | { | |
85 | assert(is_vport_class(netdev_dev_get_class(netdev_dev))); | |
86 | return CONTAINER_OF(netdev_dev, struct netdev_dev_vport, netdev_dev); | |
87 | } | |
88 | ||
89 | static struct netdev_vport * | |
90 | netdev_vport_cast(const struct netdev *netdev) | |
91 | { | |
92 | struct netdev_dev *netdev_dev = netdev_get_dev(netdev); | |
93 | assert(is_vport_class(netdev_dev_get_class(netdev_dev))); | |
94 | return CONTAINER_OF(netdev, struct netdev_vport, netdev); | |
95 | } | |
96 | ||
c3827f61 BP |
97 | /* If 'netdev' is a vport netdev, copies its kernel configuration into |
98 | * 'config'. Otherwise leaves 'config' untouched. */ | |
99 | void | |
100 | netdev_vport_get_config(const struct netdev *netdev, void *config) | |
2b9d6589 | 101 | { |
c3827f61 BP |
102 | const struct netdev_dev *dev = netdev_get_dev(netdev); |
103 | ||
104 | if (is_vport_class(netdev_dev_get_class(dev))) { | |
105 | const struct netdev_dev_vport *vport = netdev_dev_vport_cast(dev); | |
106 | memcpy(config, vport->config, VPORT_CONFIG_SIZE); | |
777ece09 | 107 | } |
2b9d6589 | 108 | } |
777ece09 | 109 | |
2b9d6589 | 110 | static int |
c3827f61 BP |
111 | netdev_vport_create(const struct netdev_class *netdev_class, const char *name, |
112 | const struct shash *args, | |
113 | struct netdev_dev **netdev_devp) | |
2b9d6589 | 114 | { |
c3827f61 BP |
115 | const struct vport_class *vport_class = vport_class_cast(netdev_class); |
116 | struct netdev_dev_vport *dev; | |
117 | int error; | |
2b9d6589 | 118 | |
c3827f61 BP |
119 | dev = xmalloc(sizeof *dev); |
120 | *netdev_devp = &dev->netdev_dev; | |
121 | netdev_dev_init(&dev->netdev_dev, name, netdev_class); | |
2b9d6589 | 122 | |
c3827f61 BP |
123 | memset(dev->config, 0, sizeof dev->config); |
124 | error = vport_class->parse_config(&dev->netdev_dev, args, dev->config); | |
2b9d6589 | 125 | |
c3827f61 BP |
126 | if (error) { |
127 | netdev_dev_uninit(&dev->netdev_dev, true); | |
2b9d6589 | 128 | } |
c3827f61 | 129 | return error; |
777ece09 JG |
130 | } |
131 | ||
2b9d6589 BP |
132 | static void |
133 | netdev_vport_destroy(struct netdev_dev *netdev_dev_) | |
134 | { | |
135 | struct netdev_dev_vport *netdev_dev = netdev_dev_vport_cast(netdev_dev_); | |
136 | ||
137 | free(netdev_dev); | |
138 | } | |
139 | ||
140 | static int | |
141 | netdev_vport_open(struct netdev_dev *netdev_dev_, int ethertype OVS_UNUSED, | |
142 | struct netdev **netdevp) | |
143 | { | |
144 | struct netdev_vport *netdev; | |
145 | ||
146 | netdev = xmalloc(sizeof *netdev); | |
147 | netdev_init(&netdev->netdev, netdev_dev_); | |
148 | ||
149 | *netdevp = &netdev->netdev; | |
150 | return 0; | |
151 | } | |
152 | ||
153 | static void | |
154 | netdev_vport_close(struct netdev *netdev_) | |
155 | { | |
156 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); | |
157 | free(netdev); | |
158 | } | |
159 | ||
160 | static int | |
c3827f61 | 161 | netdev_vport_reconfigure(struct netdev_dev *dev_, |
2b9d6589 BP |
162 | const struct shash *args) |
163 | { | |
c3827f61 BP |
164 | const struct netdev_class *netdev_class = netdev_dev_get_class(dev_); |
165 | const struct vport_class *vport_class = vport_class_cast(netdev_class); | |
166 | struct netdev_dev_vport *dev = netdev_dev_vport_cast(dev_); | |
167 | struct odp_port port; | |
168 | int error; | |
169 | ||
170 | memset(&port, 0, sizeof port); | |
171 | strncpy(port.devname, netdev_dev_get_name(dev_), sizeof port.devname); | |
172 | strncpy(port.type, netdev_dev_get_type(dev_), sizeof port.type); | |
173 | error = vport_class->parse_config(dev_, args, port.config); | |
174 | if (!error && memcmp(port.config, dev->config, sizeof dev->config)) { | |
175 | error = netdev_vport_do_ioctl(ODP_VPORT_MOD, &port); | |
176 | if (!error || error == ENODEV) { | |
177 | /* Either reconfiguration succeeded or this vport is not installed | |
178 | * in the kernel (e.g. it hasn't been added to a dpif yet with | |
179 | * dpif_port_add()). */ | |
180 | memcpy(dev->config, port.config, sizeof dev->config); | |
181 | } | |
2b9d6589 | 182 | } |
c3827f61 | 183 | return error; |
2b9d6589 BP |
184 | } |
185 | ||
186 | static int | |
777ece09 JG |
187 | netdev_vport_set_etheraddr(struct netdev *netdev, |
188 | const uint8_t mac[ETH_ADDR_LEN]) | |
189 | { | |
190 | struct odp_vport_ether vport_ether; | |
191 | int err; | |
192 | ||
193 | ovs_strlcpy(vport_ether.devname, netdev_get_name(netdev), | |
194 | sizeof vport_ether.devname); | |
195 | ||
196 | memcpy(vport_ether.ether_addr, mac, ETH_ADDR_LEN); | |
197 | ||
198 | err = netdev_vport_do_ioctl(ODP_VPORT_ETHER_SET, &vport_ether); | |
199 | if (err) { | |
200 | return err; | |
201 | } | |
202 | ||
203 | netdev_vport_poll_notify(netdev); | |
204 | return 0; | |
205 | } | |
206 | ||
2b9d6589 | 207 | static int |
777ece09 JG |
208 | netdev_vport_get_etheraddr(const struct netdev *netdev, |
209 | uint8_t mac[ETH_ADDR_LEN]) | |
210 | { | |
211 | struct odp_vport_ether vport_ether; | |
212 | int err; | |
213 | ||
214 | ovs_strlcpy(vport_ether.devname, netdev_get_name(netdev), | |
215 | sizeof vport_ether.devname); | |
216 | ||
217 | err = netdev_vport_do_ioctl(ODP_VPORT_ETHER_GET, &vport_ether); | |
218 | if (err) { | |
219 | return err; | |
220 | } | |
221 | ||
222 | memcpy(mac, vport_ether.ether_addr, ETH_ADDR_LEN); | |
223 | return 0; | |
224 | } | |
225 | ||
2b9d6589 | 226 | static int |
777ece09 JG |
227 | netdev_vport_get_mtu(const struct netdev *netdev, int *mtup) |
228 | { | |
229 | struct odp_vport_mtu vport_mtu; | |
230 | int err; | |
231 | ||
232 | ovs_strlcpy(vport_mtu.devname, netdev_get_name(netdev), | |
233 | sizeof vport_mtu.devname); | |
234 | ||
235 | err = netdev_vport_do_ioctl(ODP_VPORT_MTU_GET, &vport_mtu); | |
236 | if (err) { | |
237 | return err; | |
238 | } | |
239 | ||
240 | *mtup = vport_mtu.mtu; | |
241 | return 0; | |
242 | } | |
243 | ||
777ece09 JG |
244 | int |
245 | netdev_vport_get_stats(const struct netdev *netdev, struct netdev_stats *stats) | |
246 | { | |
247 | const char *name = netdev_get_name(netdev); | |
248 | struct odp_vport_stats_req ovsr; | |
249 | int err; | |
250 | ||
251 | ovs_strlcpy(ovsr.devname, name, sizeof ovsr.devname); | |
252 | err = netdev_vport_do_ioctl(ODP_VPORT_STATS_GET, &ovsr); | |
253 | if (err) { | |
254 | return err; | |
255 | } | |
256 | ||
257 | stats->rx_packets = ovsr.stats.rx_packets; | |
258 | stats->tx_packets = ovsr.stats.tx_packets; | |
259 | stats->rx_bytes = ovsr.stats.rx_bytes; | |
260 | stats->tx_bytes = ovsr.stats.tx_bytes; | |
261 | stats->rx_errors = ovsr.stats.rx_errors; | |
262 | stats->tx_errors = ovsr.stats.tx_errors; | |
263 | stats->rx_dropped = ovsr.stats.rx_dropped; | |
264 | stats->tx_dropped = ovsr.stats.tx_dropped; | |
ec61a01c | 265 | stats->multicast = ovsr.stats.multicast; |
777ece09 | 266 | stats->collisions = ovsr.stats.collisions; |
ec61a01c BP |
267 | stats->rx_length_errors = ovsr.stats.rx_length_errors; |
268 | stats->rx_over_errors = ovsr.stats.rx_over_errors; | |
269 | stats->rx_crc_errors = ovsr.stats.rx_crc_errors; | |
270 | stats->rx_frame_errors = ovsr.stats.rx_frame_errors; | |
271 | stats->rx_fifo_errors = ovsr.stats.rx_fifo_errors; | |
272 | stats->rx_missed_errors = ovsr.stats.rx_missed_errors; | |
273 | stats->tx_aborted_errors = ovsr.stats.tx_aborted_errors; | |
274 | stats->tx_carrier_errors = ovsr.stats.tx_carrier_errors; | |
275 | stats->tx_fifo_errors = ovsr.stats.tx_fifo_errors; | |
276 | stats->tx_heartbeat_errors = ovsr.stats.tx_heartbeat_errors; | |
277 | stats->tx_window_errors = ovsr.stats.tx_window_errors; | |
777ece09 JG |
278 | |
279 | return 0; | |
280 | } | |
281 | ||
f4b6076a JG |
282 | int |
283 | netdev_vport_set_stats(struct netdev *netdev, const struct netdev_stats *stats) | |
284 | { | |
285 | struct odp_vport_stats_req ovsr; | |
286 | int err; | |
287 | ||
288 | ovs_strlcpy(ovsr.devname, netdev_get_name(netdev), sizeof ovsr.devname); | |
289 | ||
290 | ovsr.stats.rx_packets = stats->rx_packets; | |
291 | ovsr.stats.tx_packets = stats->tx_packets; | |
292 | ovsr.stats.rx_bytes = stats->rx_bytes; | |
293 | ovsr.stats.tx_bytes = stats->tx_bytes; | |
294 | ovsr.stats.rx_errors = stats->rx_errors; | |
295 | ovsr.stats.tx_errors = stats->tx_errors; | |
296 | ovsr.stats.rx_dropped = stats->rx_dropped; | |
297 | ovsr.stats.tx_dropped = stats->tx_dropped; | |
ec61a01c | 298 | ovsr.stats.multicast = stats->multicast; |
f4b6076a | 299 | ovsr.stats.collisions = stats->collisions; |
ec61a01c BP |
300 | ovsr.stats.rx_length_errors = stats->rx_length_errors; |
301 | ovsr.stats.rx_over_errors = stats->rx_over_errors; | |
302 | ovsr.stats.rx_crc_errors = stats->rx_crc_errors; | |
303 | ovsr.stats.rx_frame_errors = stats->rx_frame_errors; | |
304 | ovsr.stats.rx_fifo_errors = stats->rx_fifo_errors; | |
305 | ovsr.stats.rx_missed_errors = stats->rx_missed_errors; | |
306 | ovsr.stats.tx_aborted_errors = stats->tx_aborted_errors; | |
307 | ovsr.stats.tx_carrier_errors = stats->tx_carrier_errors; | |
308 | ovsr.stats.tx_fifo_errors = stats->tx_fifo_errors; | |
309 | ovsr.stats.tx_heartbeat_errors = stats->tx_heartbeat_errors; | |
310 | ovsr.stats.tx_window_errors = stats->tx_window_errors; | |
f4b6076a JG |
311 | |
312 | err = netdev_vport_do_ioctl(ODP_VPORT_STATS_SET, &ovsr); | |
313 | ||
314 | /* If the vport layer doesn't know about the device, that doesn't mean it | |
315 | * doesn't exist (after all were able to open it when netdev_open() was | |
316 | * called), it just means that it isn't attached and we'll be getting | |
317 | * stats a different way. */ | |
318 | if (err == ENODEV) { | |
319 | err = EOPNOTSUPP; | |
320 | } | |
321 | ||
322 | return err; | |
323 | } | |
324 | ||
2b9d6589 | 325 | static int |
777ece09 JG |
326 | netdev_vport_update_flags(struct netdev *netdev OVS_UNUSED, |
327 | enum netdev_flags off, enum netdev_flags on OVS_UNUSED, | |
328 | enum netdev_flags *old_flagsp) | |
329 | { | |
330 | if (off & (NETDEV_UP | NETDEV_PROMISC)) { | |
331 | return EOPNOTSUPP; | |
332 | } | |
333 | ||
334 | *old_flagsp = NETDEV_UP | NETDEV_PROMISC; | |
335 | return 0; | |
336 | } | |
337 | ||
338 | static char * | |
339 | make_poll_name(const struct netdev *netdev) | |
340 | { | |
341 | return xasprintf("%s:%s", netdev_get_type(netdev), netdev_get_name(netdev)); | |
342 | } | |
343 | ||
2b9d6589 | 344 | static int |
777ece09 JG |
345 | netdev_vport_poll_add(struct netdev *netdev, |
346 | void (*cb)(struct netdev_notifier *), void *aux, | |
347 | struct netdev_notifier **notifierp) | |
348 | { | |
349 | char *poll_name = make_poll_name(netdev); | |
350 | struct netdev_vport_notifier *notifier; | |
351 | struct list *list; | |
352 | struct shash_node *shash_node; | |
353 | ||
354 | shash_node = shash_find_data(&netdev_vport_notifiers, poll_name); | |
355 | if (!shash_node) { | |
356 | list = xmalloc(sizeof *list); | |
357 | list_init(list); | |
eb5f3e93 | 358 | shash_node = shash_add(&netdev_vport_notifiers, poll_name, list); |
777ece09 JG |
359 | } else { |
360 | list = shash_node->data; | |
361 | } | |
362 | ||
363 | notifier = xmalloc(sizeof *notifier); | |
364 | netdev_notifier_init(¬ifier->notifier, netdev, cb, aux); | |
365 | list_push_back(list, ¬ifier->list_node); | |
366 | notifier->shash_node = shash_node; | |
367 | ||
368 | *notifierp = ¬ifier->notifier; | |
369 | free(poll_name); | |
370 | ||
371 | return 0; | |
372 | } | |
373 | ||
2b9d6589 | 374 | static void |
777ece09 JG |
375 | netdev_vport_poll_remove(struct netdev_notifier *notifier_) |
376 | { | |
377 | struct netdev_vport_notifier *notifier = | |
378 | CONTAINER_OF(notifier_, struct netdev_vport_notifier, notifier); | |
379 | ||
380 | struct list *list; | |
381 | ||
382 | list = list_remove(¬ifier->list_node); | |
383 | if (list_is_empty(list)) { | |
384 | shash_delete(&netdev_vport_notifiers, notifier->shash_node); | |
385 | free(list); | |
386 | } | |
387 | ||
388 | free(notifier); | |
389 | } | |
2b9d6589 BP |
390 | \f |
391 | /* Helper functions. */ | |
777ece09 | 392 | |
2b9d6589 BP |
393 | static int |
394 | netdev_vport_do_ioctl(int cmd, void *arg) | |
395 | { | |
396 | static int ioctl_fd = -1; | |
397 | ||
398 | if (ioctl_fd < 0) { | |
399 | ioctl_fd = open("/dev/net/dp0", O_RDONLY | O_NONBLOCK); | |
400 | if (ioctl_fd < 0) { | |
401 | VLOG_ERR_RL(&rl, "failed to open ioctl fd: %s", strerror(errno)); | |
402 | return errno; | |
403 | } | |
404 | } | |
405 | ||
406 | return ioctl(ioctl_fd, cmd, arg) ? errno : 0; | |
407 | } | |
408 | ||
409 | static void | |
777ece09 JG |
410 | netdev_vport_poll_notify(const struct netdev *netdev) |
411 | { | |
412 | char *poll_name = make_poll_name(netdev); | |
413 | struct list *list = shash_find_data(&netdev_vport_notifiers, | |
414 | poll_name); | |
415 | ||
416 | if (list) { | |
417 | struct netdev_vport_notifier *notifier; | |
418 | ||
4e8e4213 | 419 | LIST_FOR_EACH (notifier, list_node, list) { |
777ece09 JG |
420 | struct netdev_notifier *n = ¬ifier->notifier; |
421 | n->cb(n); | |
422 | } | |
423 | } | |
424 | ||
425 | free(poll_name); | |
426 | } | |
2b9d6589 BP |
427 | \f |
428 | /* Code specific to individual vport types. */ | |
429 | ||
430 | static int | |
c3827f61 BP |
431 | parse_tunnel_config(const struct netdev_dev *dev, const struct shash *args, |
432 | void *configp) | |
2b9d6589 | 433 | { |
c3827f61 BP |
434 | const char *name = netdev_dev_get_name(dev); |
435 | const char *type = netdev_dev_get_type(dev); | |
436 | bool is_gre = !strcmp(type, "gre"); | |
437 | struct tnl_port_config config; | |
2b9d6589 | 438 | struct shash_node *node; |
2b9d6589 BP |
439 | bool ipsec_mech_set = false; |
440 | ||
73d67a43 | 441 | memset(&config, 0, sizeof config); |
c3827f61 BP |
442 | config.flags |= TNL_F_PMTUD; |
443 | config.flags |= TNL_F_HDR_CACHE; | |
2b9d6589 BP |
444 | |
445 | SHASH_FOR_EACH (node, args) { | |
446 | if (!strcmp(node->name, "remote_ip")) { | |
447 | struct in_addr in_addr; | |
448 | if (lookup_ip(node->data, &in_addr)) { | |
c3827f61 | 449 | VLOG_WARN("%s: bad %s 'remote_ip'", name, type); |
2b9d6589 | 450 | } else { |
c3827f61 | 451 | config.daddr = in_addr.s_addr; |
2b9d6589 BP |
452 | } |
453 | } else if (!strcmp(node->name, "local_ip")) { | |
454 | struct in_addr in_addr; | |
455 | if (lookup_ip(node->data, &in_addr)) { | |
c3827f61 | 456 | VLOG_WARN("%s: bad %s 'local_ip'", name, type); |
2b9d6589 | 457 | } else { |
c3827f61 | 458 | config.saddr = in_addr.s_addr; |
2b9d6589 BP |
459 | } |
460 | } else if (!strcmp(node->name, "key") && is_gre) { | |
461 | if (!strcmp(node->data, "flow")) { | |
c3827f61 BP |
462 | config.flags |= TNL_F_IN_KEY_MATCH; |
463 | config.flags |= TNL_F_OUT_KEY_ACTION; | |
2b9d6589 | 464 | } else { |
b9298d3f BP |
465 | uint64_t key = strtoull(node->data, NULL, 0); |
466 | config.out_key = config.in_key = htonll(key); | |
2b9d6589 BP |
467 | } |
468 | } else if (!strcmp(node->name, "in_key") && is_gre) { | |
469 | if (!strcmp(node->data, "flow")) { | |
c3827f61 | 470 | config.flags |= TNL_F_IN_KEY_MATCH; |
2b9d6589 | 471 | } else { |
b9298d3f | 472 | config.in_key = htonll(strtoull(node->data, NULL, 0)); |
2b9d6589 BP |
473 | } |
474 | } else if (!strcmp(node->name, "out_key") && is_gre) { | |
475 | if (!strcmp(node->data, "flow")) { | |
c3827f61 | 476 | config.flags |= TNL_F_OUT_KEY_ACTION; |
2b9d6589 | 477 | } else { |
b9298d3f | 478 | config.out_key = htonll(strtoull(node->data, NULL, 0)); |
2b9d6589 BP |
479 | } |
480 | } else if (!strcmp(node->name, "tos")) { | |
481 | if (!strcmp(node->data, "inherit")) { | |
c3827f61 | 482 | config.flags |= TNL_F_TOS_INHERIT; |
2b9d6589 | 483 | } else { |
c3827f61 | 484 | config.tos = atoi(node->data); |
2b9d6589 BP |
485 | } |
486 | } else if (!strcmp(node->name, "ttl")) { | |
487 | if (!strcmp(node->data, "inherit")) { | |
c3827f61 | 488 | config.flags |= TNL_F_TTL_INHERIT; |
2b9d6589 | 489 | } else { |
c3827f61 | 490 | config.ttl = atoi(node->data); |
2b9d6589 BP |
491 | } |
492 | } else if (!strcmp(node->name, "csum") && is_gre) { | |
493 | if (!strcmp(node->data, "true")) { | |
c3827f61 | 494 | config.flags |= TNL_F_CSUM; |
2b9d6589 BP |
495 | } |
496 | } else if (!strcmp(node->name, "pmtud")) { | |
497 | if (!strcmp(node->data, "false")) { | |
c3827f61 | 498 | config.flags &= ~TNL_F_PMTUD; |
2b9d6589 BP |
499 | } |
500 | } else if (!strcmp(node->name, "header_cache")) { | |
501 | if (!strcmp(node->data, "false")) { | |
c3827f61 | 502 | config.flags &= ~TNL_F_HDR_CACHE; |
2b9d6589 | 503 | } |
2b9d6589 BP |
504 | } else if (!strcmp(node->name, "ipsec_cert") |
505 | || !strcmp(node->name, "ipsec_psk")) { | |
506 | ipsec_mech_set = true; | |
507 | } else { | |
508 | VLOG_WARN("%s: unknown %s argument '%s'", | |
c3827f61 | 509 | name, type, node->name); |
2b9d6589 BP |
510 | } |
511 | } | |
512 | ||
513 | /* IPsec doesn't work when header caching is enabled. Disable it if the | |
514 | * IPsec local IP address and authentication mechanism have been defined. */ | |
4c2fa71d | 515 | if (ipsec_mech_set) { |
2b9d6589 | 516 | VLOG_INFO("%s: header caching disabled due to use of IPsec", name); |
c3827f61 | 517 | config.flags &= ~TNL_F_HDR_CACHE; |
2b9d6589 BP |
518 | } |
519 | ||
c3827f61 | 520 | if (!config.daddr) { |
2b9d6589 | 521 | VLOG_WARN("%s: %s type requires valid 'remote_ip' argument", |
c3827f61 | 522 | name, type); |
2b9d6589 BP |
523 | return EINVAL; |
524 | } | |
525 | ||
c3827f61 BP |
526 | BUILD_ASSERT(sizeof config <= VPORT_CONFIG_SIZE); |
527 | memcpy(configp, &config, sizeof config); | |
2b9d6589 BP |
528 | return 0; |
529 | } | |
530 | ||
531 | static int | |
c3827f61 BP |
532 | parse_patch_config(const struct netdev_dev *dev, const struct shash *args, |
533 | void *configp) | |
2b9d6589 | 534 | { |
c3827f61 | 535 | const char *name = netdev_dev_get_name(dev); |
2b9d6589 BP |
536 | const char *peer; |
537 | ||
538 | peer = shash_find_data(args, "peer"); | |
539 | if (!peer) { | |
540 | VLOG_WARN("%s: patch type requires valid 'peer' argument", name); | |
541 | return EINVAL; | |
542 | } | |
543 | ||
544 | if (shash_count(args) > 1) { | |
545 | VLOG_WARN("%s: patch type takes only a 'peer' argument", name); | |
546 | return EINVAL; | |
547 | } | |
548 | ||
c3827f61 | 549 | if (strlen(peer) >= MIN(IFNAMSIZ, VPORT_CONFIG_SIZE)) { |
2b9d6589 BP |
550 | VLOG_WARN("%s: patch 'peer' arg too long", name); |
551 | return EINVAL; | |
552 | } | |
553 | ||
554 | if (!strcmp(name, peer)) { | |
555 | VLOG_WARN("%s: patch peer must not be self", name); | |
556 | return EINVAL; | |
557 | } | |
558 | ||
c3827f61 | 559 | strncpy(configp, peer, VPORT_CONFIG_SIZE); |
2b9d6589 BP |
560 | |
561 | return 0; | |
562 | } | |
563 | \f | |
564 | #define VPORT_FUNCTIONS \ | |
565 | NULL, /* init */ \ | |
566 | NULL, /* run */ \ | |
567 | NULL, /* wait */ \ | |
568 | \ | |
569 | netdev_vport_create, \ | |
570 | netdev_vport_destroy, \ | |
571 | netdev_vport_reconfigure, \ | |
572 | \ | |
573 | netdev_vport_open, \ | |
574 | netdev_vport_close, \ | |
575 | \ | |
576 | NULL, /* enumerate */ \ | |
577 | \ | |
578 | NULL, /* recv */ \ | |
579 | NULL, /* recv_wait */ \ | |
580 | NULL, /* drain */ \ | |
581 | \ | |
582 | NULL, /* send */ \ | |
583 | NULL, /* send_wait */ \ | |
584 | \ | |
585 | netdev_vport_set_etheraddr, \ | |
586 | netdev_vport_get_etheraddr, \ | |
587 | netdev_vport_get_mtu, \ | |
588 | NULL, /* get_ifindex */ \ | |
85da620e | 589 | NULL, /* get_carrier */ \ |
2b9d6589 BP |
590 | netdev_vport_get_stats, \ |
591 | netdev_vport_set_stats, \ | |
592 | \ | |
593 | NULL, /* get_features */ \ | |
594 | NULL, /* set_advertisements */ \ | |
595 | NULL, /* get_vlan_vid */ \ | |
596 | \ | |
597 | NULL, /* set_policing */ \ | |
598 | NULL, /* get_qos_types */ \ | |
599 | NULL, /* get_qos_capabilities */ \ | |
600 | NULL, /* get_qos */ \ | |
601 | NULL, /* set_qos */ \ | |
602 | NULL, /* get_queue */ \ | |
603 | NULL, /* set_queue */ \ | |
604 | NULL, /* delete_queue */ \ | |
605 | NULL, /* get_queue_stats */ \ | |
606 | NULL, /* dump_queues */ \ | |
607 | NULL, /* dump_queue_stats */ \ | |
608 | \ | |
609 | NULL, /* get_in4 */ \ | |
610 | NULL, /* set_in4 */ \ | |
611 | NULL, /* get_in6 */ \ | |
612 | NULL, /* add_router */ \ | |
613 | NULL, /* get_next_hop */ \ | |
614 | NULL, /* arp_lookup */ \ | |
615 | \ | |
616 | netdev_vport_update_flags, \ | |
617 | \ | |
618 | netdev_vport_poll_add, \ | |
619 | netdev_vport_poll_remove, | |
620 | ||
2b9d6589 BP |
621 | void |
622 | netdev_vport_register(void) | |
623 | { | |
c3827f61 BP |
624 | static const struct vport_class vport_classes[] = { |
625 | { { "gre", VPORT_FUNCTIONS }, parse_tunnel_config }, | |
626 | { { "capwap", VPORT_FUNCTIONS }, parse_tunnel_config }, | |
627 | { { "patch", VPORT_FUNCTIONS }, parse_patch_config } | |
628 | }; | |
629 | ||
630 | int i; | |
631 | ||
632 | for (i = 0; i < ARRAY_SIZE(vport_classes); i++) { | |
633 | netdev_register_provider(&vport_classes[i].netdev_class); | |
634 | } | |
2b9d6589 | 635 | } |