Commit ca254490 authored by David Ahern's avatar David Ahern Committed by David S. Miller

net: Add VRF support to IPv6 stack

As with IPv4 support for VRFs added to IPv6 stack by replacing hardcoded
table ids with possibly device specific ones and manipulating the oif in
the flowi6. The flow flags are used to skip oif compare in nexthop lookups
if the device is enslaved to a VRF via the L3 master device.
Signed-off-by: default avatarDavid Ahern <dsa@cumulusnetworks.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 35402e31
...@@ -81,6 +81,7 @@ ...@@ -81,6 +81,7 @@
#include <net/ip.h> #include <net/ip.h>
#include <net/netlink.h> #include <net/netlink.h>
#include <net/pkt_sched.h> #include <net/pkt_sched.h>
#include <net/l3mdev.h>
#include <linux/if_tunnel.h> #include <linux/if_tunnel.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <linux/netconf.h> #include <linux/netconf.h>
...@@ -2146,7 +2147,7 @@ addrconf_prefix_route(struct in6_addr *pfx, int plen, struct net_device *dev, ...@@ -2146,7 +2147,7 @@ addrconf_prefix_route(struct in6_addr *pfx, int plen, struct net_device *dev,
unsigned long expires, u32 flags) unsigned long expires, u32 flags)
{ {
struct fib6_config cfg = { struct fib6_config cfg = {
.fc_table = RT6_TABLE_PREFIX, .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX,
.fc_metric = IP6_RT_PRIO_ADDRCONF, .fc_metric = IP6_RT_PRIO_ADDRCONF,
.fc_ifindex = dev->ifindex, .fc_ifindex = dev->ifindex,
.fc_expires = expires, .fc_expires = expires,
...@@ -2179,8 +2180,9 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx, ...@@ -2179,8 +2180,9 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,
struct fib6_node *fn; struct fib6_node *fn;
struct rt6_info *rt = NULL; struct rt6_info *rt = NULL;
struct fib6_table *table; struct fib6_table *table;
u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX;
table = fib6_get_table(dev_net(dev), RT6_TABLE_PREFIX); table = fib6_get_table(dev_net(dev), tb_id);
if (!table) if (!table)
return NULL; return NULL;
...@@ -2211,7 +2213,7 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx, ...@@ -2211,7 +2213,7 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,
static void addrconf_add_mroute(struct net_device *dev) static void addrconf_add_mroute(struct net_device *dev)
{ {
struct fib6_config cfg = { struct fib6_config cfg = {
.fc_table = RT6_TABLE_LOCAL, .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_LOCAL,
.fc_metric = IP6_RT_PRIO_ADDRCONF, .fc_metric = IP6_RT_PRIO_ADDRCONF,
.fc_ifindex = dev->ifindex, .fc_ifindex = dev->ifindex,
.fc_dst_len = 8, .fc_dst_len = 8,
...@@ -3029,6 +3031,10 @@ static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route) ...@@ -3029,6 +3031,10 @@ static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)
{ {
struct in6_addr addr; struct in6_addr addr;
/* no link local addresses on L3 master devices */
if (netif_is_l3_master(idev->dev))
return;
ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0); ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0);
if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) { if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
......
...@@ -68,6 +68,7 @@ ...@@ -68,6 +68,7 @@
#include <net/xfrm.h> #include <net/xfrm.h>
#include <net/inet_common.h> #include <net/inet_common.h>
#include <net/dsfield.h> #include <net/dsfield.h>
#include <net/l3mdev.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -496,6 +497,9 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) ...@@ -496,6 +497,9 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
else if (!fl6.flowi6_oif) else if (!fl6.flowi6_oif)
fl6.flowi6_oif = np->ucast_oif; fl6.flowi6_oif = np->ucast_oif;
if (!fl6.flowi6_oif)
fl6.flowi6_oif = l3mdev_master_ifindex(skb->dev);
dst = icmpv6_route_lookup(net, skb, sk, &fl6); dst = icmpv6_route_lookup(net, skb, sk, &fl6);
if (IS_ERR(dst)) if (IS_ERR(dst))
goto out; goto out;
...@@ -575,7 +579,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) ...@@ -575,7 +579,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
fl6.daddr = ipv6_hdr(skb)->saddr; fl6.daddr = ipv6_hdr(skb)->saddr;
if (saddr) if (saddr)
fl6.saddr = *saddr; fl6.saddr = *saddr;
fl6.flowi6_oif = skb->dev->ifindex; fl6.flowi6_oif = l3mdev_fib_oif(skb->dev);
fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY; fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY;
fl6.flowi6_mark = mark; fl6.flowi6_mark = mark;
security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
......
...@@ -55,6 +55,7 @@ ...@@ -55,6 +55,7 @@
#include <net/xfrm.h> #include <net/xfrm.h>
#include <net/checksum.h> #include <net/checksum.h>
#include <linux/mroute6.h> #include <linux/mroute6.h>
#include <net/l3mdev.h>
static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *skb) static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *skb)
{ {
...@@ -885,7 +886,8 @@ static struct dst_entry *ip6_sk_dst_check(struct sock *sk, ...@@ -885,7 +886,8 @@ static struct dst_entry *ip6_sk_dst_check(struct sock *sk,
#ifdef CONFIG_IPV6_SUBTREES #ifdef CONFIG_IPV6_SUBTREES
ip6_rt_check(&rt->rt6i_src, &fl6->saddr, np->saddr_cache) || ip6_rt_check(&rt->rt6i_src, &fl6->saddr, np->saddr_cache) ||
#endif #endif
(fl6->flowi6_oif && fl6->flowi6_oif != dst->dev->ifindex)) { (!(fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF) &&
(fl6->flowi6_oif && fl6->flowi6_oif != dst->dev->ifindex))) {
dst_release(dst); dst_release(dst);
dst = NULL; dst = NULL;
} }
...@@ -1037,7 +1039,7 @@ struct dst_entry *ip6_dst_lookup_flow(const struct sock *sk, struct flowi6 *fl6, ...@@ -1037,7 +1039,7 @@ struct dst_entry *ip6_dst_lookup_flow(const struct sock *sk, struct flowi6 *fl6,
if (final_dst) if (final_dst)
fl6->daddr = *final_dst; fl6->daddr = *final_dst;
if (!fl6->flowi6_oif) if (!fl6->flowi6_oif)
fl6->flowi6_oif = dst->dev->ifindex; fl6->flowi6_oif = l3mdev_fib_oif(dst->dev);
return xfrm_lookup_route(sock_net(sk), dst, flowi6_to_flowi(fl6), sk, 0); return xfrm_lookup_route(sock_net(sk), dst, flowi6_to_flowi(fl6), sk, 0);
} }
......
...@@ -67,6 +67,7 @@ ...@@ -67,6 +67,7 @@
#include <net/flow.h> #include <net/flow.h>
#include <net/ip6_checksum.h> #include <net/ip6_checksum.h>
#include <net/inet_common.h> #include <net/inet_common.h>
#include <net/l3mdev.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/netfilter.h> #include <linux/netfilter.h>
...@@ -442,8 +443,11 @@ static void ndisc_send_skb(struct sk_buff *skb, ...@@ -442,8 +443,11 @@ static void ndisc_send_skb(struct sk_buff *skb,
if (!dst) { if (!dst) {
struct flowi6 fl6; struct flowi6 fl6;
int oif = l3mdev_fib_oif(skb->dev);
icmpv6_flow_init(sk, &fl6, type, saddr, daddr, skb->dev->ifindex); icmpv6_flow_init(sk, &fl6, type, saddr, daddr, oif);
if (oif != skb->dev->ifindex)
fl6.flowi6_flags |= FLOWI_FLAG_L3MDEV_SRC;
dst = icmp6_dst_alloc(skb->dev, &fl6); dst = icmp6_dst_alloc(skb->dev, &fl6);
if (IS_ERR(dst)) { if (IS_ERR(dst)) {
kfree_skb(skb); kfree_skb(skb);
...@@ -767,7 +771,7 @@ static void ndisc_recv_ns(struct sk_buff *skb) ...@@ -767,7 +771,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1); ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
if (ifp) { if (ifp) {
have_ifp:
if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) { if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
if (dad) { if (dad) {
/* /*
...@@ -793,6 +797,18 @@ static void ndisc_recv_ns(struct sk_buff *skb) ...@@ -793,6 +797,18 @@ static void ndisc_recv_ns(struct sk_buff *skb)
} else { } else {
struct net *net = dev_net(dev); struct net *net = dev_net(dev);
/* perhaps an address on the master device */
if (netif_is_l3_slave(dev)) {
struct net_device *mdev;
mdev = netdev_master_upper_dev_get_rcu(dev);
if (mdev) {
ifp = ipv6_get_ifaddr(net, &msg->target, mdev, 1);
if (ifp)
goto have_ifp;
}
}
idev = in6_dev_get(dev); idev = in6_dev_get(dev);
if (!idev) { if (!idev) {
/* XXX: count this drop? */ /* XXX: count this drop? */
...@@ -1484,6 +1500,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) ...@@ -1484,6 +1500,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
struct flowi6 fl6; struct flowi6 fl6;
int rd_len; int rd_len;
u8 ha_buf[MAX_ADDR_LEN], *ha = NULL; u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
int oif = l3mdev_fib_oif(dev);
bool ret; bool ret;
if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) { if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
...@@ -1500,7 +1517,10 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) ...@@ -1500,7 +1517,10 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
} }
icmpv6_flow_init(sk, &fl6, NDISC_REDIRECT, icmpv6_flow_init(sk, &fl6, NDISC_REDIRECT,
&saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex); &saddr_buf, &ipv6_hdr(skb)->saddr, oif);
if (oif != skb->dev->ifindex)
fl6.flowi6_flags |= FLOWI_FLAG_L3MDEV_SRC;
dst = ip6_route_output(net, NULL, &fl6); dst = ip6_route_output(net, NULL, &fl6);
if (dst->error) { if (dst->error) {
......
...@@ -61,6 +61,7 @@ ...@@ -61,6 +61,7 @@
#include <net/nexthop.h> #include <net/nexthop.h>
#include <net/lwtunnel.h> #include <net/lwtunnel.h>
#include <net/ip_tunnels.h> #include <net/ip_tunnels.h>
#include <net/l3mdev.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -1044,6 +1045,9 @@ static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, ...@@ -1044,6 +1045,9 @@ static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
saved_fn = fn; saved_fn = fn;
if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
oif = 0;
redo_rt6_select: redo_rt6_select:
rt = rt6_select(fn, oif, strict); rt = rt6_select(fn, oif, strict);
if (rt->rt6i_nsiblings) if (rt->rt6i_nsiblings)
...@@ -1141,7 +1145,7 @@ void ip6_route_input(struct sk_buff *skb) ...@@ -1141,7 +1145,7 @@ void ip6_route_input(struct sk_buff *skb)
int flags = RT6_LOOKUP_F_HAS_SADDR; int flags = RT6_LOOKUP_F_HAS_SADDR;
struct ip_tunnel_info *tun_info; struct ip_tunnel_info *tun_info;
struct flowi6 fl6 = { struct flowi6 fl6 = {
.flowi6_iif = skb->dev->ifindex, .flowi6_iif = l3mdev_fib_oif(skb->dev),
.daddr = iph->daddr, .daddr = iph->daddr,
.saddr = iph->saddr, .saddr = iph->saddr,
.flowlabel = ip6_flowinfo(iph), .flowlabel = ip6_flowinfo(iph),
...@@ -1165,8 +1169,13 @@ static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table ...@@ -1165,8 +1169,13 @@ static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table
struct dst_entry *ip6_route_output(struct net *net, const struct sock *sk, struct dst_entry *ip6_route_output(struct net *net, const struct sock *sk,
struct flowi6 *fl6) struct flowi6 *fl6)
{ {
struct dst_entry *dst;
int flags = 0; int flags = 0;
dst = l3mdev_rt6_dst_by_oif(net, fl6);
if (dst)
return dst;
fl6->flowi6_iif = LOOPBACK_IFINDEX; fl6->flowi6_iif = LOOPBACK_IFINDEX;
if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) || if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) ||
...@@ -2263,7 +2272,6 @@ static struct rt6_info *rt6_add_route_info(struct net *net, ...@@ -2263,7 +2272,6 @@ static struct rt6_info *rt6_add_route_info(struct net *net,
unsigned int pref) unsigned int pref)
{ {
struct fib6_config cfg = { struct fib6_config cfg = {
.fc_table = RT6_TABLE_INFO,
.fc_metric = IP6_RT_PRIO_USER, .fc_metric = IP6_RT_PRIO_USER,
.fc_ifindex = ifindex, .fc_ifindex = ifindex,
.fc_dst_len = prefixlen, .fc_dst_len = prefixlen,
...@@ -2274,6 +2282,7 @@ static struct rt6_info *rt6_add_route_info(struct net *net, ...@@ -2274,6 +2282,7 @@ static struct rt6_info *rt6_add_route_info(struct net *net,
.fc_nlinfo.nl_net = net, .fc_nlinfo.nl_net = net,
}; };
cfg.fc_table = l3mdev_fib_table_by_index(net, ifindex) ? : RT6_TABLE_INFO;
cfg.fc_dst = *prefix; cfg.fc_dst = *prefix;
cfg.fc_gateway = *gwaddr; cfg.fc_gateway = *gwaddr;
...@@ -2314,7 +2323,7 @@ struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, ...@@ -2314,7 +2323,7 @@ struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
unsigned int pref) unsigned int pref)
{ {
struct fib6_config cfg = { struct fib6_config cfg = {
.fc_table = RT6_TABLE_DFLT, .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT,
.fc_metric = IP6_RT_PRIO_USER, .fc_metric = IP6_RT_PRIO_USER,
.fc_ifindex = dev->ifindex, .fc_ifindex = dev->ifindex,
.fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
...@@ -2361,7 +2370,8 @@ static void rtmsg_to_fib6_config(struct net *net, ...@@ -2361,7 +2370,8 @@ static void rtmsg_to_fib6_config(struct net *net,
{ {
memset(cfg, 0, sizeof(*cfg)); memset(cfg, 0, sizeof(*cfg));
cfg->fc_table = RT6_TABLE_MAIN; cfg->fc_table = l3mdev_fib_table_by_index(net, rtmsg->rtmsg_ifindex) ?
: RT6_TABLE_MAIN;
cfg->fc_ifindex = rtmsg->rtmsg_ifindex; cfg->fc_ifindex = rtmsg->rtmsg_ifindex;
cfg->fc_metric = rtmsg->rtmsg_metric; cfg->fc_metric = rtmsg->rtmsg_metric;
cfg->fc_expires = rtmsg->rtmsg_info; cfg->fc_expires = rtmsg->rtmsg_info;
...@@ -2470,6 +2480,7 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, ...@@ -2470,6 +2480,7 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
const struct in6_addr *addr, const struct in6_addr *addr,
bool anycast) bool anycast)
{ {
u32 tb_id;
struct net *net = dev_net(idev->dev); struct net *net = dev_net(idev->dev);
struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev,
DST_NOCOUNT); DST_NOCOUNT);
...@@ -2492,7 +2503,8 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, ...@@ -2492,7 +2503,8 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
rt->rt6i_gateway = *addr; rt->rt6i_gateway = *addr;
rt->rt6i_dst.addr = *addr; rt->rt6i_dst.addr = *addr;
rt->rt6i_dst.plen = 128; rt->rt6i_dst.plen = 128;
rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL); tb_id = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL;
rt->rt6i_table = fib6_get_table(net, tb_id);
rt->dst.flags |= DST_NOCACHE; rt->dst.flags |= DST_NOCACHE;
atomic_set(&rt->dst.__refcnt, 1); atomic_set(&rt->dst.__refcnt, 1);
...@@ -3254,6 +3266,11 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) ...@@ -3254,6 +3266,11 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh)
} else { } else {
fl6.flowi6_oif = oif; fl6.flowi6_oif = oif;
if (netif_index_is_l3_master(net, oif)) {
fl6.flowi6_flags = FLOWI_FLAG_L3MDEV_SRC |
FLOWI_FLAG_SKIP_NH_OIF;
}
rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6); rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment