Skip to content

Commit afe80a4

Browse files
David Aherndavem330
David Ahern
authored andcommitted
net: vrf: ipv4 support for local traffic to local addresses
Add support for locally originated traffic to VRF-local addresses. If destination device for an skb is the loopback or VRF device then set its dst to a local version of the VRF cached dst_entry and call netif_rx to insert the packet onto the rx queue - similar to what is done for loopback. This patch handles IPv4 support; follow on patch handles IPv6. With this patch, ping, tcp and udp packets to a local IPv4 address are successfully routed: $ ip addr show dev eth1 4: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master red state UP group default qlen 1000 link/ether 02:e0:f9:1c:b9:74 brd ff:ff:ff:ff:ff:ff inet 10.100.1.1/24 brd 10.100.1.255 scope global eth1 valid_lft forever preferred_lft forever inet6 2100:1::1/120 scope global valid_lft forever preferred_lft forever inet6 fe80::e0:f9ff:fe1c:b974/64 scope link valid_lft forever preferred_lft forever $ ping -c1 -I red 10.100.1.1 ping: Warning: source address might be selected on device other than red. PING 10.100.1.1 (10.100.1.1) from 10.100.1.1 red: 56(84) bytes of data. 64 bytes from 10.100.1.1: icmp_seq=1 ttl=64 time=0.057 ms This patch also enables use of IPv4 loopback address on the VRF device: $ ip addr add dev red 127.0.0.1/8 $ ping -c1 -I red 127.0.0.1 PING 127.0.0.1 (127.0.0.1) from 127.0.0.1 red: 56(84) bytes of data. 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.058 ms Signed-off-by: David Ahern <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 911a66f commit afe80a4

File tree

1 file changed

+98
-2
lines changed

1 file changed

+98
-2
lines changed

drivers/net/vrf.c

+98-2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444

4545
struct net_vrf {
4646
struct rtable __rcu *rth;
47+
struct rtable __rcu *rth_local;
4748
struct rt6_info __rcu *rt6;
4849
u32 tb_id;
4950
};
@@ -54,9 +55,20 @@ struct pcpu_dstats {
5455
u64 tx_drps;
5556
u64 rx_pkts;
5657
u64 rx_bytes;
58+
u64 rx_drps;
5759
struct u64_stats_sync syncp;
5860
};
5961

