diff options
-rw-r--r-- | include/linux/skbuff.h | 2 | ||||
-rw-r--r-- | net/bridge/br_netfilter.c | 70 |
2 files changed, 42 insertions, 30 deletions
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 36f3f43c0117..f66a089afc41 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h | |||
@@ -169,7 +169,7 @@ struct nf_bridge_info { | |||
169 | unsigned int mask; | 169 | unsigned int mask; |
170 | struct net_device *physindev; | 170 | struct net_device *physindev; |
171 | struct net_device *physoutdev; | 171 | struct net_device *physoutdev; |
172 | unsigned long data[32 / sizeof(unsigned long)]; | 172 | char neigh_header[8]; |
173 | }; | 173 | }; |
174 | #endif | 174 | #endif |
175 | 175 | ||
diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 282ed76c49e0..ca1cb6704a78 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c | |||
@@ -111,6 +111,19 @@ static inline __be16 pppoe_proto(const struct sk_buff *skb) | |||
111 | pppoe_proto(skb) == htons(PPP_IPV6) && \ | 111 | pppoe_proto(skb) == htons(PPP_IPV6) && \ |
112 | brnf_filter_pppoe_tagged) | 112 | brnf_filter_pppoe_tagged) |
113 | 113 | ||
114 | /* largest possible L2 header, see br_nf_dev_queue_xmit() */ | ||
115 | #define NF_BRIDGE_MAX_MAC_HEADER_LENGTH (PPPOE_SES_HLEN + ETH_HLEN) | ||
116 | |||
117 | #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) | ||
118 | struct brnf_frag_data { | ||
119 | char mac[NF_BRIDGE_MAX_MAC_HEADER_LENGTH]; | ||
120 | u8 encap_size; | ||
121 | u8 size; | ||
122 | }; | ||
123 | |||
124 | static DEFINE_PER_CPU(struct brnf_frag_data, brnf_frag_data_storage); | ||
125 | #endif | ||
126 | |||
114 | static inline struct rtable *bridge_parent_rtable(const struct net_device *dev) | 127 | static inline struct rtable *bridge_parent_rtable(const struct net_device *dev) |
115 | { | 128 | { |
116 | struct net_bridge_port *port; | 129 | struct net_bridge_port *port; |
@@ -189,14 +202,6 @@ static inline void nf_bridge_pull_encap_header_rcsum(struct sk_buff *skb) | |||
189 | skb->network_header += len; | 202 | skb->network_header += len; |
190 | } | 203 | } |
191 | 204 | ||
192 | static inline void nf_bridge_save_header(struct sk_buff *skb) | ||
193 | { | ||
194 | int header_size = ETH_HLEN + nf_bridge_encap_header_len(skb); | ||
195 | |||
196 | skb_copy_from_linear_data_offset(skb, -header_size, | ||
197 | skb->nf_bridge->data, header_size); | ||
198 | } | ||
199 | |||
200 | /* When handing a packet over to the IP layer | 205 | /* When handing a packet over to the IP layer |
201 | * check whether we have a skb that is in the | 206 | * check whether we have a skb that is in the |
202 | * expected format | 207 | * expected format |
@@ -318,7 +323,7 @@ static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb) | |||
318 | */ | 323 | */ |
319 | skb_copy_from_linear_data_offset(skb, | 324 | skb_copy_from_linear_data_offset(skb, |
320 | -(ETH_HLEN-ETH_ALEN), | 325 | -(ETH_HLEN-ETH_ALEN), |
321 | skb->nf_bridge->data, | 326 | nf_bridge->neigh_header, |
322 | ETH_HLEN-ETH_ALEN); | 327 | ETH_HLEN-ETH_ALEN); |
323 | /* tell br_dev_xmit to continue with forwarding */ | 328 | /* tell br_dev_xmit to continue with forwarding */ |
324 | nf_bridge->mask |= BRNF_BRIDGED_DNAT; | 329 | nf_bridge->mask |= BRNF_BRIDGED_DNAT; |
@@ -810,30 +815,22 @@ static unsigned int br_nf_forward_arp(const struct nf_hook_ops *ops, | |||
810 | } | 815 | } |
811 | 816 | ||
812 | #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) | 817 | #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) |
813 | static bool nf_bridge_copy_header(struct sk_buff *skb) | 818 | static int br_nf_push_frag_xmit(struct sk_buff *skb) |
814 | { | 819 | { |
820 | struct brnf_frag_data *data; | ||
815 | int err; | 821 | int err; |
816 | unsigned int header_size; | ||
817 | 822 | ||
818 | nf_bridge_update_protocol(skb); | 823 | data = this_cpu_ptr(&brnf_frag_data_storage); |
819 | header_size = ETH_HLEN + nf_bridge_encap_header_len(skb); | 824 | err = skb_cow_head(skb, data->size); |
820 | err = skb_cow_head(skb, header_size); | ||
821 | if (err) | ||
822 | return false; | ||
823 | 825 | ||
824 | skb_copy_to_linear_data_offset(skb, -header_size, | 826 | if (err) { |
825 | skb->nf_bridge->data, header_size); | ||
826 | __skb_push(skb, nf_bridge_encap_header_len(skb)); | ||
827 | return true; | ||
828 | } | ||
829 | |||
830 | static int br_nf_push_frag_xmit(struct sk_buff *skb) | ||
831 | { | ||
832 | if (!nf_bridge_copy_header(skb)) { | ||
833 | kfree_skb(skb); | 827 | kfree_skb(skb); |
834 | return 0; | 828 | return 0; |
835 | } | 829 | } |
836 | 830 | ||
831 | skb_copy_to_linear_data_offset(skb, -data->size, data->mac, data->size); | ||
832 | __skb_push(skb, data->encap_size); | ||
833 | |||
837 | return br_dev_queue_push_xmit(skb); | 834 | return br_dev_queue_push_xmit(skb); |
838 | } | 835 | } |
839 | 836 | ||
@@ -851,14 +848,27 @@ static int br_nf_dev_queue_xmit(struct sk_buff *skb) | |||
851 | * boundaries by preserving frag_list rather than refragmenting. | 848 | * boundaries by preserving frag_list rather than refragmenting. |
852 | */ | 849 | */ |
853 | if (skb->len + mtu_reserved > skb->dev->mtu) { | 850 | if (skb->len + mtu_reserved > skb->dev->mtu) { |
851 | struct brnf_frag_data *data; | ||
852 | |||
854 | frag_max_size = BR_INPUT_SKB_CB(skb)->frag_max_size; | 853 | frag_max_size = BR_INPUT_SKB_CB(skb)->frag_max_size; |
855 | if (br_parse_ip_options(skb)) | 854 | if (br_parse_ip_options(skb)) |
856 | /* Drop invalid packet */ | 855 | /* Drop invalid packet */ |
857 | return NF_DROP; | 856 | return NF_DROP; |
858 | IPCB(skb)->frag_max_size = frag_max_size; | 857 | IPCB(skb)->frag_max_size = frag_max_size; |
858 | |||
859 | nf_bridge_update_protocol(skb); | ||
860 | |||
861 | data = this_cpu_ptr(&brnf_frag_data_storage); | ||
862 | data->encap_size = nf_bridge_encap_header_len(skb); | ||
863 | data->size = ETH_HLEN + data->encap_size; | ||
864 | |||
865 | skb_copy_from_linear_data_offset(skb, -data->size, data->mac, | ||
866 | data->size); | ||
867 | |||
859 | ret = ip_fragment(skb, br_nf_push_frag_xmit); | 868 | ret = ip_fragment(skb, br_nf_push_frag_xmit); |
860 | } else | 869 | } else { |
861 | ret = br_dev_queue_push_xmit(skb); | 870 | ret = br_dev_queue_push_xmit(skb); |
871 | } | ||
862 | 872 | ||
863 | return ret; | 873 | return ret; |
864 | } | 874 | } |
@@ -906,7 +916,6 @@ static unsigned int br_nf_post_routing(const struct nf_hook_ops *ops, | |||
906 | } | 916 | } |
907 | 917 | ||
908 | nf_bridge_pull_encap_header(skb); | 918 | nf_bridge_pull_encap_header(skb); |
909 | nf_bridge_save_header(skb); | ||
910 | if (pf == NFPROTO_IPV4) | 919 | if (pf == NFPROTO_IPV4) |
911 | skb->protocol = htons(ETH_P_IP); | 920 | skb->protocol = htons(ETH_P_IP); |
912 | else | 921 | else |
@@ -951,8 +960,11 @@ static void br_nf_pre_routing_finish_bridge_slow(struct sk_buff *skb) | |||
951 | skb_pull(skb, ETH_HLEN); | 960 | skb_pull(skb, ETH_HLEN); |
952 | nf_bridge->mask &= ~BRNF_BRIDGED_DNAT; | 961 | nf_bridge->mask &= ~BRNF_BRIDGED_DNAT; |
953 | 962 | ||
954 | skb_copy_to_linear_data_offset(skb, -(ETH_HLEN-ETH_ALEN), | 963 | BUILD_BUG_ON(sizeof(nf_bridge->neigh_header) != (ETH_HLEN - ETH_ALEN)); |
955 | skb->nf_bridge->data, ETH_HLEN-ETH_ALEN); | 964 | |
965 | skb_copy_to_linear_data_offset(skb, -(ETH_HLEN - ETH_ALEN), | ||
966 | nf_bridge->neigh_header, | ||
967 | ETH_HLEN - ETH_ALEN); | ||
956 | skb->dev = nf_bridge->physindev; | 968 | skb->dev = nf_bridge->physindev; |
957 | br_handle_frame_finish(skb); | 969 | br_handle_frame_finish(skb); |
958 | } | 970 | } |