Prepare Open vSwitch 1.1.2 release.
[sliver-openvswitch.git] / datapath / vport-capwap.c
index 7ae3790..e2cf400 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Nicira Networks.
+ * Copyright (c) 2010, 2011 Nicira Networks.
  * Distributed under the terms of the GNU GPL version 2.
  *
  * Significant portions of this file may be copied from parts of the Linux
@@ -115,37 +115,48 @@ static struct netns_frags frag_netns_state = {
 
 static struct socket *capwap_rcv_socket;
 
-static int capwap_hdr_len(const struct tnl_port_config *port_config)
+static int capwap_hdr_len(const struct tnl_mutable_config *mutable)
 {
-       /* CAPWAP has neither checksums nor keys, so reject ports with those. */
-       if (port_config->flags & (TNL_F_CSUM | TNL_F_IN_KEY_MATCH |
-                                 TNL_F_OUT_KEY_ACTION))
+       /* CAPWAP has no checksums. */
+       if (mutable->flags & TNL_F_CSUM)
                return -EINVAL;
 
-       if (port_config->in_key != 0 || port_config->out_key != 0)
+       /* CAPWAP has no keys, so check that the configuration for keys is the
+        * default if no key-specific attributes are used.
+        */
+       if ((mutable->flags & (TNL_F_IN_KEY_MATCH | TNL_F_OUT_KEY_ACTION)) !=
+           (TNL_F_IN_KEY_MATCH | TNL_F_OUT_KEY_ACTION))
                return -EINVAL;
 
        return CAPWAP_HLEN;
 }
 
-static struct sk_buff *capwap_build_header(struct sk_buff *skb,
-                                          const struct vport *vport,
-                                          const struct tnl_mutable_config *mutable,
-                                          struct dst_entry *dst)
+static void capwap_build_header(const struct vport *vport,
+                               const struct tnl_mutable_config *mutable,
+                               void *header)
 {
-       struct udphdr *udph = udp_hdr(skb);
-       struct capwaphdr *cwh = capwap_hdr(skb);
+       struct udphdr *udph = header;
+       struct capwaphdr *cwh = (struct capwaphdr *)(udph + 1);
 
        udph->source = htons(CAPWAP_SRC_PORT);
        udph->dest = htons(CAPWAP_DST_PORT);
-       udph->len = htons(skb->len - sizeof(struct iphdr));
        udph->check = 0;
 
        cwh->begin = NO_FRAG_HDR;
        cwh->frag_id = 0;
        cwh->frag_off = 0;
+}
+
+static struct sk_buff *capwap_update_header(const struct vport *vport,
+                                           const struct tnl_mutable_config *mutable,
+                                           struct dst_entry *dst,
+                                           struct sk_buff *skb)
+{
+       struct udphdr *udph = udp_hdr(skb);
 
-       if (unlikely(skb->len > dst_mtu(dst)))
+       udph->len = htons(skb->len - skb_transport_offset(skb));
+
+       if (unlikely(skb->len - skb_network_offset(skb) > dst_mtu(dst)))
                skb = fragment(skb, vport, dst);
 
        return skb;
@@ -195,7 +206,7 @@ static int capwap_rcv(struct sock *sk, struct sk_buff *skb)
                goto error;
        }
 
-       tnl_rcv(vport, skb);
+       tnl_rcv(vport, skb, iph->tos);
        goto out;
 
 error:
@@ -204,16 +215,17 @@ out:
        return 0;
 }
 