62+
static void vrf_rx_stats(struct net_device *dev, int len)
63+
{
64+
struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
65+
66+
u64_stats_update_begin(&dstats->syncp);
67+
dstats->rx_pkts++;
68+
dstats->rx_bytes += len;
69+
u64_stats_update_end(&dstats->syncp);
70+
}
71+
6072
static void vrf_tx_error(struct net_device *vrf_dev, struct sk_buff *skb)
6173
{
6274
vrf_dev->stats.tx_errors++;
@@ -91,6 +103,34 @@ static struct rtnl_link_stats64 *vrf_get_stats64(struct net_device *dev,
91103
return stats;
92104
}
93105

106+
/* Local traffic destined to local address. Reinsert the packet to rx
107+
* path, similar to loopback handling.
108+
*/
109+
static int vrf_local_xmit(struct sk_buff *skb, struct net_device *dev,
110+
struct dst_entry *dst)
111+
{
112+
int len = skb->len;
113+
114+
skb_orphan(skb);
115+
116+
skb_dst_set(skb, dst);
117+
skb_dst_force(skb);
118+
119+
/* set pkt_type to avoid skb hitting packet taps twice -
120+
* once on Tx and again in Rx processing
121+
*/
122+
skb->pkt_type = PACKET_LOOPBACK;
123+
124+
skb->protocol = eth_type_trans(skb, dev);
125+
126+
if (likely(netif_rx(skb) == NET_RX_SUCCESS))
127+
vrf_rx_stats(dev, len);
128+
else
129+
this_cpu_inc(dev->dstats->rx_drps);
130+
131+
return NETDEV_TX_OK;
132+
}
133+
94134
#if IS_ENABLED(CONFIG_IPV6)
95135
static netdev_tx_t vrf_process_v6_outbound(struct sk_buff *skb,
96136
struct net_device *dev)
@@ -169,6 +209,34 @@ static netdev_tx_t vrf_process_v4_outbound(struct sk_buff *skb,
169209
}
170210

171211
skb_dst_drop(skb);
212+
213+
/* if dst.dev is loopback or the VRF device again this is locally
214+
* originated traffic destined to a local address. Short circuit
215+
* to Rx path using our local dst
216+
*/
217+
if (rt->dst.dev == net->loopback_dev || rt->dst.dev == vrf_dev) {
218+
struct net_vrf *vrf = netdev_priv(vrf_dev);
219+
struct rtable *rth_local;
220+
struct dst_entry *dst = NULL;
221+
222+
ip_rt_put(rt);
223+
224+
rcu_read_lock();
225+
226+
rth_local = rcu_dereference(vrf->rth_local);
227+
if (likely(rth_local)) {
228+
dst = &rth_local->dst;
229+
dst_hold(dst);
230+
}
231+
232+
rcu_read_unlock();
233+
234+
if (unlikely(!dst))
235+
goto err;
236+
237+
return vrf_local_xmit(skb, vrf_dev, dst);
238+
}
239+
172240
skb_dst_set(skb, &rt->dst);
173241

174242
/* strip the ethernet header added for pass through VRF device */
@@ -375,29 +443,48 @@ static int vrf_output(struct net *net, struct sock *sk, struct sk_buff *skb)
375443
static void vrf_rtable_release(struct net_vrf *vrf)
376444
{
377445
struct rtable *rth = rtnl_dereference(vrf->rth);
446+
struct rtable *rth_local = rtnl_dereference(vrf->rth_local);
378447

379-
rcu_assign_pointer(vrf->rth, NULL);
448+
RCU_INIT_POINTER(vrf->rth, NULL);
449+
RCU_INIT_POINTER(vrf->rth_local, NULL);
450+
synchronize_rcu();
380451

381452
if (rth)
382453
dst_release(&rth->dst);
454+
455+
if (rth_local)
456+
dst_release(&rth_local->dst);
383457
}
384458

385459
static int vrf_rtable_create(struct net_device *dev)
386460
{
387461
struct net_vrf *vrf = netdev_priv(dev);
388-
struct rtable *rth;
462+
struct rtable *rth, *rth_local;
389463

390464
if (!fib_new_table(dev_net(dev), vrf->tb_id))
391465
return -ENOMEM;
392466

467+
/* create a dst for routing packets out through a VRF device */
393468
rth = rt_dst_alloc(dev, 0, RTN_UNICAST, 1, 1, 0);
394469
if (!rth)
395470
return -ENOMEM;
396471

472+
/* create a dst for local ingress routing - packets sent locally
473+
* to local address via the VRF device as a loopback
474+
*/
475+
rth_local = rt_dst_alloc(dev, RTCF_LOCAL, RTN_LOCAL, 1, 1, 0);
476+
if (!rth_local) {
477+
dst_release(&rth->dst);
478+
return -ENOMEM;
479+
}
480+
397481
rth->dst.output = vrf_output;
398482
rth->rt_table_id = vrf->tb_id;
399483

484+
rth_local->rt_table_id = vrf->tb_id;
485+
400486
rcu_assign_pointer(vrf->rth, rth);
487+
rcu_assign_pointer(vrf->rth_local, rth_local);
401488

402489
return 0;
403490
}
@@ -652,10 +739,19 @@ static struct sk_buff *vrf_ip_rcv(struct net_device *vrf_dev,
652739
skb->dev = vrf_dev;
653740
skb->skb_iif = vrf_dev->ifindex;
654741

742+
/* loopback traffic; do not push through packet taps again.
743+
* Reset pkt_type for upper layers to process skb
744+
*/
745+
if (skb->pkt_type == PACKET_LOOPBACK) {
746+
skb->pkt_type = PACKET_HOST;
747+
goto out;
748+
}
749+
655750
skb_push(skb, skb->mac_len);
656751
dev_queue_xmit_nit(skb, vrf_dev);
657752
skb_pull(skb, skb->mac_len);
658753

754+
out:
659755
return skb;
660756
}
661757

0 commit comments

Comments
 (0)