In update_mapping(), update m->hw_addr unconditionally.
[sliver-openvswitch.git] / datapath / nx_act_snat.c
index 0bb98e0..6334285 100644 (file)
@@ -97,6 +97,17 @@ done:
        spin_unlock_irqrestore(&p->lock, flags);
 }
 
+/* When the packet is bound for a local interface, strip off the fake
+ * routing table.
+ */
+void snat_local_in(struct sk_buff *skb)
+{
+       if (skb->dst == (struct dst_entry *)&__fake_rtable) {
+               dst_release(skb->dst);
+               skb->dst = NULL;
+       }
+}
+
 /* Check whether destination IP's address is in the IP->MAC mappings.
  * If it is, then overwrite the destination MAC with the value from the
  * cache.
@@ -105,23 +116,13 @@ done:
 static int
 dnat_mac(struct net_bridge_port *p, struct sk_buff *skb)
 {
-       struct snat_conf *sc;
+       struct snat_conf *sc = p->snat;
        struct iphdr *iph = ip_hdr(skb);
        struct ethhdr *eh = eth_hdr(skb);
        struct snat_mapping *m;
-       unsigned long flags;
-
-       spin_lock_irqsave(&p->lock, flags);
-       sc = p->snat;
-       if (!sc) {
-               spin_unlock_irqrestore(&p->lock, flags);
-               return -EINVAL;
-       }
 
-       if (skb->protocol != htons(ETH_P_IP)) {
-               spin_unlock_irqrestore(&p->lock, flags);
+       if (skb->protocol != htons(ETH_P_IP)) 
                return 0;
-       }
 
        list_for_each_entry (m, &sc->mappings, node) {
                if (m->ip_addr == iph->daddr){
@@ -129,7 +130,6 @@ dnat_mac(struct net_bridge_port *p, struct sk_buff *skb)
                        if (!make_writable(&skb)) {
                                if (net_ratelimit())
                                        printk("make_writable failed\n");
-                               spin_unlock_irqrestore(&p->lock, flags);
                                return -EINVAL;
                        }
                        m->used = jiffies;
@@ -138,20 +138,59 @@ dnat_mac(struct net_bridge_port *p, struct sk_buff *skb)
                }
        }
 
-       spin_unlock_irqrestore(&p->lock, flags);
        return 0;
 }
 
+static int
+__snat_this_address(struct snat_conf *sc, u32 ip_addr)
+{
+       if (sc) {
+               u32 h_ip_addr = ntohl(ip_addr);
+               return (h_ip_addr >= sc->ip_addr_start &&
+                       h_ip_addr <= sc->ip_addr_end);
+       }
+       return 0;
+}
+
+static int
+snat_this_address(struct net_bridge_port *p, u32 ip_addr)
+{
+       unsigned long int flags;
+       int retval;
+
+       spin_lock_irqsave(&p->lock, flags);
+       retval = __snat_this_address(p->snat, ip_addr);
+       spin_unlock_irqrestore(&p->lock, flags);
+
+       return retval;
+}
+
 static int
 snat_pre_route_finish(struct sk_buff *skb)
 {
        struct net_bridge_port *p = skb->dev->br_port;
+       struct snat_conf *sc;
+       struct iphdr *iph = ip_hdr(skb);
+       unsigned long flags;
+
+       skb->dst = (struct dst_entry *)&__fake_rtable;
+       dst_hold(skb->dst);
+
+       /* Don't process packets that were not translated due to NAT */
+       spin_lock_irqsave(&p->lock, flags);
+       sc = p->snat;
+       if (__snat_this_address(sc, iph->daddr)) {
+               spin_unlock_irqrestore(&p->lock, flags);
+               return -1;
+       }
 
        /* If SNAT is configured for this input device, check the IP->MAC
         * mappings to see if we should update the destination MAC. */
-       if (p->snat)
+       if (sc)
                dnat_mac(skb->dev->br_port, skb);
 
+       spin_unlock_irqrestore(&p->lock, flags);
+
        return 0;
 }
 
