Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: bridge: don't use nf_bridge_info data to store mac header

br_netfilter maintains an extra state, nf_bridge_info, which is attached
to skb via skb->nf_bridge pointer.

Amongst other things we use skb->nf_bridge->data to store the original
mac header for every processed skb.

This is required for ip refragmentation when using conntrack
on top of bridge, because ip_fragment doesn't copy it from original skb.

However there is no need anymore to do this unconditionally.

Move this to the one place where its needed -- when br_netfilter calls
ip_fragment().

Also switch to percpu storage for this so we can handle fragmenting
without accessing nf_bridge meta data.

Only user left is neigh resolution when DNAT is detected, to hold
the original source mac address (neigh resolution builds new mac header
using bridge mac), so rename ->data and reduce its size to whats needed.

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Florian Westphal and committed by
Pablo Neira Ayuso
e70deecb d64d80a2

+44 -32
+1 -1
include/linux/skbuff.h
··· 169 169 unsigned int mask; 170 170 struct net_device *physindev; 171 171 struct net_device *physoutdev; 172 - unsigned long data[32 / sizeof(unsigned long)]; 172 + char neigh_header[8]; 173 173 }; 174 174 #endif 175 175
+43 -31
net/bridge/br_netfilter.c
··· 111 111 pppoe_proto(skb) == htons(PPP_IPV6) && \ 112 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 127 static inline struct rtable *bridge_parent_rtable(const struct net_device *dev) 115 128 { 116 129 struct net_bridge_port *port; ··· 200 187 201 188 skb_pull_rcsum(skb, len); 202 189 skb->network_header += len; 203 - } 204 - 205 - static inline void nf_bridge_save_header(struct sk_buff *skb) 206 - { 207 - int header_size = ETH_HLEN + nf_bridge_encap_header_len(skb); 208 - 209 - skb_copy_from_linear_data_offset(skb, -header_size, 210 - skb->nf_bridge->data, header_size); 211 190 } 212 191 213 192 /* When handing a packet over to the IP layer ··· 323 318 */ 324 319 skb_copy_from_linear_data_offset(skb, 325 320 -(ETH_HLEN-ETH_ALEN), 326 - skb->nf_bridge->data, 321 + nf_bridge->neigh_header, 327 322 ETH_HLEN-ETH_ALEN); 328 323 /* tell br_dev_xmit to continue with forwarding */ 329 324 nf_bridge->mask |= BRNF_BRIDGED_DNAT; ··· 815 810 } 816 811 817 812 #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) 818 - static bool nf_bridge_copy_header(struct sk_buff *skb) 819 - { 820 - int err; 821 - unsigned int header_size; 822 - 823 - nf_bridge_update_protocol(skb); 824 - header_size = ETH_HLEN + nf_bridge_encap_header_len(skb); 825 - err = skb_cow_head(skb, header_size); 826 - if (err) 827 - return false; 828 - 829 - skb_copy_to_linear_data_offset(skb, -header_size, 830 - skb->nf_bridge->data, header_size); 831 - __skb_push(skb, nf_bridge_encap_header_len(skb)); 832 - return true; 833 - } 834 - 835 813 static int br_nf_push_frag_xmit(struct sk_buff *skb) 836 814 { 837 - if (!nf_bridge_copy_header(skb)) { 815 + struct brnf_frag_data *data; 816 + int err; 817 + 818 + data = this_cpu_ptr(&brnf_frag_data_storage); 819 + err = skb_cow_head(skb, data->size); 820 + 821 + if (err) { 838 822 kfree_skb(skb); 839 823 return 0; 840 824 } 825 + 826 + skb_copy_to_linear_data_offset(skb, -data->size, data->mac, data->size); 827 + __skb_push(skb, data->encap_size); 841 828 842 829 return br_dev_queue_push_xmit(skb); 843 830 } ··· 848 851 * boundaries by preserving frag_list rather than refragmenting. 849 852 */ 850 853 if (skb->len + mtu_reserved > skb->dev->mtu) { 854 + struct brnf_frag_data *data; 855 + 851 856 frag_max_size = BR_INPUT_SKB_CB(skb)->frag_max_size; 852 857 if (br_parse_ip_options(skb)) 853 858 /* Drop invalid packet */ 854 859 return NF_DROP; 855 860 IPCB(skb)->frag_max_size = frag_max_size; 861 + 862 + nf_bridge_update_protocol(skb); 863 + 864 + data = this_cpu_ptr(&brnf_frag_data_storage); 865 + data->encap_size = nf_bridge_encap_header_len(skb); 866 + data->size = ETH_HLEN + data->encap_size; 867 + 868 + skb_copy_from_linear_data_offset(skb, -data->size, data->mac, 869 + data->size); 870 + 856 871 ret = ip_fragment(skb, br_nf_push_frag_xmit); 857 - } else 872 + } else { 858 873 ret = br_dev_queue_push_xmit(skb); 874 + } 859 875 860 876 return ret; 861 877 } ··· 916 906 } 917 907 918 908 nf_bridge_pull_encap_header(skb); 919 - nf_bridge_save_header(skb); 920 909 if (pf == NFPROTO_IPV4) 921 910 skb->protocol = htons(ETH_P_IP); 922 911 else ··· 960 951 skb_pull(skb, ETH_HLEN); 961 952 nf_bridge->mask &= ~BRNF_BRIDGED_DNAT; 962 953 963 - skb_copy_to_linear_data_offset(skb, -(ETH_HLEN-ETH_ALEN), 964 - skb->nf_bridge->data, ETH_HLEN-ETH_ALEN); 954 + BUILD_BUG_ON(sizeof(nf_bridge->neigh_header) != (ETH_HLEN - ETH_ALEN)); 955 + 956 + skb_copy_to_linear_data_offset(skb, -(ETH_HLEN - ETH_ALEN), 957 + nf_bridge->neigh_header, 958 + ETH_HLEN - ETH_ALEN); 965 959 skb->dev = nf_bridge->physindev; 966 960 br_handle_frame_finish(skb); 967 961 }