Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 103247
b: refs/heads/master
c: 4ce2417
h: refs/heads/master
i:
  103245: cba2e6a
  103243: 072b5e2
  103239: 039187b
  103231: 1bed8fe
v: v3
  • Loading branch information
David S. Miller committed Jul 6, 2008
1 parent e016465 commit 3a65fe2
Show file tree
Hide file tree
Showing 15 changed files with 382 additions and 90 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 055e5110ae0c0c1176a75b78d789294f2ff2f7af
refs/heads/master: 4ce2417bfb3c7b1c5ed449782f4d86bafd7f1b69
150 changes: 148 additions & 2 deletions trunk/drivers/net/tun.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
#include <linux/if_tun.h>
#include <linux/crc32.h>
#include <linux/nsproxy.h>
#include <linux/virtio_net.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>

Expand Down Expand Up @@ -283,6 +284,7 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv,
struct tun_pi pi = { 0, __constant_htons(ETH_P_IP) };
struct sk_buff *skb;
size_t len = count, align = 0;
struct virtio_net_hdr gso = { 0 };

if (!(tun->flags & TUN_NO_PI)) {
if ((len -= sizeof(pi)) > count)
Expand All @@ -292,6 +294,17 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv,
return -EFAULT;
}

if (tun->flags & TUN_VNET_HDR) {
if ((len -= sizeof(gso)) > count)
return -EINVAL;

if (memcpy_fromiovec((void *)&gso, iv, sizeof(gso)))
return -EFAULT;

if (gso.hdr_len > len)
return -EINVAL;
}

if ((tun->flags & TUN_TYPE_MASK) == TUN_TAP_DEV) {
align = NET_IP_ALIGN;
if (unlikely(len < ETH_HLEN))
Expand All @@ -311,6 +324,16 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv,
return -EFAULT;
}

if (gso.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
if (!skb_partial_csum_set(skb, gso.csum_start,
gso.csum_offset)) {
tun->dev->stats.rx_frame_errors++;
kfree_skb(skb);
return -EINVAL;
}
} else if (tun->flags & TUN_NOCHECKSUM)
skb->ip_summed = CHECKSUM_UNNECESSARY;

switch (tun->flags & TUN_TYPE_MASK) {
case TUN_TUN_DEV:
if (tun->flags & TUN_NO_PI) {
Expand All @@ -337,8 +360,35 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv,
break;
};

if (tun->flags & TUN_NOCHECKSUM)
skb->ip_summed = CHECKSUM_UNNECESSARY;
if (gso.gso_type != VIRTIO_NET_HDR_GSO_NONE) {
pr_debug("GSO!\n");
switch (gso.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
case VIRTIO_NET_HDR_GSO_TCPV4:
skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
break;
case VIRTIO_NET_HDR_GSO_TCPV6:
skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
break;
default:
tun->dev->stats.rx_frame_errors++;
kfree_skb(skb);
return -EINVAL;
}

if (gso.gso_type & VIRTIO_NET_HDR_GSO_ECN)
skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN;

skb_shinfo(skb)->gso_size = gso.gso_size;
if (skb_shinfo(skb)->gso_size == 0) {
tun->dev->stats.rx_frame_errors++;
kfree_skb(skb);
return -EINVAL;
}

/* Header must be checked, and gso_segs computed. */
skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
skb_shinfo(skb)->gso_segs = 0;
}

netif_rx_ni(skb);
tun->dev->last_rx = jiffies;
Expand Down Expand Up @@ -384,6 +434,39 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun,
total += sizeof(pi);
}

if (tun->flags & TUN_VNET_HDR) {
struct virtio_net_hdr gso = { 0 }; /* no info leak */
if ((len -= sizeof(gso)) < 0)
return -EINVAL;

if (skb_is_gso(skb)) {
struct skb_shared_info *sinfo = skb_shinfo(skb);

/* This is a hint as to how much should be linear. */
gso.hdr_len = skb_headlen(skb);
gso.gso_size = sinfo->gso_size;
if (sinfo->gso_type & SKB_GSO_TCPV4)
gso.gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
else if (sinfo->gso_type & SKB_GSO_TCPV6)
gso.gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
else
BUG();
if (sinfo->gso_type & SKB_GSO_TCP_ECN)
gso.gso_type |= VIRTIO_NET_HDR_GSO_ECN;
} else
gso.gso_type = VIRTIO_NET_HDR_GSO_NONE;

if (skb->ip_summed == CHECKSUM_PARTIAL) {
gso.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
gso.csum_start = skb->csum_start - skb_headroom(skb);
gso.csum_offset = skb->csum_offset;
} /* else everything is zero */

if (unlikely(memcpy_toiovec(iv, (void *)&gso, sizeof(gso))))
return -EFAULT;
total += sizeof(gso);
}

