Skip to content

Commit a6e225c

Browse files
committed
Merge branch 'vrf-ipv6-mcast-link-local'
David Ahern says: ==================== net: vrf: Handle ipv6 multicast and link-local addresses IPv6 multicast and link-local addresses require special handling by the VRF driver. Rather than using the VRF device index and full FIB lookups, packets to/from these addresses should use direct FIB lookups based on the VRF device table. Multicast routes do not make sense for the L3 master device directly. Accordingly, do not add mcast routes for the device, and the VRF driver should fail attempts to send packets to ipv6 mcast addresses on the device (e.g, ping6 ff02::1%<vrf> should fail) With this change connections into and out of a VRF enslaved device work for multicast and link-local addresses (icmp, tcp, and udp). e.g., 1. packets into VM with VRF config: ping6 -c3 fe80::e0:f9ff:fe1c:b974%br1 ping6 -c3 ff02::1%br1 ssh -6 fe80::e0:f9ff:fe1c:b974%br1 2. packets going out a VRF enslaved device: ping6 -c3 fe80::18f8:83ff:fe4b:7a2e%eth1 ping6 -c3 ff02::1%eth1 ssh -6 root@fe80::18f8:83ff:fe4b:7a2e%eth1 ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents c9ad5a6 + 9ff7438 commit a6e225c

File tree

7 files changed

+105
-14
lines changed

7 files changed

+105
-14
lines changed

drivers/net/vrf.c

Lines changed: 94 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -785,9 +785,63 @@ static bool ipv6_ndisc_frame(const struct sk_buff *skb)
785785
return rc;
786786
}
787787

