diff options
Diffstat (limited to 'drivers/net/vrf.c')
-rw-r--r-- | drivers/net/vrf.c | 171 |
1 files changed, 42 insertions, 129 deletions
diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 8a1eaf3c302a..feb1b2e15c2e 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -47,9 +47,7 @@ static unsigned int vrf_net_id; struct net_vrf { struct rtable __rcu *rth; - struct rtable __rcu *rth_local; struct rt6_info __rcu *rt6; - struct rt6_info __rcu *rt6_local; u32 tb_id; }; @@ -134,7 +132,6 @@ static int vrf_local_xmit(struct sk_buff *skb, struct net_device *dev, skb_orphan(skb); skb_dst_set(skb, dst); - skb_dst_force(skb); /* set pkt_type to avoid skb hitting packet taps twice - * once on Tx and again in Rx processing @@ -194,42 +191,10 @@ static netdev_tx_t vrf_process_v6_outbound(struct sk_buff *skb, /* if dst.dev is loopback or the VRF device again this is locally * originated traffic destined to a local address. Short circuit - * to Rx path using our local dst + * to Rx path */ - if (dst->dev == net->loopback_dev || dst->dev == dev) { - struct net_vrf *vrf = netdev_priv(dev); - struct rt6_info *rt6_local; - - /* release looked up dst and use cached local dst */ - dst_release(dst); - - rcu_read_lock(); - - rt6_local = rcu_dereference(vrf->rt6_local); - if (unlikely(!rt6_local)) { - rcu_read_unlock(); - goto err; - } - - /* Ordering issue: cached local dst is created on newlink - * before the IPv6 initialization. Using the local dst - * requires rt6i_idev to be set so make sure it is. - */ - if (unlikely(!rt6_local->rt6i_idev)) { - rt6_local->rt6i_idev = in6_dev_get(dev); - if (!rt6_local->rt6i_idev) { - rcu_read_unlock(); - goto err; - } - } - - dst = &rt6_local->dst; - dst_hold(dst); - - rcu_read_unlock(); - - return vrf_local_xmit(skb, dev, &rt6_local->dst); - } + if (dst->dev == dev) + return vrf_local_xmit(skb, dev, dst); skb_dst_set(skb, dst); @@ -296,30 +261,10 @@ static netdev_tx_t vrf_process_v4_outbound(struct sk_buff *skb, /* if dst.dev is loopback or the VRF device again this is locally * originated traffic destined to a local address. Short circuit - * to Rx path using our local dst + * to Rx path */ - if (rt->dst.dev == net->loopback_dev || rt->dst.dev == vrf_dev) { - struct net_vrf *vrf = netdev_priv(vrf_dev); - struct rtable *rth_local; - struct dst_entry *dst = NULL; - - ip_rt_put(rt); - - rcu_read_lock(); - - rth_local = rcu_dereference(vrf->rth_local); - if (likely(rth_local)) { - dst = &rth_local->dst; - dst_hold(dst); - } - - rcu_read_unlock(); - - if (unlikely(!dst)) - goto err; - - return vrf_local_xmit(skb, vrf_dev, dst); - } + if (rt->dst.dev == vrf_dev) + return vrf_local_xmit(skb, vrf_dev, &rt->dst); skb_dst_set(skb, &rt->dst); @@ -528,12 +473,10 @@ static struct sk_buff *vrf_ip6_out(struct net_device *vrf_dev, static void vrf_rt6_release(struct net_device *dev, struct net_vrf *vrf) { struct rt6_info *rt6 = rtnl_dereference(vrf->rt6); - struct rt6_info *rt6_local = rtnl_dereference(vrf->rt6_local); struct net *net = dev_net(dev); struct dst_entry *dst; RCU_INIT_POINTER(vrf->rt6, NULL); - RCU_INIT_POINTER(vrf->rt6_local, NULL); synchronize_rcu(); /* move dev in dst's to loopback so this VRF device can be deleted @@ -546,19 +489,6 @@ static void vrf_rt6_release(struct net_device *dev, struct net_vrf *vrf) dev_hold(dst->dev); dst_release(dst); } - - if (rt6_local) { - if (rt6_local->rt6i_idev) { - in6_dev_put(rt6_local->rt6i_idev); - rt6_local->rt6i_idev = NULL; - } - - dst = &rt6_local->dst; - dev_put(dst->dev); - dst->dev = net->loopback_dev; - dev_hold(dst->dev); - dst_release(dst); - } } static int vrf_rt6_create(struct net_device *dev) @@ -567,7 +497,7 @@ static int vrf_rt6_create(struct net_device *dev) struct net_vrf *vrf = netdev_priv(dev); struct net *net = dev_net(dev); struct fib6_table *rt6i_table; - struct rt6_info *rt6, *rt6_local; + struct rt6_info *rt6; int rc = -ENOMEM; /* IPv6 can be CONFIG enabled and then disabled runtime */ @@ -586,22 +516,7 @@ static int vrf_rt6_create(struct net_device *dev) rt6->rt6i_table = rt6i_table; rt6->dst.output = vrf_output6; - /* create a dst for local routing - packets sent locally - * to local address via the VRF device as a loopback - */ - rt6_local = ip6_dst_alloc(net, dev, flags); - if (!rt6_local) { - dst_release(&rt6->dst); - goto out; - } - - rt6_local->rt6i_idev = in6_dev_get(dev); - rt6_local->rt6i_flags = RTF_UP | RTF_NONEXTHOP | RTF_LOCAL; - rt6_local->rt6i_table = rt6i_table; - rt6_local->dst.input = ip6_input; - rcu_assign_pointer(vrf->rt6, rt6); - rcu_assign_pointer(vrf->rt6_local, rt6_local); rc = 0; out: @@ -788,12 +703,10 @@ static struct sk_buff *vrf_l3_out(struct net_device *vrf_dev, static void vrf_rtable_release(struct net_device *dev, struct net_vrf *vrf) { struct rtable *rth = rtnl_dereference(vrf->rth); - struct rtable *rth_local = rtnl_dereference(vrf->rth_local); struct net *net = dev_net(dev); struct dst_entry *dst; RCU_INIT_POINTER(vrf->rth, NULL); - RCU_INIT_POINTER(vrf->rth_local, NULL); synchronize_rcu(); /* move dev in dst's to loopback so this VRF device can be deleted @@ -806,20 +719,12 @@ static void vrf_rtable_release(struct net_device *dev, struct net_vrf *vrf) dev_hold(dst->dev); dst_release(dst); } - - if (rth_local) { - dst = &rth_local->dst; - dev_put(dst->dev); - dst->dev = net->loopback_dev; - dev_hold(dst->dev); - dst_release(dst); - } } static int vrf_rtable_create(struct net_device *dev) { struct net_vrf *vrf = netdev_priv(dev); - struct rtable *rth, *rth_local; + struct rtable *rth; if (!fib_new_table(dev_net(dev), vrf->tb_id)) return -ENOMEM; @@ -829,22 +734,10 @@ static int vrf_rtable_create(struct net_device *dev) if (!rth) return -ENOMEM; - /* create a dst for local ingress routing - packets sent locally - * to local address via the VRF device as a loopback - */ - rth_local = rt_dst_alloc(dev, RTCF_LOCAL, RTN_LOCAL, 1, 1, 0); - if (!rth_local) { - dst_release(&rth->dst); - return -ENOMEM; - } - rth->dst.output = vrf_output; rth->rt_table_id = vrf->tb_id; - rth_local->rt_table_id = vrf->tb_id; - rcu_assign_pointer(vrf->rth, rth); - rcu_assign_pointer(vrf->rth_local, rth_local); return 0; } @@ -871,18 +764,22 @@ static void cycle_netdev(struct net_device *dev) } } -static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev) +static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev, + struct netlink_ext_ack *extack) { int ret; /* do not allow loopback device to be enslaved to a VRF. * The vrf device acts as the loopback for the vrf. */ - if (port_dev == dev_net(dev)->loopback_dev) + if (port_dev == dev_net(dev)->loopback_dev) { + NL_SET_ERR_MSG(extack, + "Can not enslave loopback device to a VRF"); return -EOPNOTSUPP; + } port_dev->priv_flags |= IFF_L3MDEV_SLAVE; - ret = netdev_master_upper_dev_link(port_dev, dev, NULL, NULL); + ret = netdev_master_upper_dev_link(port_dev, dev, NULL, NULL, extack); if (ret < 0) goto err; @@ -895,12 +792,19 @@ err: return ret; } -static int vrf_add_slave(struct net_device *dev, struct net_device *port_dev) +static int vrf_add_slave(struct net_device *dev, struct net_device *port_dev, + struct netlink_ext_ack *extack) { - if (netif_is_l3_master(port_dev) || netif_is_l3_slave(port_dev)) + if (netif_is_l3_master(port_dev)) { + NL_SET_ERR_MSG(extack, + "Can not enslave an L3 master device to a VRF"); + return -EINVAL; + } + + if (netif_is_l3_slave(port_dev)) return -EINVAL; - return do_vrf_add_slave(dev, port_dev); + return do_vrf_add_slave(dev, port_dev, extack); } /* inverse of do_vrf_add_slave */ @@ -1063,12 +967,12 @@ static void vrf_ip6_input_dst(struct sk_buff *skb, struct net_device *vrf_dev, { const struct ipv6hdr *iph = ipv6_hdr(skb); struct flowi6 fl6 = { + .flowi6_iif = ifindex, + .flowi6_mark = skb->mark, + .flowi6_proto = iph->nexthdr, .daddr = iph->daddr, .saddr = iph->saddr, .flowlabel = ip6_flowinfo(iph), - .flowi6_mark = skb->mark, - .flowi6_proto = iph->nexthdr, - .flowi6_iif = ifindex, }; struct net *net = dev_net(vrf_dev); struct rt6_info *rt6; @@ -1271,7 +1175,7 @@ static int vrf_fib_rule(const struct net_device *dev, __u8 family, bool add_it) frh->family = family; frh->action = FR_ACT_TO_TBL; - if (nla_put_u32(skb, FRA_L3MDEV, 1)) + if (nla_put_u8(skb, FRA_L3MDEV, 1)) goto nla_put_failure; if (nla_put_u32(skb, FRA_PRIORITY, FIB_RULE_PREF)) @@ -1371,10 +1275,14 @@ static int vrf_validate(struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) { if (tb[IFLA_ADDRESS]) { - if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) + if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) { + NL_SET_ERR_MSG(extack, "Invalid hardware address"); return -EINVAL; - if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) + } + if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) { + NL_SET_ERR_MSG(extack, "Invalid hardware address"); return -EADDRNOTAVAIL; + } } return 0; } @@ -1399,12 +1307,17 @@ static int vrf_newlink(struct net *src_net, struct net_device *dev, struct net *net; int err; - if (!data || !data[IFLA_VRF_TABLE]) + if (!data || !data[IFLA_VRF_TABLE]) { + NL_SET_ERR_MSG(extack, "VRF table id is missing"); return -EINVAL; + } vrf->tb_id = nla_get_u32(data[IFLA_VRF_TABLE]); - if (vrf->tb_id == RT_TABLE_UNSPEC) + if (vrf->tb_id == RT_TABLE_UNSPEC) { + NL_SET_ERR_MSG_ATTR(extack, data[IFLA_VRF_TABLE], + "Invalid VRF table id"); return -EINVAL; + } dev->priv_flags |= IFF_L3MDEV_MASTER; |