Skip to content

Commit a208fd3

Browse files
committed
Merge: netfilter: bridge: confirm multicast packets before passing them up the stack
MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/4782 JIRA: https://issues.redhat.com/browse/RHEL-36872 Upstream Status: commit 62e7151 CVE: CVE-2024-27415 Signed-off-by: Florian Westphal <fwestpha@redhat.com> Approved-by: Marcelo Ricardo Leitner <mleitner@redhat.com> Approved-by: Phil Sutter <psutter@redhat.com> Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> Merged-by: Lucas Zampieri <lzampier@redhat.com>
2 parents 5f3b665 + 19a318e commit a208fd3

File tree

6 files changed

+147
-5
lines changed

6 files changed

+147
-5
lines changed

include/linux/netfilter.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,7 @@ struct nf_ct_hook {
475475
bool (*get_tuple_skb)(struct nf_conntrack_tuple *,
476476
const struct sk_buff *);
477477
void (*attach)(struct sk_buff *nskb, const struct sk_buff *skb);
478+
int (*confirm)(struct sk_buff *skb);
478479
};
479480
extern const struct nf_ct_hook __rcu *nf_ct_hook;
480481

net/bridge/br_input.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ br_netif_receive_skb(struct net *net, struct sock *sk, struct sk_buff *skb)
3030
return netif_receive_skb(skb);
3131
}
3232

33-
static int br_pass_frame_up(struct sk_buff *skb)
33+
static int br_pass_frame_up(struct sk_buff *skb, bool promisc)
3434
{
3535
struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev;
3636
struct net_bridge *br = netdev_priv(brdev);
@@ -65,6 +65,8 @@ static int br_pass_frame_up(struct sk_buff *skb)
6565
br_multicast_count(br, NULL, skb, br_multicast_igmp_type(skb),
6666
BR_MCAST_DIR_TX);
6767

68+
BR_INPUT_SKB_CB(skb)->promisc = promisc;
69+
6870
return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN,
6971
dev_net(indev), NULL, skb, indev, NULL,
7072
br_netif_receive_skb);
@@ -82,6 +84,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
8284
struct net_bridge_mcast *brmctx;
8385
struct net_bridge_vlan *vlan;
8486
struct net_bridge *br;
87+
bool promisc;
8588
u16 vid = 0;
8689
u8 state;
8790

@@ -137,7 +140,9 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
137140
if (p->flags & BR_LEARNING)
138141
br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, 0);
139142

140-
local_rcv = !!(br->dev->flags & IFF_PROMISC);
143+
promisc = !!(br->dev->flags & IFF_PROMISC);
144+
local_rcv = promisc;
145+
141146
if (is_multicast_ether_addr(eth_hdr(skb)->h_dest)) {
142147
/* by definition the broadcast is also a multicast address */
143148
if (is_broadcast_ether_addr(eth_hdr(skb)->h_dest)) {
@@ -200,7 +205,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
200205
unsigned long now = jiffies;
201206

202207
if (test_bit(BR_FDB_LOCAL, &dst->flags))
203-
return br_pass_frame_up(skb);
208+
return br_pass_frame_up(skb, false);
204209

205210
if (now != dst->used)
206211
dst->used = now;
@@ -213,7 +218,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
213218
}
214219

215220
if (local_rcv)
216-
return br_pass_frame_up(skb);
221+
return br_pass_frame_up(skb, promisc);
217222

218223
out:
219224
return 0;
@@ -386,6 +391,8 @@ static rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
386391
goto forward;
387392
}
388393

