Allow controller to set MAC address to use in ARP responses for SNAT IPs.
authorroot <root@marbles.nicira.com>
Wed, 14 Jan 2009 01:30:08 +0000 (17:30 -0800)
committerroot <root@marbles.nicira.com>
Wed, 14 Jan 2009 01:37:03 +0000 (17:37 -0800)
This allows the controller to set a MAC address to use in response to
an ARP request for the NAT IP address on a non-NAT interface.  This is
useful if a NAT'd device needs to communicate with a non-NAT'd device,
when they are on the same interface on the OpenFlow switch.  When the
non-NAT'd device requests the MAC address of the NAT IP address, the
switch responds with the supplied MAC address (often the L3 router
behind it).  This allows communication in both directions to bounce off
the L3 router and not confuse controller.
(cherry picked from commit 0aee2db61a1e34e6cb0a34365e51385059fc84b7)

datapath/nx_act_snat.c
datapath/nx_act_snat.h
include/openflow/nicira-ext.h

index d3750f1..43dbdb4 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright (c) 2008 Nicira Networks
  */
 
+#include <linux/etherdevice.h>
 #include <linux/netdevice.h>
 #include <linux/netfilter.h>
 #include <linux/netfilter_ipv4.h>
@@ -165,6 +166,19 @@ snat_this_address(struct net_bridge_port *p, u32 ip_addr)
        return retval;
 }
 
+/* Must hold RCU lock. */
+static struct net_bridge_port *
+get_nbp_by_ip_addr(struct datapath *dp, u32 ip_addr)
+{
+       struct net_bridge_port *p;
+
+       list_for_each_entry_rcu (p, &dp->port_list, node)
+               if (snat_this_address(p, ip_addr))
+                       return p;
+
+       return NULL;
+}
+
 static int
 snat_pre_route_finish(struct sk_buff *skb)
 {
@@ -206,8 +220,10 @@ snat_pre_route_finish(struct sk_buff *skb)
 static int 
 handle_arp_snat(struct sk_buff *skb)
 {
-       struct net_bridge_port *p = skb->dev->br_port;
+       struct net_bridge_port *s_nbp = skb->dev->br_port;
+       struct net_bridge_port *nat_nbp;
        struct ip_arphdr *ah;
+       uint8_t mac_addr[ETH_ALEN];
 
        if (!pskb_may_pull(skb, sizeof *ah))
                return 0;
@@ -219,13 +235,24 @@ handle_arp_snat(struct sk_buff *skb)
                        || ah->ar_pln != 4)
                return 0;
 
-       /* We're only interested in addresses we rewrite. */
-       if (!snat_this_address(p, ah->ar_tip)) {
+       rcu_read_lock();
+       nat_nbp = get_nbp_by_ip_addr(s_nbp->dp, ah->ar_tip);
+       if (!nat_nbp) {
+               rcu_read_unlock();
                return 0;
        }
+       if (s_nbp == nat_nbp) 
+               memcpy(mac_addr, s_nbp->dp->netdev->dev_addr, sizeof(mac_addr));
+       else if (!is_zero_ether_addr(nat_nbp->snat->mac_addr)) 
+               memcpy(mac_addr, nat_nbp->snat->mac_addr, sizeof(mac_addr));
+       else {
+               rcu_read_unlock();
+               return 0;
+       }
+       rcu_read_unlock();
 
        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);
+                        ah->ar_sha, mac_addr, ah->ar_sha);
 
        return -1;
 }
@@ -477,9 +504,10 @@ snat_free_conf(struct net_bridge_port *p)
 
 /* Remove SNAT configuration from an interface. */
 static int 
