if (skb_ensure_writable(skb, 0))
return EBT_DROP;
- if (xt_hooknum(par) != NF_BR_BROUTING)
- /* rcu_read_lock()ed by nf_hook_thresh */
- ether_addr_copy(eth_hdr(skb)->h_dest,
- br_port_get_rcu(xt_in(par))->br->dev->dev_addr);
- else
+ if (xt_hooknum(par) != NF_BR_BROUTING) {
+ const struct net_device *dev;
+
+ dev = netdev_master_upper_dev_get_rcu(xt_in(par));
+ if (!dev)
+ return EBT_DROP;
+
+ ether_addr_copy(eth_hdr(skb)->h_dest, dev->dev_addr);
+ } else {
ether_addr_copy(eth_hdr(skb)->h_dest, xt_in(par)->dev_addr);
+ }
+
skb->pkt_type = PACKET_HOST;
return info->target;
}
return -1;
}
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
+static int nflog_put_master_ifindex(struct sk_buff *nlskb, int attr,
+ const struct net_device *dev)
+{
+ const struct net_device *upper;
+
+ if (dev && !netif_is_bridge_port(dev))
+ return 0;
+
+ upper = netdev_master_upper_dev_get_rcu((struct net_device *)dev);
+ if (upper && nla_put_be32(nlskb, attr, htonl(upper->ifindex)))
+ return -EMSGSIZE;
+
+ return 0;
+}
+#endif
+
/* This is an inline function, we don't really care about a long
* list of arguments */
static inline int
/* rcu_read_lock()ed by nf_hook_thresh or
* nf_log_packet.
*/
- nla_put_be32(inst->skb, NFULA_IFINDEX_INDEV,
- htonl(br_port_get_rcu(indev)->br->dev->ifindex)))
+ nflog_put_master_ifindex(inst->skb, NFULA_IFINDEX_INDEV, indev))
goto nla_put_failure;
} else {
int physinif;
/* rcu_read_lock()ed by nf_hook_thresh or
* nf_log_packet.
*/
- nla_put_be32(inst->skb, NFULA_IFINDEX_OUTDEV,
- htonl(br_port_get_rcu(outdev)->br->dev->ifindex)))
+ nflog_put_master_ifindex(inst->skb, NFULA_IFINDEX_OUTDEV, outdev))
goto nla_put_failure;
} else {
struct net_device *physoutdev;
return false;
}
+static bool nf_bridge_port_valid(const struct net_device *dev)
+{
+ if (!dev)
+ return true;
+
+ return netif_is_bridge_port(dev);
+}
+
+/* queued skbs leave rcu protection. We bump device refcount so that
+ * the device cannot go away. However, while packet was out the port
+ * could have been removed from the bridge.
+ *
+ * Ensure in+outdev are still part of a bridge at reinject time.
+ *
+ * The device rx_handler_data could even be pointing at data that is
+ * not a net_bridge_port structure.
+ */
+static bool nf_bridge_ports_valid(const struct nf_queue_entry *entry)
+{
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
+ if (!nf_bridge_port_valid(entry->physin) ||
+ !nf_bridge_port_valid(entry->physout))
+ return false;
+#endif
+ if (entry->state.pf != PF_BRIDGE)
+ return true;
+
+ if (!nf_bridge_port_valid(entry->state.in) ||
+ !nf_bridge_port_valid(entry->state.out))
+ return false;
+
+ return true;
+}
+
static void nfqnl_reinject(struct nf_queue_entry *entry, unsigned int verdict)
{
const struct nf_ct_hook *ct_hook;
+ if (!nf_bridge_ports_valid(entry))
+ verdict = NF_DROP;
+
if (verdict == NF_ACCEPT ||
verdict == NF_REPEAT ||
verdict == NF_STOP) {
return skb_checksum_help(entskb);
}
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
+static int nfqnl_put_master_ifindex(struct sk_buff *nlskb, int attr,
+ const struct net_device *dev)
+{
+ const struct net_device *upper;
+
+ if (dev && !netif_is_bridge_port(dev))
+ return 0;
+
+ upper = netdev_master_upper_dev_get_rcu((struct net_device *)dev);
+ if (upper && nla_put_be32(nlskb, attr, htonl(upper->ifindex)))
+ return -EMSGSIZE;
+
+ return 0;
+}
+#endif
+
static struct sk_buff *
nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
struct nf_queue_entry *entry,
* netfilter_bridge) */
if (nla_put_be32(skb, NFQA_IFINDEX_PHYSINDEV,
htonl(indev->ifindex)) ||
- /* this is the bridge group "brX" */
- /* rcu_read_lock()ed by __nf_queue */
- nla_put_be32(skb, NFQA_IFINDEX_INDEV,
- htonl(br_port_get_rcu(indev)->br->dev->ifindex)))
+ nfqnl_put_master_ifindex(skb, NFQA_IFINDEX_INDEV, indev))
goto nla_put_failure;
} else {
int physinif;
* netfilter_bridge) */
if (nla_put_be32(skb, NFQA_IFINDEX_PHYSOUTDEV,
htonl(outdev->ifindex)) ||
- /* this is the bridge group "brX" */
- /* rcu_read_lock()ed by __nf_queue */
- nla_put_be32(skb, NFQA_IFINDEX_OUTDEV,
- htonl(br_port_get_rcu(outdev)->br->dev->ifindex)))
+ nfqnl_put_master_ifindex(skb, NFQA_IFINDEX_OUTDEV, outdev))
goto nla_put_failure;
} else {
int physoutif;