From dfc7aa676ab44db7a49284a80798c7be5369db85 Mon Sep 17 00:00:00 2001 From: Justin Pettit Date: Tue, 20 Jan 2009 18:22:49 -0800 Subject: [PATCH] Fix fragment issue for large IP packets when SNAT action enabled. The Netfilter code automatically reassembles IP fragments. We need to explicitly have them refragmented before transmitting. (Bug #823) --- datapath/datapath.c | 51 ++++++++++++++++++++++++++++++++++++++++-- datapath/nx_act_snat.c | 42 ++++++++++++++++++++++++++++++++++ datapath/nx_act_snat.h | 1 + 3 files changed, 92 insertions(+), 2 deletions(-) diff --git a/datapath/datapath.c b/datapath/datapath.c index 5272ceaf3..28f6c4fc0 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -467,9 +467,8 @@ do_port_input(struct net_bridge_port *p, struct sk_buff *skb) #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. */ @@ -567,6 +566,53 @@ 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) +{ + /* 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) { @@ -582,6 +628,7 @@ dp_xmit_skb(struct sk_buff *skb) return len; } +#endif /* Takes ownership of 'skb' and transmits it to 'out_port' on 'dp'. */ diff --git a/datapath/nx_act_snat.c b/datapath/nx_act_snat.c index 43dbdb4dc..127714fad 100644 --- a/datapath/nx_act_snat.c +++ b/datapath/nx_act_snat.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,42 @@ struct ip_arphdr { } __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 @@ -205,6 +242,7 @@ snat_pre_route_finish(struct sk_buff *skb) /* 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); @@ -381,6 +419,10 @@ snat_pre_route(struct sk_buff *skb) 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; diff --git a/datapath/nx_act_snat.h b/datapath/nx_act_snat.h index 28a0e5959..7570aa0f0 100644 --- a/datapath/nx_act_snat.h +++ b/datapath/nx_act_snat.h @@ -34,6 +34,7 @@ struct snat_conf { 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); -- 2.43.0