Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next
[firefly-linux-kernel-4.4.55.git] / net / bridge / br_netfilter.c
index 7527e94dd5dce327064fe045506458d1a42f3c0c..ab55e2472beb0e44dece07e327f2e0eb8d3f502c 100644 (file)
@@ -111,6 +111,24 @@ static inline __be16 pppoe_proto(const struct sk_buff *skb)
         pppoe_proto(skb) == htons(PPP_IPV6) && \
         brnf_filter_pppoe_tagged)
 
+/* largest possible L2 header, see br_nf_dev_queue_xmit() */
+#define NF_BRIDGE_MAX_MAC_HEADER_LENGTH (PPPOE_SES_HLEN + ETH_HLEN)
+
+#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4)
+struct brnf_frag_data {
+       char mac[NF_BRIDGE_MAX_MAC_HEADER_LENGTH];
+       u8 encap_size;
+       u8 size;
+};
+
+static DEFINE_PER_CPU(struct brnf_frag_data, brnf_frag_data_storage);
+#endif
+
+static struct nf_bridge_info *nf_bridge_info_get(const struct sk_buff *skb)
+{
+       return skb->nf_bridge;
+}
+
 static inline struct rtable *bridge_parent_rtable(const struct net_device *dev)
 {
        struct net_bridge_port *port;
@@ -189,14 +207,6 @@ static inline void nf_bridge_pull_encap_header_rcsum(struct sk_buff *skb)
        skb->network_header += len;
 }
 
-static inline void nf_bridge_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);
-}
-
 /* When handing a packet over to the IP layer
  * check whether we have a skb that is in the
  * expected format
@@ -252,23 +262,29 @@ drop:
 
 static void nf_bridge_update_protocol(struct sk_buff *skb)
 {
-       if (skb->nf_bridge->mask & BRNF_8021Q)
+       switch (skb->nf_bridge->orig_proto) {
+       case BRNF_PROTO_8021Q:
                skb->protocol = htons(ETH_P_8021Q);
-       else if (skb->nf_bridge->mask & BRNF_PPPoE)
+               break;
+       case BRNF_PROTO_PPPOE:
                skb->protocol = htons(ETH_P_PPP_SES);
+               break;
+       case BRNF_PROTO_UNCHANGED:
+               break;
+       }
 }
 
 /* PF_BRIDGE/PRE_ROUTING *********************************************/
 /* Undo the changes made for ip6tables PREROUTING and continue the
  * bridge PRE_ROUTING hook. */
-static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb)
+static int br_nf_pre_routing_finish_ipv6(struct sock *sk, struct sk_buff *skb)
 {
-       struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+       struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
        struct rtable *rt;
 
-       if (nf_bridge->mask & BRNF_PKT_TYPE) {
+       if (nf_bridge->pkt_otherhost) {
                skb->pkt_type = PACKET_OTHERHOST;
-               nf_bridge->mask ^= BRNF_PKT_TYPE;
+               nf_bridge->pkt_otherhost = false;
        }
        nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
 
@@ -282,7 +298,8 @@ static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb)
        skb->dev = nf_bridge->physindev;
        nf_bridge_update_protocol(skb);
        nf_bridge_push_encap_header(skb);
-       NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+       NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, sk, skb,
+                      skb->dev, NULL,
                       br_handle_frame_finish, 1);
 
        return 0;
@@ -293,9 +310,8 @@ static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb)
  * don't, we use the neighbour framework to find out. In both cases, we make
  * sure that br_handle_frame_finish() is called afterwards.
  */