@@ -165,9 +204,6 @@ handle_arp_snat(struct sk_buff *skb)
 {
        struct net_bridge_port *p = skb->dev->br_port;
        struct ip_arphdr *ah = (struct ip_arphdr *)arp_hdr(skb);
-       uint32_t ip_addr;
-       unsigned long flags;
-       struct snat_conf *sc;
 
        if ((ah->ar_op != htons(ARPOP_REQUEST)) 
                        || ah->ar_hln != ETH_ALEN
@@ -175,17 +211,10 @@ handle_arp_snat(struct sk_buff *skb)
                        || ah->ar_pln != 4)
                return 0;
 
-       ip_addr = ntohl(ah->ar_tip);
-       spin_lock_irqsave(&p->lock, flags);
-       sc = p->snat;
-
        /* We're only interested in addresses we rewrite. */
-       if (!sc || (sc && ((ip_addr < sc->ip_addr_start) 
-                       || (ip_addr > sc->ip_addr_end)))) {
-               spin_unlock_irqrestore(&p->lock, flags);
+       if (!snat_this_address(p, ah->ar_tip)) {
                return 0;
        }
-       spin_unlock_irqrestore(&p->lock, flags);
 
        arp_send(ARPOP_REPLY, ETH_P_ARP, ah->ar_sip, skb->dev, ah->ar_tip, 
                         ah->ar_sha, p->dp->netdev->dev_addr, ah->ar_sha);
@@ -202,29 +231,18 @@ static int
 handle_icmp_snat(struct sk_buff *skb)
 {
        struct net_bridge_port *p = skb->dev->br_port;
-       struct snat_conf *sc;
        struct ethhdr *eh;
        struct iphdr *iph = ip_hdr(skb);
-       uint32_t ip_addr;
        struct icmphdr *icmph;
        unsigned int datalen;
        uint8_t tmp_eth[ETH_ALEN];
        uint32_t tmp_ip;
        struct sk_buff *nskb;
-       unsigned long flags;
-
-
-       ip_addr = ntohl(iph->daddr);
-       spin_lock_irqsave(&p->lock, flags);
-       sc = p->snat;
 
        /* We're only interested in addresses we rewrite. */
-       if (!sc || (sc && ((ip_addr < sc->ip_addr_start) 
-                       || (ip_addr > sc->ip_addr_end)))) {
-               spin_unlock_irqrestore(&p->lock, flags);
+       if (!snat_this_address(p, iph->daddr)) {
                return 0;
        }
-       spin_unlock_irqrestore(&p->lock, flags);
 
        icmph = (struct icmphdr *) ((u_int32_t *)iph + iph->ihl);
        datalen = skb->len - iph->ihl * 4;
@@ -308,10 +326,7 @@ snat_pre_route(struct sk_buff *skb)
        if (pskb_trim_rcsum(skb, len))
                goto ipv4_error;
 
-       skb->dst = (struct dst_entry *)&__fake_rtable;
-       dst_hold(skb->dst);
-
-       return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
+       return NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, skb->dev, NULL,
                        snat_pre_route_finish);
 
 ipv4_error:
@@ -322,7 +337,7 @@ ipv4_error:
 static int 
 snat_skb_finish(struct sk_buff *skb)
 {
-       NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL, skb->dev, 
+       NF_HOOK(PF_INET, NF_INET_POST_ROUTING, skb, NULL, skb->dev, 
                        dp_xmit_skb_push);
 
        return 0;
@@ -331,12 +346,12 @@ snat_skb_finish(struct sk_buff *skb)
 /* Update the MAC->IP mappings for the private side of the SNAT'd
  * interface. */
 static void
-update_mapping(struct net_bridge_port *p, struct sk_buff *skb)
+update_mapping(struct net_bridge_port *p, const struct sk_buff *skb)
 {
        unsigned long flags;
        struct snat_conf *sc;
-       struct iphdr *iph = ip_hdr(skb);
-       struct ethhdr *eh = eth_hdr(skb);
+       const struct iphdr *iph = ip_hdr(skb);
+       const struct ethhdr *eh = eth_hdr(skb);
        struct snat_mapping *m;
 
        spin_lock_irqsave(&p->lock, flags);
@@ -346,15 +361,15 @@ update_mapping(struct net_bridge_port *p, struct sk_buff *skb)
        
        list_for_each_entry (m, &sc->mappings, node) {
                if (m->ip_addr == iph->saddr){
-                       if (memcmp(m->hw_addr, eh->h_source, ETH_ALEN)) {
-                               memcpy(m->hw_addr, eh->h_source, ETH_ALEN);
-                       }
+                       memcpy(m->hw_addr, eh->h_source, ETH_ALEN);
                        m->used = jiffies;
                        goto done;
                }
        }
 
        m = kmalloc(sizeof *m, GFP_ATOMIC);
+       if (!m)
+               goto done;
        m->ip_addr = iph->saddr;
        memcpy(m->hw_addr, eh->h_source, ETH_ALEN);
        m->used = jiffies;
@@ -370,7 +385,7 @@ done:
  * unmodified.  'skb' is not consumed, so caller will need to free it.
  */
 void 
-snat_skb(struct datapath *dp, struct sk_buff *skb, int out_port)
+snat_skb(struct datapath *dp, const struct sk_buff *skb, int out_port)
 {
        struct net_bridge_port *p = dp->ports[out_port];
        struct sk_buff *nskb;
@@ -398,7 +413,7 @@ snat_skb(struct datapath *dp, struct sk_buff *skb, int out_port)
        /* Take the Ethernet header back off for netfilter hooks. */
        skb_pull(nskb, ETH_HLEN);
 
-       NF_HOOK(PF_INET, NF_IP_FORWARD, nskb, skb->dev, nskb->dev, 
+       NF_HOOK(PF_INET, NF_INET_FORWARD, nskb, skb->dev, nskb->dev, 
                        snat_skb_finish);
 }
 
@@ -481,7 +496,7 @@ snat_add_port(struct datapath *dp, uint16_t port,
        spin_lock_irqsave(&p->lock, flags);
        if (p->snat) {
                if ((p->snat->ip_addr_start == ip_addr_start) 
-                               && (p->snat->ip_addr_end = ip_addr_end)) {
+                               && (p->snat->ip_addr_end == ip_addr_end)) {
                        p->snat->mac_timeout = mac_timeout;
                        spin_unlock_irqrestore(&p->lock, flags);
                        return 0;