X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=datapath%2Fvport-lisp.c;h=a1e2b2b69caa678249e3ceb259f83f2bb797719b;hb=e2f3178f0582eda302bdc5629189b6a56d9fbcdd;hp=209df5905d7cdca4746d9d7b6a2cc3312fcfb455;hpb=3cfede14890f1fae3a8faf7ba179018201079dc6;p=sliver-openvswitch.git diff --git a/datapath/vport-lisp.c b/datapath/vport-lisp.c index 209df5905..a1e2b2b69 100644 --- a/datapath/vport-lisp.c +++ b/datapath/vport-lisp.c @@ -20,7 +20,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) #include #include @@ -30,13 +29,14 @@ #include #include +#include #include +#include #include "datapath.h" -#include "tunnel.h" +#include "gso.h" #include "vport.h" - /* * LISP encapsulation header: * @@ -160,17 +160,39 @@ static __be64 instance_id_to_tunnel_id(__u8 *iid) #endif } +/* Compute source UDP port for outgoing packet. + * Currently we use the flow hash. + */ +static u16 get_src_port(struct net *net, struct sk_buff *skb) +{ + u32 hash = skb_get_hash(skb); + unsigned int range; + int high; + int low; + + if (!hash) { + struct sw_flow_key *pkt_key = OVS_CB(skb)->pkt_key; + + hash = jhash2((const u32 *)pkt_key, + sizeof(*pkt_key) / sizeof(u32), 0); + } + + inet_get_local_port_range(net, &low, &high); + range = (high - low) + 1; + return (((u64) hash * range) >> 32) + low; +} + static void lisp_build_header(const struct vport *vport, - struct sk_buff *skb, - int tunnel_hlen) + struct sk_buff *skb) { + struct net *net = ovs_dp_get_net(vport->dp); struct lisp_port *lisp_port = lisp_vport(vport); struct udphdr *udph = udp_hdr(skb); struct lisphdr *lisph = (struct lisphdr *)(udph + 1); const struct ovs_key_ipv4_tunnel *tun_key = OVS_CB(skb)->tun_key; udph->dest = lisp_port->dst_port; - udph->source = htons(ovs_tnl_get_src_port(skb)); + udph->source = htons(get_src_port(net, skb)); udph->check = 0; udph->len = htons(skb->len - skb_transport_offset(skb)); @@ -204,13 +226,11 @@ static int lisp_rcv(struct sock *sk, struct sk_buff *skb) if (unlikely(!lisp_port)) goto error; - if (unlikely(!pskb_may_pull(skb, LISP_HLEN))) + if (iptunnel_pull_header(skb, LISP_HLEN, 0)) goto error; lisph = lisp_hdr(skb); - skb_pull_rcsum(skb, LISP_HLEN); - if (lisph->instance_id_present != 1) key = 0; else @@ -218,7 +238,7 @@ static int lisp_rcv(struct sock *sk, struct sk_buff *skb) /* Save outer tunnel values */ iph = ip_hdr(skb); - tnl_tun_key_init(&tun_key, iph, key, OVS_TNL_F_KEY); + ovs_flow_tun_key_init(&tun_key, iph, key, TUNNEL_KEY); /* Drop non-IP inner packets */ inner_iph = (struct iphdr *)(lisph + 1); @@ -232,6 +252,7 @@ static int lisp_rcv(struct sock *sk, struct sk_buff *skb) default: goto error; } + skb->protocol = protocol; /* Add Ethernet header */ ethh = (struct ethhdr *)skb_push(skb, ETH_HLEN); @@ -242,7 +263,7 @@ static int lisp_rcv(struct sock *sk, struct sk_buff *skb) ovs_skb_postpush_rcsum(skb, skb->data, ETH_HLEN); - ovs_tnl_rcv(vport_from_priv(lisp_port), skb, &tun_key); + ovs_vport_receive(vport_from_priv(lisp_port), skb, &tun_key); goto out; error: @@ -361,27 +382,118 @@ error: return ERR_PTR(err); } -static int lisp_tnl_send(struct vport *vport, struct sk_buff *skb) +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,12,0) + +static void lisp_fix_segment(struct sk_buff *skb) +{ + struct udphdr *udph = udp_hdr(skb); + + udph->len = htons(skb->len - skb_transport_offset(skb)); +} + +static int handle_offloads(struct sk_buff *skb) +{ + if (skb_is_gso(skb)) + OVS_GSO_CB(skb)->fix_segment = lisp_fix_segment; + else if (skb->ip_summed != CHECKSUM_PARTIAL) + skb->ip_summed = CHECKSUM_NONE; + return 0; +} +#else +static int handle_offloads(struct sk_buff *skb) +{ + if (skb_is_gso(skb)) { + int err = skb_unclone(skb, GFP_ATOMIC); + if (unlikely(err)) + return err; + + skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL; + } else if (skb->ip_summed != CHECKSUM_PARTIAL) + skb->ip_summed = CHECKSUM_NONE; + + skb->encapsulation = 1; + return 0; +} +#endif + +static int lisp_send(struct vport *vport, struct sk_buff *skb) { - int tnl_len; int network_offset = skb_network_offset(skb); + struct rtable *rt; + int min_headroom; + __be32 saddr; + __be16 df; + int sent_len; + int err; if (unlikely(!OVS_CB(skb)->tun_key)) return -EINVAL; - /* We only encapsulate IPv4 and IPv6 packets */ - switch (skb->protocol) { - case htons(ETH_P_IP): - case htons(ETH_P_IPV6): - /* Pop off "inner" Ethernet header */ - skb_pull(skb, network_offset); - tnl_len = ovs_tnl_send(vport, skb, IPPROTO_UDP, - LISP_HLEN, lisp_build_header); - return tnl_len > 0 ? tnl_len + network_offset : tnl_len; - default: + if (skb->protocol != htons(ETH_P_IP) && + skb->protocol != htons(ETH_P_IPV6)) { kfree_skb(skb); return 0; } + + /* Route lookup */ + saddr = OVS_CB(skb)->tun_key->ipv4_src; + rt = find_route(ovs_dp_get_net(vport->dp), + &saddr, + OVS_CB(skb)->tun_key->ipv4_dst, + IPPROTO_UDP, + OVS_CB(skb)->tun_key->ipv4_tos, + skb->mark); + if (IS_ERR(rt)) { + err = PTR_ERR(rt); + goto error; + } + + min_headroom = LL_RESERVED_SPACE(rt_dst(rt).dev) + rt_dst(rt).header_len + + sizeof(struct iphdr) + LISP_HLEN; + + if (skb_headroom(skb) < min_headroom || skb_header_cloned(skb)) { + int head_delta = SKB_DATA_ALIGN(min_headroom - + skb_headroom(skb) + + 16); + + err = pskb_expand_head(skb, max_t(int, head_delta, 0), + 0, GFP_ATOMIC); + if (unlikely(err)) + goto err_free_rt; + } + + /* Reset l2 headers. */ + skb_pull(skb, network_offset); + skb_reset_mac_header(skb); + vlan_set_tci(skb, 0); + + skb_reset_inner_headers(skb); + + __skb_push(skb, LISP_HLEN); + skb_reset_transport_header(skb); + + lisp_build_header(vport, skb); + + /* Offloading */ + err = handle_offloads(skb); + if (err) + goto err_free_rt; + + skb->local_df = 1; + + df = OVS_CB(skb)->tun_key->tun_flags & + TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; + sent_len = iptunnel_xmit(rt, skb, + saddr, OVS_CB(skb)->tun_key->ipv4_dst, + IPPROTO_UDP, OVS_CB(skb)->tun_key->ipv4_tos, + OVS_CB(skb)->tun_key->ipv4_ttl, df, false); + + return sent_len > 0 ? sent_len + network_offset : sent_len; + +err_free_rt: + ip_rt_put(rt); +error: + return err; } static const char *lisp_get_name(const struct vport *vport) @@ -396,8 +508,5 @@ const struct vport_ops ovs_lisp_vport_ops = { .destroy = lisp_tnl_destroy, .get_name = lisp_get_name, .get_options = lisp_get_options, - .send = lisp_tnl_send, + .send = lisp_send, }; -#else -#warning LISP tunneling will not be available on kernels before 2.6.26 -#endif /* Linux kernel < 2.6.26 */