X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=datapath%2Fvport-capwap.c;h=f0bb3270fc45c9f692f014cd3da83535a2b84d23;hb=58a89177ab00d6a1c1b13479f8fd8e3a7b0aca6c;hp=59fb05100a085535f1c2da6d6b2a02dd9f041ead;hpb=d295e8e97acae13552a5b220d3fbcff8201064a2;p=sliver-openvswitch.git diff --git a/datapath/vport-capwap.c b/datapath/vport-capwap.c index 59fb05100..f0bb3270f 100644 --- a/datapath/vport-capwap.c +++ b/datapath/vport-capwap.c @@ -1,11 +1,13 @@ /* - * 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 * kernel, by Linus Torvalds and others. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) @@ -19,6 +21,7 @@ #include #include #include +#include #include "tunnel.h" #include "vport.h" @@ -112,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; @@ -160,7 +174,7 @@ static inline struct sk_buff *process_capwap_proto(struct sk_buff *skb) return defrag(skb, true); else { if (net_ratelimit()) - printk(KERN_WARNING "openvswitch: unparsable packet receive on capwap socket\n"); + pr_warn("unparsable packet receive on capwap socket\n"); kfree_skb(skb); return NULL; @@ -192,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: @@ -201,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. */ @@ -225,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, @@ -238,18 +253,17 @@ static int capwap_init(void) defrag_init(); - return tnl_init(); + return 0; error_sock: sock_release(capwap_rcv_socket); error: - printk(KERN_WARNING "openvswitch: cannot register capwap protocol handler\n"); + pr_warn("cannot register capwap protocol handler\n"); return err; } static void capwap_exit(void) { - tnl_exit(); defrag_exit(); sock_release(capwap_rcv_socket); } @@ -279,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()) - printk(KERN_WARNING "openvswitch: capwap link mtu (%d) is less than minimum packet (%d)\n", - dst_mtu(dst), hlen + ~FRAG_OFF_MASK + 1); + pr_warn("capwap link mtu (%d) is less than minimum packet (%d)\n", + dst_mtu(dst), + hlen - skb_network_offset(skb) + ~FRAG_OFF_MASK + 1); goto error; } @@ -297,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; @@ -314,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) @@ -353,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; @@ -633,23 +647,23 @@ 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, }; - -#endif /* Linux kernel >= 2.6.26 */ +#else +#warning CAPWAP tunneling will not be available on kernels before 2.6.26 +#endif /* Linux kernel < 2.6.26 */