-struct tnl_ops capwap_tnl_ops = {
+static const struct tnl_ops capwap_tnl_ops = {
        .tunnel_type    = TNL_T_PROTO_CAPWAP,
        .ipproto        = IPPROTO_UDP,
        .hdr_len        = capwap_hdr_len,
        .build_header   = capwap_build_header,
+       .update_header  = capwap_update_header,
 };
 
-static struct vport *capwap_create(const char *name, const void __user *config)
+static struct vport *capwap_create(const struct vport_parms *parms)
 {
-       return tnl_create(name, config, &capwap_vport_ops, &capwap_tnl_ops);
+       return tnl_create(parms, &capwap_vport_ops, &capwap_tnl_ops);
 }
 
 /* Random value.  Irrelevant as long as it's not 0 since we set the handler. */
@@ -228,7 +240,7 @@ static int capwap_init(void)
                goto error;
 
        sin.sin_family = AF_INET;
-       sin.sin_addr.s_addr = INADDR_ANY;
+       sin.sin_addr.s_addr = htonl(INADDR_ANY);
        sin.sin_port = htons(CAPWAP_DST_PORT);
 
        err = kernel_bind(capwap_rcv_socket, (struct sockaddr *)&sin,
@@ -241,7 +253,7 @@ static int capwap_init(void)
 
        defrag_init();
 
-       return tnl_init();
+       return 0;
 
 error_sock:
        sock_release(capwap_rcv_socket);
@@ -252,7 +264,6 @@ error:
 
 static void capwap_exit(void)
 {
-       tnl_exit();
        defrag_exit();
        sock_release(capwap_rcv_socket);
 }
@@ -282,17 +293,19 @@ static struct sk_buff *fragment(struct sk_buff *skb, const struct vport *vport,
                                struct dst_entry *dst)
 {
        struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
-       unsigned int hlen = sizeof(struct iphdr) + CAPWAP_HLEN;
-       unsigned int headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len;
+       unsigned int hlen = skb_transport_offset(skb) + CAPWAP_HLEN;
+       unsigned int headroom;
+       unsigned int max_frame_len = dst_mtu(dst) + skb_network_offset(skb);
        struct sk_buff *result = NULL, *list_cur = NULL;
        unsigned int remaining;
        unsigned int offset;
        __be16 frag_id;
 
-       if (hlen + ~FRAG_OFF_MASK + 1 > dst_mtu(dst)) {
+       if (hlen + ~FRAG_OFF_MASK + 1 > max_frame_len) {
                if (net_ratelimit())
                        pr_warn("capwap link mtu (%d) is less than minimum packet (%d)\n",
-                               dst_mtu(dst), hlen + ~FRAG_OFF_MASK + 1);
+                               dst_mtu(dst),
+                               hlen - skb_network_offset(skb) + ~FRAG_OFF_MASK + 1);
                goto error;
        }
 
@@ -300,14 +313,17 @@ static struct sk_buff *fragment(struct sk_buff *skb, const struct vport *vport,
        offset = 0;
        frag_id = htons(atomic_inc_return(&tnl_vport->frag_id));
 
+       headroom = dst->header_len + 16;
+       if (!skb_network_offset(skb))
+               headroom += LL_RESERVED_SPACE(dst->dev);
+
        while (remaining) {
                struct sk_buff *skb2;
                int frag_size;
-               struct iphdr *iph;
                struct udphdr *udph;
                struct capwaphdr *cwh;
 
-               frag_size = min(remaining, dst_mtu(dst) - hlen);
+               frag_size = min(remaining, max_frame_len - hlen);
                if (remaining > frag_size)
                        frag_size &= FRAG_OFF_MASK;
 
@@ -317,23 +333,22 @@ static struct sk_buff *fragment(struct sk_buff *skb, const struct vport *vport,
 
                skb_reserve(skb2, headroom);
                __skb_put(skb2, hlen + frag_size);
-               skb_reset_network_header(skb2);
-               skb_set_transport_header(skb2, sizeof(struct iphdr));
 
-               /* Copy IP/UDP/CAPWAP header. */
+               if (skb_network_offset(skb))
+                       skb_reset_mac_header(skb2);
+               skb_set_network_header(skb2, skb_network_offset(skb));
+               skb_set_transport_header(skb2, skb_transport_offset(skb));
+
+               /* Copy (Ethernet)/IP/UDP/CAPWAP header. */
                copy_skb_metadata(skb, skb2);
-               skb_copy_from_linear_data(skb, skb_network_header(skb2), hlen);
+               skb_copy_from_linear_data(skb, skb2->data, hlen);
 
                /* Copy this data chunk. */
                if (skb_copy_bits(skb, hlen + offset, skb2->data + hlen, frag_size))
                        BUG();
 
-               iph = ip_hdr(skb2);
-               iph->tot_len = hlen + frag_size;
-               ip_send_check(iph);
-
                udph = udp_hdr(skb2);
-               udph->len = htons(skb2->len - sizeof(struct iphdr));
+               udph->len = htons(skb2->len - skb_transport_offset(skb2));
 
                cwh = capwap_hdr(skb2);
                if (remaining > frag_size)
@@ -356,11 +371,7 @@ static struct sk_buff *fragment(struct sk_buff *skb, const struct vport *vport,
        goto out;
 
 error:
-       while (result) {
-               list_cur = result->next;
-               kfree_skb(result);
-               result = list_cur;
-       }
+       tnl_free_linked_skbs(result);
 out:
        kfree_skb(skb);
        return result;
@@ -636,22 +647,21 @@ static void capwap_frag_expire(unsigned long ifq)
        inet_frag_put(&fq->ifq, &frag_state);
 }
 
-struct vport_ops capwap_vport_ops = {
-       .type           = "capwap",
+const struct vport_ops capwap_vport_ops = {
+       .type           = ODP_VPORT_TYPE_CAPWAP,
        .flags          = VPORT_F_GEN_STATS,
        .init           = capwap_init,
        .exit           = capwap_exit,
        .create         = capwap_create,
-       .modify         = tnl_modify,
        .destroy        = tnl_destroy,
-       .set_mtu        = tnl_set_mtu,
        .set_addr       = tnl_set_addr,
        .get_name       = tnl_get_name,
        .get_addr       = tnl_get_addr,
+       .get_options    = tnl_get_options,
+       .set_options    = tnl_set_options,
        .get_dev_flags  = vport_gen_get_dev_flags,
        .is_running     = vport_gen_is_running,
        .get_operstate  = vport_gen_get_operstate,
-       .get_mtu        = tnl_get_mtu,
        .send           = tnl_send,
 };