394+
BR_INPUT_SKB_CB(skb)->promisc = false;
395+
389396
/* The else clause should be hit when nf_hook():
390397
* - returns < 0 (drop/error)
391398
* - returns = 0 (stolen/nf_queue)

net/bridge/br_netfilter_hooks.c

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@
4343
#include <linux/sysctl.h>
4444
#endif
4545

46+
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
47+
#include <net/netfilter/nf_conntrack_core.h>
48+
#endif
49+
4650
static unsigned int brnf_net_id __read_mostly;
4751

4852
struct brnf_net {
@@ -553,6 +557,90 @@ static unsigned int br_nf_pre_routing(void *priv,
553557
return NF_STOLEN;
554558
}
555559

560+
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
561+
/* conntracks' nf_confirm logic cannot handle cloned skbs referencing
562+
* the same nf_conn entry, which will happen for multicast (broadcast)
563+
* Frames on bridges.
564+
*
565+
* Example:
566+
* macvlan0
567+
* br0
568+
* ethX ethY
569+
*
570+
* ethX (or Y) receives multicast or broadcast packet containing
571+
* an IP packet, not yet in conntrack table.
572+
*
573+
* 1. skb passes through bridge and fake-ip (br_netfilter)Prerouting.
574+
* -> skb->_nfct now references a unconfirmed entry
575+
* 2. skb is broad/mcast packet. bridge now passes clones out on each bridge
576+
* interface.
577+
* 3. skb gets passed up the stack.
578+
* 4. In macvlan case, macvlan driver retains clone(s) of the mcast skb
579+
* and schedules a work queue to send them out on the lower devices.
580+
*
581+
* The clone skb->_nfct is not a copy, it is the same entry as the
582+
* original skb. The macvlan rx handler then returns RX_HANDLER_PASS.
583+
* 5. Normal conntrack hooks (in NF_INET_LOCAL_IN) confirm the orig skb.
584+
*
585+
* The Macvlan broadcast worker and normal confirm path will race.
586+
*
587+
* This race will not happen if step 2 already confirmed a clone. In that
588+
* case later steps perform skb_clone() with skb->_nfct already confirmed (in
589+
* hash table). This works fine.
590+
*
591+
* But such confirmation won't happen when eb/ip/nftables rules dropped the
592+
* packets before they reached the nf_confirm step in postrouting.
593+
*
594+
* Work around this problem by explicit confirmation of the entry at
595+
* LOCAL_IN time, before upper layer has a chance to clone the unconfirmed
596+
* entry.
597+
*
598+
*/
599+
static unsigned int br_nf_local_in(void *priv,
600+
struct sk_buff *skb,
601+
const struct nf_hook_state *state)
602+
{
603+
bool promisc = BR_INPUT_SKB_CB(skb)->promisc;
604+
struct nf_conntrack *nfct = skb_nfct(skb);
605+
const struct nf_ct_hook *ct_hook;
606+
struct nf_conn *ct;
607+
int ret;
608+
609+
if (promisc) {
610+
nf_reset_ct(skb);
611+
return NF_ACCEPT;
612+
}
613+
614+
if (!nfct || skb->pkt_type == PACKET_HOST)
615+
return NF_ACCEPT;
616+
617+
ct = container_of(nfct, struct nf_conn, ct_general);
618+
if (likely(nf_ct_is_confirmed(ct)))
619+
return NF_ACCEPT;
620+
621+
/* We can't call nf_confirm here, it would create a dependency
622+
* on nf_conntrack module.
623+
*/
624+
ct_hook = rcu_dereference(nf_ct_hook);
625+
if (!ct_hook) {
626+
skb->_nfct = 0ul;
627+
nf_conntrack_put(nfct);
628+
return NF_ACCEPT;
629+
}
630+
631+
nf_bridge_pull_encap_header(skb);
632+
ret = ct_hook->confirm(skb);
633+
switch (ret & NF_VERDICT_MASK) {
634+
case NF_STOLEN:
635+
return NF_STOLEN;
636+
default:
637+
nf_bridge_push_encap_header(skb);
638+
break;
639+
}
640+
641+
return ret;
642+
}
643+
#endif
556644

