From: root Date: Wed, 14 Jan 2009 01:30:08 +0000 (-0800) Subject: Allow controller to set MAC address to use in ARP responses for SNAT IPs. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0aee2db61a1e34e6cb0a34365e51385059fc84b7;p=openvswitch Allow controller to set MAC address to use in ARP responses for SNAT IPs. 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. --- diff --git a/datapath/nx_act_snat.c b/datapath/nx_act_snat.c index 5b26c625..5301fb68 100644 --- a/datapath/nx_act_snat.c +++ b/datapath/nx_act_snat.c @@ -4,6 +4,7 @@ * Copyright (c) 2008, 2009 Nicira Networks */ +#include #include #include #include @@ -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; isnat[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; diff --git a/datapath/nx_act_snat.h b/datapath/nx_act_snat.h index 7d5f37dd..28a0e595 100644 --- a/datapath/nx_act_snat.h +++ b/datapath/nx_act_snat.h @@ -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 */ }; diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h index ece73c45..b6f6487a 100644 --- a/include/openflow/nicira-ext.h +++ b/include/openflow/nicira-ext.h @@ -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 {