Implement set-field for IPv6 ND fields (nd_target, nd_sll, and nd_tll).
[cascardo/ovs.git] / lib / odp-execute.c
index e2bc6de..98ac18c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
  * Copyright (c) 2013 Simon Horman
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,6 +18,8 @@
 #include <config.h>
 #include "odp-execute.h"
 #include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
 #include <netinet/ip6.h>
 #include <stdlib.h>
 #include <string.h>
@@ -112,9 +114,11 @@ odp_set_tcp(struct ofpbuf *packet, const struct ovs_key_tcp *key,
 {
     struct tcp_header *th = ofpbuf_l4(packet);
 
-    packet_set_tcp_port(packet,
-                        key->tcp_src | (th->tcp_src & ~mask->tcp_src),
-                        key->tcp_dst | (th->tcp_dst & ~mask->tcp_dst));
+    if (OVS_LIKELY(th && ofpbuf_get_tcp_payload(packet))) {
+        packet_set_tcp_port(packet,
+                            key->tcp_src | (th->tcp_src & ~mask->tcp_src),
+                            key->tcp_dst | (th->tcp_dst & ~mask->tcp_dst));
+    }
 }
 
 static void
@@ -123,9 +127,11 @@ odp_set_udp(struct ofpbuf *packet, const struct ovs_key_udp *key,
 {
     struct udp_header *uh = ofpbuf_l4(packet);
 
-    packet_set_udp_port(packet,
-                        key->udp_src | (uh->udp_src & ~mask->udp_src),
-                        key->udp_dst | (uh->udp_dst & ~mask->udp_dst));
+    if (OVS_LIKELY(uh && ofpbuf_get_udp_payload(packet))) {
+        packet_set_udp_port(packet,
+                            key->udp_src | (uh->udp_src & ~mask->udp_src),
+                            key->udp_dst | (uh->udp_dst & ~mask->udp_dst));
+    }
 }
 
 static void
@@ -134,9 +140,11 @@ odp_set_sctp(struct ofpbuf *packet, const struct ovs_key_sctp *key,
 {
     struct sctp_header *sh = ofpbuf_l4(packet);
 
-    packet_set_sctp_port(packet,
-                         key->sctp_src | (sh->sctp_src & ~mask->sctp_src),
-                         key->sctp_dst | (sh->sctp_dst & ~mask->sctp_dst));
+    if (OVS_LIKELY(sh && ofpbuf_get_sctp_payload(packet))) {
+        packet_set_sctp_port(packet,
+                             key->sctp_src | (sh->sctp_src & ~mask->sctp_src),
+                             key->sctp_dst | (sh->sctp_dst & ~mask->sctp_dst));
+    }
 }
 
 static void
@@ -175,15 +183,54 @@ set_arp(struct ofpbuf *packet, const struct ovs_key_arp *key,
 }
 
 static void
-odp_execute_set_action(struct dpif_packet *packet, const struct nlattr *a,
-                       struct pkt_metadata *md)
+odp_set_nd(struct ofpbuf *packet, const struct ovs_key_nd *key,
+           const struct ovs_key_nd *mask)
+{
+    const struct ovs_nd_msg *ns = ofpbuf_l4(packet);
+    const struct ovs_nd_opt *nd_opt = ofpbuf_get_nd_payload(packet);
+
+    if (OVS_LIKELY(ns && nd_opt)) {
+        int bytes_remain = ofpbuf_l4_size(packet) - sizeof(*ns);
+        ovs_be32 tgt_buf[4];
+        uint8_t sll_buf[ETH_ADDR_LEN] = {0};
+        uint8_t tll_buf[ETH_ADDR_LEN] = {0};
+
+        while (bytes_remain >= ND_OPT_LEN && nd_opt->nd_opt_len != 0) {
+            if (nd_opt->nd_opt_type == ND_OPT_SOURCE_LINKADDR
+                && nd_opt->nd_opt_len == 1) {
+                memcpy(sll_buf, nd_opt->nd_opt_data, ETH_ADDR_LEN);
+                ether_addr_copy_masked(sll_buf, key->nd_sll, mask->nd_sll);
+
+                /* A packet can only contain one SLL or TLL option */
+                break;
+            } else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LINKADDR
+                       && nd_opt->nd_opt_len == 1) {
+                memcpy(tll_buf, nd_opt->nd_opt_data, ETH_ADDR_LEN);
+                ether_addr_copy_masked(tll_buf, key->nd_tll, mask->nd_tll);
+
+                /* A packet can only contain one SLL or TLL option */
+                break;
+            }
+
+            nd_opt += nd_opt->nd_opt_len;
+            bytes_remain -= nd_opt->nd_opt_len * ND_OPT_LEN;
+        }
+
+        packet_set_nd(packet,
+                      mask_ipv6_addr(ns->target.be32,
+                                     key->nd_target, mask->nd_target, tgt_buf),
+                      sll_buf,
+                      tll_buf);
+    }
+}
+
+static void
+odp_execute_set_action(struct dpif_packet *packet, const struct nlattr *a)
 {
     enum ovs_key_attr type = nl_attr_type(a);
     const struct ovs_key_ipv4 *ipv4_key;
     const struct ovs_key_ipv6 *ipv6_key;
-    const struct ovs_key_tcp *tcp_key;
-    const struct ovs_key_udp *udp_key;
-    const struct ovs_key_sctp *sctp_key;
+    struct pkt_metadata *md = &packet->md;
 
     switch (type) {
     case OVS_KEY_ATTR_PRIORITY:
@@ -218,21 +265,33 @@ odp_execute_set_action(struct dpif_packet *packet, const struct nlattr *a,
         break;
 
     case OVS_KEY_ATTR_TCP:
-        tcp_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_tcp));
-        packet_set_tcp_port(&packet->ofpbuf, tcp_key->tcp_src,
-                            tcp_key->tcp_dst);
+        if (OVS_LIKELY(ofpbuf_get_tcp_payload(&packet->ofpbuf))) {
+            const struct ovs_key_tcp *tcp_key
+                = nl_attr_get_unspec(a, sizeof(struct ovs_key_tcp));
+
+            packet_set_tcp_port(&packet->ofpbuf, tcp_key->tcp_src,
+                                tcp_key->tcp_dst);
+        }
         break;
 
     case OVS_KEY_ATTR_UDP:
-        udp_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_udp));
-        packet_set_udp_port(&packet->ofpbuf, udp_key->udp_src,
-                            udp_key->udp_dst);
+        if (OVS_LIKELY(ofpbuf_get_udp_payload(&packet->ofpbuf))) {
+            const struct ovs_key_udp *udp_key
+                = nl_attr_get_unspec(a, sizeof(struct ovs_key_udp));
+
+            packet_set_udp_port(&packet->ofpbuf, udp_key->udp_src,
+                                udp_key->udp_dst);
+        }
         break;
 
     case OVS_KEY_ATTR_SCTP:
-        sctp_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_sctp));
-        packet_set_sctp_port(&packet->ofpbuf, sctp_key->sctp_src,
-                             sctp_key->sctp_dst);
+        if (OVS_LIKELY(ofpbuf_get_sctp_payload(&packet->ofpbuf))) {
+            const struct ovs_key_sctp *sctp_key
+                = nl_attr_get_unspec(a, sizeof(struct ovs_key_sctp));
+
+            packet_set_sctp_port(&packet->ofpbuf, sctp_key->sctp_src,
+                                 sctp_key->sctp_dst);
+        }
         break;
 
     case OVS_KEY_ATTR_MPLS:
