datapath: Fix MPLS action validation.
[cascardo/ovs.git] / datapath / vport-lisp.c
index 8e3ff69..1eaeddb 100644 (file)
@@ -165,16 +165,28 @@ static __be64 instance_id_to_tunnel_id(__u8 *iid)
  */
 static u16 get_src_port(struct net *net, struct sk_buff *skb)
 {
-       u32 hash = skb_get_rxhash(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);
+               if (skb->protocol == htons(ETH_P_IP)) {
+                       struct iphdr *iph;
+                       int size = (sizeof(iph->saddr) * 2) / sizeof(u32);
+
+                       iph = (struct iphdr *) skb_inner_network_header(skb);
+                       hash = jhash2((const u32 *)&iph->saddr, size, 0);
+               } else if (skb->protocol == htons(ETH_P_IPV6)) {
+                       struct ipv6hdr *ipv6hdr;
+
+                       ipv6hdr = (struct ipv6hdr *) skb_inner_network_header(skb);
+                       hash = jhash2((const u32 *)&ipv6hdr->saddr,
+                                     (sizeof(struct in6_addr) * 2) / sizeof(u32), 0);
+               } else {
+                       pr_warn_once("LISP inner protocol is not IP when "
+                                    "calculating hash.\n");
+               }
        }
 
        inet_get_local_port_range(net, &low, &high);
@@ -189,8 +201,9 @@ static void lisp_build_header(const struct vport *vport,
        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;
+       const struct ovs_key_ipv4_tunnel *tun_key;
 
+       tun_key = &OVS_CB(skb)->egress_tun_info->tunnel;
        udph->dest = lisp_port->dst_port;
        udph->source = htons(get_src_port(net, skb));
        udph->check = 0;
@@ -217,7 +230,7 @@ static int lisp_rcv(struct sock *sk, struct sk_buff *skb)
        struct lisp_port *lisp_port;
        struct lisphdr *lisph;
        struct iphdr *iph, *inner_iph;
-       struct ovs_key_ipv4_tunnel tun_key;
+       struct ovs_tunnel_info tun_info;
        __be64 key;
        struct ethhdr *ethh;
        __be16 protocol;
@@ -238,7 +251,9 @@ static int lisp_rcv(struct sock *sk, struct sk_buff *skb)
 
        /* Save outer tunnel values */
        iph = ip_hdr(skb);
-       ovs_flow_tun_key_init(&tun_key, iph, key, TUNNEL_KEY);
+       ovs_flow_tun_info_init(&tun_info, iph,
+                              udp_hdr(skb)->source, udp_hdr(skb)->dest,
+                              key, TUNNEL_KEY, NULL, 0);
 
        /* Drop non-IP inner packets */
        inner_iph = (struct iphdr *)(lisph + 1);
@@ -263,7 +278,7 @@ static int lisp_rcv(struct sock *sk, struct sk_buff *skb)
 
        ovs_skb_postpush_rcsum(skb, skb->data, ETH_HLEN);
 
-       ovs_vport_receive(vport_from_priv(lisp_port), skb, &tun_key);
+       ovs_vport_receive(vport_from_priv(lisp_port), skb, &tun_info);
        goto out;
 
 error:
@@ -402,6 +417,12 @@ static int handle_offloads(struct sk_buff *skb)
 #else
 static int handle_offloads(struct sk_buff *skb)
 {
+       if ((ovs_skb_get_inner_protocol(skb) || skb->encapsulation) &&
+           skb_is_gso(skb)) {
+               kfree_skb(skb);
+               return -ENOSYS;
+       }
+
        if (skb_is_gso(skb)) {
                int err = skb_unclone(skb, GFP_ATOMIC);
                if (unlikely(err))
@@ -418,6 +439,7 @@ static int handle_offloads(struct sk_buff *skb)
 
 static int lisp_send(struct vport *vport, struct sk_buff *skb)
 {
+       struct ovs_key_ipv4_tunnel *tun_key;
        int network_offset = skb_network_offset(skb);
        struct rtable *rt;
        int min_headroom;
@@ -426,9 +448,11 @@ static int lisp_send(struct vport *vport, struct sk_buff *skb)
        int sent_len;
        int err;
 
-       if (unlikely(!OVS_CB(skb)->tun_key))
+       if (unlikely(!OVS_CB(skb)->egress_tun_info))
                return -EINVAL;
 
+       tun_key = &OVS_CB(skb)->egress_tun_info->tunnel;
+
        if (skb->protocol != htons(ETH_P_IP) &&
            skb->protocol != htons(ETH_P_IPV6)) {
                kfree_skb(skb);
@@ -436,12 +460,10 @@ static int lisp_send(struct vport *vport, struct sk_buff *skb)
        }
 
        /* Route lookup */
-       saddr = OVS_CB(skb)->tun_key->ipv4_src;
+       saddr = 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,
+                       &saddr, tun_key->ipv4_dst,
+                       IPPROTO_UDP, tun_key->ipv4_tos,
                        skb->mark);
        if (IS_ERR(rt)) {
                err = PTR_ERR(rt);
@@ -479,14 +501,13 @@ static int lisp_send(struct vport *vport, struct sk_buff *skb)
        if (err)
                goto err_free_rt;
 
-       skb->local_df = 1;
+       skb->ignore_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);
+       df = tun_key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0;
+       sent_len = iptunnel_xmit(skb->sk, rt, skb,
+                            saddr, tun_key->ipv4_dst,
+                            IPPROTO_UDP, tun_key->ipv4_tos,
+                            tun_key->ipv4_ttl, df, false);
 
        return sent_len > 0 ? sent_len + network_offset : sent_len;
 
@@ -502,11 +523,33 @@ static const char *lisp_get_name(const struct vport *vport)
        return lisp_port->name;
 }
 
+static int lisp_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
+                                   struct ovs_tunnel_info *egress_tun_info)
+{
+       struct net *net = ovs_dp_get_net(vport->dp);
+       struct lisp_port *lisp_port = lisp_vport(vport);
+
+       if (skb->protocol != htons(ETH_P_IP) &&
+           skb->protocol != htons(ETH_P_IPV6)) {
+               return -EINVAL;
+       }
+
+       /*
+        * Get tp_src and tp_dst, refert to lisp_build_header().
+        */
+       return ovs_tunnel_get_egress_info(egress_tun_info, net,
+                                         OVS_CB(skb)->egress_tun_info,
+                                         IPPROTO_UDP, skb->mark,
+                                         htons(get_src_port(net, skb)),
+                                         lisp_port->dst_port);
+}
+
 const struct vport_ops ovs_lisp_vport_ops = {
-       .type           = OVS_VPORT_TYPE_LISP,
-       .create         = lisp_tnl_create,
-       .destroy        = lisp_tnl_destroy,
-       .get_name       = lisp_get_name,
-       .get_options    = lisp_get_options,
-       .send           = lisp_send,
+       .type                   = OVS_VPORT_TYPE_LISP,
+       .create                 = lisp_tnl_create,
+       .destroy                = lisp_tnl_destroy,
+       .get_name               = lisp_get_name,
+       .get_options            = lisp_get_options,
+       .send                   = lisp_send,
+       .get_egress_tun_info    = lisp_get_egress_tun_info,
 };