const struct in6_addr *saddr,
                                        const __be16 sport,
                                        const struct in6_addr *daddr,
-                                       const u16 hnum, const int dif);
+                                       const u16 hnum, const int dif,
+                                       const int sdif);
 
 struct sock *inet6_lookup_listener(struct net *net,
                                   struct inet_hashinfo *hashinfo,
                                   const struct in6_addr *saddr,
                                   const __be16 sport,
                                   const struct in6_addr *daddr,
-                                  const unsigned short hnum, const int dif);
+                                  const unsigned short hnum,
+                                  const int dif, const int sdif);
 
 static inline struct sock *__inet6_lookup(struct net *net,
                                          struct inet_hashinfo *hashinfo,
                                          const __be16 sport,
                                          const struct in6_addr *daddr,
                                          const u16 hnum,
-                                         const int dif,
+                                         const int dif, const int sdif,
                                          bool *refcounted)
 {
        struct sock *sk = __inet6_lookup_established(net, hashinfo, saddr,
-                                               sport, daddr, hnum, dif);
+                                                    sport, daddr, hnum,
+                                                    dif, sdif);
        *refcounted = true;
        if (sk)
                return sk;
        *refcounted = false;
        return inet6_lookup_listener(net, hashinfo, skb, doff, saddr, sport,
-                                    daddr, hnum, dif);
+                                    daddr, hnum, dif, sdif);
 }
 
 static inline struct sock *__inet6_lookup_skb(struct inet_hashinfo *hashinfo,
                                              struct sk_buff *skb, int doff,
                                              const __be16 sport,
                                              const __be16 dport,
-                                             int iif,
+                                             int iif, int sdif,
                                              bool *refcounted)
 {
        struct sock *sk = skb_steal_sock(skb);
        return __inet6_lookup(dev_net(skb_dst(skb)->dev), hashinfo, skb,
                              doff, &ipv6_hdr(skb)->saddr, sport,
                              &ipv6_hdr(skb)->daddr, ntohs(dport),
-                             iif, refcounted);
+                             iif, sdif, refcounted);
 }
 
 struct sock *inet6_lookup(struct net *net, struct inet_hashinfo *hashinfo,
 int inet6_hash(struct sock *sk);
 #endif /* IS_ENABLED(CONFIG_IPV6) */
 
-#define INET6_MATCH(__sk, __net, __saddr, __daddr, __ports, __dif)     \
+#define INET6_MATCH(__sk, __net, __saddr, __daddr, __ports, __dif, __sdif) \
        (((__sk)->sk_portpair == (__ports))                     &&      \
         ((__sk)->sk_family == AF_INET6)                        &&      \
         ipv6_addr_equal(&(__sk)->sk_v6_daddr, (__saddr))               &&      \
         ipv6_addr_equal(&(__sk)->sk_v6_rcv_saddr, (__daddr))   &&      \
         (!(__sk)->sk_bound_dev_if      ||                              \
-          ((__sk)->sk_bound_dev_if == (__dif)))                &&      \
+          ((__sk)->sk_bound_dev_if == (__dif)) ||                      \
+          ((__sk)->sk_bound_dev_if == (__sdif)))               &&      \
         net_eq(sock_net(__sk), (__net)))
 
 #endif /* _INET6_HASHTABLES_H */
 
 
        return l3_slave ? skb->skb_iif : TCP_SKB_CB(skb)->header.h6.iif;
 }
