Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ip6_tun: Add infrastructure for doing encapsulation

Add encap_hlen and ip_tunnel_encap structure to ip6_tnl. Add functions
for getting encap hlen, setting up encap on a tunnel, performing
encapsulation operation.

Signed-off-by: Tom Herbert <tom@herbertland.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Tom Herbert and committed by
David S. Miller
058214a4 5f914b68

+145 -14
+59 -1
include/net/ip6_tunnel.h
··· 52 52 __u32 o_seqno; /* The last output seqno */ 53 53 int hlen; /* tun_hlen + encap_hlen */ 54 54 int tun_hlen; /* Precalculated header length */ 55 + int encap_hlen; /* Encap header length (FOU,GUE) */ 56 + struct ip_tunnel_encap encap; 55 57 int mlink; 56 - 57 58 }; 59 + 60 + struct ip6_tnl_encap_ops { 61 + size_t (*encap_hlen)(struct ip_tunnel_encap *e); 62 + int (*build_header)(struct sk_buff *skb, struct ip_tunnel_encap *e, 63 + u8 *protocol, struct flowi6 *fl6); 64 + }; 65 + 66 + extern const struct ip6_tnl_encap_ops __rcu * 67 + ip6tun_encaps[MAX_IPTUN_ENCAP_OPS]; 68 + 69 + int ip6_tnl_encap_add_ops(const struct ip6_tnl_encap_ops *ops, 70 + unsigned int num); 71 + int ip6_tnl_encap_del_ops(const struct ip6_tnl_encap_ops *ops, 72 + unsigned int num); 73 + int ip6_tnl_encap_setup(struct ip6_tnl *t, 74 + struct ip_tunnel_encap *ipencap); 75 + 76 + static inline int ip6_encap_hlen(struct ip_tunnel_encap *e) 77 + { 78 + const struct ip6_tnl_encap_ops *ops; 79 + int hlen = -EINVAL; 80 + 81 + if (e->type == TUNNEL_ENCAP_NONE) 82 + return 0; 83 + 84 + if (e->type >= MAX_IPTUN_ENCAP_OPS) 85 + return -EINVAL; 86 + 87 + rcu_read_lock(); 88 + ops = rcu_dereference(ip6tun_encaps[e->type]); 89 + if (likely(ops && ops->encap_hlen)) 90 + hlen = ops->encap_hlen(e); 91 + rcu_read_unlock(); 92 + 93 + return hlen; 94 + } 95 + 96 + static inline int ip6_tnl_encap(struct sk_buff *skb, struct ip6_tnl *t, 97 + u8 *protocol, struct flowi6 *fl6) 98 + { 99 + const struct ip6_tnl_encap_ops *ops; 100 + int ret = -EINVAL; 101 + 102 + if (t->encap.type == TUNNEL_ENCAP_NONE) 103 + return 0; 104 + 105 + if (t->encap.type >= MAX_IPTUN_ENCAP_OPS) 106 + return -EINVAL; 107 + 108 + rcu_read_lock(); 109 + ops = rcu_dereference(ip6tun_encaps[t->encap.type]); 110 + if (likely(ops && ops->build_header)) 111 + ret = ops->build_header(skb, &t->encap, protocol, fl6); 112 + rcu_read_unlock(); 113 + 114 + return ret; 115 + } 58 116 59 117 /* Tunnel encapsulation limit destination sub-option */ 60 118
+5
net/ipv4/ip_tunnel_core.c
··· 37 37 #include <net/icmp.h> 38 38 #include <net/protocol.h> 39 39 #include <net/ip_tunnels.h> 40 + #include <net/ip6_tunnel.h> 40 41 #include <net/arp.h> 41 42 #include <net/checksum.h> 42 43 #include <net/dsfield.h> ··· 51 50 const struct ip_tunnel_encap_ops __rcu * 52 51 iptun_encaps[MAX_IPTUN_ENCAP_OPS] __read_mostly; 53 52 EXPORT_SYMBOL(iptun_encaps); 53 + 54 + const struct ip6_tnl_encap_ops __rcu * 55 + ip6tun_encaps[MAX_IPTUN_ENCAP_OPS] __read_mostly; 56 + EXPORT_SYMBOL(ip6tun_encaps); 54 57 55 58 void iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb, 56 59 __be32 src, __be32 dst, __u8 proto,
+81 -13
net/ipv6/ip6_tunnel.c
··· 1010 1010 struct dst_entry *dst = NULL, *ndst = NULL; 1011 1011 struct net_device *tdev; 1012 1012 int mtu; 1013 - unsigned int max_headroom = sizeof(struct ipv6hdr); 1013 + unsigned int psh_hlen = sizeof(struct ipv6hdr) + t->encap_hlen; 1014 + unsigned int max_headroom = psh_hlen; 1014 1015 int err = -1; 1015 1016 1016 1017 /* NBMA tunnel */ ··· 1064 1063 t->parms.name); 1065 1064 goto tx_err_dst_release; 1066 1065 } 1067 - mtu = dst_mtu(dst) - sizeof(*ipv6h); 1066 + mtu = dst_mtu(dst) - psh_hlen; 1068 1067 if (encap_limit >= 0) { 1069 1068 max_headroom += 8; 1070 1069 mtu -= 8; ··· 1125 1124 skb->encapsulation = 1; 1126 1125 } 1127 1126 1127 + /* Calculate max headroom for all the headers and adjust 1128 + * needed_headroom if necessary. 1129 + */ 1128 1130 max_headroom = LL_RESERVED_SPACE(dst->dev) + sizeof(struct ipv6hdr) 1129 - + dst->header_len; 1131 + + dst->header_len + t->hlen; 1130 1132 if (max_headroom > dev->needed_headroom) 1131 1133 dev->needed_headroom = max_headroom; 1134 + 1135 + err = ip6_tnl_encap(skb, t, &proto, fl6); 1136 + if (err) 1137 + return err; 1132 1138 1133 1139 skb_push(skb, sizeof(struct ipv6hdr)); 1134 1140 skb_reset_network_header(skb); ··· 1288 1280 struct net_device *dev = t->dev; 1289 1281 struct __ip6_tnl_parm *p = &t->parms; 1290 1282 struct flowi6 *fl6 = &t->fl.u.ip6; 1283 + int t_hlen; 1291 1284 1292 1285 memcpy(dev->dev_addr, &p->laddr, sizeof(struct in6_addr)); 1293 1286 memcpy(dev->broadcast, &p->raddr, sizeof(struct in6_addr)); ··· 1312 1303 else 1313 1304 dev->flags &= ~IFF_POINTOPOINT; 1314 1305 1306 + t->tun_hlen = 0; 1307 + t->hlen = t->encap_hlen + t->tun_hlen; 1308 + t_hlen = t->hlen + sizeof(struct ipv6hdr); 1309 + 1315 1310 if (p->flags & IP6_TNL_F_CAP_XMIT) { 1316 1311 int strict = (ipv6_addr_type(&p->raddr) & 1317 1312 (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL)); ··· 1329 1316 1330 1317 if (rt->dst.dev) { 1331 1318 dev->hard_header_len = rt->dst.dev->hard_header_len + 1332 - sizeof(struct ipv6hdr); 1319 + t_hlen; 1333 1320 1334 - dev->mtu = rt->dst.dev->mtu - sizeof(struct ipv6hdr); 1321 + dev->mtu = rt->dst.dev->mtu - t_hlen; 1335 1322 if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) 1336 1323 dev->mtu -= 8; 1337 1324 ··· 1577 1564 } 1578 1565 EXPORT_SYMBOL(ip6_tnl_get_iflink); 1579 1566 1567 + int ip6_tnl_encap_add_ops(const struct ip6_tnl_encap_ops *ops, 1568 + unsigned int num) 1569 + { 1570 + if (num >= MAX_IPTUN_ENCAP_OPS) 1571 + return -ERANGE; 1572 + 1573 + return !cmpxchg((const struct ip6_tnl_encap_ops **) 1574 + &ip6tun_encaps[num], 1575 + NULL, ops) ? 0 : -1; 1576 + } 1577 + EXPORT_SYMBOL(ip6_tnl_encap_add_ops); 1578 + 1579 + int ip6_tnl_encap_del_ops(const struct ip6_tnl_encap_ops *ops, 1580 + unsigned int num) 1581 + { 1582 + int ret; 1583 + 1584 + if (num >= MAX_IPTUN_ENCAP_OPS) 1585 + return -ERANGE; 1586 + 1587 + ret = (cmpxchg((const struct ip6_tnl_encap_ops **) 1588 + &ip6tun_encaps[num], 1589 + ops, NULL) == ops) ? 0 : -1; 1590 + 1591 + synchronize_net(); 1592 + 1593 + return ret; 1594 + } 1595 + EXPORT_SYMBOL(ip6_tnl_encap_del_ops); 1596 + 1597 + int ip6_tnl_encap_setup(struct ip6_tnl *t, 1598 + struct ip_tunnel_encap *ipencap) 1599 + { 1600 + int hlen; 1601 + 1602 + memset(&t->encap, 0, sizeof(t->encap)); 1603 + 1604 + hlen = ip6_encap_hlen(ipencap); 1605 + if (hlen < 0) 1606 + return hlen; 1607 + 1608 + t->encap.type = ipencap->type; 1609 + t->encap.sport = ipencap->sport; 1610 + t->encap.dport = ipencap->dport; 1611 + t->encap.flags = ipencap->flags; 1612 + 1613 + t->encap_hlen = hlen; 1614 + t->hlen = t->encap_hlen + t->tun_hlen; 1615 + 1616 + return 0; 1617 + } 1618 + EXPORT_SYMBOL_GPL(ip6_tnl_encap_setup); 1619 + 1580 1620 static const struct net_device_ops ip6_tnl_netdev_ops = { 1581 1621 .ndo_init = ip6_tnl_dev_init, 1582 1622 .ndo_uninit = ip6_tnl_dev_uninit, ··· 1651 1585 1652 1586 static void ip6_tnl_dev_setup(struct net_device *dev) 1653 1587 { 1654 - struct ip6_tnl *t; 1655 - 1656 1588 dev->netdev_ops = &ip6_tnl_netdev_ops; 1657 1589 dev->destructor = ip6_dev_free; 1658 1590 1659 1591 dev->type = ARPHRD_TUNNEL6; 1660 - dev->hard_header_len = LL_MAX_HEADER + sizeof(struct ipv6hdr); 1661 - dev->mtu = ETH_DATA_LEN - sizeof(struct ipv6hdr); 1662 - t = netdev_priv(dev); 1663 - if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) 1664 - dev->mtu -= 8; 1665 1592 dev->flags |= IFF_NOARP; 1666 1593 dev->addr_len = sizeof(struct in6_addr); 1594 + dev->features |= NETIF_F_LLTX; 1667 1595 netif_keep_dst(dev); 1668 1596 /* This perm addr will be used as interface identifier by IPv6 */ 1669 1597 dev->addr_assign_type = NET_ADDR_RANDOM; ··· 1675 1615 { 1676 1616 struct ip6_tnl *t = netdev_priv(dev); 1677 1617 int ret; 1618 + int t_hlen; 1678 1619 1679 1620 t->dev = dev; 1680 1621 t->net = dev_net(dev); ··· 1691 1630 if (ret) 1692 1631 goto destroy_dst; 1693 1632 1694 - t->hlen = 0; 1695 1633 t->tun_hlen = 0; 1634 + t->hlen = t->encap_hlen + t->tun_hlen; 1635 + t_hlen = t->hlen + sizeof(struct ipv6hdr); 1636 + 1637 + dev->type = ARPHRD_TUNNEL6; 1638 + dev->hard_header_len = LL_MAX_HEADER + t_hlen; 1639 + dev->mtu = ETH_DATA_LEN - t_hlen; 1640 + if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) 1641 + dev->mtu -= 8; 1696 1642 1697 1643 return 0; 1698 1644