datapath: Report kernel's flow key when passing packets up to userspace.
[sliver-openvswitch.git] / lib / dpif-linux.c
index 89cee1f..5c2c755 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include "dpif-provider.h"
 #include "netdev.h"
 #include "netdev-vport.h"
+#include "netlink.h"
 #include "ofpbuf.h"
+#include "openvswitch/tunnel.h"
+#include "packets.h"
 #include "poll-loop.h"
 #include "rtnetlink.h"
+#include "rtnetlink-link.h"
 #include "shash.h"
 #include "svec.h"
 #include "util.h"
@@ -71,7 +75,7 @@ static int get_openvswitch_major(void);
 static int create_minor(const char *name, int minor, struct dpif **dpifp);
 static int open_minor(int minor, struct dpif **dpifp);
 static int make_openvswitch_device(int minor, char **fnp);
-static void dpif_linux_port_changed(const struct rtnetlink_change *,
+static void dpif_linux_port_changed(const struct rtnetlink_link_change *,
                                     void *dpif);
 
 static struct dpif_linux *
@@ -176,7 +180,7 @@ static void
 dpif_linux_close(struct dpif *dpif_)
 {
     struct dpif_linux *dpif = dpif_linux_cast(dpif_);
-    rtnetlink_notifier_unregister(&dpif->port_notifier);
+    rtnetlink_link_notifier_unregister(&dpif->port_notifier);
     shash_destroy(&dpif->changed_ports);
     free(dpif->local_ifname);
     close(dpif->fd);
@@ -227,18 +231,31 @@ dpif_linux_set_drop_frags(struct dpif *dpif_, bool drop_frags)
 }
 
 static void
-translate_vport_type_to_netdev_type(char *type, size_t size)
+translate_vport_type_to_netdev_type(struct odp_port *port)
 {
+    char *type = port->type;
+
     if (!strcmp(type, "netdev")) {
-        ovs_strlcpy(type, "system", size);
+        ovs_strlcpy(type, "system", sizeof port->type);
+    } else if (!strcmp(type, "gre")) {
+        const struct tnl_port_config *config;
+
+        config = (struct tnl_port_config *)port->config;
+        if (config->flags & TNL_F_IPSEC) {
+            ovs_strlcpy(type, "ipsec_gre", sizeof port->type);
+        }
     }
 }
 
 static void
-translate_netdev_type_to_vport_type(char *type, size_t size)
+translate_netdev_type_to_vport_type(struct odp_port *port)
 {
+    char *type = port->type;
+
     if (!strcmp(type, "system")) {
-        ovs_strlcpy(type, "netdev", size);
+        ovs_strlcpy(type, "netdev", sizeof port->type);
+    } else if (!strcmp(type, "ipsec_gre")) {
+        ovs_strlcpy(type, "gre", sizeof port->type);
     }
 }
 
@@ -254,8 +271,8 @@ dpif_linux_port_add(struct dpif *dpif, struct netdev *netdev,
     memset(&port, 0, sizeof port);
     strncpy(port.devname, name, sizeof port.devname);
     strncpy(port.type, type, sizeof port.type);
-    translate_netdev_type_to_vport_type(port.type, sizeof port.type);
     netdev_vport_get_config(netdev, port.config);
+    translate_netdev_type_to_vport_type(&port);
 
     error = do_ioctl(dpif, ODP_VPORT_ATTACH, &port);
     if (!error) {
@@ -277,7 +294,7 @@ dpif_linux_port_query__(const struct dpif *dpif, struct odp_port *port)
 {
     int error = do_ioctl(dpif, ODP_VPORT_QUERY, port);
     if (!error) {
-        translate_vport_type_to_netdev_type(port->type, sizeof port->type);
+        translate_vport_type_to_netdev_type(port);
     }
     return error;
 }
@@ -323,7 +340,7 @@ dpif_linux_port_list(const struct dpif *dpif_, struct odp_port *ports, int n)
     for (i = 0; i < pv.n_ports; i++) {
         struct odp_port *port = &pv.ports[i];
 
-        translate_vport_type_to_netdev_type(port->type, sizeof port->type);
+        translate_vport_type_to_netdev_type(port);
     }
     return pv.n_ports;
 }
@@ -353,7 +370,7 @@ dpif_linux_port_poll_wait(const struct dpif *dpif_)
     if (!shash_is_empty(&dpif->changed_ports) || dpif->change_error) {
         poll_immediate_wake();
     } else {
-        rtnetlink_notifier_wait();
+        rtnetlink_link_notifier_wait();
     }
 }
 
@@ -379,26 +396,40 @@ dpif_linux_flow_del(struct dpif *dpif_, struct odp_flow *flow)
 }
 
 static int