+
+/* TCP_SKB_CB reference means this can not be used from early demux */
+static inline int tcp_v6_sdif(const struct sk_buff *skb)
+{
+#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV)
+       if (skb && ipv6_l3mdev_skb(TCP_SKB_CB(skb)->header.h6.flags))
+               return TCP_SKB_CB(skb)->header.h6.iif;
+#endif
+       return 0;
+}
 #endif
 
 /* TCP_SKB_CB reference means this can not be used from early demux */
 
        sk = __inet6_lookup_established(net, &dccp_hashinfo,
                                        &hdr->daddr, dh->dccph_dport,
                                        &hdr->saddr, ntohs(dh->dccph_sport),
-                                       inet6_iif(skb));
+                                       inet6_iif(skb), 0);
 
        if (!sk) {
                __ICMP6_INC_STATS(net, __in6_dev_get(skb->dev),
 lookup:
        sk = __inet6_lookup_skb(&dccp_hashinfo, skb, __dccp_hdr_len(dh),
                                dh->dccph_sport, dh->dccph_dport,
-                               inet6_iif(skb), &refcounted);
+                               inet6_iif(skb), 0, &refcounted);
        if (!sk) {
                dccp_pr_debug("failed to look up flow ID in table and "
                              "get corresponding socket\n");
 
                                           const __be16 sport,
                                           const struct in6_addr *daddr,
                                           const u16 hnum,
-                                          const int dif)
+                                          const int dif, const int sdif)
 {
        struct sock *sk;
        const struct hlist_nulls_node *node;
        sk_nulls_for_each_rcu(sk, node, &head->chain) {
                if (sk->sk_hash != hash)
                        continue;
-               if (!INET6_MATCH(sk, net, saddr, daddr, ports, dif))
+               if (!INET6_MATCH(sk, net, saddr, daddr, ports, dif, sdif))
                        continue;
                if (unlikely(!refcount_inc_not_zero(&sk->sk_refcnt)))
                        goto out;
 
-               if (unlikely(!INET6_MATCH(sk, net, saddr, daddr, ports, dif))) {
+               if (unlikely(!INET6_MATCH(sk, net, saddr, daddr, ports, dif, sdif))) {
                        sock_gen_put(sk);
                        goto begin;
                }
 static inline int compute_score(struct sock *sk, struct net *net,
                                const unsigned short hnum,
                                const struct in6_addr *daddr,
-                               const int dif, bool exact_dif)
+                               const int dif, const int sdif, bool exact_dif)
 {
        int score = -1;
 
                        score++;
                }
                if (sk->sk_bound_dev_if || exact_dif) {
-                       if (sk->sk_bound_dev_if != dif)
+                       bool dev_match = (sk->sk_bound_dev_if == dif ||
+                                         sk->sk_bound_dev_if == sdif);
+
+                       if (exact_dif && !dev_match)
                                return -1;
-                       score++;
+                       if (sk->sk_bound_dev_if && dev_match)
+                               score++;
                }
                if (sk->sk_incoming_cpu == raw_smp_processor_id())
                        score++;
                struct sk_buff *skb, int doff,
                const struct in6_addr *saddr,
                const __be16 sport, const struct in6_addr *daddr,
-               const unsigned short hnum, const int dif)
+               const unsigned short hnum, const int dif, const int sdif)
 {
        unsigned int hash = inet_lhashfn(net, hnum);
        struct inet_listen_hashbucket *ilb = &hashinfo->listening_hash[hash];
        u32 phash = 0;
 
        sk_for_each(sk, &ilb->head) {
-               score = compute_score(sk, net, hnum, daddr, dif, exact_dif);
+               score = compute_score(sk, net, hnum, daddr, dif, sdif, exact_dif);
                if (score > hiscore) {
                        reuseport = sk->sk_reuseport;
                        if (reuseport) {
        bool refcounted;
 
        sk = __inet6_lookup(net, hashinfo, skb, doff, saddr, sport, daddr,
-                           ntohs(dport), dif, &refcounted);
+                           ntohs(dport), dif, 0, &refcounted);
        if (sk && !refcounted && !refcount_inc_not_zero(&sk->sk_refcnt))
                sk = NULL;
        return sk;
        const struct in6_addr *daddr = &sk->sk_v6_rcv_saddr;
        const struct in6_addr *saddr = &sk->sk_v6_daddr;
        const int dif = sk->sk_bound_dev_if;
-       const __portpair ports = INET_COMBINED_PORTS(inet->inet_dport, lport);
        struct net *net = sock_net(sk);
+       const int sdif = l3mdev_master_ifindex_by_index(net, dif);
+       const __portpair ports = INET_COMBINED_PORTS(inet->inet_dport, lport);
        const unsigned int hash = inet6_ehashfn(net, daddr, lport, saddr,
                                                inet->inet_dport);
        struct inet_ehash_bucket *head = inet_ehash_bucket(hinfo, hash);
                if (sk2->sk_hash != hash)
                        continue;
 
-               if (likely(INET6_MATCH(sk2, net, saddr, daddr, ports, dif))) {
+               if (likely(INET6_MATCH(sk2, net, saddr, daddr, ports,
+                                      dif, sdif))) {
                        if (sk2->sk_state == TCP_TIME_WAIT) {
                                tw = inet_twsk(sk2);
                                if (twsk_unique(sk, sk2, twp))
 
        sk = __inet6_lookup_established(net, &tcp_hashinfo,
                                        &hdr->daddr, th->dest,
                                        &hdr->saddr, ntohs(th->source),
-                                       skb->dev->ifindex);
+                                       skb->dev->ifindex, inet6_sdif(skb));
 
        if (!sk) {
                __ICMP6_INC_STATS(net, __in6_dev_get(skb->dev),
                                           &tcp_hashinfo, NULL, 0,
                                           &ipv6h->saddr,
                                           th->source, &ipv6h->daddr,
-                                          ntohs(th->source), tcp_v6_iif(skb));
+                                          ntohs(th->source), tcp_v6_iif(skb),
+                                          tcp_v6_sdif(skb));
                if (!sk1)
                        goto out;
 
 
 static int tcp_v6_rcv(struct sk_buff *skb)
 {
+       int sdif = inet6_sdif(skb);
        const struct tcphdr *th;
        const struct ipv6hdr *hdr;
        bool refcounted;
 
 lookup:
        sk = __inet6_lookup_skb(&tcp_hashinfo, skb, __tcp_hdrlen(th),
-                               th->source, th->dest, inet6_iif(skb),
+                               th->source, th->dest, inet6_iif(skb), sdif,
                                &refcounted);
        if (!sk)
                goto no_tcp_socket;
                                            skb, __tcp_hdrlen(th),
                                            &ipv6_hdr(skb)->saddr, th->source,
                                            &ipv6_hdr(skb)->daddr,
-                                           ntohs(th->dest), tcp_v6_iif(skb));
+                                           ntohs(th->dest), tcp_v6_iif(skb),
+                                           sdif);
                if (sk2) {
                        struct inet_timewait_sock *tw = inet_twsk(sk);
                        inet_twsk_deschedule_put(tw);
        sk = __inet6_lookup_established(dev_net(skb->dev), &tcp_hashinfo,
                                        &hdr->saddr, th->source,
                                        &hdr->daddr, ntohs(th->dest),
-                                       inet6_iif(skb));
+                                       inet6_iif(skb), inet6_sdif(skb));
        if (sk) {
                skb->sk = sk;
                skb->destructor = sock_edemux;
 
 static struct sock *__udp6_lib_demux_lookup(struct net *net,
                        __be16 loc_port, const struct in6_addr *loc_addr,
                        __be16 rmt_port, const struct in6_addr *rmt_addr,
-                       int dif)
+                       int dif, int sdif)
 {
        unsigned short hnum = ntohs(loc_port);
        unsigned int hash2 = udp6_portaddr_hash(net, loc_addr, hnum);
 
        udp_portaddr_for_each_entry_rcu(sk, &hslot2->head) {
                if (sk->sk_state == TCP_ESTABLISHED &&
-                   INET6_MATCH(sk, net, rmt_addr, loc_addr, ports, dif))
+                   INET6_MATCH(sk, net, rmt_addr, loc_addr, ports, dif, sdif))
                        return sk;
                /* Only check first socket in chain */
                break;
        struct sock *sk;
        struct dst_entry *dst;
        int dif = skb->dev->ifindex;
+       int sdif = inet6_sdif(skb);
 
        if (!pskb_may_pull(skb, skb_transport_offset(skb) +
            sizeof(struct udphdr)))
                sk = __udp6_lib_demux_lookup(net, uh->dest,
                                             &ipv6_hdr(skb)->daddr,
                                             uh->source, &ipv6_hdr(skb)->saddr,
-                                            dif);
+                                            dif, sdif);
        else
                return;
 
 
                                                   thoff + __tcp_hdrlen(tcph),
                                                   saddr, sport,
                                                   daddr, ntohs(dport),
-                                                  in->ifindex);
+                                                  in->ifindex, 0);
 
                        if (sk && !refcount_inc_not_zero(&sk->sk_refcnt))
                                sk = NULL;
                case NFT_LOOKUP_ESTABLISHED:
                        sk = __inet6_lookup_established(net, &tcp_hashinfo,
                                                        saddr, sport, daddr, ntohs(dport),
-                                                       in->ifindex);
+                                                       in->ifindex, 0);
                        break;
                default:
                        BUG();