use new vsys promisc script
[sliver-openvswitch.git] / lib / netdev-pltap.c
1 /*
2  * Copyright (c) 2012 Giuseppe Lettieri
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 <sys/types.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <arpa/inet.h>
23 #include <sys/ioctl.h>
24 #include <sys/socket.h>
25 #include <net/if.h>
26 #include <net/if_arp.h>
27 #include <linux/if_tun.h>
28 #include <netinet/in.h>
29 #include <errno.h>
30
31 #include "flow.h"
32 #include "list.h"
33 #include "netdev-provider.h"
34 #include "odp-util.h"
35 #include "ofp-print.h"
36 #include "ofpbuf.h"
37 #include "packets.h"
38 #include "poll-loop.h"
39 #include "shash.h"
40 #include "sset.h"
41 #include "unixctl.h"
42 #include "socket-util.h"
43 #include "vlog.h"
44 #include "tunalloc.h"
45
46 VLOG_DEFINE_THIS_MODULE(netdev_pltap);
47
48 struct netdev_dev_pltap {
49     struct netdev_dev netdev_dev;
50     char *real_name;
51     char *error;
52     struct netdev_stats stats;
53     enum netdev_flags flags;
54     int fd;
55     struct sockaddr_in local_addr;
56     int local_netmask;
57     bool valid_local_ip;
58     bool valid_local_netmask;
59     bool finalized;
60     bool sync_flags_needed;
61     struct list sync_list;
62     unsigned int change_seq;
63 };
64
65 static struct list sync_list;
66
67 struct netdev_pltap {
68     struct netdev netdev;
69 };
70
71 static int af_inet_sock = -1;
72
73 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
74
75 static struct shash pltap_netdev_devs = SHASH_INITIALIZER(&pltap_netdev_devs);
76
77 static int netdev_pltap_create(const struct netdev_class *, const char *,
78                                struct netdev_dev **);
79
80 static struct shash pltap_creating = SHASH_INITIALIZER(&pltap_creating);
81
82 static void netdev_pltap_update_seq(struct netdev_dev_pltap *);
83 static int get_flags(struct netdev_dev_pltap *dev, enum netdev_flags *flags);
84
85 static bool
86 is_pltap_class(const struct netdev_class *class)
87 {
88     return class->create == netdev_pltap_create;
89 }
90
91 static struct netdev_dev_pltap *
92 netdev_dev_pltap_cast(const struct netdev_dev *netdev_dev)
93 {
94     assert(is_pltap_class(netdev_dev_get_class(netdev_dev)));
95     return CONTAINER_OF(netdev_dev, struct netdev_dev_pltap, netdev_dev);
96 }
97
98 static struct netdev_pltap *
99 netdev_pltap_cast(const struct netdev *netdev)
100 {
101     struct netdev_dev *netdev_dev = netdev_get_dev(netdev);
102     assert(is_pltap_class(netdev_dev_get_class(netdev_dev)));
103     return CONTAINER_OF(netdev, struct netdev_pltap, netdev);
104 }
105
106 static void sync_needed(struct netdev_dev_pltap *dev)
107 {
108     if (dev->sync_flags_needed)
109         return;
110
111     dev->sync_flags_needed = true;
112     list_insert(&sync_list, &dev->sync_list);
113         
114 }
115
116 static void sync_done(struct netdev_dev_pltap *dev)
117 {
118     if (!dev->sync_flags_needed)
119         return;
120
121     (void) list_remove(&dev->sync_list);
122     dev->sync_flags_needed = false;
123 }
124
125 static int
126 netdev_pltap_create(const struct netdev_class *class OVS_UNUSED, const char *name,
127                     struct netdev_dev **netdev_devp)
128 {
129     struct netdev_dev_pltap *netdev_dev;
130     int error;
131
132     netdev_dev = xzalloc(sizeof *netdev_dev);
133
134     netdev_dev->real_name = xzalloc(IFNAMSIZ + 1);
135     netdev_dev->error = NULL;
136     memset(&netdev_dev->local_addr, 0, sizeof(netdev_dev->local_addr));
137     netdev_dev->valid_local_ip = false;
138     netdev_dev->valid_local_netmask = false;
139     netdev_dev->finalized = false;
140     netdev_dev->flags = 0;
141     netdev_dev->sync_flags_needed = false;
142     list_init(&netdev_dev->sync_list);
143
144
145     /* Open tap device. */
146     netdev_dev->fd = tun_alloc(IFF_TAP, netdev_dev->real_name);
147     if (netdev_dev->fd < 0) {
148         error = errno;
149         VLOG_WARN("tun_alloc(IFF_TAP, %s) failed: %s", name, strerror(error));
150         goto cleanup;
151     }
152     VLOG_DBG("real_name = %s", netdev_dev->real_name);
153
154     /* Make non-blocking. */
155     error = set_nonblocking(netdev_dev->fd);
156     if (error) {
157         goto cleanup;
158     }
159
160     netdev_dev_init(&netdev_dev->netdev_dev, name, &netdev_pltap_class);
161     shash_add(&pltap_netdev_devs, name, netdev_dev);
162     *netdev_devp = &netdev_dev->netdev_dev;
163     return 0;
164
165 cleanup:
166     free(netdev_dev);
167     return error;
168 }
169
170 static void
171 netdev_pltap_destroy(struct netdev_dev *netdev_dev_)
172 {
173     struct netdev_dev_pltap *netdev_dev = netdev_dev_pltap_cast(netdev_dev_);
174
175     if (netdev_dev->fd != -1)
176         close(netdev_dev->fd);
177
178     shash_find_and_delete(&pltap_netdev_devs,
179                           netdev_dev_get_name(netdev_dev_));
180     free(netdev_dev);
181 }
182
183 static int
184 netdev_pltap_open(struct netdev_dev *netdev_dev_, struct netdev **netdevp)
185 {
186     struct netdev_pltap *netdev;
187
188     netdev = xmalloc(sizeof *netdev);
189     netdev_init(&netdev->netdev, netdev_dev_);
190
191     *netdevp = &netdev->netdev;
192     return 0;
193 }
194
195 static void
196 netdev_pltap_close(struct netdev *netdev_)
197 {
198     struct netdev_pltap *netdev = netdev_pltap_cast(netdev_);
199     free(netdev);
200 }
201
202 static int vsys_transaction(const char *script,
203         const char *msg, char *reply, size_t reply_size)
204 {
205     int ifd = -1, ofd = -1, maxfd;
206     size_t bytes_to_write, bytes_to_read,
207            bytes_written = 0, bytes_read = 0;
208     int error = 0;
209     char *ofname = NULL, *ifname = NULL;
210
211     ofname = xasprintf("/vsys/%s.out", script);
212     ifname = xasprintf("/vsys/%s.in", script);
213     if (!ofname || !ifname) {
214         VLOG_ERR("Out of memory");
215         error = ENOMEM;
216         goto cleanup;
217     }
218
219     ofd = open(ofname, O_RDONLY | O_NONBLOCK);
220     if (ofd < 0) {
221         VLOG_ERR("Cannot open %s: %s", ofname, strerror(errno));
222         error = errno;
223         goto cleanup;
224     }
225     ifd = open(ifname, O_WRONLY | O_NONBLOCK);
226     if (ifd < 0) {
227         VLOG_ERR("Cannot open %s: %s", ifname, strerror(errno));
228         error = errno;
229         goto cleanup;
230     }
231     maxfd = (ifd < ofd) ? ofd : ifd;
232
233     bytes_to_write = strlen(msg);
234     bytes_to_read = reply_size;
235     while (bytes_to_write || bytes_to_read) {
236         fd_set readset, writeset, errorset;
237
238         FD_ZERO(&readset);
239         FD_ZERO(&writeset);
240         FD_ZERO(&errorset);
241         if (bytes_to_write) {
242             FD_SET(ifd, &writeset);
243             FD_SET(ifd, &errorset);
244         }
245         FD_SET(ofd, &readset);
246         FD_SET(ofd, &errorset);
247         if (select(maxfd + 1, &readset, &writeset, &errorset, NULL) < 0) {
248             if (errno == EINTR)
249                 continue;
250             VLOG_ERR("selec error: %s", strerror(errno));
251             error = errno;
252             goto cleanup;
253         }
254         if (FD_ISSET(ifd, &errorset) || FD_ISSET(ofd, &errorset)) {
255             VLOG_ERR("error condition on ifd or ofd");
256             goto cleanup;
257         }
258         if (FD_ISSET(ifd, &writeset)) {
259             ssize_t n = write(ifd, msg + bytes_written, bytes_to_write);    
260             if (n < 0) {
261                 if (errno != EAGAIN && errno != EINTR) {
262                     VLOG_ERR("write on %s: %s", ifname, strerror(errno));
263                     error = errno;
264                     goto cleanup;
265                 }
266             } else {
267                 bytes_written += n;
268                 bytes_to_write -= n;
269                 if (bytes_to_write == 0)
270                     close(ifd);
271             }
272         }
273         if (FD_ISSET(ofd, &readset)) {
274             ssize_t n = read(ofd, reply + bytes_read, bytes_to_read);    
275             if (n < 0) {
276                 if (errno != EAGAIN && errno != EINTR) {
277                     VLOG_ERR("read on %s: %s", ofname, strerror(errno));
278                     error = errno;
279                     goto cleanup;
280                 }
281             } else if (n == 0) {
282                 bytes_to_read = 0;
283             } else {
284                 bytes_read += n;
285                 bytes_to_read -= n;
286             }
287         }
288     }
289     if (bytes_read) {
290         reply[bytes_read] = '\0';
291         VLOG_ERR("%s returned: %s", script, reply);
292         error = EAGAIN;
293         goto cleanup;
294     }
295
296 cleanup:
297     free(ofname);
298     free(ifname);
299     close(ifd);
300     close(ofd);
301     return error;
302 }
303
304 static int
305 netdev_pltap_sync_flags(struct netdev_dev_pltap *dev)
306 {
307     int error = 0;
308     char *msg = NULL, *reply = NULL;
309     const size_t reply_size = 1024;
310     enum netdev_flags flags = 0;
311
312
313     if (dev->fd < 0 || !dev->finalized) {
314         /* pretend we have synchronized, we will do it later */
315         sync_needed(dev);
316         return 0;
317     }
318
319     error = get_flags(dev, &flags);
320     if (error) {
321         VLOG_ERR("get_flags(%s): %s", dev->real_name, strerror(error));
322         goto cleanup;
323     }
324     VLOG_DBG("sync_flags(%s): current: %s %s",
325         dev->real_name,
326         (flags & NETDEV_UP ? "UP" : "-"),
327         (flags & NETDEV_PROMISC ? "PROMISC" : "-"));
328
329     if ((dev->flags & NETDEV_PROMISC) ^ (flags & NETDEV_PROMISC)) {
330             msg = xasprintf("%s\n%s",
331                dev->real_name,
332                (dev->flags & NETDEV_PROMISC ? "" : "-\n"));
333             reply = (char*)xmalloc(reply_size);
334             if (!msg || !reply) {
335                 VLOG_ERR("Out of memory\n");
336                 error = ENOMEM;
337                 goto cleanup;
338             }
339             error = vsys_transaction("promisc", msg, reply, reply_size);
340             if (error) {
341                 dev->error = reply;
342                 reply = NULL; /* prevent free of reply msg */
343                 goto cleanup;
344             }
345             netdev_pltap_update_seq(dev);
346     }
347
348 cleanup:
349
350     sync_done(dev);
351     free(msg);
352     free(reply);
353
354     return error;
355 }
356
357 static int
358 netdev_pltap_create_finalize(struct netdev_dev_pltap *dev)
359 {
360     int error = 0;
361     char *msg = NULL, *reply = NULL;
362     const size_t reply_size = 1024;
363
364     if (dev->finalized)
365         return 0;
366     if (!dev->valid_local_ip || !dev->valid_local_netmask)
367         return 0;
368     
369     msg = xasprintf("%s\n"IP_FMT"\n%d\n",
370        dev->real_name,
371        IP_ARGS(&dev->local_addr.sin_addr),
372        dev->local_netmask);
373     reply = (char*)xmalloc(reply_size);
374     if (!msg || !reply) {
375         VLOG_ERR("Out of memory");
376         error = ENOMEM;
377         goto cleanup;
378     }
379     error = vsys_transaction("vif_up", msg, reply, reply_size);
380     if (error) {
381         dev->error = reply;
382         reply = NULL; /* prevent free of reply msg */
383         goto cleanup;
384     }
385     dev->finalized = true;
386     error = netdev_pltap_sync_flags(dev);
387     if (error) {
388         goto cleanup;
389     }
390     free(dev->error);
391     dev->error = NULL;
392     netdev_pltap_update_seq(dev);
393
394 cleanup:
395     free(msg);
396     free(reply);
397     return error;
398 }
399
400 static int
401 netdev_pltap_get_config(struct netdev_dev *dev_, struct smap *args)
402 {
403     struct netdev_dev_pltap *netdev_dev = netdev_dev_pltap_cast(dev_);
404
405     if (netdev_dev->valid_local_ip)
406         smap_add_format(args, "local_ip", IP_FMT,
407             IP_ARGS(&netdev_dev->local_addr.sin_addr));
408     if (netdev_dev->valid_local_netmask)
409         smap_add_format(args, "local_netmask", "%"PRIu32,
410             ntohs(netdev_dev->local_netmask));
411     return netdev_pltap_create_finalize(netdev_dev);
412 }
413
414 static int
415 netdev_pltap_set_config(struct netdev_dev *dev_, const struct smap *args)
416 {
417     struct netdev_dev_pltap *netdev_dev = netdev_dev_pltap_cast(dev_);
418     struct shash_node *node;
419
420     VLOG_DBG("pltap_set_config(%s)", netdev_dev_get_name(dev_));
421     SMAP_FOR_EACH(node, args) {
422         VLOG_DBG("arg: %s->%s", node->name, (char*)node->data);
423         if (!strcmp(node->name, "local_ip")) {
424             struct in_addr addr;
425             if (lookup_ip(node->data, &addr)) {
426                 VLOG_WARN("%s: bad 'local_ip'", node->name);
427             } else {
428                 netdev_dev->local_addr.sin_addr = addr;
429                 netdev_dev->valid_local_ip = true;
430             }
431         } else if (!strcmp(node->name, "local_netmask")) {
432             netdev_dev->local_netmask = atoi(node->data);
433             // XXX check valididy
434             netdev_dev->valid_local_netmask = true;
435         } else {
436             VLOG_WARN("%s: unknown argument '%s'", 
437                 netdev_dev_get_name(dev_), node->name);
438         }
439     }
440     return netdev_pltap_create_finalize(netdev_dev);        
441 }
442
443 static int
444 netdev_pltap_listen(struct netdev *netdev_ OVS_UNUSED)
445 {
446     return 0;
447 }
448
449 static int
450 netdev_pltap_recv(struct netdev *netdev_, void *buffer, size_t size)
451 {
452     struct netdev_dev_pltap *dev = 
453         netdev_dev_pltap_cast(netdev_get_dev(netdev_));
454     char prefix[4];
455     struct iovec iov[2] = {
456         { .iov_base = prefix, .iov_len = 4 },
457         { .iov_base = buffer, .iov_len = size }
458     };
459     if (!dev->finalized)
460         return -EAGAIN;
461     for (;;) {
462         ssize_t retval;
463         retval = readv(dev->fd, iov, 2);
464         if (retval >= 0) {
465             if (retval <= size) {
466                 return retval;
467             } else {
468                 return -EMSGSIZE;
469             }
470         } else if (errno != EINTR) {
471             if (errno != EAGAIN) {
472                 VLOG_WARN_RL(&rl, "error receiveing Ethernet packet on %s: %s",
473                     netdev_get_name(netdev_), strerror(errno));
474             }
475             return -errno;
476         }
477     }
478 }
479
480 static void
481 netdev_pltap_recv_wait(struct netdev *netdev_)
482 {
483     struct netdev_dev_pltap *dev = 
484         netdev_dev_pltap_cast(netdev_get_dev(netdev_));
485     if (dev->finalized && dev->fd >= 0) {
486         poll_fd_wait(dev->fd, POLLIN);
487     }
488 }
489
490 static int
491 netdev_pltap_send(struct netdev *netdev_, const void *buffer, size_t size)
492 {
493     struct netdev_dev_pltap *dev = 
494         netdev_dev_pltap_cast(netdev_get_dev(netdev_));
495     char prefix[4] = { 0, 0, 8, 6 };
496     struct iovec iov[2] = {
497         { .iov_base = prefix, .iov_len = 4 },
498         { .iov_base = (char*) buffer, .iov_len = size }
499     };
500     if (dev->fd < 0 || !dev->finalized)
501         return EAGAIN;
502     for (;;) {
503         ssize_t retval;
504         retval = writev(dev->fd, iov, 2);
505         if (retval >= 0) {
506             if (retval != size + 4) {
507                 VLOG_WARN_RL(&rl, "sent partial Ethernet packet (%zd bytes of %zu) on %s",
508                              retval, size + 4, netdev_get_name(netdev_));
509             }
510             return 0;
511         } else if (errno != EINTR) {
512             if (errno != EAGAIN) {
513                 VLOG_WARN_RL(&rl, "error sending Ethernet packet on %s: %s",
514                     netdev_get_name(netdev_), strerror(errno));
515             }
516             return errno;
517         }
518     }
519 }
520
521 static void
522 netdev_pltap_send_wait(struct netdev *netdev_)
523 {
524     struct netdev_dev_pltap *dev = 
525         netdev_dev_pltap_cast(netdev_get_dev(netdev_));
526     if (dev->finalized && dev->fd >= 0) {
527         poll_fd_wait(dev->fd, POLLOUT);
528     }
529 }
530
531 static int
532 netdev_pltap_drain(struct netdev *netdev_)
533 {
534     struct netdev_dev_pltap *dev = 
535         netdev_dev_pltap_cast(netdev_get_dev(netdev_));
536     char buffer[128];
537     int error;
538
539     if (dev->fd < 0 || !dev->finalized)
540         return 0;
541     for (;;) {
542         error = recv(dev->fd, buffer, 128, MSG_TRUNC);
543         if (error) {
544             if (error == -EAGAIN)
545                 break;
546             else if (error != -EMSGSIZE)
547                 return error;
548         }
549     }
550     return 0;
551 }
552
553 static int
554 netdev_pltap_set_etheraddr(struct netdev *netdevi OVS_UNUSED,
555                            const uint8_t mac[ETH_ADDR_LEN] OVS_UNUSED)
556 {
557     return ENOTSUP;
558 }
559
560
561 // XXX from netdev-linux.c
562 static int
563 get_etheraddr(struct netdev_dev_pltap *dev, uint8_t ea[ETH_ADDR_LEN])
564 {
565     struct ifreq ifr;
566     int hwaddr_family;
567
568     memset(&ifr, 0, sizeof ifr);
569     ovs_strzcpy(ifr.ifr_name, dev->real_name, sizeof ifr.ifr_name);
570     if (ioctl(af_inet_sock, SIOCGIFHWADDR, &ifr) < 0) {
571         /* ENODEV probably means that a vif disappeared asynchronously and
572          * hasn't been removed from the database yet, so reduce the log level
573          * to INFO for that case. */
574         VLOG(errno == ENODEV ? VLL_INFO : VLL_ERR,
575              "ioctl(SIOCGIFHWADDR) on %s device failed: %s",
576              dev->real_name, strerror(errno));
577         return errno;
578     }
579     hwaddr_family = ifr.ifr_hwaddr.sa_family;
580     if (hwaddr_family != AF_UNSPEC && hwaddr_family != ARPHRD_ETHER) {
581         VLOG_WARN("%s device has unknown hardware address family %d",
582                   dev->real_name, hwaddr_family);
583     }
584     memcpy(ea, ifr.ifr_hwaddr.sa_data, ETH_ADDR_LEN);
585     return 0;
586 }
587
588 static int
589 get_flags(struct netdev_dev_pltap *dev, enum netdev_flags *flags)
590 {
591     struct ifreq ifr;
592
593     memset(&ifr, 0, sizeof ifr);
594     ovs_strzcpy(ifr.ifr_name, dev->real_name, sizeof ifr.ifr_name);
595     if (ioctl(af_inet_sock, SIOCGIFFLAGS, &ifr) < 0)
596         return errno;
597     *flags = 0;
598     if (ifr.ifr_flags & IFF_UP)
599         *flags |= NETDEV_UP;
600     if (ifr.ifr_flags & IFF_PROMISC)
601         *flags |= NETDEV_PROMISC;
602     return 0;
603 }
604
605 static int
606 netdev_pltap_get_etheraddr(const struct netdev *netdev,
607                            uint8_t mac[ETH_ADDR_LEN])
608 {
609     struct netdev_dev_pltap *dev = 
610         netdev_dev_pltap_cast(netdev_get_dev(netdev));
611     if (dev->fd < 0 || !dev->finalized)
612         return EAGAIN;
613     return get_etheraddr(dev, mac);
614 }
615
616
617 // XXX can we read stats in planetlab?
618 static int
619 netdev_pltap_get_stats(const struct netdev *netdev OVS_UNUSED, struct netdev_stats *stats OVS_UNUSED)
620 {
621     return ENOTSUP;
622 }
623
624 static int
625 netdev_pltap_set_stats(struct netdev *netdev OVS_UNUSED, const struct netdev_stats *stats OVS_UNUSED)
626 {
627     return ENOTSUP;
628 }
629
630
631 static int
632 netdev_pltap_update_flags(struct netdev *netdev,
633                           enum netdev_flags off, enum netdev_flags on,
634                           enum netdev_flags *old_flagsp)
635 {
636     struct netdev_dev_pltap *dev =
637         netdev_dev_pltap_cast(netdev_get_dev(netdev));
638
639     if ((off | on) & ~(NETDEV_UP | NETDEV_PROMISC)) {
640         return EINVAL;
641     }
642
643     *old_flagsp = dev->flags;
644     dev->flags |= on;
645     dev->flags &= ~off;
646     if (*old_flagsp != dev->flags) {
647         /* we cannot sync here, since we may be in a signal handler */
648         sync_needed(dev);
649     }
650
651     return 0;
652 }
653
654 static unsigned int
655 netdev_pltap_change_seq(const struct netdev *netdev)
656 {
657     return netdev_dev_pltap_cast(netdev_get_dev(netdev))->change_seq;
658 }
659 \f
660 /* Helper functions. */
661
662 static void
663 netdev_pltap_update_seq(struct netdev_dev_pltap *dev)
664 {
665     dev->change_seq++;
666     if (!dev->change_seq) {
667         dev->change_seq++;
668     }
669 }
670
671 static void
672 netdev_pltap_get_real_name(struct unixctl_conn *conn,
673                      int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED)
674 {
675     struct netdev_dev_pltap *pltap_dev;
676
677     pltap_dev = shash_find_data(&pltap_netdev_devs, argv[1]);
678     if (!pltap_dev) {
679         unixctl_command_reply_error(conn, "no such pltap netdev");
680         return;
681     }
682     if (pltap_dev->error) {
683         unixctl_command_reply_error(conn, pltap_dev->error);
684         return;
685     }
686
687     unixctl_command_reply(conn, pltap_dev->real_name);
688 }
689
690 static int
691 netdev_pltap_init(void)
692 {
693     list_init(&sync_list);
694     af_inet_sock = socket(AF_INET, SOCK_DGRAM, 0);
695     if (af_inet_sock < 0) {
696         VLOG_ERR("failed to create inet socket: %s", strerror(errno));
697     }
698     unixctl_command_register("netdev-pltap/get-tapname", "port",
699                              1, 1, netdev_pltap_get_real_name, NULL);
700     return 0;
701 }
702
703 static void
704 netdev_pltap_run(void)
705 {
706     struct netdev_dev_pltap *iter, *next;
707     LIST_FOR_EACH_SAFE(iter, next, sync_list, &sync_list) {
708         netdev_pltap_sync_flags(iter);
709     }
710 }
711
712 static void
713 netdev_pltap_wait(void)
714 {
715     if (!list_is_empty(&sync_list))
716         poll_immediate_wake();
717 }
718
719 const struct netdev_class netdev_pltap_class = {
720     "pltap",
721     netdev_pltap_init,
722     netdev_pltap_run,  
723     netdev_pltap_wait,            
724
725     netdev_pltap_create,
726     netdev_pltap_destroy,
727     netdev_pltap_get_config,
728     netdev_pltap_set_config, 
729
730     netdev_pltap_open,
731     netdev_pltap_close,
732
733     netdev_pltap_listen,
734     netdev_pltap_recv,
735     netdev_pltap_recv_wait,
736     netdev_pltap_drain,
737
738     netdev_pltap_send, 
739     netdev_pltap_send_wait,  
740
741     netdev_pltap_set_etheraddr,
742     netdev_pltap_get_etheraddr,
743     NULL,                       /* get_mtu */
744     NULL,                       /* set_mtu */
745     NULL,                       /* get_ifindex */
746     NULL,                       /* get_carrier */
747     NULL,                       /* get_carrier_resets */
748     NULL,                       /* get_miimon */
749     netdev_pltap_get_stats,
750     netdev_pltap_set_stats,
751
752     NULL,                       /* get_features */
753     NULL,                       /* set_advertisements */
754
755     NULL,                       /* set_policing */
756     NULL,                       /* get_qos_types */
757     NULL,                       /* get_qos_capabilities */
758     NULL,                       /* get_qos */
759     NULL,                       /* set_qos */
760     NULL,                       /* get_queue */
761     NULL,                       /* set_queue */
762     NULL,                       /* delete_queue */
763     NULL,                       /* get_queue_stats */
764     NULL,                       /* dump_queues */
765     NULL,                       /* dump_queue_stats */
766
767     NULL,                       /* get_in4 */
768     NULL,                       /* set_in4 */
769     NULL,                       /* get_in6 */
770     NULL,                       /* add_router */
771     NULL,                       /* get_next_hop */
772     NULL,                       /* get_drv_info */
773     NULL,                       /* arp_lookup */
774
775     netdev_pltap_update_flags,
776
777     netdev_pltap_change_seq
778 };