@@ -243,6 +302,15 @@ odp_execute_set_action(struct dpif_packet *packet, const struct nlattr *a,
         set_arp(&packet->ofpbuf, nl_attr_get(a), NULL);
         break;
 
+    case OVS_KEY_ATTR_ND:
+        if (OVS_LIKELY(ofpbuf_get_nd_payload(&packet->ofpbuf))) {
+            const struct ovs_key_nd *nd_key
+                   = nl_attr_get_unspec(a, sizeof(struct ovs_key_nd));
+            packet_set_nd(&packet->ofpbuf, nd_key->nd_target,
+                          nd_key->nd_sll, nd_key->nd_tll);
+        }
+        break;
+
     case OVS_KEY_ATTR_DP_HASH:
         md->dp_hash = nl_attr_get_u32(a);
         dpif_packet_set_dp_hash(packet, md->dp_hash);
@@ -259,7 +327,6 @@ odp_execute_set_action(struct dpif_packet *packet, const struct nlattr *a,
     case OVS_KEY_ATTR_VLAN:
     case OVS_KEY_ATTR_ICMP:
     case OVS_KEY_ATTR_ICMPV6:
-    case OVS_KEY_ATTR_ND:
     case OVS_KEY_ATTR_TCP_FLAGS:
     case __OVS_KEY_ATTR_MAX:
     default:
@@ -271,8 +338,9 @@ odp_execute_set_action(struct dpif_packet *packet, const struct nlattr *a,
 
 static void
 odp_execute_masked_set_action(struct dpif_packet *packet,
-                              const struct nlattr *a, struct pkt_metadata *md)
+                              const struct nlattr *a)
 {
+    struct pkt_metadata *md = &packet->md;
     enum ovs_key_attr type = nl_attr_type(a);
     struct mpls_hdr *mh;
 
@@ -331,6 +399,11 @@ odp_execute_masked_set_action(struct dpif_packet *packet,
                 get_mask(a, struct ovs_key_arp));
         break;
 
+    case OVS_KEY_ATTR_ND:
+        odp_set_nd(&packet->ofpbuf, nl_attr_get(a),
+                   get_mask(a, struct ovs_key_nd));
+        break;
+
     case OVS_KEY_ATTR_DP_HASH:
         md->dp_hash = nl_attr_get_u32(a)
             | (dpif_packet_get_dp_hash(packet) & ~*get_mask(a, uint32_t));
@@ -350,7 +423,6 @@ odp_execute_masked_set_action(struct dpif_packet *packet,
     case OVS_KEY_ATTR_VLAN:
     case OVS_KEY_ATTR_ICMP:
     case OVS_KEY_ATTR_ICMPV6:
-    case OVS_KEY_ATTR_ND:
     case OVS_KEY_ATTR_TCP_FLAGS:
     case __OVS_KEY_ATTR_MAX:
     default:
@@ -360,7 +432,7 @@ odp_execute_masked_set_action(struct dpif_packet *packet,
 
 static void
 odp_execute_sample(void *dp, struct dpif_packet *packet, bool steal,
-                   struct pkt_metadata *md, const struct nlattr *action,
+                   const struct nlattr *action,
                    odp_execute_cb dp_execute_action)
 {
     const struct nlattr *subactions = NULL;
@@ -391,13 +463,12 @@ odp_execute_sample(void *dp, struct dpif_packet *packet, bool steal,
         }
     }
 
-    odp_execute_actions(dp, &packet, 1, steal, md, nl_attr_get(subactions),
+    odp_execute_actions(dp, &packet, 1, steal, nl_attr_get(subactions),
                         nl_attr_get_size(subactions), dp_execute_action);
 }
 
 void
-odp_execute_actions(void *dp, struct dpif_packet **packets, int cnt,
-                    bool steal, struct pkt_metadata *md,
+odp_execute_actions(void *dp, struct dpif_packet **packets, int cnt, bool steal,
                     const struct nlattr *actions, size_t actions_len,
                     odp_execute_cb dp_execute_action)
 {
@@ -412,6 +483,8 @@ odp_execute_actions(void *dp, struct dpif_packet **packets, int cnt,
         switch ((enum ovs_action_attr) type) {
             /* These only make sense in the context of a datapath. */
         case OVS_ACTION_ATTR_OUTPUT:
+        case OVS_ACTION_ATTR_TUNNEL_PUSH:
+        case OVS_ACTION_ATTR_TUNNEL_POP:
         case OVS_ACTION_ATTR_USERSPACE:
         case OVS_ACTION_ATTR_RECIRC:
             if (dp_execute_action) {
@@ -419,7 +492,7 @@ odp_execute_actions(void *dp, struct dpif_packet **packets, int cnt,
                  * not need it any more. */
                 bool may_steal = steal && last_action;
 
-                dp_execute_action(dp, packets, cnt, md, a, may_steal);
+                dp_execute_action(dp, packets, cnt, a, may_steal);
 
                 if (last_action) {
                     /* We do not need to free the packets. dp_execute_actions()
@@ -441,16 +514,9 @@ odp_execute_actions(void *dp, struct dpif_packet **packets, int cnt,
                 uint32_t hash;
 
                 for (i = 0; i < cnt; i++) {
-                    struct ofpbuf *buf = &packets[i]->ofpbuf;
-
-                    flow_extract(buf, md, &flow);
+                    flow_extract(&packets[i]->ofpbuf, &packets[i]->md, &flow);
                     hash = flow_hash_5tuple(&flow, hash_act->hash_basis);
 
-                    /* The hash of the first packet is in shared metadata */
-                    if (i == 0) {
-                        md->dp_hash = hash ? hash : 1;
-                    }
-
                     /* We also store the hash value with each packet */
                     dpif_packet_set_dp_hash(packets[i], hash ? hash : 1);
                 }
@@ -501,19 +567,19 @@ odp_execute_actions(void *dp, struct dpif_packet **packets, int cnt,
 
         case OVS_ACTION_ATTR_SET:
             for (i = 0; i < cnt; i++) {
-                odp_execute_set_action(packets[i], nl_attr_get(a), md);
+                odp_execute_set_action(packets[i], nl_attr_get(a));
             }
             break;
 
         case OVS_ACTION_ATTR_SET_MASKED:
             for (i = 0; i < cnt; i++) {
-                odp_execute_masked_set_action(packets[i], nl_attr_get(a), md);
+                odp_execute_masked_set_action(packets[i], nl_attr_get(a));
             }
             break;
 
         case OVS_ACTION_ATTR_SAMPLE:
             for (i = 0; i < cnt; i++) {
-                odp_execute_sample(dp, packets[i], steal && last_action, md, a,
+                odp_execute_sample(dp, packets[i], steal && last_action, a,
                                    dp_execute_action);
             }