Copy skbs when we save them.
[sliver-openvswitch.git] / datapath / forward.c
index 48bec7c..33b3a5e 100644 (file)
@@ -12,7 +12,9 @@
 #include <linux/types.h>
 #include "forward.h"
 #include "datapath.h"
+#include "openflow/nicira-ext.h"
 #include "dp_act.h"
+#include "nx_msg.h"
 #include "chain.h"
 #include "flow.h"
 
@@ -66,6 +68,8 @@ int run_flow_through_tables(struct sw_chain *chain, struct sk_buff *skb,
 void fwd_port_input(struct sw_chain *chain, struct sk_buff *skb,
                    struct net_bridge_port *p)
 {
+       WARN_ON_ONCE(skb_shared(skb));
+       WARN_ON_ONCE(skb->destructor);
        if (run_flow_through_tables(chain, skb, p))
                dp_output_control(chain->dp, skb, fwd_save_skb(skb), 
                                  chain->dp->miss_send_len,
@@ -118,8 +122,6 @@ recv_packet_out(struct sw_chain *chain, const struct sender *sender,
 {
        const struct ofp_packet_out *opo = msg;
        struct sk_buff *skb;
-       struct vlan_ethhdr *mac;
-       int nh_ofs;
        uint16_t v_code;
        struct sw_flow_key key;
        size_t actions_len = ntohs(opo->actions_len);
@@ -141,16 +143,11 @@ recv_packet_out(struct sw_chain *chain, const struct sender *sender,
                /* FIXME?  We don't reserve NET_IP_ALIGN or NET_SKB_PAD since
                 * we're just transmitting this raw without examining anything
                 * at those layers. */
-               memcpy(skb_put(skb, data_len), (uint8_t *)opo->actions + actions_len, 
-                               data_len);
-
-               skb_set_mac_header(skb, 0);
-               mac = vlan_eth_hdr(skb);
-               if (likely(mac->h_vlan_proto != htons(ETH_P_8021Q)))
-                       nh_ofs = sizeof(struct ethhdr);
-               else
-                       nh_ofs = sizeof(struct vlan_ethhdr);
-               skb_set_network_header(skb, nh_ofs);
+               skb_put(skb, data_len);
+               skb_copy_to_linear_data(skb,
+                                       (uint8_t *)opo->actions + actions_len, 
+                                       data_len);
+               skb_reset_mac_header(skb);
        } else {
                skb = retrieve_skb(ntohl(opo->buffer_id));
                if (!skb)
@@ -222,7 +219,7 @@ add_flow(struct sw_chain *chain, const struct sender *sender,
        if (v_code != ACT_VALIDATION_OK) {
                dp_send_error_msg(chain->dp, sender, OFPET_BAD_ACTION, v_code,
                                  ofm, ntohs(ofm->header.length));
-               goto error;
+               goto error_free_flow;
        }
 
        /* Fill out flow. */
@@ -238,7 +235,11 @@ add_flow(struct sw_chain *chain, const struct sender *sender,
 
        /* Act. */
        error = chain_insert(chain, flow);
-       if (error)
+       if (error == -ENOBUFS) {
+               dp_send_error_msg(chain->dp, sender, OFPET_FLOW_MOD_FAILED, 
+                               OFPFMFC_ALL_TABLES_FULL, ofm, ntohs(ofm->header.length));
+               goto error_free_flow;
+       } else if (error)
                goto error_free_flow;
        error = 0;
        if (ntohl(ofm->buffer_id) != (uint32_t) -1) {
@@ -333,6 +334,25 @@ recv_flow(struct sw_chain *chain, const struct sender *sender, const void *msg)
        }
 }
 
+static int
+recv_vendor(struct sw_chain *chain, const struct sender *sender, 
+               const void *msg)
+{
+       const struct ofp_vendor_header *ovh = msg;
+
+       switch(ntohl(ovh->vendor))
+       {
+       case NX_VENDOR_ID:
+               return nx_recv_msg(chain, sender, msg);
+       default:
+               if (net_ratelimit())
+                       printk("Uknown vendor: %#x\n", ntohl(ovh->vendor));
+               dp_send_error_msg(chain->dp, sender, OFPET_BAD_REQUEST,
+                                 OFPBRC_BAD_VENDOR, msg, ntohs(ovh->header.length));
+               return -EINVAL;
+       }
+}
+
 /* 'msg', which is 'length' bytes long, was received across Netlink from
  * 'sender'.  Apply it to 'chain'. */
 int
@@ -351,6 +371,18 @@ fwd_control_input(struct sw_chain *chain, const struct sender *sender,
                        sizeof (struct ofp_header),
                        recv_hello,
                },
+               [OFPT_ECHO_REQUEST] = {
+                       sizeof (struct ofp_header),
+                       recv_echo_request,
+               },
+               [OFPT_ECHO_REPLY] = {
+                       sizeof (struct ofp_header),
+                       recv_echo_reply,
+               },
+               [OFPT_VENDOR] = {
+                       sizeof (struct ofp_vendor_header),
+                       recv_vendor,
+               },
                [OFPT_FEATURES_REQUEST] = {
                        sizeof (struct ofp_header),
                        recv_features_request,
@@ -374,15 +406,7 @@ fwd_control_input(struct sw_chain *chain, const struct sender *sender,
                [OFPT_PORT_MOD] = {
                        sizeof (struct ofp_port_mod),
                        recv_port_mod,
-               },
-               [OFPT_ECHO_REQUEST] = {
-                       sizeof (struct ofp_header),
-                       recv_echo_request,
-               },
-               [OFPT_ECHO_REPLY] = {
-                       sizeof (struct ofp_header),
-                       recv_echo_reply,
-               },
+               }
        };
 
        struct ofp_header *oh;
@@ -399,8 +423,12 @@ fwd_control_input(struct sw_chain *chain, const struct sender *sender,
                                  OFPBRC_BAD_VERSION, msg, length);
                return -EINVAL;
        }
-       if (ntohs(oh->length) > length)
+       if (ntohs(oh->length) != length) {
+               if (net_ratelimit())
+                       printk("received message length wrong: %d/%d\n", 
+                               ntohs(oh->length), length);
                return -EINVAL;
+       }
 
        if (oh->type < ARRAY_SIZE(packets)) {
                const struct openflow_packet *pkt = &packets[oh->type];
@@ -437,6 +465,11 @@ uint32_t fwd_save_skb(struct sk_buff *skb)
        unsigned long int flags;
        uint32_t id;
 
+       /* FIXME: Probably just need a skb_clone() here. */
+       skb = skb_copy(skb, GFP_ATOMIC);
+       if (!skb)
+               return -1;
+
        spin_lock_irqsave(&buffer_lock, flags);
        buffer_idx = (buffer_idx + 1) & PKT_BUFFER_MASK;
        p = &buffers[buffer_idx];
@@ -445,9 +478,13 @@ uint32_t fwd_save_skb(struct sk_buff *skb)
                 * OVERWRITE_SECS old. */
                if (time_before(jiffies, p->exp_jiffies)) {
                        spin_unlock_irqrestore(&buffer_lock, flags);
+                       kfree_skb(skb);
                        return -1;
                } else {
-                       /* Defer kfree_skb() until interrupts re-enabled. */
+                       /* Defer kfree_skb() until interrupts re-enabled.
+                        * FIXME: we only need to do that if it has a
+                        * destructor, but it never should since we orphan
+                        * sk_buffs on entry. */
                        old_skb = p->skb;
                }
        }
@@ -455,7 +492,6 @@ uint32_t fwd_save_skb(struct sk_buff *skb)
         * special. */
        if (++p->cookie >= (1u << PKT_COOKIE_BITS) - 1)
                p->cookie = 0;
-       skb_get(skb);
        p->skb = skb;
        p->exp_jiffies = jiffies + OVERWRITE_JIFFIES;
        id = buffer_idx | (p->cookie << PKT_BUFFER_BITS);
@@ -528,4 +564,3 @@ void fwd_exit(void)
 {
        fwd_discard_all();
 }
-