557645
/* PF_BRIDGE/FORWARD *************************************************/
558646
static int br_nf_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
@@ -964,6 +1052,14 @@ static const struct nf_hook_ops br_nf_ops[] = {
9641052
.hooknum = NF_BR_PRE_ROUTING,
9651053
.priority = NF_BR_PRI_BRNF,
9661054
},
1055+
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
1056+
{
1057+
.hook = br_nf_local_in,
1058+
.pf = NFPROTO_BRIDGE,
1059+
.hooknum = NF_BR_LOCAL_IN,
1060+
.priority = NF_BR_PRI_LAST,
1061+
},
1062+
#endif
9671063
{
9681064
.hook = br_nf_forward,
9691065
.pf = NFPROTO_BRIDGE,

net/bridge/br_private.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,7 @@ struct br_input_skb_cb {
589589
#endif
590590
u8 proxyarp_replied:1;
591591
u8 src_port_isolated:1;
592+
u8 promisc:1;
592593
#ifdef CONFIG_BRIDGE_VLAN_FILTERING
593594
u8 vlan_filtered:1;
594595
#endif

net/bridge/netfilter/nf_conntrack_bridge.c

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ static int nf_br_ip_fragment(struct net *net, struct sock *sk,
3737
ktime_t tstamp = skb->tstamp;
3838
struct ip_frag_state state;
3939
struct iphdr *iph;
40-
int err;
40+
int err = 0;
4141

4242
/* for offloaded checksums cleanup checksum before fragmentation */
4343
if (skb->ip_summed == CHECKSUM_PARTIAL &&
@@ -291,6 +291,36 @@ static unsigned int nf_ct_bridge_pre(void *priv, struct sk_buff *skb,
291291
return nf_conntrack_in(skb, &bridge_state);
292292
}
293293

294+
static unsigned int nf_ct_bridge_in(void *priv, struct sk_buff *skb,
295+
const struct nf_hook_state *state)
296+
{
297+
bool promisc = BR_INPUT_SKB_CB(skb)->promisc;
298+
struct nf_conntrack *nfct = skb_nfct(skb);
299+
struct nf_conn *ct;
300+
301+
if (promisc) {
302+
nf_reset_ct(skb);
303+
return NF_ACCEPT;
304+
}
305+
306+
if (!nfct || skb->pkt_type == PACKET_HOST)
307+
return NF_ACCEPT;
308+
309+
/* nf_conntrack_confirm() cannot handle concurrent clones,
310+
* this happens for broad/multicast frames with e.g. macvlan on top
311+
* of the bridge device.
312+
*/
313+
ct = container_of(nfct, struct nf_conn, ct_general);
314+
if (nf_ct_is_confirmed(ct) || nf_ct_is_template(ct))
315+
return NF_ACCEPT;
316+
317+
/* let inet prerouting call conntrack again */
318+
skb->_nfct = 0;
319+
nf_ct_put(ct);
320+
321+
return NF_ACCEPT;
322+
}
323+
294324
static void nf_ct_bridge_frag_save(struct sk_buff *skb,
295325
struct nf_bridge_frag_data *data)
296326
{
@@ -415,6 +445,12 @@ static struct nf_hook_ops nf_ct_bridge_hook_ops[] __read_mostly = {
415445
.hooknum = NF_BR_PRE_ROUTING,
416446
.priority = NF_IP_PRI_CONNTRACK,
417447
},
448+
{
449+
.hook = nf_ct_bridge_in,
450+
.pf = NFPROTO_BRIDGE,
451+
.hooknum = NF_BR_LOCAL_IN,
452+
.priority = NF_IP_PRI_CONNTRACK_CONFIRM,
453+
},
418454
{
419455
.hook = nf_ct_bridge_post,
420456
.pf = NFPROTO_BRIDGE,

net/netfilter/nf_conntrack_core.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2790,6 +2790,7 @@ static const struct nf_ct_hook nf_conntrack_hook = {
27902790
.destroy = nf_ct_destroy,
27912791
.get_tuple_skb = nf_conntrack_get_tuple_skb,
27922792
.attach = nf_conntrack_attach,
2793+
.confirm = __nf_conntrack_confirm,
27932794
};
27942795

27952796
void nf_conntrack_init_end(void)

0 commit comments

Comments
 (0)