Remove experimental SNAT (source NAT) support
This commit is contained in:
1 parent
2dcc603a5c
commit
43f19193fb
16 files changed
+2
-1277
No files matched your search
@@ -19,7 +19,6 @@ ofdatapath_sources = \
|
||||
flow.c \
|
||||
forward.c \
|
||||
nx_act.c \
|
||||
nx_act_snat.c \
|
||||
nx_msg.c \
|
||||
table-hash.c \
|
||||
table-linear.c
|
||||
@@ -34,7 +33,6 @@ ofdatapath_headers = \
|
||||
forward.h \
|
||||
dp_act.h \
|
||||
nx_act.h \
|
||||
nx_act_snat.h \
|
||||
nx_msg.h \
|
||||
table.h
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
#include "openflow/nicira-ext.h"
|
||||
#include "openflow/openflow-netlink.h"
|
||||
#include "datapath.h"
|
||||
#include "nx_act_snat.h"
|
||||
#include "table.h"
|
||||
#include "chain.h"
|
||||
#include "dp_dev.h"
|
||||
@@ -443,9 +442,6 @@ int add_switch_port(struct datapath *dp, struct net_device *dev)
|
||||
* Called with RTNL lock and dp_mutex. */
|
||||
int dp_del_switch_port(struct net_bridge_port *p)
|
||||
{
|
||||
#ifdef SUPPORT_SNAT
|
||||
unsigned long flags;
|
||||
#endif
|
||||
|
||||
#if CONFIG_SYSFS
|
||||
if ((p->port_no != OFPP_LOCAL) && dp_del_if_hook)
|
||||
@@ -462,13 +458,6 @@ int dp_del_switch_port(struct net_bridge_port *p)
|
||||
/* Then wait until no one is still using it, and destroy it. */
|
||||
synchronize_rcu();
|
||||
|
||||
#ifdef SUPPORT_SNAT
|
||||
/* Free any SNAT configuration on the port. */
|
||||
spin_lock_irqsave(&p->lock, flags);
|
||||
snat_free_conf(p);
|
||||
spin_unlock_irqrestore(&p->lock, flags);
|
||||
#endif
|
||||
|
||||
/* Notify the ctlpath that this port no longer exists */
|
||||
dp_send_port_status(p, OFPPR_DELETE);
|
||||
|
||||
@@ -520,16 +509,6 @@ static int dp_maint_func(void *data)
|
||||
|
||||
allow_signal(SIGKILL);
|
||||
while (!signal_pending(current)) {
|
||||
#ifdef SUPPORT_SNAT
|
||||
struct net_bridge_port *p;
|
||||
|
||||
/* Expire old SNAT entries */
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu (p, &dp->port_list, node)
|
||||
snat_maint(p);
|
||||
rcu_read_unlock();
|
||||
#endif
|
||||
|
||||
/* Timeout old entries */
|
||||
chain_timeout(dp->chain);
|
||||
msleep_interruptible(MAINT_SLEEP_MSECS);
|
||||
@@ -552,12 +531,6 @@ do_port_input(struct net_bridge_port *p, struct sk_buff *skb)
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
#ifdef SUPPORT_SNAT
|
||||
/* Check if this packet needs early SNAT processing. */
|
||||
if (snat_pre_route(skb))
|
||||
return;
|
||||
#endif
|
||||
|
||||
/* Push the Ethernet header back on. */
|
||||
skb_push(skb, ETH_HLEN);
|
||||
skb_reset_mac_header(skb);
|
||||
@@ -653,56 +626,6 @@ void dp_set_origin(struct datapath *dp, uint16_t in_port,
|
||||
skb->dev = NULL;
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_SNAT
|
||||
static int
|
||||
dp_xmit_skb_finish(struct sk_buff *skb)
|
||||
{
|
||||
/* Copy back the Ethernet header that was stowed earlier. */
|
||||
if (skb->protocol == htons(ETH_P_IP) && snat_copy_header(skb)) {
|
||||
kfree_skb(skb);
|
||||
return -EINVAL;
|
||||
}
|
||||
skb_reset_mac_header(skb);
|
||||
|
||||
if (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb)) {
|
||||
printk("dropped over-mtu packet: %d > %d\n",
|
||||
packet_length(skb), skb->dev->mtu);
|
||||
kfree_skb(skb);
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
skb_push(skb, ETH_HLEN);
|
||||
dev_queue_xmit(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
dp_xmit_skb(struct sk_buff *skb)
|
||||
{
|
||||
int len = skb->len;
|
||||
int err;
|
||||
|
||||
skb_pull(skb, ETH_HLEN);
|
||||
|
||||
/* The ip_fragment function does not copy the Ethernet header into
|
||||
* the newly generated frames, so stow the original. */
|
||||
if (skb->protocol == htons(ETH_P_IP))
|
||||
snat_save_header(skb);
|
||||
|
||||
if (skb->protocol == htons(ETH_P_IP) &&
|
||||
skb->len > skb->dev->mtu &&
|
||||
!skb_is_gso(skb)) {
|
||||
err = ip_fragment(skb, dp_xmit_skb_finish);
|
||||
} else {
|
||||
err = dp_xmit_skb_finish(skb);
|
||||
}
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return len;
|
||||
}
|
||||
#else
|
||||
int
|
||||
dp_xmit_skb(struct sk_buff *skb)
|
||||
{
|
||||
@@ -720,7 +643,6 @@ dp_xmit_skb(struct sk_buff *skb)
|
||||
|
||||
return len;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Takes ownership of 'skb' and transmits it to 'out_port' on 'dp'.
|
||||
*/
|
||||
@@ -761,9 +683,6 @@ int dp_output_port(struct datapath *dp, struct sk_buff *skb, int out_port,
|
||||
|
||||
case OFPP_LOCAL: {
|
||||
struct net_device *dev = dp->netdev;
|
||||
#ifdef SUPPORT_SNAT
|
||||
snat_local_in(skb);
|
||||
#endif
|
||||
return dev ? dp_dev_recv(dev, skb) : -ESRCH;
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,6 @@ struct net_bridge_port {
|
||||
struct datapath *dp;
|
||||
struct net_device *dev;
|
||||
struct kobject kobj;
|
||||
struct snat_conf *snat; /* Only set if SNAT is configured for this port. */
|
||||
struct list_head node; /* Element in datapath.ports. */
|
||||
};
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ EXTRA_CFLAGS := -DVERSION=\"$(VERSION)\"
|
||||
EXTRA_CFLAGS += -I$(srcdir)/..
|
||||
EXTRA_CFLAGS += -I$(builddir)/..
|
||||
EXTRA_CFLAGS += -I$(top_srcdir)/include
|
||||
EXTRA_CFLAGS += @SUPPORT_SNAT@
|
||||
ifeq '$(BUILDNR)' '0'
|
||||
EXTRA_CFLAGS += -DBUILDNR=\"\"
|
||||
else
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "openflow/nicira-ext.h"
|
||||
#include "dp_act.h"
|
||||
#include "nx_act.h"
|
||||
#include "nx_act_snat.h"
|
||||
|
||||
uint16_t
|
||||
nx_validate_act(struct datapath *dp, const struct sw_flow_key *key,
|
||||
@@ -16,17 +15,6 @@ nx_validate_act(struct datapath *dp, const struct sw_flow_key *key,
|
||||
if (len < sizeof *nah)
|
||||
return OFPBAC_BAD_LEN;
|
||||
|
||||
#ifdef SUPPORT_SNAT
|
||||
if (nah->subtype == ntohs(NXAST_SNAT)) {
|
||||
struct nx_action_snat *nas = (struct nx_action_snat *)nah;
|
||||
if (len != sizeof(*nas))
|
||||
return OFPBAC_BAD_LEN;
|
||||
else if (ntohs(nas->port) >= OFPP_MAX)
|
||||
return OFPBAC_BAD_ARGUMENT;
|
||||
|
||||
return ACT_VALIDATION_OK;
|
||||
}
|
||||
#endif
|
||||
return OFPBAC_BAD_VENDOR_TYPE;
|
||||
}
|
||||
|
||||
@@ -34,13 +22,6 @@ struct sk_buff *
|
||||
nx_execute_act(struct sk_buff *skb, const struct sw_flow_key *key,
|
||||
const struct nx_action_header *nah)
|
||||
{
|
||||
#ifdef SUPPORT_SNAT
|
||||
if (nah->subtype == ntohs(NXAST_SNAT)) {
|
||||
struct nx_action_snat *nas = (struct nx_action_snat *)nah;
|
||||
snat_skb(skb->dev->br_port->dp, skb, ntohs(nas->port));
|
||||
}
|
||||
#endif
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
@@ -1,656 +0,0 @@
|
||||
#ifdef SUPPORT_SNAT
|
||||
/*
|
||||
* Distributed under the terms of the GNU GPL version 2.
|
||||
* Copyright (c) 2008, 2009 Nicira Networks
|
||||
*/
|
||||
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/netfilter_bridge.h>
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <linux/in.h>
|
||||
#include <net/ip.h>
|
||||
#include <linux/icmp.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <net/arp.h>
|
||||
#include <net/route.h>
|
||||
|
||||
#include "forward.h"
|
||||
#include "dp_act.h"
|
||||
#include "nx_act_snat.h"
|
||||
|
||||
|
||||
/* We need these fake structures to make netfilter happy --
|
||||
* lots of places assume that skb->dst != NULL, which isn't
|
||||
* all that unreasonable.
|
||||
*
|
||||
* Currently, we fill in the PMTU entry because netfilter
|
||||
* refragmentation needs it, and the rt_flags entry because
|
||||
* ipt_REJECT needs it. Future netfilter modules might
|
||||
* require us to fill additional fields. */
|
||||
static struct net_device __fake_net_device = {
|
||||
.hard_header_len = ETH_HLEN
|
||||
};
|
||||
|
||||
static struct rtable __fake_rtable = {
|
||||
.u = {
|
||||
.dst = {
|
||||
.__refcnt = ATOMIC_INIT(1),
|
||||
.dev = &__fake_net_device,
|
||||
.path = &__fake_rtable.u.dst,
|
||||
.metrics = {[RTAX_MTU - 1] = 1500},
|
||||
.flags = DST_NOXFRM,
|
||||
}
|
||||
},
|
||||
.rt_flags = 0,
|
||||
};
|
||||
|
||||
/* Define ARP for IP since the Linux headers don't do it cleanly. */
|
||||
struct ip_arphdr {
|
||||
uint16_t ar_hrd;
|
||||
uint16_t ar_pro;
|
||||
uint8_t ar_hln;
|
||||
uint8_t ar_pln;
|
||||
uint16_t ar_op;
|
||||
uint8_t ar_sha[ETH_ALEN];
|
||||
uint32_t ar_sip;
|
||||
uint8_t ar_tha[ETH_ALEN];
|
||||
uint32_t ar_tip;
|
||||
} __attribute__((packed));
|
||||
OFP_ASSERT(sizeof(struct ip_arphdr) == 28);
|
||||
|
||||
static inline struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
|
||||
{
|
||||
skb->nf_bridge = kzalloc(sizeof(struct nf_bridge_info), GFP_ATOMIC);
|
||||
if (likely(skb->nf_bridge))
|
||||
atomic_set(&(skb->nf_bridge->use), 1);
|
||||
|
||||
return skb->nf_bridge;
|
||||
}
|
||||
|
||||
/* Save a copy of the original Ethernet header. */
|
||||
void snat_save_header(struct sk_buff *skb)
|
||||
{
|
||||
int header_size = ETH_HLEN + nf_bridge_encap_header_len(skb);
|
||||
|
||||
if (!skb->nf_bridge)
|
||||
return;
|
||||
|
||||
skb_copy_from_linear_data_offset(skb, -header_size,
|
||||
skb->nf_bridge->data, header_size);
|
||||
}
|
||||
|
||||
/* Restore a saved Ethernet header. */
|
||||
int snat_copy_header(struct sk_buff *skb)
|
||||
{
|
||||
int err;
|
||||
int header_size = ETH_HLEN + nf_bridge_encap_header_len(skb);
|
||||
|
||||
if (!skb->nf_bridge)
|
||||
return 0;
|
||||
|
||||
err = skb_cow_head(skb, header_size);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
skb_copy_to_linear_data_offset(skb, -header_size,
|
||||
skb->nf_bridge->data, header_size);
|
||||
__skb_push(skb, nf_bridge_encap_header_len(skb));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Push the Ethernet header back on and tranmit the packet. */
|
||||
static int
|
||||
dp_xmit_skb_push(struct sk_buff *skb)
|
||||
{
|
||||
skb_push(skb, ETH_HLEN);
|
||||
return dp_xmit_skb(skb);
|
||||
}
|
||||
|
||||
/* Perform maintainence related to a SNAT'd interface. Currently, this only
|
||||
* checks whether MAC->IP bindings have expired.
|
||||
*
|
||||
* Called with the RCU read lock */
|
||||
void
|
||||
snat_maint(struct net_bridge_port *p)
|
||||
{
|
||||
struct snat_conf *sc;
|
||||
struct snat_mapping *m, *n;
|
||||
unsigned long flags;
|
||||
unsigned long timeout;
|
||||
|
||||
spin_lock_irqsave(&p->lock, flags);
|
||||
sc = p->snat;
|
||||
if (!sc)
|
||||
goto done;
|
||||
|
||||
timeout = sc->mac_timeout * HZ;
|
||||
|
||||
list_for_each_entry_safe (m, n, &sc->mappings, node) {
|
||||
if (time_after(jiffies, m->used + timeout)) {
|
||||
list_del(&m->node);
|
||||
kfree(m);
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* Returns -1 if there is a problem, otherwise 0. */
|
||||
static int
|
||||
dnat_mac(struct net_bridge_port *p, struct sk_buff *skb)
|
||||
{
|
||||
struct snat_conf *sc = p->snat;
|
||||
struct iphdr *iph = ip_hdr(skb);
|
||||
struct ethhdr *eh = eth_hdr(skb);
|
||||
struct snat_mapping *m;
|
||||
|
||||
if (skb->protocol != htons(ETH_P_IP))
|
||||
return 0;
|
||||
|
||||
list_for_each_entry (m, &sc->mappings, node) {
|
||||
if (m->ip_addr == iph->daddr){
|
||||
/* Found it! */
|
||||
if (!make_writable(&skb))
|
||||
return -EINVAL;
|
||||
m->used = jiffies;
|
||||
memcpy(eh->h_dest, m->hw_addr, ETH_ALEN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
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)) {
|
||||
/* If SNAT is configured for this input device, check the
|
||||
* IP->MAC mappings to see if we should update the destination
|
||||
* MAC. */
|
||||
if (sc)
|
||||
dnat_mac(skb->dev->br_port, skb);
|
||||
|
||||
}
|
||||
spin_unlock_irqrestore(&p->lock, flags);
|
||||
|
||||
/* Pass the translated packet as input to the OpenFlow stack, which
|
||||
* consumes it. */
|
||||
skb_push(skb, ETH_HLEN);
|
||||
skb_reset_mac_header(skb);
|
||||
fwd_port_input(p->dp->chain, skb, p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Checks whether 'skb' is an ARP request for an SNAT'd interface. If
|
||||
* so, it will generate a response.
|
||||
*
|
||||
* Returns 0 if the packet was not handled. Otherwise, -1 is returned
|
||||
* and the caller is responsible for freeing 'skb'. */
|
||||
static int
|
||||
handle_arp_snat(struct sk_buff *skb)
|
||||
{
|
||||
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 = (struct ip_arphdr *)arp_hdr(skb);
|
||||
if ((ah->ar_op != htons(ARPOP_REQUEST))
|
||||
|| ah->ar_hln != ETH_ALEN
|
||||
|| ah->ar_pro != htons(ETH_P_IP)
|
||||
|| ah->ar_pln != 4)
|
||||
return 0;
|
||||
|
||||
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, mac_addr, ah->ar_sha);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Checks whether 'skb' is a ping request for an SNAT'd interface. If
|
||||
* so, it will generate a response.
|
||||
*
|
||||
* Returns 0 if the packet was not handled. Otherwise, -1 is returned
|
||||
* and the caller is responsible for freeing 'skb'. */
|
||||
static int
|
||||
handle_icmp_snat(struct sk_buff *skb)
|
||||
{
|
||||
struct net_bridge_port *p = skb->dev->br_port;
|
||||
struct ethhdr *eh;
|
||||
struct iphdr *iph;
|
||||
struct icmphdr *icmph;
|
||||
uint8_t tmp_eth[ETH_ALEN];
|
||||
uint32_t tmp_ip;
|
||||
struct sk_buff *nskb;
|
||||
|
||||
/* We're only interested in addresses we rewrite. */
|
||||
iph = ip_hdr(skb);
|
||||
if (!snat_this_address(p, iph->daddr)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Drop fragments and packets not long enough to hold the ICMP
|
||||
* header. */
|
||||
if ((ntohs(iph->frag_off) & IP_OFFSET) != 0 ||
|
||||
!pskb_may_pull(skb, skb_transport_offset(skb) + 4))
|
||||
return 0;
|
||||
|
||||
/* We only respond to echo requests to our address. Continue
|
||||
* processing replies and other ICMP messages since they may be
|
||||
* intended for NAT'd hosts. */
|
||||
icmph = icmp_hdr(skb);
|
||||
if (icmph->type != ICMP_ECHO)
|
||||
return 0;
|
||||
|
||||
/* Send an echo reply in response */
|
||||
nskb = skb_copy(skb, GFP_ATOMIC);
|
||||
if (!nskb)
|
||||
return -1;
|
||||
|
||||
/* Update Ethernet header. */
|
||||
eh = eth_hdr(nskb);
|
||||
memcpy(tmp_eth, eh->h_dest, ETH_ALEN);
|
||||
memcpy(eh->h_dest, eh->h_source, ETH_ALEN);
|
||||
memcpy(eh->h_source, tmp_eth, ETH_ALEN);
|
||||
|
||||
/* Update IP header.
|
||||
* This is kind of busted, at least in that it doesn't check that the
|
||||
* echoed IP options make sense. */
|
||||
iph = ip_hdr(nskb);
|
||||
iph->id = 0;
|
||||
iph->frag_off = 0;
|
||||
iph->ttl = IPDEFTTL;
|
||||
iph->check = 0;
|
||||
tmp_ip = iph->daddr;
|
||||
iph->daddr = iph->saddr;
|
||||
iph->saddr = tmp_ip;
|
||||
iph->check = ip_fast_csum((void *)iph, iph->ihl);
|
||||
|
||||
/* Update ICMP header. */
|
||||
icmph = icmp_hdr(nskb);
|
||||
icmph->type = ICMP_ECHOREPLY;
|
||||
icmph->checksum = 0;
|
||||
icmph->checksum = ip_compute_csum((void *)icmph,
|
||||
nskb->tail - skb_transport_header(nskb));
|
||||
|
||||
dp_xmit_skb_push(nskb);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check if any SNAT maintenance needs to be done on 'skb' before it's
|
||||
* checked against the datapath's tables. This includes DNAT
|
||||
* modification based on prior SNAT action and responding to ARP and
|
||||
* echo requests for the SNAT interface.
|
||||
*
|
||||
* Returns -1 if the packet was handled and consumed, 0 if the caller
|
||||
* should continue to process 'skb'.
|
||||
*/
|
||||
int
|
||||
snat_pre_route(struct sk_buff *skb)
|
||||
{
|
||||
struct iphdr *iph;
|
||||
int len;
|
||||
|
||||
WARN_ON_ONCE(skb_network_offset(skb));
|
||||
if (skb->protocol == htons(ETH_P_ARP)) {
|
||||
if (handle_arp_snat(skb))
|
||||
goto consume;
|
||||
return 0;
|
||||
}
|
||||
else if (skb->protocol != htons(ETH_P_IP))
|
||||
return 0;
|
||||
|
||||
if (!pskb_may_pull(skb, sizeof *iph))
|
||||
goto consume;
|
||||
|
||||
iph = ip_hdr(skb);
|
||||
if (iph->ihl < 5 || iph->version != 4)
|
||||
goto consume;
|
||||
|
||||
if (!pskb_may_pull(skb, ip_hdrlen(skb)))
|
||||
goto consume;
|
||||
skb_set_transport_header(skb, ip_hdrlen(skb));
|
||||
|
||||
/* Check if we need to echo reply for this address */
|
||||
iph = ip_hdr(skb);
|
||||
if ((iph->protocol == IPPROTO_ICMP) && (handle_icmp_snat(skb)))
|
||||
goto consume;
|
||||
|
||||
iph = ip_hdr(skb);
|
||||
if (unlikely(ip_fast_csum((void *)iph, iph->ihl)))
|
||||
goto consume;
|
||||
|
||||
len = ntohs(iph->tot_len);
|
||||
if ((skb->len < len) || len < (iph->ihl*4))
|
||||
goto consume;
|
||||
|
||||
if (pskb_trim_rcsum(skb, len))
|
||||
goto consume;
|
||||
|
||||
nf_bridge_put(skb->nf_bridge);
|
||||
if (!nf_bridge_alloc(skb))
|
||||
return 0;
|
||||
|
||||
NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, skb->dev, NULL,
|
||||
snat_pre_route_finish);
|
||||
return -1;
|
||||
|
||||
consume:
|
||||
kfree_skb(skb);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
snat_skb_finish(struct sk_buff *skb)
|
||||
{
|
||||
NF_HOOK(PF_INET, NF_INET_POST_ROUTING, skb, NULL, skb->dev,
|
||||
dp_xmit_skb_push);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Update the MAC->IP mappings for the private side of the SNAT'd
|
||||
* interface. */
|
||||
static void
|
||||
update_mapping(struct net_bridge_port *p, const struct sk_buff *skb)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct snat_conf *sc;
|
||||
const struct iphdr *iph = ip_hdr(skb);
|
||||
const struct ethhdr *eh = eth_hdr(skb);
|
||||
struct snat_mapping *m;
|
||||
|
||||
spin_lock_irqsave(&p->lock, flags);
|
||||
sc = p->snat;
|
||||
if (!sc)
|
||||
goto done;
|
||||
|
||||
list_for_each_entry (m, &sc->mappings, node) {
|
||||
if (m->ip_addr == iph->saddr){
|
||||
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;
|
||||
|
||||
list_add(&m->node, &sc->mappings);
|
||||
|
||||
done:
|
||||
spin_unlock_irqrestore(&p->lock, flags);
|
||||
}
|
||||
|
||||
/* Perform SNAT modification on 'skb' and send out 'out_port'. If the
|
||||
* port was not configured for SNAT, it will be sent through the interface
|
||||
* unmodified. 'skb' is not consumed, so caller will need to free it.
|
||||
*/
|
||||
void
|
||||
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;
|
||||
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
/* FIXME: Expensive. Just need to skb_clone() here?
|
||||
* (However, the skb_copy() does linearize and ensure that the headers
|
||||
* are accessible.) */
|
||||
nskb = skb_copy(skb, GFP_ATOMIC);
|
||||
if (!nskb)
|
||||
return;
|
||||
|
||||
nskb->dev = p->dev;
|
||||
|
||||
/* We only SNAT IP, so just send it on its way if not */
|
||||
if (skb->protocol != htons(ETH_P_IP)) {
|
||||
dp_xmit_skb(nskb);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the source MAC to the OF interface */
|
||||
memcpy(eth_hdr(nskb)->h_source, dp->netdev->dev_addr, ETH_ALEN);
|
||||
|
||||
update_mapping(p, skb);
|
||||
|
||||
/* Take the Ethernet header back off for netfilter hooks. */
|
||||
skb_pull(nskb, ETH_HLEN);
|
||||
|
||||
NF_HOOK(PF_INET, NF_INET_FORWARD, nskb, skb->dev, nskb->dev,
|
||||
snat_skb_finish);
|
||||
}
|
||||
|
||||
/* Remove SNAT configuration on port 'p'.
|
||||
*
|
||||
* NB: The caller must hold the port's spinlock. */
|
||||
int
|
||||
snat_free_conf(struct net_bridge_port *p)
|
||||
{
|
||||
struct snat_conf *sc = p->snat;
|
||||
|
||||
if (!sc)
|
||||
return -EINVAL;
|
||||
|
||||
/* Free existing mapping entries */
|
||||
while (!list_empty(&sc->mappings)) {
|
||||
struct snat_mapping *m = list_entry(sc->mappings.next,
|
||||
struct snat_mapping, node);
|
||||
list_del(&m->node);
|
||||
kfree(m);
|
||||
}
|
||||
|
||||
kfree(p->snat);
|
||||
p->snat = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Remove SNAT configuration from an interface. */
|
||||
static int
|
||||
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) {
|
||||
if (net_ratelimit())
|
||||
printk(KERN_NOTICE "%s: attempt to remove snat on "
|
||||
"non-existent port: %d\n",
|
||||
dp->netdev->name, port);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&p->lock, flags);
|
||||
if (snat_free_conf(p)) {
|
||||
/* SNAT not configured on this port */
|
||||
spin_unlock_irqrestore(&p->lock, flags);
|
||||
if (net_ratelimit())
|
||||
printk(KERN_NOTICE "%s: attempt to remove snat on "
|
||||
"non-snat port: %d\n", dp->netdev->name, port);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&p->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add SNAT configuration to an interface. */
|
||||
static int
|
||||
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;
|
||||
|
||||
if (!p) {
|
||||
if (net_ratelimit())
|
||||
printk(KERN_NOTICE "%s: attempt to add snat on "
|
||||
"non-existent port: %d\n",
|
||||
dp->netdev->name, port);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* If SNAT is already configured on the port, check whether the same
|
||||
* IP addresses are used. If so, just update the mac timeout
|
||||
* configuration. Otherwise, drop all SNAT configuration and
|
||||
* reconfigure it. */
|
||||
spin_lock_irqsave(&p->lock, flags);
|
||||
if (p->snat) {
|
||||
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;
|
||||
}
|
||||
|
||||
/* Free the existing configuration and mappings. */
|
||||
snat_free_conf(p);
|
||||
}
|
||||
|
||||
sc = kzalloc(sizeof *sc, GFP_ATOMIC);
|
||||
if (!sc) {
|
||||
spin_unlock_irqrestore(&p->lock, flags);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
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;
|
||||
spin_unlock_irqrestore(&p->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Handle a SNAT configuration message.
|
||||
*
|
||||
* Returns 0 if no problems are found. Otherwise, a negative errno. */
|
||||
int
|
||||
snat_mod_config(struct datapath *dp, const struct nx_act_config *nac)
|
||||
{
|
||||
int n_entries = (ntohs(nac->header.header.length) - sizeof *nac)
|
||||
/ sizeof (struct nx_snat_config);
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
for (i=0; i<n_entries; i++) {
|
||||
const struct nx_snat_config *nsc = &nac->snat[i];
|
||||
int r = 0;
|
||||
|
||||
if (nsc->command == NXSC_ADD)
|
||||
r = snat_add_port(dp, nsc);
|
||||
else
|
||||
r = snat_del_port(dp, nsc);
|
||||
|
||||
if (r)
|
||||
ret = r;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
@@ -1,44 +0,0 @@
|
||||
#ifdef SUPPORT_SNAT
|
||||
#ifndef ACT_SNAT_H
|
||||
#define ACT_SNAT_H
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/rcupdate.h>
|
||||
|
||||
#include "openflow/nicira-ext.h"
|
||||
#include "datapath.h"
|
||||
|
||||
/* Cache of IP->MAC mappings on the side hidden by the SNAT */
|
||||
struct snat_mapping {
|
||||
struct list_head node;
|
||||
uint32_t ip_addr; /* Stored in network-order */
|
||||
uint8_t hw_addr[ETH_ALEN];
|
||||
unsigned long used; /* Last used time (in jiffies). */
|
||||
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
|
||||
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 */
|
||||
};
|
||||
|
||||
#define MAC_TIMEOUT_DEFAULT 120
|
||||
|
||||
void snat_local_in(struct sk_buff *skb);
|
||||
int snat_pre_route(struct sk_buff *skb);
|
||||
void snat_skb(struct datapath *dp, const struct sk_buff *skb, int out_port);
|
||||
void snat_save_header(struct sk_buff *skb);
|
||||
int snat_copy_header(struct sk_buff *skb);
|
||||
void snat_maint(struct net_bridge_port *p);
|
||||
int snat_mod_config(struct datapath *, const struct nx_act_config *);
|
||||
int snat_free_conf(struct net_bridge_port *p);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "chain.h"
|
||||
#include "datapath.h"
|
||||
#include "openflow/nicira-ext.h"
|
||||
#include "nx_act_snat.h"
|
||||
#include "nx_msg.h"
|
||||
|
||||
|
||||
@@ -24,20 +23,6 @@ nx_recv_msg(struct sw_chain *chain, const struct sender *sender,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_SNAT
|
||||
case NXT_ACT_SET_CONFIG: {
|
||||
const struct nx_act_config *nac = msg;
|
||||
if (ntohs(nh->header.length) < sizeof(*nac))
|
||||
return -EINVAL;
|
||||
|
||||
if (nac->type == htons(NXAST_SNAT))
|
||||
return snat_mod_config(chain->dp, nac);
|
||||
else
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
dp_send_error_msg(chain->dp, sender, OFPET_BAD_REQUEST,
|
||||
OFPBRC_BAD_SUBTYPE, msg, ntohs(nh->header.length));
|
||||
|
||||
+2
-14
@@ -214,20 +214,8 @@ static void ofp_print_port_name(struct ds *string, uint16_t port)
|
||||
static void
|
||||
ofp_print_nx_action(struct ds *string, const struct nx_action_header *nah)
|
||||
{
|
||||
|
||||
if (nah->subtype == htonl(NXAST_SNAT)) {
|
||||
const struct nx_action_snat *nas = (struct nx_action_snat *)nah;
|
||||
uint16_t port = ntohs(nas->port);
|
||||
|
||||
if (port < OFPP_MAX) {
|
||||
ds_put_format(string, "nat:%"PRIu16, port);
|
||||
} else {
|
||||
ds_put_format(string, "nat:%"PRIu16" (invalid port)", port);
|
||||
}
|
||||
} else {
|
||||
ds_put_format(string, "***unknown Nicira action:%d***\n",
|
||||
ntohl(nah->subtype));
|
||||
}
|
||||
ds_put_format(string, "***unknown Nicira action:%d***\n",
|
||||
ntohl(nah->subtype));
|
||||
}
|
||||
|
||||
static int
|
||||
|
||||
@@ -33,7 +33,6 @@ VLOG_MODULE(process)
|
||||
VLOG_MODULE(protocol_stat)
|
||||
VLOG_MODULE(secchan)
|
||||
VLOG_MODULE(rconn)
|
||||
VLOG_MODULE(snat)
|
||||
VLOG_MODULE(stp)
|
||||
VLOG_MODULE(stp_secchan)
|
||||
VLOG_MODULE(stats)
|
||||
|
||||
@@ -91,25 +91,6 @@ AC_DEFUN([OFP_CHECK_OPENSSL],
|
||||
AC_DEFINE([HAVE_OPENSSL], [1], [Define to 1 if OpenSSL is installed.])
|
||||
fi])
|
||||
|
||||
dnl Checks for --enable-snat and defines SUPPORT_SNAT if it is specified.
|
||||
AC_DEFUN([OFP_CHECK_SNAT],
|
||||
[AC_ARG_ENABLE(
|
||||
[snat],
|
||||
[AC_HELP_STRING([--enable-snat],
|
||||
[Enable support for source-NAT action])],
|
||||
[case "${enableval}" in
|
||||
(yes) snat=true ;;
|
||||
(no) snat=false ;;
|
||||
(*) AC_MSG_ERROR([bad value ${enableval} for --enable-snat]) ;;
|
||||
esac],
|
||||
[snat=false])
|
||||
AM_CONDITIONAL([SUPPORT_SNAT], [test x$snat = xtrue])
|
||||
if test x$snat = xtrue; then
|
||||
AC_DEFINE([SUPPORT_SNAT], [1], [Define to 1 if SNAT is desired.])
|
||||
SUPPORT_SNAT=-DSUPPORT_SNAT
|
||||
AC_SUBST([SUPPORT_SNAT])
|
||||
fi])
|
||||
|
||||
dnl Checks for libraries needed by lib/fault.c.
|
||||
AC_DEFUN([OFP_CHECK_FAULT_LIBS],
|
||||
[AC_CHECK_LIB([dl], [dladdr], [FAULT_LIBS=-ldl])
|
||||
@@ -177,7 +158,6 @@ AC_DEFUN([OFP_CHECK_LIBOPENFLOW],
|
||||
AC_REQUIRE([OFP_CHECK_NDEBUG])
|
||||
AC_REQUIRE([OFP_CHECK_NETLINK])
|
||||
AC_REQUIRE([OFP_CHECK_OPENSSL])
|
||||
AC_REQUIRE([OFP_CHECK_SNAT])
|
||||
AC_REQUIRE([OFP_CHECK_FAULT_LIBS])
|
||||
AC_REQUIRE([OFP_CHECK_SOCKET_LIBS])
|
||||
AC_REQUIRE([OFP_CHECK_PKIDIR])
|
||||
|
||||
@@ -27,11 +27,6 @@ secchan_ofprotocol_SOURCES = \
|
||||
secchan/status.h \
|
||||
secchan/stp-secchan.c \
|
||||
secchan/stp-secchan.h
|
||||
if SUPPORT_SNAT
|
||||
secchan_ofprotocol_SOURCES += \
|
||||
secchan/snat.c \
|
||||
secchan/snat.h
|
||||
endif
|
||||
secchan_ofprotocol_LDADD = lib/libopenflow.a $(FAULT_LIBS) $(SSL_LIBS)
|
||||
|
||||
EXTRA_DIST += secchan/ofprotocol.8.in
|
||||
|
||||
@@ -61,9 +61,6 @@
|
||||
#include "poll-loop.h"
|
||||
#include "ratelimit.h"
|
||||
#include "rconn.h"
|
||||
#ifdef SUPPORT_SNAT
|
||||
#include "snat.h"
|
||||
#endif
|
||||
#include "flow-end.h"
|
||||
#include "stp-secchan.h"
|
||||
#include "status.h"
|
||||
@@ -213,9 +210,6 @@ main(int argc, char *argv[])
|
||||
/* Set up hooks. */
|
||||
port_watcher_start(&secchan, local_rconn, remote_rconn, &pw);
|
||||
discovery = s.discovery ? discovery_init(&s, pw, switch_status) : NULL;
|
||||
#ifdef SUPPORT_SNAT
|
||||
snat_start(&secchan, pw);
|
||||
#endif
|
||||
flow_end_start(&secchan, s.netflow_dst, local_rconn, remote_rconn);
|
||||
if (s.enable_stp) {
|
||||
stp_start(&secchan, pw, local_rconn, remote_rconn);
|
||||
|
||||
-293
@@ -1,293 +0,0 @@
|
||||
/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
|
||||
* Junior University
|
||||
*
|
||||
* We are making the OpenFlow specification and associated documentation
|
||||
* (Software) available for public use and benefit with the expectation
|
||||
* that others will use, modify and enhance the Software and contribute
|
||||
* those enhancements back to the community. However, since we would
|
||||
* like to make the Software available for broadest use, with as few
|
||||
* restrictions as possible permission is hereby granted, free of
|
||||
* charge, to any person obtaining a copy of this Software to deal in
|
||||
* the Software under the copyrights without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* The name and trademarks of copyright holder(s) may NOT be used in
|
||||
* advertising or publicity pertaining to the Software or any
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "snat.h"
|
||||
#include <arpa/inet.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include "openflow/nicira-ext.h"
|
||||
#include "ofpbuf.h"
|
||||
#include "openflow/openflow.h"
|
||||
#include "port-watcher.h"
|
||||
|
||||
#define THIS_MODULE VLM_snat
|
||||
#include "vlog.h"
|
||||
|
||||
struct snat_port_conf {
|
||||
struct list node;
|
||||
struct nx_snat_config config;
|
||||
};
|
||||
|
||||
struct snat_data {
|
||||
struct port_watcher *pw;
|
||||
struct list port_list;
|
||||
};
|
||||
|
||||
|
||||
/* Source-NAT configuration monitor. */
|
||||
#define SNAT_CMD_LEN 1024
|
||||
|
||||
/* Commands to configure iptables. There is no programmatic interface
|
||||
* to iptables from the kernel, so we're stuck making command-line calls
|
||||
* in user-space. */
|
||||
#define SNAT_FLUSH_ALL_CMD "/sbin/iptables -t nat -F"
|
||||
#define SNAT_FLUSH_CHAIN_CMD "/sbin/iptables -t nat -F of-snat-%s"
|
||||
|
||||
#define SNAT_ADD_CHAIN_CMD "/sbin/iptables -t nat -N of-snat-%s"
|
||||
#define SNAT_CONF_CHAIN_CMD "/sbin/iptables -t nat -A POSTROUTING -o %s -j of-snat-%s"
|
||||
|
||||
#define SNAT_ADD_IP_CMD "/sbin/iptables -t nat -A of-snat-%s -j SNAT --to %s-%s"
|
||||
#define SNAT_ADD_TCP_CMD "/sbin/iptables -t nat -A of-snat-%s -j SNAT -p TCP --to %s-%s:%d-%d"
|
||||
#define SNAT_ADD_UDP_CMD "/sbin/iptables -t nat -A of-snat-%s -j SNAT -p UDP --to %s-%s:%d-%d"
|
||||
|
||||
#define SNAT_UNSET_CHAIN_CMD "/sbin/iptables -t nat -D POSTROUTING -o %s -j of-snat-%s"
|
||||
#define SNAT_DEL_CHAIN_CMD "/sbin/iptables -t nat -X of-snat-%s"
|
||||
|
||||
static void
|
||||
snat_add_rules(const struct nx_snat_config *sc, const uint8_t *dev_name)
|
||||
{
|
||||
char command[SNAT_CMD_LEN];
|
||||
char ip_str_start[16];
|
||||
char ip_str_end[16];
|
||||
|
||||
|
||||
snprintf(ip_str_start, sizeof ip_str_start, IP_FMT,
|
||||
IP_ARGS(&sc->ip_addr_start));
|
||||
snprintf(ip_str_end, sizeof ip_str_end, IP_FMT,
|
||||
IP_ARGS(&sc->ip_addr_end));
|
||||
|
||||
/* We always attempt to remove existing entries, so that we know
|
||||
* there's a pristine state for SNAT on the interface. We just ignore
|
||||
* the results of these calls, since iptables will complain about
|
||||
* any non-existent entries. */
|
||||
|
||||
/* Flush the chain that does the SNAT. */
|
||||
snprintf(command, sizeof(command), SNAT_FLUSH_CHAIN_CMD, dev_name);
|
||||
system(command);
|
||||
|
||||
/* We always try to create the a new chain. */
|
||||
snprintf(command, sizeof(command), SNAT_ADD_CHAIN_CMD, dev_name);
|
||||
system(command);
|
||||
|
||||
/* Disassociate any old SNAT chain from the POSTROUTING chain. */
|
||||
snprintf(command, sizeof(command), SNAT_UNSET_CHAIN_CMD, dev_name,
|
||||
dev_name);
|
||||
system(command);
|
||||
|
||||
/* Associate the new chain with the POSTROUTING hook. */
|
||||
snprintf(command, sizeof(command), SNAT_CONF_CHAIN_CMD, dev_name,
|
||||
dev_name);
|
||||
if (system(command) != 0) {
|
||||
VLOG_ERR("SNAT: problem flushing chain for add");
|
||||
return;
|
||||
}
|
||||
|
||||
/* If configured, restrict TCP source port ranges. */
|
||||
if ((sc->tcp_start != 0) && (sc->tcp_end != 0)) {
|
||||
snprintf(command, sizeof(command), SNAT_ADD_TCP_CMD,
|
||||
dev_name, ip_str_start, ip_str_end,
|
||||
ntohs(sc->tcp_start), ntohs(sc->tcp_end));
|
||||
if (system(command) != 0) {
|
||||
VLOG_ERR("SNAT: problem adding TCP rule");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* If configured, restrict UDP source port ranges. */
|
||||
if ((sc->udp_start != 0) && (sc->udp_end != 0)) {
|
||||
snprintf(command, sizeof(command), SNAT_ADD_UDP_CMD,
|
||||
dev_name, ip_str_start, ip_str_end,
|
||||
ntohs(sc->udp_start), ntohs(sc->udp_end));
|
||||
if (system(command) != 0) {
|
||||
VLOG_ERR("SNAT: problem adding UDP rule");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add a rule that covers all IP traffic that would not be covered
|
||||
* by the prior TCP or UDP ranges. */
|
||||
snprintf(command, sizeof(command), SNAT_ADD_IP_CMD,
|
||||
dev_name, ip_str_start, ip_str_end);
|
||||
if (system(command) != 0) {
|
||||
VLOG_ERR("SNAT: problem adding base rule");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
snat_del_rules(const uint8_t *dev_name)
|
||||
{
|
||||
char command[SNAT_CMD_LEN];
|
||||
|
||||
/* Flush the chain that does the SNAT. */
|
||||
snprintf(command, sizeof(command), SNAT_FLUSH_CHAIN_CMD, dev_name);
|
||||
if (system(command) != 0) {
|
||||
VLOG_ERR("SNAT: problem flushing chain for deletion");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Disassociate the SNAT chain from the POSTROUTING chain. */
|
||||
snprintf(command, sizeof(command), SNAT_UNSET_CHAIN_CMD, dev_name,
|
||||
dev_name);
|
||||
if (system(command) != 0) {
|
||||
VLOG_ERR("SNAT: problem unsetting chain");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Now we can finally delete our SNAT chain. */
|
||||
snprintf(command, sizeof(command), SNAT_DEL_CHAIN_CMD, dev_name);
|
||||
if (system(command) != 0) {
|
||||
VLOG_ERR("SNAT: problem deleting chain");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
snat_config(const struct nx_snat_config *sc, struct snat_data *snat)
|
||||
{
|
||||
struct snat_port_conf *c, *spc=NULL;
|
||||
const uint8_t *netdev_name;
|
||||
|
||||
netdev_name = (const uint8_t *) port_watcher_get_name(snat->pw,
|
||||
ntohs(sc->port));
|
||||
if (!netdev_name) {
|
||||
return;
|
||||
}
|
||||
|
||||
LIST_FOR_EACH(c, struct snat_port_conf, node, &snat->port_list) {
|
||||
if (c->config.port == sc->port) {
|
||||
spc = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sc->command == NXSC_ADD) {
|
||||
if (!spc) {
|
||||
spc = xmalloc(sizeof(*c));
|
||||
if (!spc) {
|
||||
VLOG_ERR("SNAT: no memory for new entry");
|
||||
return;
|
||||
}
|
||||
list_push_back(&snat->port_list, &spc->node);
|
||||
}
|
||||
memcpy(&spc->config, sc, sizeof(spc->config));
|
||||
snat_add_rules(sc, netdev_name);
|
||||
} else if (spc) {
|
||||
snat_del_rules(netdev_name);
|
||||
list_remove(&spc->node);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
snat_remote_packet_cb(struct relay *r, void *snat_)
|
||||
{
|
||||
struct snat_data *snat = snat_;
|
||||
struct ofpbuf *msg = r->halves[HALF_REMOTE].rxbuf;
|
||||
struct nicira_header *request = msg->data;
|
||||
struct nx_act_config *nac = msg->data;
|
||||
int n_configs, i;
|
||||
|
||||
|
||||
if (msg->size < sizeof(struct nx_act_config)) {
|
||||
return false;
|
||||
}
|
||||
request = msg->data;
|
||||
if (request->header.type != OFPT_VENDOR
|
||||
|| request->vendor != htonl(NX_VENDOR_ID)
|
||||
|| request->subtype != htonl(NXT_ACT_SET_CONFIG)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* We're only interested in attempts to configure SNAT */
|
||||
if (nac->type != htons(NXAST_SNAT)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
n_configs = (msg->size - sizeof *nac) / sizeof *nac->snat;
|
||||
for (i=0; i<n_configs; i++) {
|
||||
snat_config(&nac->snat[i], snat);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
snat_port_changed_cb(uint16_t port_no UNUSED,
|
||||
const struct ofp_phy_port *old,
|
||||
const struct ofp_phy_port *new,
|
||||
void *snat_)
|
||||
{
|
||||
struct snat_data *snat = snat_;
|
||||
struct snat_port_conf *c;
|
||||
|
||||
/* We're only interested in ports that went away */
|
||||
if (old && !new) {
|
||||
return;
|
||||
}
|
||||
|
||||
LIST_FOR_EACH(c, struct snat_port_conf, node, &snat->port_list) {
|
||||
if (c->config.port == old->port_no) {
|
||||
snat_del_rules(old->name);
|
||||
list_remove(&c->node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct hook_class snat_hook_class = {
|
||||
NULL, /* local_packet_cb */
|
||||
snat_remote_packet_cb, /* remote_packet_cb */
|
||||
NULL, /* periodic_cb */
|
||||
NULL, /* wait_cb */
|
||||
NULL, /* closing_cb */
|
||||
};
|
||||
|
||||
void
|
||||
snat_start(struct secchan *secchan, struct port_watcher *pw)
|
||||
{
|
||||
int ret;
|
||||
struct snat_data *snat;
|
||||
|
||||
ret = system(SNAT_FLUSH_ALL_CMD);
|
||||
if (ret != 0) {
|
||||
VLOG_ERR("SNAT: problem flushing tables");
|
||||
}
|
||||
|
||||
snat = xcalloc(1, sizeof *snat);
|
||||
snat->pw = pw;
|
||||
list_init(&snat->port_list);
|
||||
|
||||
port_watcher_register_callback(pw, snat_port_changed_cb, snat);
|
||||
add_hook(secchan, &snat_hook_class, snat);
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
|
||||
* Junior University
|
||||
*
|
||||
* We are making the OpenFlow specification and associated documentation
|
||||
* (Software) available for public use and benefit with the expectation
|
||||
* that others will use, modify and enhance the Software and contribute
|
||||
* those enhancements back to the community. However, since we would
|
||||
* like to make the Software available for broadest use, with as few
|
||||
* restrictions as possible permission is hereby granted, free of
|
||||
* charge, to any person obtaining a copy of this Software to deal in
|
||||
* the Software under the copyrights without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* The name and trademarks of copyright holder(s) may NOT be used in
|
||||
* advertising or publicity pertaining to the Software or any
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#ifndef SNAT_H
|
||||
#define SNAT_H 1
|
||||
|
||||
#include "secchan.h"
|
||||
|
||||
struct port_watcher;
|
||||
struct secchan;
|
||||
|
||||
void snat_start(struct secchan *, struct port_watcher *);
|
||||
|
||||
#endif /* snat.h */
|
||||
@@ -235,10 +235,6 @@ usage(void)
|
||||
" dump-flows SWITCH FLOW print matching FLOWs\n"
|
||||
" dump-aggregate SWITCH print aggregate flow statistics\n"
|
||||
" dump-aggregate SWITCH FLOW print aggregate stats for FLOWs\n"
|
||||
#ifdef SUPPORT_SNAT
|
||||
" add-snat SWITCH IFACE IP add SNAT config to IFACE\n"
|
||||
" del-snat SWITCH IFACE delete SNAT config on IFACE\n"
|
||||
#endif
|
||||
" add-flow SWITCH FLOW add flow described by FLOW\n"
|
||||
" add-flows SWITCH FILE add flows from FILE\n"
|
||||
" mod-flows SWITCH FLOW modify actions of matching FLOWs\n"
|
||||
@@ -830,19 +826,6 @@ str_to_action(char *str, struct ofpbuf *b)
|
||||
ah->type = htons(OFPAT_STRIP_VLAN);
|
||||
} else if (!strcasecmp(act, "output")) {
|
||||
put_output_action(b, str_to_u32(arg));
|
||||
#ifdef SUPPORT_SNAT
|
||||
} else if (!strcasecmp(act, "nat")) {
|
||||
struct nx_action_snat *sa;
|
||||
|
||||
if (str_to_u32(arg) > OFPP_MAX) {
|
||||
ofp_fatal(0, "Invalid nat port: %s\n", arg);
|
||||
}
|
||||
|
||||
sa = put_action(b, sizeof *sa, OFPAT_VENDOR);
|
||||
sa->vendor = htonl(NX_VENDOR_ID);
|
||||
sa->subtype = htons(NXAST_SNAT);
|
||||
sa->port = htons(str_to_u32(arg));
|
||||
#endif
|
||||
} else if (!strcasecmp(act, "TABLE")) {
|
||||
put_output_action(b, OFPP_TABLE);
|
||||
} else if (!strcasecmp(act, "NORMAL")) {
|
||||
@@ -1067,60 +1050,6 @@ do_dump_aggregate(const struct settings *s UNUSED, int argc, char *argv[])
|
||||
dump_stats_transaction(argv[1], request);
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_SNAT
|
||||
static void
|
||||
do_add_snat(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
|
||||
{
|
||||
struct vconn *vconn;
|
||||
struct ofpbuf *buffer;
|
||||
struct nx_act_config *nac;
|
||||
size_t size;
|
||||
|
||||
/* Parse and send. */
|
||||
size = sizeof *nac + sizeof nac->snat[0];
|
||||
nac = make_openflow(size, OFPT_VENDOR, &buffer);
|
||||
|
||||
nac->header.vendor = htonl(NX_VENDOR_ID);
|
||||
nac->header.subtype = htonl(NXT_ACT_SET_CONFIG);
|
||||
|
||||
nac->type = htons(NXAST_SNAT);
|
||||
nac->snat[0].command = NXSC_ADD;
|
||||
nac->snat[0].port = htons(str_to_u32(argv[2]));
|
||||
nac->snat[0].mac_timeout = htons(0);
|
||||
str_to_ip(argv[3], &nac->snat[0].ip_addr_start);
|
||||
str_to_ip(argv[3], &nac->snat[0].ip_addr_end);
|
||||
|
||||
open_vconn(argv[1], &vconn);
|
||||
send_openflow_buffer(vconn, buffer);
|
||||
vconn_close(vconn);
|
||||
}
|
||||
|
||||
static void
|
||||
do_del_snat(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
|
||||
{
|
||||
struct vconn *vconn;
|
||||
struct ofpbuf *buffer;
|
||||
struct nx_act_config *nac;
|
||||
size_t size;
|
||||
|
||||
/* Parse and send. */
|
||||
size = sizeof *nac + sizeof nac->snat[0];
|
||||
nac = make_openflow(size, OFPT_VENDOR, &buffer);
|
||||
|
||||
nac->header.vendor = htonl(NX_VENDOR_ID);
|
||||
nac->header.subtype = htonl(NXT_ACT_SET_CONFIG);
|
||||
|
||||
nac->type = htons(NXAST_SNAT);
|
||||
nac->snat[0].command = NXSC_DELETE;
|
||||
nac->snat[0].port = htons(str_to_u32(argv[2]));
|
||||
nac->snat[0].mac_timeout = htons(0);
|
||||
|
||||
open_vconn(argv[1], &vconn);
|
||||
send_openflow_buffer(vconn, buffer);
|
||||
vconn_close(vconn);
|
||||
}
|
||||
#endif /* SUPPORT_SNAT */
|
||||
|
||||
static void
|
||||
do_add_flow(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
|
||||
{
|
||||
@@ -1567,10 +1496,6 @@ static struct command all_commands[] = {
|
||||
{ "dump-tables", 1, 1, do_dump_tables },
|
||||
{ "dump-flows", 1, 2, do_dump_flows },
|
||||
{ "dump-aggregate", 1, 2, do_dump_aggregate },
|
||||
#ifdef SUPPORT_SNAT
|
||||
{ "add-snat", 3, 3, do_add_snat },
|
||||
{ "del-snat", 2, 2, do_del_snat },
|
||||
#endif
|
||||
{ "add-flow", 2, 2, do_add_flow },
|
||||
{ "add-flows", 2, 2, do_add_flows },
|
||||
{ "mod-flows", 2, 2, do_mod_flows },
|
||||
|
||||
Reference in new issue
Block a user