* Copyright (c) 2008, 2009 Nicira Networks
*/
+#include <linux/etherdevice.h>
#include <linux/netdevice.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
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)
{
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;
|| 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;
}
/* 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) {
/* 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;
* 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;
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;
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;