788+
static struct rt6_info *vrf_ip6_route_lookup(struct net *net,
789+
const struct net_device *dev,
790+
struct flowi6 *fl6,
791+
int ifindex,
792+
int flags)
793+
{
794+
struct net_vrf *vrf = netdev_priv(dev);
795+
struct fib6_table *table = NULL;
796+
struct rt6_info *rt6;
797+
798+
rcu_read_lock();
799+
800+
/* fib6_table does not have a refcnt and can not be freed */
801+
rt6 = rcu_dereference(vrf->rt6);
802+
if (likely(rt6))
803+
table = rt6->rt6i_table;
804+
805+
rcu_read_unlock();
806+
807+
if (!table)
808+
return NULL;
809+
810+
return ip6_pol_route(net, table, ifindex, fl6, flags);
811+
}
812+
813+
static void vrf_ip6_input_dst(struct sk_buff *skb, struct net_device *vrf_dev,
814+
int ifindex)
815+
{
816+
const struct ipv6hdr *iph = ipv6_hdr(skb);
817+
struct flowi6 fl6 = {
818+
.daddr = iph->daddr,
819+
.saddr = iph->saddr,
820+
.flowlabel = ip6_flowinfo(iph),
821+
.flowi6_mark = skb->mark,
822+
.flowi6_proto = iph->nexthdr,
823+
.flowi6_iif = ifindex,
824+
};
825+
struct net *net = dev_net(vrf_dev);
826+
struct rt6_info *rt6;
827+
828+
rt6 = vrf_ip6_route_lookup(net, vrf_dev, &fl6, ifindex,
829+
RT6_LOOKUP_F_HAS_SADDR | RT6_LOOKUP_F_IFACE);
830+
if (unlikely(!rt6))
831+
return;
832+
833+
if (unlikely(&rt6->dst == &net->ipv6.ip6_null_entry->dst))
834+
return;
835+
836+
skb_dst_set(skb, &rt6->dst);
837+
}
838+
788839
static struct sk_buff *vrf_ip6_rcv(struct net_device *vrf_dev,
789840
struct sk_buff *skb)
790841
{
842+
int orig_iif = skb->skb_iif;
843+
bool need_strict;
844+
791845
/* loopback traffic; do not push through packet taps again.
792846
* Reset pkt_type for upper layers to process skb
793847
*/
@@ -798,8 +852,11 @@ static struct sk_buff *vrf_ip6_rcv(struct net_device *vrf_dev,
798852
goto out;
799853
}
800854

801-
/* if packet is NDISC keep the ingress interface */
802-
if (!ipv6_ndisc_frame(skb)) {
855+
/* if packet is NDISC or addressed to multicast or link-local
856+
* then keep the ingress interface
857+
*/
858+
need_strict = rt6_need_strict(&ipv6_hdr(skb)->daddr);
859+
if (!ipv6_ndisc_frame(skb) && !need_strict) {
803860
skb->dev = vrf_dev;
804861
skb->skb_iif = vrf_dev->ifindex;
805862

@@ -810,6 +867,9 @@ static struct sk_buff *vrf_ip6_rcv(struct net_device *vrf_dev,
810867
IP6CB(skb)->flags |= IP6SKB_L3SLAVE;
811868
}
812869

870+
if (need_strict)
871+
vrf_ip6_input_dst(skb, vrf_dev, orig_iif);
872+
813873
out:
814874
return skb;
815875
}
@@ -861,13 +921,37 @@ static struct sk_buff *vrf_l3_rcv(struct net_device *vrf_dev,
861921

862922
#if IS_ENABLED(CONFIG_IPV6)
863923
static struct dst_entry *vrf_get_rt6_dst(const struct net_device *dev,
864-
const struct flowi6 *fl6)
924+
struct flowi6 *fl6)
865925
{
926+
bool need_strict = rt6_need_strict(&fl6->daddr);
927+
struct net_vrf *vrf = netdev_priv(dev);
928+
struct net *net = dev_net(dev);
866929
struct dst_entry *dst = NULL;
930+
struct rt6_info *rt;
867931

868-
if (!(fl6->flowi6_flags & FLOWI_FLAG_L3MDEV_SRC)) {
869-
struct net_vrf *vrf = netdev_priv(dev);
870-
struct rt6_info *rt;
932+
/* send to link-local or multicast address */
933+
if (need_strict) {
934+
int flags = RT6_LOOKUP_F_IFACE;
935+
936+
/* VRF device does not have a link-local address and
937+
* sending packets to link-local or mcast addresses over
938+
* a VRF device does not make sense
939+
*/
940+
if (fl6->flowi6_oif == dev->ifindex) {
941+
struct dst_entry *dst = &net->ipv6.ip6_null_entry->dst;
942+
943+
dst_hold(dst);
944+
return dst;
945+
}
946+
947+
if (!ipv6_addr_any(&fl6->saddr))
948+
flags |= RT6_LOOKUP_F_HAS_SADDR;
949+
950+
rt = vrf_ip6_route_lookup(net, dev, fl6, fl6->flowi6_oif, flags);
951+
if (rt)
952+
dst = &rt->dst;
953+
954+
} else if (!(fl6->flowi6_flags & FLOWI_FLAG_L3MDEV_SRC)) {
871955

872956
rcu_read_lock();
873957

@@ -880,6 +964,10 @@ static struct dst_entry *vrf_get_rt6_dst(const struct net_device *dev,
880964
rcu_read_unlock();
881965
}
882966

967+
/* make sure oif is set to VRF device for lookup */
968+
if (!need_strict)
969+
fl6->flowi6_oif = dev->ifindex;
970+
883971
return dst;
884972
}
885973
#endif

include/net/ip6_route.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ static inline struct dst_entry *ip6_route_output(struct net *net,
7676

7777
struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6,
7878
int flags);
79+
struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
80+
int ifindex, struct flowi6 *fl6, int flags);
7981

8082
int ip6_route_init(void);
8183
void ip6_route_cleanup(void);

include/net/l3mdev.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ struct l3mdev_ops {
3838

3939
/* IPv6 ops */
4040
struct dst_entry * (*l3mdev_get_rt6_dst)(const struct net_device *dev,
41-
const struct flowi6 *fl6);
41+
struct flowi6 *fl6);
4242
};
4343

4444
#ifdef CONFIG_NET_L3_MASTER_DEV
@@ -139,7 +139,7 @@ static inline bool netif_index_is_l3_master(struct net *net, int ifindex)
139139

140140
int l3mdev_get_saddr(struct net *net, int ifindex, struct flowi4 *fl4);
141141

142-
struct dst_entry *l3mdev_get_rt6_dst(struct net *net, const struct flowi6 *fl6);
142+
struct dst_entry *l3mdev_get_rt6_dst(struct net *net, struct flowi6 *fl6);
143143

144144
static inline
145145
struct sk_buff *l3mdev_l3_rcv(struct sk_buff *skb, u16 proto)
@@ -225,7 +225,7 @@ static inline int l3mdev_get_saddr(struct net *net, int ifindex,
225225
}
226226

227227
static inline
228-
struct dst_entry *l3mdev_get_rt6_dst(struct net *net, const struct flowi6 *fl6)
228+
struct dst_entry *l3mdev_get_rt6_dst(struct net *net, struct flowi6 *fl6)
229229
{
230230
return NULL;
231231
}

net/ipv6/addrconf.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2254,7 +2254,7 @@ static struct inet6_dev *addrconf_add_dev(struct net_device *dev)
22542254
return ERR_PTR(-EACCES);
22552255

22562256
/* Add default multicast route */
2257-
if (!(dev->flags & IFF_LOOPBACK))
2257+
if (!(dev->flags & IFF_LOOPBACK) && !netif_is_l3_master(dev))
22582258
addrconf_add_mroute(dev);
22592259

22602260
return idev;

net/ipv6/icmp.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
587587
fl6.daddr = ipv6_hdr(skb)->saddr;
588588
if (saddr)
589589
fl6.saddr = *saddr;
590-
fl6.flowi6_oif = l3mdev_fib_oif(skb->dev);
590+
fl6.flowi6_oif = skb->dev->ifindex;
591591
fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY;
592592
fl6.flowi6_mark = mark;
593593
security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));

net/ipv6/route.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,8 +1042,8 @@ static struct rt6_info *rt6_make_pcpu_route(struct rt6_info *rt)
10421042
return pcpu_rt;
10431043
}
10441044

1045-
static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
1046-
struct flowi6 *fl6, int flags)
1045+
struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
1046+
int oif, struct flowi6 *fl6, int flags)
10471047
{
10481048
struct fib6_node *fn, *saved_fn;
10491049
struct rt6_info *rt;
@@ -1139,6 +1139,7 @@ static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
11391139

11401140
}
11411141
}
1142+
EXPORT_SYMBOL_GPL(ip6_pol_route);
11421143

11431144
static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table,
11441145
struct flowi6 *fl6, int flags)

net/l3mdev/l3mdev.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ EXPORT_SYMBOL_GPL(l3mdev_fib_table_by_index);
108108
*/
109109

110110
struct dst_entry *l3mdev_get_rt6_dst(struct net *net,
111-
const struct flowi6 *fl6)
111+
struct flowi6 *fl6)
112112
{
113113
struct dst_entry *dst = NULL;
114114
struct net_device *dev;

0 commit comments

Comments
 (0)