|
@@ -42,9 +42,6 @@
|
|
#define DRV_NAME "vrf"
|
|
#define DRV_NAME "vrf"
|
|
#define DRV_VERSION "1.0"
|
|
#define DRV_VERSION "1.0"
|
|
|
|
|
|
-#define vrf_master_get_rcu(dev) \
|
|
|
|
- ((struct net_device *)rcu_dereference(dev->rx_handler_data))
|
|
|
|
-
|
|
|
|
struct net_vrf {
|
|
struct net_vrf {
|
|
struct rtable *rth;
|
|
struct rtable *rth;
|
|
struct rt6_info *rt6;
|
|
struct rt6_info *rt6;
|
|
@@ -60,90 +57,12 @@ struct pcpu_dstats {
|
|
struct u64_stats_sync syncp;
|
|
struct u64_stats_sync syncp;
|
|
};
|
|
};
|
|
|
|
|
|
-/* neighbor handling is done with actual device; do not want
|
|
|
|
- * to flip skb->dev for those ndisc packets. This really fails
|
|
|
|
- * for multiple next protocols (e.g., NEXTHDR_HOP). But it is
|
|
|
|
- * a start.
|
|
|
|
- */
|
|
|
|
-#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
-static bool check_ipv6_frame(const struct sk_buff *skb)
|
|
|
|
-{
|
|
|
|
- const struct ipv6hdr *ipv6h;
|
|
|
|
- struct ipv6hdr _ipv6h;
|
|
|
|
- bool rc = true;
|
|
|
|
-
|
|
|
|
- ipv6h = skb_header_pointer(skb, 0, sizeof(_ipv6h), &_ipv6h);
|
|
|
|
- if (!ipv6h)
|
|
|
|
- goto out;
|
|
|
|
-
|
|
|
|
- if (ipv6h->nexthdr == NEXTHDR_ICMP) {
|
|
|
|
- const struct icmp6hdr *icmph;
|
|
|
|
- struct icmp6hdr _icmph;
|
|
|
|
-
|
|
|
|
- icmph = skb_header_pointer(skb, sizeof(_ipv6h),
|
|
|
|
- sizeof(_icmph), &_icmph);
|
|
|
|
- if (!icmph)
|
|
|
|
- goto out;
|
|
|
|
-
|
|
|
|
- switch (icmph->icmp6_type) {
|
|
|
|
- case NDISC_ROUTER_SOLICITATION:
|
|
|
|
- case NDISC_ROUTER_ADVERTISEMENT:
|
|
|
|
- case NDISC_NEIGHBOUR_SOLICITATION:
|
|
|
|
- case NDISC_NEIGHBOUR_ADVERTISEMENT:
|
|
|
|
- case NDISC_REDIRECT:
|
|
|
|
- rc = false;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-out:
|
|
|
|
- return rc;
|
|
|
|
-}
|
|
|
|
-#else
|
|
|
|
-static bool check_ipv6_frame(const struct sk_buff *skb)
|
|
|
|
-{
|
|
|
|
- return false;
|
|
|
|
-}
|
|
|
|
-#endif
|
|
|
|
-
|
|
|
|
-static bool is_ip_rx_frame(struct sk_buff *skb)
|
|
|
|
-{
|
|
|
|
- switch (skb->protocol) {
|
|
|
|
- case htons(ETH_P_IP):
|
|
|
|
- return true;
|
|
|
|
- case htons(ETH_P_IPV6):
|
|
|
|
- return check_ipv6_frame(skb);
|
|
|
|
- }
|
|
|
|
- return false;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
static void vrf_tx_error(struct net_device *vrf_dev, struct sk_buff *skb)
|
|
static void vrf_tx_error(struct net_device *vrf_dev, struct sk_buff *skb)
|
|
{
|
|
{
|
|
vrf_dev->stats.tx_errors++;
|
|
vrf_dev->stats.tx_errors++;
|
|
kfree_skb(skb);
|
|
kfree_skb(skb);
|
|
}
|
|
}
|
|
|
|
|
|
-/* note: already called with rcu_read_lock */
|
|
|
|
-static rx_handler_result_t vrf_handle_frame(struct sk_buff **pskb)
|
|
|
|
-{
|
|
|
|
- struct sk_buff *skb = *pskb;
|
|
|
|
-
|
|
|
|
- if (is_ip_rx_frame(skb)) {
|
|
|
|
- struct net_device *dev = vrf_master_get_rcu(skb->dev);
|
|
|
|
- struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
|
|
|
|
-
|
|
|
|
- u64_stats_update_begin(&dstats->syncp);
|
|
|
|
- dstats->rx_pkts++;
|
|
|
|
- dstats->rx_bytes += skb->len;
|
|
|
|
- u64_stats_update_end(&dstats->syncp);
|
|
|
|
-
|
|
|
|
- skb->dev = dev;
|
|
|
|
-
|
|
|
|
- return RX_HANDLER_ANOTHER;
|
|
|
|
- }
|
|
|
|
- return RX_HANDLER_PASS;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
static struct rtnl_link_stats64 *vrf_get_stats64(struct net_device *dev,
|
|
static struct rtnl_link_stats64 *vrf_get_stats64(struct net_device *dev,
|
|
struct rtnl_link_stats64 *stats)
|
|
struct rtnl_link_stats64 *stats)
|
|
{
|
|
{
|
|
@@ -506,28 +425,14 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
|
|
{
|
|
{
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
- /* register the packet handler for slave ports */
|
|
|
|
- ret = netdev_rx_handler_register(port_dev, vrf_handle_frame, dev);
|
|
|
|
- if (ret) {
|
|
|
|
- netdev_err(port_dev,
|
|
|
|
- "Device %s failed to register rx_handler\n",
|
|
|
|
- port_dev->name);
|
|
|
|
- goto out_fail;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
ret = netdev_master_upper_dev_link(port_dev, dev, NULL, NULL);
|
|
ret = netdev_master_upper_dev_link(port_dev, dev, NULL, NULL);
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
- goto out_unregister;
|
|
|
|
|
|
+ return ret;
|
|
|
|
|
|
port_dev->priv_flags |= IFF_L3MDEV_SLAVE;
|
|
port_dev->priv_flags |= IFF_L3MDEV_SLAVE;
|
|
cycle_netdev(port_dev);
|
|
cycle_netdev(port_dev);
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
-
|
|
|
|
-out_unregister:
|
|
|
|
- netdev_rx_handler_unregister(port_dev);
|
|
|
|
-out_fail:
|
|
|
|
- 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)
|
|
@@ -544,8 +449,6 @@ static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev)
|
|
netdev_upper_dev_unlink(port_dev, dev);
|
|
netdev_upper_dev_unlink(port_dev, dev);
|
|
port_dev->priv_flags &= ~IFF_L3MDEV_SLAVE;
|
|
port_dev->priv_flags &= ~IFF_L3MDEV_SLAVE;
|
|
|
|
|
|
- netdev_rx_handler_unregister(port_dev);
|
|
|
|
-
|
|
|
|
cycle_netdev(port_dev);
|
|
cycle_netdev(port_dev);
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
@@ -669,6 +572,95 @@ static int vrf_get_saddr(struct net_device *dev, struct flowi4 *fl4)
|
|
return rc;
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
+/* neighbor handling is done with actual device; do not want
|
|
|
|
+ * to flip skb->dev for those ndisc packets. This really fails
|
|
|
|
+ * for multiple next protocols (e.g., NEXTHDR_HOP). But it is
|
|
|
|
+ * a start.
|
|
|
|
+ */
|
|
|
|
+static bool ipv6_ndisc_frame(const struct sk_buff *skb)
|
|
|
|
+{
|
|
|
|
+ const struct ipv6hdr *iph = ipv6_hdr(skb);
|
|
|
|
+ bool rc = false;
|
|
|
|
+
|
|
|
|
+ if (iph->nexthdr == NEXTHDR_ICMP) {
|
|
|
|
+ const struct icmp6hdr *icmph;
|
|
|
|
+ struct icmp6hdr _icmph;
|
|
|
|
+
|
|
|
|
+ icmph = skb_header_pointer(skb, sizeof(*iph),
|
|
|
|
+ sizeof(_icmph), &_icmph);
|
|
|
|
+ if (!icmph)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ switch (icmph->icmp6_type) {
|
|
|
|
+ case NDISC_ROUTER_SOLICITATION:
|
|
|
|
+ case NDISC_ROUTER_ADVERTISEMENT:
|
|
|
|
+ case NDISC_NEIGHBOUR_SOLICITATION:
|
|
|
|
+ case NDISC_NEIGHBOUR_ADVERTISEMENT:
|
|
|
|
+ case NDISC_REDIRECT:
|
|
|
|
+ rc = true;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+out:
|
|
|
|
+ return rc;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct sk_buff *vrf_ip6_rcv(struct net_device *vrf_dev,
|
|
|
|
+ struct sk_buff *skb)
|
|
|
|
+{
|
|
|
|
+ /* if packet is NDISC keep the ingress interface */
|
|
|
|
+ if (!ipv6_ndisc_frame(skb)) {
|
|
|
|
+ skb->dev = vrf_dev;
|
|
|
|
+ skb->skb_iif = vrf_dev->ifindex;
|
|
|
|
+
|
|
|
|
+ skb_push(skb, skb->mac_len);
|
|
|
|
+ dev_queue_xmit_nit(skb, vrf_dev);
|
|
|
|
+ skb_pull(skb, skb->mac_len);
|
|
|
|
+
|
|
|
|
+ IP6CB(skb)->flags |= IP6SKB_L3SLAVE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return skb;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#else
|
|
|
|
+static struct sk_buff *vrf_ip6_rcv(struct net_device *vrf_dev,
|
|
|
|
+ struct sk_buff *skb)
|
|
|
|
+{
|
|
|
|
+ return skb;
|
|
|
|
+}
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+static struct sk_buff *vrf_ip_rcv(struct net_device *vrf_dev,
|
|
|
|
+ struct sk_buff *skb)
|
|
|
|
+{
|
|
|
|
+ skb->dev = vrf_dev;
|
|
|
|
+ skb->skb_iif = vrf_dev->ifindex;
|
|
|
|
+
|
|
|
|
+ skb_push(skb, skb->mac_len);
|
|
|
|
+ dev_queue_xmit_nit(skb, vrf_dev);
|
|
|
|
+ skb_pull(skb, skb->mac_len);
|
|
|
|
+
|
|
|
|
+ return skb;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* called with rcu lock held */
|
|
|
|
+static struct sk_buff *vrf_l3_rcv(struct net_device *vrf_dev,
|
|
|
|
+ struct sk_buff *skb,
|
|
|
|
+ u16 proto)
|
|
|
|
+{
|
|
|
|
+ switch (proto) {
|
|
|
|
+ case AF_INET:
|
|
|
|
+ return vrf_ip_rcv(vrf_dev, skb);
|
|
|
|
+ case AF_INET6:
|
|
|
|
+ return vrf_ip6_rcv(vrf_dev, skb);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return skb;
|
|
|
|
+}
|
|
|
|
+
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
static struct dst_entry *vrf_get_rt6_dst(const struct net_device *dev,
|
|
static struct dst_entry *vrf_get_rt6_dst(const struct net_device *dev,
|
|
const struct flowi6 *fl6)
|
|
const struct flowi6 *fl6)
|
|
@@ -690,6 +682,7 @@ static const struct l3mdev_ops vrf_l3mdev_ops = {
|
|
.l3mdev_fib_table = vrf_fib_table,
|
|
.l3mdev_fib_table = vrf_fib_table,
|
|
.l3mdev_get_rtable = vrf_get_rtable,
|
|
.l3mdev_get_rtable = vrf_get_rtable,
|
|
.l3mdev_get_saddr = vrf_get_saddr,
|
|
.l3mdev_get_saddr = vrf_get_saddr,
|
|
|
|
+ .l3mdev_l3_rcv = vrf_l3_rcv,
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
.l3mdev_get_rt6_dst = vrf_get_rt6_dst,
|
|
.l3mdev_get_rt6_dst = vrf_get_rt6_dst,
|
|
#endif
|
|
#endif
|