-snat_del_port(struct datapath *dp, uint16_t port)
+snat_del_port(struct datapath *dp, const struct nx_snat_config *nsc)
 {
        unsigned long flags;
+       uint16_t port = ntohs(nsc->port);
        struct net_bridge_port *p = dp->ports[port];
 
        if (!p) {
@@ -504,15 +532,14 @@ snat_del_port(struct datapath *dp, uint16_t port)
 
 /* Add SNAT configuration to an interface.  */
 static int 
-snat_add_port(struct datapath *dp, uint16_t port, 
-               uint32_t ip_addr_start, uint32_t ip_addr_end,
-               uint16_t mac_timeout)
+snat_add_port(struct datapath *dp, const struct nx_snat_config *nsc)
 {
        unsigned long flags;
+       uint16_t port = ntohs(nsc->port);
        struct net_bridge_port *p = dp->ports[port];
+       uint16_t mac_timeout = ntohs(nsc->mac_timeout);
        struct snat_conf *sc;
        
-
        if (mac_timeout == 0)
                mac_timeout = MAC_TIMEOUT_DEFAULT;
 
@@ -528,8 +555,8 @@ snat_add_port(struct datapath *dp, uint16_t port,
         * reconfigure it. */
        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)) {
+               if ((p->snat->ip_addr_start == ntohl(nsc->ip_addr_start)
+                               && (p->snat->ip_addr_end == ntohl(nsc->ip_addr_end))) {
                        p->snat->mac_timeout = mac_timeout;
                        spin_unlock_irqrestore(&p->lock, flags);
                        return 0;
@@ -545,9 +572,10 @@ snat_add_port(struct datapath *dp, uint16_t port,
                return -ENOMEM;
        }
 
-       sc->ip_addr_start = ip_addr_start;
-       sc->ip_addr_end = ip_addr_end;
+       sc->ip_addr_start = ntohl(nsc->ip_addr_start);
+       sc->ip_addr_end = ntohl(nsc->ip_addr_end);
        sc->mac_timeout = mac_timeout;
+       memcpy(sc->mac_addr, nsc->mac_addr, sizeof(sc->mac_addr));
        INIT_LIST_HEAD(&sc->mappings);
 
        p->snat = sc;
@@ -568,16 +596,13 @@ snat_mod_config(struct datapath *dp, const struct nx_act_config *nac)
        int i;
 
        for (i=0; i<n_entries; i++) {
-               const struct nx_snat_config *sc = &nac->snat[i];
-               uint16_t port = ntohs(sc->port);
+               const struct nx_snat_config *nsc = &nac->snat[i];
                int r = 0;
 
-               if (sc->command == NXSC_ADD)
-                       r = snat_add_port(dp, port, 
-                                       ntohl(sc->ip_addr_start), ntohl(sc->ip_addr_end), 
-                                       ntohs(sc->mac_timeout));
+               if (nsc->command == NXSC_ADD)
+                       r = snat_add_port(dp, nsc);
                else 
-                       r = snat_del_port(dp, port);
+                       r = snat_del_port(dp, nsc);
 
                if (r)
                        ret = r;
index 7d5f37d..28a0e59 100644 (file)
@@ -23,6 +23,9 @@ struct snat_conf {
        uint32_t ip_addr_start;      /* Stored in host-order */
        uint32_t ip_addr_end;        /* Stored in host-order */
        uint16_t mac_timeout;
+
+       uint8_t mac_addr[ETH_ALEN];
+
        struct list_head mappings;   /* List of snat_mapping entries */
 };
 
index ece73c4..b6f6487 100644 (file)
@@ -82,8 +82,16 @@ struct nx_snat_config {
     uint16_t tcp_end;
     uint16_t udp_start;
     uint16_t udp_end;
+
+    /* MAC address to use for ARP requests for a SNAT IP address that 
+     * comes in on a different interface than 'port'.  A value of all 
+     * zeros silently drops those ARP requests.  Requests that arrive 
+     * on 'port' get a response with the mac address of the datapath 
+     * device. */
+    uint8_t mac_addr[OFP_ETH_ALEN];
+    uint8_t pad2[2];
 };
-OFP_ASSERT(sizeof(struct nx_snat_config) == 24);
+OFP_ASSERT(sizeof(struct nx_snat_config) == 32);
 
 /* Action configuration.  Not all actions require separate configuration. */
 struct nx_act_config {