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:30:56 +0000 (17:30 -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.

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

index 5b26c62578eb195862543048d2c41f0f55f70cbd..5301fb68b6e8002a399c978163516123c2abe3db 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright (c) 2008, 2009 Nicira Networks
  */
 
+#include <linux/etherdevice.h>
 #include <linux/netdevice.h>
 #include <linux/netfilter.h>
 #include <linux/netfilter_ipv4.h>
@@ -162,6 +163,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)
 {
@@ -203,8 +217,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;
@@ -216,13 +232,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;
 }
@@ -471,9 +498,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) {
@@ -501,15 +529,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;
 
@@ -527,8 +554,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;
@@ -544,9 +571,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;
@@ -567,16 +595,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 7d5f37dd60809c71c6ac6ea63a0c73f53193c263..28a0e595928d12af147a3b5ba0d214c30d0007ac 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 ece73c4514ad2e57d6665babb60267d4e454d9c2..b6f6487a72ac4216aab9527f8a0fb8281372c23d 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 {