-dpif_linux_flow_list(const struct dpif *dpif_, struct odp_flow flows[], int n)
+dpif_linux_flow_dump_start(const struct dpif *dpif OVS_UNUSED, void **statep)
 {
-    struct odp_flowvec fv;
+    *statep = xzalloc(sizeof(struct odp_flow_dump));
+    return 0;
+}
+
+static int
+dpif_linux_flow_dump_next(const struct dpif *dpif, void *state,
+                          struct odp_flow *flow)
+{
+    struct odp_flow_dump *dump = state;
     int error;
 
-    fv.flows = flows;
-    fv.n_flows = n;
-    error = do_ioctl(dpif_, ODP_FLOW_LIST, &fv);
-    return error ? -error : fv.n_flows;
+    dump->flow = flow;
+    error = do_ioctl(dpif, ODP_FLOW_DUMP, dump);
+    return error ? error : flow->flags & ODPFF_EOF ? EOF : 0;
+}
+
+static int
+dpif_linux_flow_dump_done(const struct dpif *dpif OVS_UNUSED, void *state)
+{
+    free(state);
+    return 0;
 }
 
 static int
 dpif_linux_execute(struct dpif *dpif_,
-                   const union odp_action actions[], int n_actions,
+                   const struct nlattr *actions, size_t actions_len,
                    const struct ofpbuf *buf)
 {
     struct odp_execute execute;
     memset(&execute, 0, sizeof execute);
-    execute.actions = (union odp_action *) actions;
-    execute.n_actions = n_actions;
+    execute.actions = (struct nlattr *) actions;
+    execute.actions_len = actions_len;
     execute.data = buf->data;
     execute.length = buf->size;
     return do_ioctl(dpif_, ODP_EXECUTE, &execute);
@@ -442,14 +473,61 @@ dpif_linux_queue_to_priority(const struct dpif *dpif OVS_UNUSED,
 }
 
 static int
-dpif_linux_recv(struct dpif *dpif_, struct ofpbuf **bufp)
+parse_odp_packet(struct ofpbuf *buf, struct dpif_upcall *upcall)
+{
+    static const struct nl_policy odp_packet_policy[] = {
+        /* Always present. */
+        [ODP_PACKET_ATTR_TYPE] = { .type = NL_A_U32 },
+        [ODP_PACKET_ATTR_PACKET] = { .type = NL_A_UNSPEC,
+                                     .min_len = ETH_HEADER_LEN },
+        [ODP_PACKET_ATTR_KEY] = { .type = NL_A_NESTED },
+
+        /* _ODPL_ACTION_NR only. */
+        [ODP_PACKET_ATTR_USERDATA] = { .type = NL_A_U64, .optional = true },
+
+        /* _ODPL_SFLOW_NR only. */
+        [ODP_PACKET_ATTR_SAMPLE_POOL] = { .type = NL_A_U32, .optional = true },
+        [ODP_PACKET_ATTR_ACTIONS] = { .type = NL_A_NESTED, .optional = true },
+    };
+
+    struct odp_packet *odp_packet = buf->data;
+    struct nlattr *a[ARRAY_SIZE(odp_packet_policy)];
+
+    if (!nl_policy_parse(buf, sizeof *odp_packet, odp_packet_policy,
+                         a, ARRAY_SIZE(odp_packet_policy))) {
+        return EINVAL;
+    }
+
+    memset(upcall, 0, sizeof *upcall);
+    upcall->type = nl_attr_get_u32(a[ODP_PACKET_ATTR_TYPE]);
+    upcall->packet = buf;
+    upcall->packet->data = (void *) nl_attr_get(a[ODP_PACKET_ATTR_PACKET]);
+    upcall->packet->size = nl_attr_get_size(a[ODP_PACKET_ATTR_PACKET]);
+    upcall->key = (void *) nl_attr_get(a[ODP_PACKET_ATTR_KEY]);
+    upcall->key_len = nl_attr_get_size(a[ODP_PACKET_ATTR_KEY]);
+    upcall->userdata = (a[ODP_PACKET_ATTR_USERDATA]
+                        ? nl_attr_get_u64(a[ODP_PACKET_ATTR_USERDATA])
+                        : 0);
+    upcall->sample_pool = (a[ODP_PACKET_ATTR_SAMPLE_POOL]
+                        ? nl_attr_get_u32(a[ODP_PACKET_ATTR_SAMPLE_POOL])
+                           : 0);
+    if (a[ODP_PACKET_ATTR_ACTIONS]) {
+        upcall->actions = (void *) nl_attr_get(a[ODP_PACKET_ATTR_ACTIONS]);
+        upcall->actions_len = nl_attr_get_size(a[ODP_PACKET_ATTR_ACTIONS]);
+    }
+
+    return 0;
+}
+
+static int
+dpif_linux_recv(struct dpif *dpif_, struct dpif_upcall *upcall)
 {
     struct dpif_linux *dpif = dpif_linux_cast(dpif_);
     struct ofpbuf *buf;
     int retval;
     int error;
 
-    buf = ofpbuf_new_with_headroom(65536, DPIF_RECV_MSG_PADDING);
+    buf = ofpbuf_new(65536);
     retval = read(dpif->fd, ofpbuf_tail(buf), ofpbuf_tailroom(buf));
     if (retval < 0) {
         error = errno;
@@ -457,30 +535,30 @@ dpif_linux_recv(struct dpif *dpif_, struct ofpbuf **bufp)
             VLOG_WARN_RL(&error_rl, "%s: read failed: %s",
                          dpif_name(dpif_), strerror(error));
         }
-    } else if (retval >= sizeof(struct odp_msg)) {
-        struct odp_msg *msg = buf->data;
-        if (msg->length <= retval) {
-            buf->size += retval;
-            *bufp = buf;
-            return 0;
+    } else if (retval >= sizeof(struct odp_packet)) {
+        struct odp_packet *odp_packet = buf->data;
+        buf->size += retval;
+
+        if (odp_packet->len <= retval) {
+            error = parse_odp_packet(buf, upcall);
         } else {
             VLOG_WARN_RL(&error_rl, "%s: discarding message truncated "
                          "from %"PRIu32" bytes to %d",
-                         dpif_name(dpif_), msg->length, retval);
+                         dpif_name(dpif_), odp_packet->len, retval);
             error = ERANGE;
         }
     } else if (!retval) {
         VLOG_WARN_RL(&error_rl, "%s: unexpected end of file", dpif_name(dpif_));
         error = EPROTO;
     } else {
-        VLOG_WARN_RL(&error_rl,
-                     "%s: discarding too-short message (%d bytes)",
+        VLOG_WARN_RL(&error_rl, "%s: discarding too-short message (%d bytes)",
                      dpif_name(dpif_), retval);
         error = ERANGE;
     }
 
-    *bufp = NULL;
-    ofpbuf_delete(buf);
+    if (error) {
+        ofpbuf_delete(buf);
+    }
     return error;
 }
 
@@ -514,7 +592,9 @@ const struct dpif_class dpif_linux_class = {
     dpif_linux_flow_put,
     dpif_linux_flow_del,
     dpif_linux_flow_flush,
-    dpif_linux_flow_list,
+    dpif_linux_flow_dump_start,
+    dpif_linux_flow_dump_next,
+    dpif_linux_flow_dump_done,
     dpif_linux_execute,
     dpif_linux_recv_get_mask,
     dpif_linux_recv_set_mask,
@@ -756,8 +836,9 @@ open_minor(int minor, struct dpif **dpifp)
     fd = open(fn, O_RDONLY | O_NONBLOCK);
     if (fd >= 0) {
         struct dpif_linux *dpif = xmalloc(sizeof *dpif);
-        error = rtnetlink_notifier_register(&dpif->port_notifier,
-                                           dpif_linux_port_changed, dpif);
+        error = rtnetlink_link_notifier_register(&dpif->port_notifier,
+                                                 dpif_linux_port_changed,
+                                                 dpif);
         if (!error) {
             char *name;
 
@@ -785,7 +866,8 @@ open_minor(int minor, struct dpif **dpifp)
 }
 
 static void
-dpif_linux_port_changed(const struct rtnetlink_change *change, void *dpif_)
+dpif_linux_port_changed(const struct rtnetlink_link_change *change,
+                        void *dpif_)
 {
     struct dpif_linux *dpif = dpif_;