-static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
+static int br_nf_pre_routing_finish_bridge(struct sock *sk, struct sk_buff *skb)
 {
-       struct nf_bridge_info *nf_bridge = skb->nf_bridge;
        struct neighbour *neigh;
        struct dst_entry *dst;
 
@@ -305,12 +321,13 @@ static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
        dst = skb_dst(skb);
        neigh = dst_neigh_lookup_skb(dst, skb);
        if (neigh) {
+               struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
                int ret;
 
                if (neigh->hh.hh_len) {
                        neigh_hh_bridge(&neigh->hh, skb);
                        skb->dev = nf_bridge->physindev;
-                       ret = br_handle_frame_finish(skb);
+                       ret = br_handle_frame_finish(sk, skb);
                } else {
                        /* the neighbour function below overwrites the complete
                         * MAC header, so we save the Ethernet source address and
@@ -318,7 +335,7 @@ static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
                         */
                        skb_copy_from_linear_data_offset(skb,
                                                         -(ETH_HLEN-ETH_ALEN),
-                                                        skb->nf_bridge->data,
+                                                        nf_bridge->neigh_header,
                                                         ETH_HLEN-ETH_ALEN);
                        /* tell br_dev_xmit to continue with forwarding */
                        nf_bridge->mask |= BRNF_BRIDGED_DNAT;
@@ -387,11 +404,11 @@ static bool dnat_took_place(const struct sk_buff *skb)
  * device, we proceed as if ip_route_input() succeeded. If it differs from the
  * logical bridge port or if ip_route_output_key() fails we drop the packet.
  */
-static int br_nf_pre_routing_finish(struct sk_buff *skb)
+static int br_nf_pre_routing_finish(struct sock *sk, struct sk_buff *skb)
 {
        struct net_device *dev = skb->dev;
        struct iphdr *iph = ip_hdr(skb);
-       struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+       struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
        struct rtable *rt;
        int err;
        int frag_max_size;
@@ -399,9 +416,9 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb)
        frag_max_size = IPCB(skb)->frag_max_size;
        BR_INPUT_SKB_CB(skb)->frag_max_size = frag_max_size;
 
-       if (nf_bridge->mask & BRNF_PKT_TYPE) {
+       if (nf_bridge->pkt_otherhost) {
                skb->pkt_type = PACKET_OTHERHOST;
-               nf_bridge->mask ^= BRNF_PKT_TYPE;
+               nf_bridge->pkt_otherhost = false;
        }
        nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
        if (dnat_took_place(skb)) {
@@ -440,7 +457,7 @@ bridged_dnat:
                                nf_bridge_push_encap_header(skb);
                                NF_HOOK_THRESH(NFPROTO_BRIDGE,
                                               NF_BR_PRE_ROUTING,
-                                              skb, skb->dev, NULL,
+                                              sk, skb, skb->dev, NULL,
                                               br_nf_pre_routing_finish_bridge,
                                               1);
                                return 0;
@@ -460,7 +477,8 @@ bridged_dnat:
        skb->dev = nf_bridge->physindev;
        nf_bridge_update_protocol(skb);
        nf_bridge_push_encap_header(skb);
-       NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+       NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, sk, skb,
+                      skb->dev, NULL,
                       br_handle_frame_finish, 1);
 
        return 0;
@@ -483,20 +501,21 @@ static struct net_device *brnf_get_logical_dev(struct sk_buff *skb, const struct
 /* Some common code for IPv4/IPv6 */
 static struct net_device *setup_pre_routing(struct sk_buff *skb)
 {
-       struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+       struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
 
        if (skb->pkt_type == PACKET_OTHERHOST) {
                skb->pkt_type = PACKET_HOST;
-               nf_bridge->mask |= BRNF_PKT_TYPE;
+               nf_bridge->pkt_otherhost = true;
        }
 
        nf_bridge->mask |= BRNF_NF_BRIDGE_PREROUTING;
        nf_bridge->physindev = skb->dev;
        skb->dev = brnf_get_logical_dev(skb, skb->dev);
+
        if (skb->protocol == htons(ETH_P_8021Q))
-               nf_bridge->mask |= BRNF_8021Q;
+               nf_bridge->orig_proto = BRNF_PROTO_8021Q;
        else if (skb->protocol == htons(ETH_P_PPP_SES))
-               nf_bridge->mask |= BRNF_PPPoE;
+               nf_bridge->orig_proto = BRNF_PROTO_PPPOE;
 
        /* Must drop socket now because of tproxy. */
        skb_orphan(skb);
@@ -596,7 +615,8 @@ static unsigned int br_nf_pre_routing_ipv6(const struct nf_hook_ops *ops,
                return NF_DROP;
 
        skb->protocol = htons(ETH_P_IPV6);
-       NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, skb, skb->dev, NULL,
+       NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, state->sk, skb,
+               skb->dev, NULL,
                br_nf_pre_routing_finish_ipv6);
 
        return NF_STOLEN;
@@ -651,7 +671,8 @@ static unsigned int br_nf_pre_routing(const struct nf_hook_ops *ops,
 
        skb->protocol = htons(ETH_P_IP);
 
-       NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, skb->dev, NULL,
+       NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, state->sk, skb,
+               skb->dev, NULL,
                br_nf_pre_routing_finish);
 
        return NF_STOLEN;
@@ -674,16 +695,23 @@ static unsigned int br_nf_local_in(const struct nf_hook_ops *ops,
 }
 
 /* PF_BRIDGE/FORWARD *************************************************/
-static int br_nf_forward_finish(struct sk_buff *skb)
+static int br_nf_forward_finish(struct sock *sk, struct sk_buff *skb)
 {
-       struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+       struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
        struct net_device *in;
 
        if (!IS_ARP(skb) && !IS_VLAN_ARP(skb)) {
+               int frag_max_size;
+
+               if (skb->protocol == htons(ETH_P_IP)) {
+                       frag_max_size = IPCB(skb)->frag_max_size;
+                       BR_INPUT_SKB_CB(skb)->frag_max_size = frag_max_size;
+               }
+
                in = nf_bridge->physindev;
-               if (nf_bridge->mask & BRNF_PKT_TYPE) {
+               if (nf_bridge->pkt_otherhost) {
                        skb->pkt_type = PACKET_OTHERHOST;
-                       nf_bridge->mask ^= BRNF_PKT_TYPE;
+                       nf_bridge->pkt_otherhost = false;
                }
                nf_bridge_update_protocol(skb);
        } else {
@@ -691,8 +719,8 @@ static int br_nf_forward_finish(struct sk_buff *skb)
        }
        nf_bridge_push_encap_header(skb);
 
-       NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_FORWARD, skb, in,
-                      skb->dev, br_forward_finish, 1);
+       NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_FORWARD, sk, skb,
+                      in, skb->dev, br_forward_finish, 1);
        return 0;
 }
 
@@ -718,6 +746,10 @@ static unsigned int br_nf_forward_ip(const struct nf_hook_ops *ops,
        if (!nf_bridge_unshare(skb))
                return NF_DROP;
 
+       nf_bridge = nf_bridge_info_get(skb);
+       if (!nf_bridge)
+               return NF_DROP;
+
        parent = bridge_parent(state->out);
        if (!parent)
                return NF_DROP;
@@ -731,14 +763,19 @@ static unsigned int br_nf_forward_ip(const struct nf_hook_ops *ops,
 
        nf_bridge_pull_encap_header(skb);
 
-       nf_bridge = skb->nf_bridge;
        if (skb->pkt_type == PACKET_OTHERHOST) {
                skb->pkt_type = PACKET_HOST;
-               nf_bridge->mask |= BRNF_PKT_TYPE;
+               nf_bridge->pkt_otherhost = true;
        }
 
-       if (pf == NFPROTO_IPV4 && br_parse_ip_options(skb))
-               return NF_DROP;
+       if (pf == NFPROTO_IPV4) {
+               int frag_max = BR_INPUT_SKB_CB(skb)->frag_max_size;
+
+               if (br_parse_ip_options(skb))
+                       return NF_DROP;
+
+               IPCB(skb)->frag_max_size = frag_max;
+       }
 
        nf_bridge->physoutdev = skb->dev;
        if (pf == NFPROTO_IPV4)
@@ -746,7 +783,8 @@ static unsigned int br_nf_forward_ip(const struct nf_hook_ops *ops,
        else
                skb->protocol = htons(ETH_P_IPV6);
 
-       NF_HOOK(pf, NF_INET_FORWARD, skb, brnf_get_logical_dev(skb, state->in),
+       NF_HOOK(pf, NF_INET_FORWARD, NULL, skb,
+               brnf_get_logical_dev(skb, state->in),
                parent, br_nf_forward_finish);
 
        return NF_STOLEN;
@@ -780,69 +818,74 @@ static unsigned int br_nf_forward_arp(const struct nf_hook_ops *ops,
                return NF_ACCEPT;
        }
        *d = state->in;
-       NF_HOOK(NFPROTO_ARP, NF_ARP_FORWARD, skb, state->in,
-               state->out, br_nf_forward_finish);
+       NF_HOOK(NFPROTO_ARP, NF_ARP_FORWARD, state->sk, skb,
+               state->in, state->out, br_nf_forward_finish);
 
        return NF_STOLEN;
 }
 
 #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4)
-static bool nf_bridge_copy_header(struct sk_buff *skb)
+static int br_nf_push_frag_xmit(struct sock *sk, struct sk_buff *skb)
 {
+       struct brnf_frag_data *data;
        int err;
-       unsigned int header_size;
-
-       nf_bridge_update_protocol(skb);
-       header_size = ETH_HLEN + nf_bridge_encap_header_len(skb);
-       err = skb_cow_head(skb, header_size);
-       if (err)
-               return false;
 
-       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 true;
-}
+       data = this_cpu_ptr(&brnf_frag_data_storage);
+       err = skb_cow_head(skb, data->size);
 
-static int br_nf_push_frag_xmit(struct sk_buff *skb)
-{
-       if (!nf_bridge_copy_header(skb)) {
+       if (err) {
                kfree_skb(skb);
                return 0;
        }
 
-       return br_dev_queue_push_xmit(skb);
+       skb_copy_to_linear_data_offset(skb, -data->size, data->mac, data->size);
+       __skb_push(skb, data->encap_size);
+
+       return br_dev_queue_push_xmit(sk, skb);
 }
 
-static int br_nf_dev_queue_xmit(struct sk_buff *skb)
+static int br_nf_dev_queue_xmit(struct sock *sk, struct sk_buff *skb)
 {
        int ret;
        int frag_max_size;
        unsigned int mtu_reserved;
 
        if (skb_is_gso(skb) || skb->protocol != htons(ETH_P_IP))
-               return br_dev_queue_push_xmit(skb);
+               return br_dev_queue_push_xmit(sk, skb);
 
        mtu_reserved = nf_bridge_mtu_reduction(skb);
        /* This is wrong! We should preserve the original fragment
         * boundaries by preserving frag_list rather than refragmenting.
         */
        if (skb->len + mtu_reserved > skb->dev->mtu) {
+               struct brnf_frag_data *data;
+
                frag_max_size = BR_INPUT_SKB_CB(skb)->frag_max_size;
                if (br_parse_ip_options(skb))
                        /* Drop invalid packet */
                        return NF_DROP;
                IPCB(skb)->frag_max_size = frag_max_size;
-               ret = ip_fragment(skb, br_nf_push_frag_xmit);
-       } else
-               ret = br_dev_queue_push_xmit(skb);
+
+               nf_bridge_update_protocol(skb);
+
+               data = this_cpu_ptr(&brnf_frag_data_storage);
+               data->encap_size = nf_bridge_encap_header_len(skb);
+               data->size = ETH_HLEN + data->encap_size;
+
+               skb_copy_from_linear_data_offset(skb, -data->size, data->mac,
+                                                data->size);
+
+               ret = ip_fragment(sk, skb, br_nf_push_frag_xmit);
+       } else {
+               ret = br_dev_queue_push_xmit(sk, skb);
+       }
 
        return ret;
 }
 #else
-static int br_nf_dev_queue_xmit(struct sk_buff *skb)
+static int br_nf_dev_queue_xmit(struct sock *sk, struct sk_buff *skb)
 {
-        return br_dev_queue_push_xmit(skb);
+        return br_dev_queue_push_xmit(sk, skb);
 }
 #endif
 
@@ -851,7 +894,7 @@ static unsigned int br_nf_post_routing(const struct nf_hook_ops *ops,
                                       struct sk_buff *skb,
                                       const struct nf_hook_state *state)
 {
-       struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+       struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
        struct net_device *realoutdev = bridge_parent(skb->dev);
        u_int8_t pf;
 
@@ -877,17 +920,17 @@ static unsigned int br_nf_post_routing(const struct nf_hook_ops *ops,
         * about the value of skb->pkt_type. */
        if (skb->pkt_type == PACKET_OTHERHOST) {
                skb->pkt_type = PACKET_HOST;
-               nf_bridge->mask |= BRNF_PKT_TYPE;
+               nf_bridge->pkt_otherhost = true;
        }
 
        nf_bridge_pull_encap_header(skb);
-       nf_bridge_save_header(skb);
        if (pf == NFPROTO_IPV4)
                skb->protocol = htons(ETH_P_IP);
        else
                skb->protocol = htons(ETH_P_IPV6);
 
-       NF_HOOK(pf, NF_INET_POST_ROUTING, skb, NULL, realoutdev,
+       NF_HOOK(pf, NF_INET_POST_ROUTING, state->sk, skb,
+               NULL, realoutdev,
                br_nf_dev_queue_xmit);
 
        return NF_STOLEN;
@@ -919,15 +962,18 @@ static unsigned int ip_sabotage_in(const struct nf_hook_ops *ops,
  */
 static void br_nf_pre_routing_finish_bridge_slow(struct sk_buff *skb)
 {
-       struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+       struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
 
        skb_pull(skb, ETH_HLEN);
        nf_bridge->mask &= ~BRNF_BRIDGED_DNAT;
 
-       skb_copy_to_linear_data_offset(skb, -(ETH_HLEN-ETH_ALEN),
-                                      skb->nf_bridge->data, ETH_HLEN-ETH_ALEN);
+       BUILD_BUG_ON(sizeof(nf_bridge->neigh_header) != (ETH_HLEN - ETH_ALEN));
+
+       skb_copy_to_linear_data_offset(skb, -(ETH_HLEN - ETH_ALEN),
+                                      nf_bridge->neigh_header,
+                                      ETH_HLEN - ETH_ALEN);
        skb->dev = nf_bridge->physindev;
-       br_handle_frame_finish(skb);
+       br_handle_frame_finish(NULL, skb);
 }
 
 static int br_nf_dev_xmit(struct sk_buff *skb)