--- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1030,6 +1030,8 @@ struct netdev_net_notifier { struct notifier_block *nb; }; +struct flow_offload_hw_path; + /* * This structure defines the management hooks for network devices. * The following hooks can be defined; unless noted otherwise, they are @@ -1571,6 +1573,7 @@ struct net_device_ops { struct net_device * (*ndo_get_peer_dev)(struct net_device *dev); int (*ndo_fill_forward_path)(struct net_device_path_ctx *ctx, struct net_device_path *path); + int (*ndo_flow_offload_check)(struct flow_offload_hw_path *path); }; /** --- a/include/linux/ppp_channel.h +++ b/include/linux/ppp_channel.h @@ -31,6 +31,7 @@ struct ppp_channel_ops { int (*fill_forward_path)(struct net_device_path_ctx *, struct net_device_path *, const struct ppp_channel *); + int (*flow_offload_check)(struct ppp_channel *, struct flow_offload_hw_path *); }; struct ppp_channel { --- /dev/null +++ b/include/net/netfilter/nf_hnat.h @@ -0,0 +1,19 @@ +#define FLOW_OFFLOAD_PATH_ETHERNET BIT(0) +#define FLOW_OFFLOAD_PATH_VLAN BIT(1) +#define FLOW_OFFLOAD_PATH_PPPOE BIT(2) +#define FLOW_OFFLOAD_PATH_DSA BIT(3) +#define FLOW_OFFLOAD_PATH_DSLITE BIT(4) +#define FLOW_OFFLOAD_PATH_6RD BIT(5) + +struct flow_offload_hw_path { + struct net_device *dev; + struct net_device *virt_dev; + u32 flags; + + u8 eth_src[ETH_ALEN]; + u8 eth_dest[ETH_ALEN]; + u16 vlan_proto; + u16 vlan_id; + u16 dsa_port; + u16 pppoe_sid; +}; --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -52,6 +52,7 @@ #include #include #include +#include #define PPP_VERSION "2.4.2" @@ -1487,6 +1488,26 @@ static int ppp_fill_forward_path(struct return chan->ops->fill_forward_path(ctx, path, chan); } +static int ppp_flow_offload_check(struct flow_offload_hw_path *path) +{ + struct ppp *ppp = netdev_priv(path->dev); + struct ppp_channel *chan; + struct channel *pch; + + if (ppp->flags & SC_MULTILINK) + return -EOPNOTSUPP; + + if (list_empty(&ppp->channels)) + return -ENODEV; + + pch = list_first_entry(&ppp->channels, struct channel, clist); + chan = pch->chan; + if (!chan->ops->flow_offload_check) + return -EOPNOTSUPP; + + return chan->ops->flow_offload_check(chan, path); +} + static const struct net_device_ops ppp_netdev_ops = { .ndo_init = ppp_dev_init, .ndo_uninit = ppp_dev_uninit, @@ -1494,6 +1515,7 @@ static const struct net_device_ops ppp_n .ndo_do_ioctl = ppp_net_ioctl, .ndo_get_stats64 = ppp_get_stats64, .ndo_fill_forward_path = ppp_fill_forward_path, + .ndo_flow_offload_check = ppp_flow_offload_check, }; static struct device_type ppp_type = { --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -79,6 +79,7 @@ #include #include +#include #define PPPOE_HASH_BITS 4 #define PPPOE_HASH_SIZE (1 << PPPOE_HASH_BITS) @@ -994,9 +995,33 @@ static int pppoe_fill_forward_path(struc return 0; } +static int pppoe_flow_offload_check(struct ppp_channel *chan, + struct flow_offload_hw_path *path) +{ + struct sock *sk = (struct sock *)chan->private; + struct pppox_sock *po = pppox_sk(sk); + struct net_device *dev = po->pppoe_dev; + + if (sock_flag(sk, SOCK_DEAD) || + !(sk->sk_state & PPPOX_CONNECTED) || !dev) + return -ENODEV; + + path->dev = po->pppoe_dev; + path->flags |= FLOW_OFFLOAD_PATH_PPPOE; + memcpy(path->eth_src, po->pppoe_dev->dev_addr, ETH_ALEN); + memcpy(path->eth_dest, po->pppoe_pa.remote, ETH_ALEN); + path->pppoe_sid = be16_to_cpu(po->num); + + if (path->dev->netdev_ops->ndo_flow_offload_check) + return path->dev->netdev_ops->ndo_flow_offload_check(path); + + return 0; +} + static const struct ppp_channel_ops pppoe_chan_ops = { .start_xmit = pppoe_xmit, .fill_forward_path = pppoe_fill_forward_path, + .flow_offload_check = pppoe_flow_offload_check, }; static int pppoe_recvmsg(struct socket *sock, struct msghdr *m, --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include "dsa_priv.h" @@ -1655,6 +1657,25 @@ static int dsa_slave_fill_forward_path(s return 0; } +static int dsa_flow_offload_check(struct flow_offload_hw_path *path) +{ + struct net_device *dev = path->dev; + struct dsa_port *dp; + + if (!(path->flags & FLOW_OFFLOAD_PATH_ETHERNET)) + return -EINVAL; + + dp = dsa_slave_to_port(dev); + path->dsa_port = dp->index; + path->dev = dsa_slave_to_master(dev); + path->flags |= FLOW_OFFLOAD_PATH_DSA; + + if (path->dev->netdev_ops->ndo_flow_offload_check) + return path->dev->netdev_ops->ndo_flow_offload_check(path); + + return 0; +} + static const struct net_device_ops dsa_slave_netdev_ops = { .ndo_open = dsa_slave_open, .ndo_stop = dsa_slave_close, @@ -1679,6 +1700,7 @@ static const struct net_device_ops dsa_s .ndo_vlan_rx_add_vid = dsa_slave_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = dsa_slave_vlan_rx_kill_vid, .ndo_get_devlink_port = dsa_slave_get_devlink_port, + .ndo_flow_offload_check = dsa_flow_offload_check, .ndo_change_mtu = dsa_slave_change_mtu, .ndo_fill_forward_path = dsa_slave_fill_forward_path, }; --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -56,6 +56,7 @@ #include #include #include +#include MODULE_AUTHOR("Ville Nuorvala"); MODULE_DESCRIPTION("IPv6 tunneling device"); @@ -1948,6 +1949,20 @@ int ip6_tnl_change_mtu(struct net_device } EXPORT_SYMBOL(ip6_tnl_change_mtu); +static int ipip6_dev_flow_offload_check(struct flow_offload_hw_path *path) +{ + struct net_device *dev = path->dev; + struct ip6_tnl *tnl = netdev_priv(dev); + + if (path->flags & FLOW_OFFLOAD_PATH_DSLITE) + return -EEXIST; + + path->flags |= FLOW_OFFLOAD_PATH_DSLITE; + path->dev = tnl->dev; + + return 0; +} + int ip6_tnl_get_iflink(const struct net_device *dev) { struct ip6_tnl *t = netdev_priv(dev); @@ -2017,6 +2032,7 @@ static const struct net_device_ops ip6_t .ndo_change_mtu = ip6_tnl_change_mtu, .ndo_get_stats = ip6_get_stats, .ndo_get_iflink = ip6_tnl_get_iflink, + .ndo_flow_offload_check = ipip6_dev_flow_offload_check, }; #define IPXIPX_FEATURES (NETIF_F_SG | \ --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -31,6 +31,8 @@ #include "vlanproc.h" #include #include +#include +#include /* * Create the VLAN header for an arbitrary protocol layer @@ -790,6 +792,25 @@ static int vlan_dev_fill_forward_path(st return 0; } +static int vlan_dev_flow_offload_check(struct flow_offload_hw_path *path) +{ + struct net_device *dev = path->dev; + struct vlan_dev_priv *vlan = vlan_dev_priv(dev); + + if (path->flags & FLOW_OFFLOAD_PATH_VLAN) + return -EEXIST; + + path->flags |= FLOW_OFFLOAD_PATH_VLAN; + path->vlan_proto = vlan->vlan_proto; + path->vlan_id = vlan->vlan_id; + path->virt_dev = dev; + path->dev = vlan->real_dev; + + if (vlan->real_dev->netdev_ops->ndo_flow_offload_check) + return vlan->real_dev->netdev_ops->ndo_flow_offload_check(path); + return 0; +} + static const struct ethtool_ops vlan_ethtool_ops = { .get_link_ksettings = vlan_ethtool_get_link_ksettings, .get_drvinfo = vlan_ethtool_get_drvinfo, @@ -829,6 +850,7 @@ static const struct net_device_ops vlan_ .ndo_fix_features = vlan_dev_fix_features, .ndo_get_iflink = vlan_dev_get_iflink, .ndo_fill_forward_path = vlan_dev_fill_forward_path, + .ndo_flow_offload_check = vlan_dev_flow_offload_check, }; static void vlan_dev_free(struct net_device *dev)