len = min_t(int, skb->len, len);

skb_copy_datagram_iovec(skb, 0, iv, len);
Expand Down Expand Up @@ -598,6 +681,11 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
else
tun->flags &= ~TUN_ONE_QUEUE;

if (ifr->ifr_flags & IFF_VNET_HDR)
tun->flags |= TUN_VNET_HDR;
else
tun->flags &= ~TUN_VNET_HDR;

file->private_data = tun;
tun->attached = 1;
get_net(dev_net(tun->dev));
Expand All @@ -611,6 +699,46 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
return err;
}

/* This is like a cut-down ethtool ops, except done via tun fd so no
* privs required. */
static int set_offload(struct net_device *dev, unsigned long arg)
{
unsigned int old_features, features;

old_features = dev->features;
/* Unset features, set them as we chew on the arg. */
features = (old_features & ~(NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST
|NETIF_F_TSO_ECN|NETIF_F_TSO|NETIF_F_TSO6));

if (arg & TUN_F_CSUM) {
features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST;
arg &= ~TUN_F_CSUM;

if (arg & (TUN_F_TSO4|TUN_F_TSO6)) {
if (arg & TUN_F_TSO_ECN) {
features |= NETIF_F_TSO_ECN;
arg &= ~TUN_F_TSO_ECN;
}
if (arg & TUN_F_TSO4)
features |= NETIF_F_TSO;
if (arg & TUN_F_TSO6)
features |= NETIF_F_TSO6;
arg &= ~(TUN_F_TSO4|TUN_F_TSO6);
}
}

/* This gives the user a way to test for new features in future by
* trying to set them. */
if (arg)
return -EINVAL;

dev->features = features;
if (old_features != dev->features)
netdev_features_change(dev);

return 0;
}

static int tun_chr_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
Expand Down Expand Up @@ -640,6 +768,15 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
return 0;
}

if (cmd == TUNGETFEATURES) {
/* Currently this just means: "what IFF flags are valid?".
* This is needed because we never checked for invalid flags on
* TUNSETIFF. */
return put_user(IFF_TUN | IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE |
IFF_VNET_HDR,
(unsigned int __user*)argp);
}

if (!tun)
return -EBADFD;

Expand Down Expand Up @@ -707,6 +844,15 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
break;
#endif

case TUNSETOFFLOAD:
{
int ret;
rtnl_lock();
ret = set_offload(tun->dev, arg);
rtnl_unlock();
return ret;
}

case SIOCGIFFLAGS:
ifr.ifr_flags = tun->if_flags;
if (copy_to_user( argp, &ifr, sizeof ifr))
Expand Down
10 changes: 10 additions & 0 deletions trunk/include/linux/if_tun.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#define TUN_NO_PI 0x0040
#define TUN_ONE_QUEUE 0x0080
#define TUN_PERSIST 0x0100
#define TUN_VNET_HDR 0x0200

/* Ioctl defines */
#define TUNSETNOCSUM _IOW('T', 200, int)
Expand All @@ -40,12 +41,21 @@
#define TUNSETOWNER _IOW('T', 204, int)
#define TUNSETLINK _IOW('T', 205, int)
#define TUNSETGROUP _IOW('T', 206, int)
#define TUNGETFEATURES _IOR('T', 207, unsigned int)
#define TUNSETOFFLOAD _IOW('T', 208, unsigned int)

/* TUNSETIFF ifr flags */
#define IFF_TUN 0x0001
#define IFF_TAP 0x0002
#define IFF_NO_PI 0x1000
#define IFF_ONE_QUEUE 0x2000
#define IFF_VNET_HDR 0x4000

/* Features for GSO (TUNSETOFFLOAD). */
#define TUN_F_CSUM 0x01 /* You can hand me unchecksummed packets. */
#define TUN_F_TSO4 0x02 /* I can handle TSO for IPv4 packets */
#define TUN_F_TSO6 0x04 /* I can handle TSO for IPv6 packets */
#define TUN_F_TSO_ECN 0x08 /* I can handle TSO with ECN bits. */

