#ifdef SUPPORT_SNAT
/* Check if this packet needs early SNAT processing. */
- if (snat_pre_route(skb)) {
+ if (snat_pre_route(skb))
return;
- }
#endif
/* Push the Ethernet header back on. */
skb->dev = NULL;
}
+#ifdef SUPPORT_SNAT
+static int
+dp_xmit_skb_finish(struct sk_buff *skb)
+{
+ /* The ip_fragment function does not copy the Ethernet header into
+ * the newly generated frames, so put back the values stowed
+ * earlier. */
+ if (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);
+
+ 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)
{
return len;
}
+#endif
/* Takes ownership of 'skb' and transmits it to 'out_port' on 'dp'.
*/
#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>
} __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. */
+static inline void snat_save_header(struct sk_buff *skb)
+{
+ int header_size = ETH_HLEN + nf_bridge_encap_header_len(skb);
+
+ 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
/* Pass the translated packet as input to the OpenFlow stack, which
* consumes it. */
+ snat_save_header(skb);
skb_push(skb, ETH_HLEN);
skb_reset_mac_header(skb);
fwd_port_input(p->dp->chain, skb, p);
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;
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);
+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);