1*1d41e2faSAkihiko Odaki /* SPDX-License-Identifier: GPL-2.0-or-later */ 2*1d41e2faSAkihiko Odaki #ifndef TUN_VNET_H 3*1d41e2faSAkihiko Odaki #define TUN_VNET_H 4*1d41e2faSAkihiko Odaki 5*1d41e2faSAkihiko Odaki /* High bits in flags field are unused. */ 6*1d41e2faSAkihiko Odaki #define TUN_VNET_LE 0x80000000 7*1d41e2faSAkihiko Odaki #define TUN_VNET_BE 0x40000000 8*1d41e2faSAkihiko Odaki 9*1d41e2faSAkihiko Odaki static inline bool tun_vnet_legacy_is_little_endian(unsigned int flags) 10*1d41e2faSAkihiko Odaki { 11*1d41e2faSAkihiko Odaki bool be = IS_ENABLED(CONFIG_TUN_VNET_CROSS_LE) && 12*1d41e2faSAkihiko Odaki (flags & TUN_VNET_BE); 13*1d41e2faSAkihiko Odaki 14*1d41e2faSAkihiko Odaki return !be && virtio_legacy_is_little_endian(); 15*1d41e2faSAkihiko Odaki } 16*1d41e2faSAkihiko Odaki 17*1d41e2faSAkihiko Odaki static inline long tun_get_vnet_be(unsigned int flags, int __user *argp) 18*1d41e2faSAkihiko Odaki { 19*1d41e2faSAkihiko Odaki int be = !!(flags & TUN_VNET_BE); 20*1d41e2faSAkihiko Odaki 21*1d41e2faSAkihiko Odaki if (!IS_ENABLED(CONFIG_TUN_VNET_CROSS_LE)) 22*1d41e2faSAkihiko Odaki return -EINVAL; 23*1d41e2faSAkihiko Odaki 24*1d41e2faSAkihiko Odaki if (put_user(be, argp)) 25*1d41e2faSAkihiko Odaki return -EFAULT; 26*1d41e2faSAkihiko Odaki 27*1d41e2faSAkihiko Odaki return 0; 28*1d41e2faSAkihiko Odaki } 29*1d41e2faSAkihiko Odaki 30*1d41e2faSAkihiko Odaki static inline long tun_set_vnet_be(unsigned int *flags, int __user *argp) 31*1d41e2faSAkihiko Odaki { 32*1d41e2faSAkihiko Odaki int be; 33*1d41e2faSAkihiko Odaki 34*1d41e2faSAkihiko Odaki if (!IS_ENABLED(CONFIG_TUN_VNET_CROSS_LE)) 35*1d41e2faSAkihiko Odaki return -EINVAL; 36*1d41e2faSAkihiko Odaki 37*1d41e2faSAkihiko Odaki if (get_user(be, argp)) 38*1d41e2faSAkihiko Odaki return -EFAULT; 39*1d41e2faSAkihiko Odaki 40*1d41e2faSAkihiko Odaki if (be) 41*1d41e2faSAkihiko Odaki *flags |= TUN_VNET_BE; 42*1d41e2faSAkihiko Odaki else 43*1d41e2faSAkihiko Odaki *flags &= ~TUN_VNET_BE; 44*1d41e2faSAkihiko Odaki 45*1d41e2faSAkihiko Odaki return 0; 46*1d41e2faSAkihiko Odaki } 47*1d41e2faSAkihiko Odaki 48*1d41e2faSAkihiko Odaki static inline bool tun_vnet_is_little_endian(unsigned int flags) 49*1d41e2faSAkihiko Odaki { 50*1d41e2faSAkihiko Odaki return flags & TUN_VNET_LE || tun_vnet_legacy_is_little_endian(flags); 51*1d41e2faSAkihiko Odaki } 52*1d41e2faSAkihiko Odaki 53*1d41e2faSAkihiko Odaki static inline u16 tun_vnet16_to_cpu(unsigned int flags, __virtio16 val) 54*1d41e2faSAkihiko Odaki { 55*1d41e2faSAkihiko Odaki return __virtio16_to_cpu(tun_vnet_is_little_endian(flags), val); 56*1d41e2faSAkihiko Odaki } 57*1d41e2faSAkihiko Odaki 58*1d41e2faSAkihiko Odaki static inline __virtio16 cpu_to_tun_vnet16(unsigned int flags, u16 val) 59*1d41e2faSAkihiko Odaki { 60*1d41e2faSAkihiko Odaki return __cpu_to_virtio16(tun_vnet_is_little_endian(flags), val); 61*1d41e2faSAkihiko Odaki } 62*1d41e2faSAkihiko Odaki 63*1d41e2faSAkihiko Odaki static inline long tun_vnet_ioctl(int *vnet_hdr_sz, unsigned int *flags, 64*1d41e2faSAkihiko Odaki unsigned int cmd, int __user *sp) 65*1d41e2faSAkihiko Odaki { 66*1d41e2faSAkihiko Odaki int s; 67*1d41e2faSAkihiko Odaki 68*1d41e2faSAkihiko Odaki switch (cmd) { 69*1d41e2faSAkihiko Odaki case TUNGETVNETHDRSZ: 70*1d41e2faSAkihiko Odaki s = *vnet_hdr_sz; 71*1d41e2faSAkihiko Odaki if (put_user(s, sp)) 72*1d41e2faSAkihiko Odaki return -EFAULT; 73*1d41e2faSAkihiko Odaki return 0; 74*1d41e2faSAkihiko Odaki 75*1d41e2faSAkihiko Odaki case TUNSETVNETHDRSZ: 76*1d41e2faSAkihiko Odaki if (get_user(s, sp)) 77*1d41e2faSAkihiko Odaki return -EFAULT; 78*1d41e2faSAkihiko Odaki if (s < (int)sizeof(struct virtio_net_hdr)) 79*1d41e2faSAkihiko Odaki return -EINVAL; 80*1d41e2faSAkihiko Odaki 81*1d41e2faSAkihiko Odaki *vnet_hdr_sz = s; 82*1d41e2faSAkihiko Odaki return 0; 83*1d41e2faSAkihiko Odaki 84*1d41e2faSAkihiko Odaki case TUNGETVNETLE: 85*1d41e2faSAkihiko Odaki s = !!(*flags & TUN_VNET_LE); 86*1d41e2faSAkihiko Odaki if (put_user(s, sp)) 87*1d41e2faSAkihiko Odaki return -EFAULT; 88*1d41e2faSAkihiko Odaki return 0; 89*1d41e2faSAkihiko Odaki 90*1d41e2faSAkihiko Odaki case TUNSETVNETLE: 91*1d41e2faSAkihiko Odaki if (get_user(s, sp)) 92*1d41e2faSAkihiko Odaki return -EFAULT; 93*1d41e2faSAkihiko Odaki if (s) 94*1d41e2faSAkihiko Odaki *flags |= TUN_VNET_LE; 95*1d41e2faSAkihiko Odaki else 96*1d41e2faSAkihiko Odaki *flags &= ~TUN_VNET_LE; 97*1d41e2faSAkihiko Odaki return 0; 98*1d41e2faSAkihiko Odaki 99*1d41e2faSAkihiko Odaki case TUNGETVNETBE: 100*1d41e2faSAkihiko Odaki return tun_get_vnet_be(*flags, sp); 101*1d41e2faSAkihiko Odaki 102*1d41e2faSAkihiko Odaki case TUNSETVNETBE: 103*1d41e2faSAkihiko Odaki return tun_set_vnet_be(flags, sp); 104*1d41e2faSAkihiko Odaki 105*1d41e2faSAkihiko Odaki default: 106*1d41e2faSAkihiko Odaki return -EINVAL; 107*1d41e2faSAkihiko Odaki } 108*1d41e2faSAkihiko Odaki } 109*1d41e2faSAkihiko Odaki 110*1d41e2faSAkihiko Odaki static inline int tun_vnet_hdr_get(int sz, unsigned int flags, 111*1d41e2faSAkihiko Odaki struct iov_iter *from, 112*1d41e2faSAkihiko Odaki struct virtio_net_hdr *hdr) 113*1d41e2faSAkihiko Odaki { 114*1d41e2faSAkihiko Odaki u16 hdr_len; 115*1d41e2faSAkihiko Odaki 116*1d41e2faSAkihiko Odaki if (iov_iter_count(from) < sz) 117*1d41e2faSAkihiko Odaki return -EINVAL; 118*1d41e2faSAkihiko Odaki 119*1d41e2faSAkihiko Odaki if (!copy_from_iter_full(hdr, sizeof(*hdr), from)) 120*1d41e2faSAkihiko Odaki return -EFAULT; 121*1d41e2faSAkihiko Odaki 122*1d41e2faSAkihiko Odaki hdr_len = tun_vnet16_to_cpu(flags, hdr->hdr_len); 123*1d41e2faSAkihiko Odaki 124*1d41e2faSAkihiko Odaki if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { 125*1d41e2faSAkihiko Odaki hdr_len = max(tun_vnet16_to_cpu(flags, hdr->csum_start) + tun_vnet16_to_cpu(flags, hdr->csum_offset) + 2, hdr_len); 126*1d41e2faSAkihiko Odaki hdr->hdr_len = cpu_to_tun_vnet16(flags, hdr_len); 127*1d41e2faSAkihiko Odaki } 128*1d41e2faSAkihiko Odaki 129*1d41e2faSAkihiko Odaki if (hdr_len > iov_iter_count(from)) 130*1d41e2faSAkihiko Odaki return -EINVAL; 131*1d41e2faSAkihiko Odaki 132*1d41e2faSAkihiko Odaki iov_iter_advance(from, sz - sizeof(*hdr)); 133*1d41e2faSAkihiko Odaki 134*1d41e2faSAkihiko Odaki return hdr_len; 135*1d41e2faSAkihiko Odaki } 136*1d41e2faSAkihiko Odaki 137*1d41e2faSAkihiko Odaki static inline int tun_vnet_hdr_put(int sz, struct iov_iter *iter, 138*1d41e2faSAkihiko Odaki const struct virtio_net_hdr *hdr) 139*1d41e2faSAkihiko Odaki { 140*1d41e2faSAkihiko Odaki if (unlikely(iov_iter_count(iter) < sz)) 141*1d41e2faSAkihiko Odaki return -EINVAL; 142*1d41e2faSAkihiko Odaki 143*1d41e2faSAkihiko Odaki if (unlikely(copy_to_iter(hdr, sizeof(*hdr), iter) != sizeof(*hdr))) 144*1d41e2faSAkihiko Odaki return -EFAULT; 145*1d41e2faSAkihiko Odaki 146*1d41e2faSAkihiko Odaki iov_iter_advance(iter, sz - sizeof(*hdr)); 147*1d41e2faSAkihiko Odaki 148*1d41e2faSAkihiko Odaki return 0; 149*1d41e2faSAkihiko Odaki } 150*1d41e2faSAkihiko Odaki 151*1d41e2faSAkihiko Odaki static inline int tun_vnet_hdr_to_skb(unsigned int flags, struct sk_buff *skb, 152*1d41e2faSAkihiko Odaki const struct virtio_net_hdr *hdr) 153*1d41e2faSAkihiko Odaki { 154*1d41e2faSAkihiko Odaki return virtio_net_hdr_to_skb(skb, hdr, tun_vnet_is_little_endian(flags)); 155*1d41e2faSAkihiko Odaki } 156*1d41e2faSAkihiko Odaki 157*1d41e2faSAkihiko Odaki static inline int tun_vnet_hdr_from_skb(unsigned int flags, 158*1d41e2faSAkihiko Odaki const struct net_device *dev, 159*1d41e2faSAkihiko Odaki const struct sk_buff *skb, 160*1d41e2faSAkihiko Odaki struct virtio_net_hdr *hdr) 161*1d41e2faSAkihiko Odaki { 162*1d41e2faSAkihiko Odaki int vlan_hlen = skb_vlan_tag_present(skb) ? VLAN_HLEN : 0; 163*1d41e2faSAkihiko Odaki 164*1d41e2faSAkihiko Odaki if (virtio_net_hdr_from_skb(skb, hdr, 165*1d41e2faSAkihiko Odaki tun_vnet_is_little_endian(flags), true, 166*1d41e2faSAkihiko Odaki vlan_hlen)) { 167*1d41e2faSAkihiko Odaki struct skb_shared_info *sinfo = skb_shinfo(skb); 168*1d41e2faSAkihiko Odaki 169*1d41e2faSAkihiko Odaki if (net_ratelimit()) { 170*1d41e2faSAkihiko Odaki netdev_err(dev, "unexpected GSO type: 0x%x, gso_size %d, hdr_len %d\n", 171*1d41e2faSAkihiko Odaki sinfo->gso_type, tun_vnet16_to_cpu(flags, hdr->gso_size), 172*1d41e2faSAkihiko Odaki tun_vnet16_to_cpu(flags, hdr->hdr_len)); 173*1d41e2faSAkihiko Odaki print_hex_dump(KERN_ERR, "tun: ", 174*1d41e2faSAkihiko Odaki DUMP_PREFIX_NONE, 175*1d41e2faSAkihiko Odaki 16, 1, skb->head, 176*1d41e2faSAkihiko Odaki min(tun_vnet16_to_cpu(flags, hdr->hdr_len), 64), true); 177*1d41e2faSAkihiko Odaki } 178*1d41e2faSAkihiko Odaki WARN_ON_ONCE(1); 179*1d41e2faSAkihiko Odaki return -EINVAL; 180*1d41e2faSAkihiko Odaki } 181*1d41e2faSAkihiko Odaki 182*1d41e2faSAkihiko Odaki return 0; 183*1d41e2faSAkihiko Odaki } 184*1d41e2faSAkihiko Odaki 185*1d41e2faSAkihiko Odaki #endif /* TUN_VNET_H */ 186