struct tun_pi {
unsigned short flags;
Expand Down
2 changes: 1 addition & 1 deletion trunk/include/net/fib_rules.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ struct fib_rules_ops

/* Called after modifications to the rules set, must flush
* the route cache if one exists. */
void (*flush_cache)(void);
void (*flush_cache)(struct fib_rules_ops *ops);

int nlgroup;
const struct nla_policy *policy;
Expand Down
4 changes: 4 additions & 0 deletions trunk/include/net/netns/ipv4.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ struct netns_ipv4 {
struct ctl_table_header *forw_hdr;
struct ctl_table_header *frags_hdr;
struct ctl_table_header *ipv4_hdr;
struct ctl_table_header *route_hdr;
#endif
struct ipv4_devconf *devconf_all;
struct ipv4_devconf *devconf_dflt;
Expand Down Expand Up @@ -45,5 +46,8 @@ struct netns_ipv4 {
int sysctl_icmp_ratelimit;
int sysctl_icmp_ratemask;
int sysctl_icmp_errors_use_inbound_ifaddr;

struct timer_list rt_secret_timer;
atomic_t rt_genid;
};
#endif
2 changes: 1 addition & 1 deletion trunk/include/net/route.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ struct in_device;
extern int ip_rt_init(void);
extern void ip_rt_redirect(__be32 old_gw, __be32 dst, __be32 new_gw,
__be32 src, struct net_device *dev);
extern void rt_cache_flush(int how);
extern void rt_cache_flush(struct net *net, int how);
extern int __ip_route_output_key(struct net *, struct rtable **, const struct flowi *flp);
extern int ip_route_output_key(struct net *, struct rtable **, struct flowi *flp);
extern int ip_route_output_flow(struct net *, struct rtable **rp, struct flowi *flp, struct sock *sk, int flags);
Expand Down
2 changes: 1 addition & 1 deletion trunk/net/core/fib_rules.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ static void rules_ops_put(struct fib_rules_ops *ops)
static void flush_route_cache(struct fib_rules_ops *ops)
{
if (ops->flush_cache)
ops->flush_cache();
ops->flush_cache(ops);
}

int fib_rules_register(struct fib_rules_ops *ops)
Expand Down
2 changes: 1 addition & 1 deletion trunk/net/decnet/dn_rules.c
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ static u32 dn_fib_rule_default_pref(struct fib_rules_ops *ops)
return 0;
}

static void dn_fib_rule_flush_cache(void)
static void dn_fib_rule_flush_cache(struct fib_rules_ops *ops)
{
dn_rt_cache_flush(-1);
}
Expand Down
2 changes: 1 addition & 1 deletion trunk/net/ipv4/arp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1197,7 +1197,7 @@ static int arp_netdev_event(struct notifier_block *this, unsigned long event, vo
switch (event) {
case NETDEV_CHANGEADDR:
neigh_changeaddr(&arp_tbl, dev);
rt_cache_flush(0);
rt_cache_flush(dev_net(dev), 0);
break;
default:
break;
Expand Down
8 changes: 5 additions & 3 deletions trunk/net/ipv4/devinet.c
Original file line number Diff line number Diff line change
Expand Up @@ -1348,7 +1348,7 @@ static int devinet_sysctl_forward(ctl_table *ctl, int write,
dev_disable_lro(idev->dev);
}
rtnl_unlock();
rt_cache_flush(0);
rt_cache_flush(net, 0);
}
}

Expand All @@ -1362,9 +1362,10 @@ int ipv4_doint_and_flush(ctl_table *ctl, int write,
int *valp = ctl->data;
int val = *valp;
int ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
struct net *net = ctl->extra2;

if (write && *valp != val)
rt_cache_flush(0);
rt_cache_flush(net, 0);

return ret;
}
Expand All @@ -1375,9 +1376,10 @@ int ipv4_doint_and_flush_strategy(ctl_table *table, int __user *name, int nlen,
{
int ret = devinet_conf_sysctl(table, name, nlen, oldval, oldlenp,
newval, newlen);
struct net *net = table->extra2;

if (ret == 1)
rt_cache_flush(0);
rt_cache_flush(net, 0);

return ret;
}
Expand Down
Loading

0 comments on commit 3a65fe2

Please sign in to comment.