diff options
author | Tom Herbert <therbert@google.com> | 2014-09-17 15:25:59 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-09-19 17:15:32 -0400 |
commit | 14909664e4e192f4c6f6fcdccd9919af7cf783ab (patch) | |
tree | e18f5d4fd6133197cfe3c3b7e7da49ed7b5cd657 /net/ipv6 | |
parent | 56328486539ddd07cbaafec7a542a2c8a3043623 (diff) |
sit: Setup and TX path for sit/UDP foo-over-udp encapsulation
Added netlink handling of IP tunnel encapulation paramters, properly
adjust MTU for encapsulation. Added ip_tunnel_encap call to
ipip6_tunnel_xmit to actually perform FOU encapsulation.
Signed-off-by: Tom Herbert <therbert@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/sit.c | 107 |
1 files changed, 97 insertions, 10 deletions
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 86e3fa81f85e..db75809ab843 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c | |||
@@ -822,6 +822,8 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, | |||
822 | int addr_type; | 822 | int addr_type; |
823 | u8 ttl; | 823 | u8 ttl; |
824 | int err; | 824 | int err; |
825 | u8 protocol = IPPROTO_IPV6; | ||
826 | int t_hlen = tunnel->hlen + sizeof(struct iphdr); | ||
825 | 827 | ||
826 | if (skb->protocol != htons(ETH_P_IPV6)) | 828 | if (skb->protocol != htons(ETH_P_IPV6)) |
827 | goto tx_error; | 829 | goto tx_error; |
@@ -911,8 +913,14 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, | |||
911 | goto tx_error; | 913 | goto tx_error; |
912 | } | 914 | } |
913 | 915 | ||
916 | skb = iptunnel_handle_offloads(skb, false, SKB_GSO_SIT); | ||
917 | if (IS_ERR(skb)) { | ||
918 | ip_rt_put(rt); | ||
919 | goto out; | ||
920 | } | ||
921 | |||
914 | if (df) { | 922 | if (df) { |
915 | mtu = dst_mtu(&rt->dst) - sizeof(struct iphdr); | 923 | mtu = dst_mtu(&rt->dst) - t_hlen; |
916 | 924 | ||
917 | if (mtu < 68) { | 925 | if (mtu < 68) { |
918 | dev->stats.collisions++; | 926 | dev->stats.collisions++; |
@@ -947,7 +955,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, | |||
947 | /* | 955 | /* |
948 | * Okay, now see if we can stuff it in the buffer as-is. | 956 | * Okay, now see if we can stuff it in the buffer as-is. |
949 | */ | 957 | */ |
950 | max_headroom = LL_RESERVED_SPACE(tdev)+sizeof(struct iphdr); | 958 | max_headroom = LL_RESERVED_SPACE(tdev) + t_hlen; |
951 | 959 | ||
952 | if (skb_headroom(skb) < max_headroom || skb_shared(skb) || | 960 | if (skb_headroom(skb) < max_headroom || skb_shared(skb) || |
953 | (skb_cloned(skb) && !skb_clone_writable(skb, 0))) { | 961 | (skb_cloned(skb) && !skb_clone_writable(skb, 0))) { |
@@ -969,14 +977,13 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, | |||
969 | ttl = iph6->hop_limit; | 977 | ttl = iph6->hop_limit; |
970 | tos = INET_ECN_encapsulate(tos, ipv6_get_dsfield(iph6)); | 978 | tos = INET_ECN_encapsulate(tos, ipv6_get_dsfield(iph6)); |
971 | 979 | ||
972 | skb = iptunnel_handle_offloads(skb, false, SKB_GSO_SIT); | 980 | if (ip_tunnel_encap(skb, tunnel, &protocol, &fl4) < 0) { |
973 | if (IS_ERR(skb)) { | ||
974 | ip_rt_put(rt); | 981 | ip_rt_put(rt); |
975 | goto out; | 982 | goto tx_error; |
976 | } | 983 | } |
977 | 984 | ||
978 | err = iptunnel_xmit(skb->sk, rt, skb, fl4.saddr, fl4.daddr, | 985 | err = iptunnel_xmit(skb->sk, rt, skb, fl4.saddr, fl4.daddr, |
979 | IPPROTO_IPV6, tos, ttl, df, | 986 | protocol, tos, ttl, df, |
980 | !net_eq(tunnel->net, dev_net(dev))); | 987 | !net_eq(tunnel->net, dev_net(dev))); |
981 | iptunnel_xmit_stats(err, &dev->stats, dev->tstats); | 988 | iptunnel_xmit_stats(err, &dev->stats, dev->tstats); |
982 | return NETDEV_TX_OK; | 989 | return NETDEV_TX_OK; |
@@ -1059,8 +1066,10 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev) | |||
1059 | tdev = __dev_get_by_index(tunnel->net, tunnel->parms.link); | 1066 | tdev = __dev_get_by_index(tunnel->net, tunnel->parms.link); |
1060 | 1067 | ||
1061 | if (tdev) { | 1068 | if (tdev) { |
1069 | int t_hlen = tunnel->hlen + sizeof(struct iphdr); | ||
1070 | |||
1062 | dev->hard_header_len = tdev->hard_header_len + sizeof(struct iphdr); | 1071 | dev->hard_header_len = tdev->hard_header_len + sizeof(struct iphdr); |
1063 | dev->mtu = tdev->mtu - sizeof(struct iphdr); | 1072 | dev->mtu = tdev->mtu - t_hlen; |
1064 | if (dev->mtu < IPV6_MIN_MTU) | 1073 | if (dev->mtu < IPV6_MIN_MTU) |
1065 | dev->mtu = IPV6_MIN_MTU; | 1074 | dev->mtu = IPV6_MIN_MTU; |
1066 | } | 1075 | } |
@@ -1307,7 +1316,10 @@ done: | |||
1307 | 1316 | ||
1308 | static int ipip6_tunnel_change_mtu(struct net_device *dev, int new_mtu) | 1317 | static int ipip6_tunnel_change_mtu(struct net_device *dev, int new_mtu) |
1309 | { | 1318 | { |
1310 | if (new_mtu < IPV6_MIN_MTU || new_mtu > 0xFFF8 - sizeof(struct iphdr)) | 1319 | struct ip_tunnel *tunnel = netdev_priv(dev); |
1320 | int t_hlen = tunnel->hlen + sizeof(struct iphdr); | ||
1321 | |||
1322 | if (new_mtu < IPV6_MIN_MTU || new_mtu > 0xFFF8 - t_hlen) | ||
1311 | return -EINVAL; | 1323 | return -EINVAL; |
1312 | dev->mtu = new_mtu; | 1324 | dev->mtu = new_mtu; |
1313 | return 0; | 1325 | return 0; |
@@ -1338,12 +1350,15 @@ static void ipip6_dev_free(struct net_device *dev) | |||
1338 | 1350 | ||
1339 | static void ipip6_tunnel_setup(struct net_device *dev) | 1351 | static void ipip6_tunnel_setup(struct net_device *dev) |
1340 | { | 1352 | { |
1353 | struct ip_tunnel *tunnel = netdev_priv(dev); | ||
1354 | int t_hlen = tunnel->hlen + sizeof(struct iphdr); | ||
1355 | |||
1341 | dev->netdev_ops = &ipip6_netdev_ops; | 1356 | dev->netdev_ops = &ipip6_netdev_ops; |
1342 | dev->destructor = ipip6_dev_free; | 1357 | dev->destructor = ipip6_dev_free; |
1343 | 1358 | ||
1344 | dev->type = ARPHRD_SIT; | 1359 | dev->type = ARPHRD_SIT; |
1345 | dev->hard_header_len = LL_MAX_HEADER + sizeof(struct iphdr); | 1360 | dev->hard_header_len = LL_MAX_HEADER + t_hlen; |
1346 | dev->mtu = ETH_DATA_LEN - sizeof(struct iphdr); | 1361 | dev->mtu = ETH_DATA_LEN - t_hlen; |
1347 | dev->flags = IFF_NOARP; | 1362 | dev->flags = IFF_NOARP; |
1348 | dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; | 1363 | dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; |
1349 | dev->iflink = 0; | 1364 | dev->iflink = 0; |
@@ -1466,6 +1481,40 @@ static void ipip6_netlink_parms(struct nlattr *data[], | |||
1466 | 1481 | ||
1467 | } | 1482 | } |
1468 | 1483 | ||
1484 | /* This function returns true when ENCAP attributes are present in the nl msg */ | ||
1485 | static bool ipip6_netlink_encap_parms(struct nlattr *data[], | ||
1486 | struct ip_tunnel_encap *ipencap) | ||
1487 | { | ||
1488 | bool ret = false; | ||
1489 | |||
1490 | memset(ipencap, 0, sizeof(*ipencap)); | ||
1491 | |||
1492 | if (!data) | ||
1493 | return ret; | ||
1494 | |||
1495 | if (data[IFLA_IPTUN_ENCAP_TYPE]) { | ||
1496 | ret = true; | ||
1497 | ipencap->type = nla_get_u16(data[IFLA_IPTUN_ENCAP_TYPE]); | ||
1498 | } | ||
1499 | |||
1500 | if (data[IFLA_IPTUN_ENCAP_FLAGS]) { | ||
1501 | ret = true; | ||
1502 | ipencap->flags = nla_get_u16(data[IFLA_IPTUN_ENCAP_FLAGS]); | ||
1503 | } | ||
1504 | |||
1505 | if (data[IFLA_IPTUN_ENCAP_SPORT]) { | ||
1506 | ret = true; | ||
1507 | ipencap->sport = nla_get_u16(data[IFLA_IPTUN_ENCAP_SPORT]); | ||
1508 | } | ||
1509 | |||
1510 | if (data[IFLA_IPTUN_ENCAP_DPORT]) { | ||
1511 | ret = true; | ||
1512 | ipencap->dport = nla_get_u16(data[IFLA_IPTUN_ENCAP_DPORT]); | ||
1513 | } | ||
1514 | |||
1515 | return ret; | ||
1516 | } | ||
1517 | |||
1469 | #ifdef CONFIG_IPV6_SIT_6RD | 1518 | #ifdef CONFIG_IPV6_SIT_6RD |
1470 | /* This function returns true when 6RD attributes are present in the nl msg */ | 1519 | /* This function returns true when 6RD attributes are present in the nl msg */ |
1471 | static bool ipip6_netlink_6rd_parms(struct nlattr *data[], | 1520 | static bool ipip6_netlink_6rd_parms(struct nlattr *data[], |
@@ -1509,12 +1558,20 @@ static int ipip6_newlink(struct net *src_net, struct net_device *dev, | |||
1509 | { | 1558 | { |
1510 | struct net *net = dev_net(dev); | 1559 | struct net *net = dev_net(dev); |
1511 | struct ip_tunnel *nt; | 1560 | struct ip_tunnel *nt; |
1561 | struct ip_tunnel_encap ipencap; | ||
1512 | #ifdef CONFIG_IPV6_SIT_6RD | 1562 | #ifdef CONFIG_IPV6_SIT_6RD |
1513 | struct ip_tunnel_6rd ip6rd; | 1563 | struct ip_tunnel_6rd ip6rd; |
1514 | #endif | 1564 | #endif |
1515 | int err; | 1565 | int err; |
1516 | 1566 | ||
1517 | nt = netdev_priv(dev); | 1567 | nt = netdev_priv(dev); |
1568 | |||
1569 | if (ipip6_netlink_encap_parms(data, &ipencap)) { | ||
1570 | err = ip_tunnel_encap_setup(nt, &ipencap); | ||
1571 | if (err < 0) | ||
1572 | return err; | ||
1573 | } | ||
1574 | |||
1518 | ipip6_netlink_parms(data, &nt->parms); | 1575 | ipip6_netlink_parms(data, &nt->parms); |
1519 | 1576 | ||
1520 | if (ipip6_tunnel_locate(net, &nt->parms, 0)) | 1577 | if (ipip6_tunnel_locate(net, &nt->parms, 0)) |
@@ -1537,15 +1594,23 @@ static int ipip6_changelink(struct net_device *dev, struct nlattr *tb[], | |||
1537 | { | 1594 | { |
1538 | struct ip_tunnel *t = netdev_priv(dev); | 1595 | struct ip_tunnel *t = netdev_priv(dev); |
1539 | struct ip_tunnel_parm p; | 1596 | struct ip_tunnel_parm p; |
1597 | struct ip_tunnel_encap ipencap; | ||
1540 | struct net *net = t->net; | 1598 | struct net *net = t->net; |
1541 | struct sit_net *sitn = net_generic(net, sit_net_id); | 1599 | struct sit_net *sitn = net_generic(net, sit_net_id); |
1542 | #ifdef CONFIG_IPV6_SIT_6RD | 1600 | #ifdef CONFIG_IPV6_SIT_6RD |
1543 | struct ip_tunnel_6rd ip6rd; | 1601 | struct ip_tunnel_6rd ip6rd; |
1544 | #endif | 1602 | #endif |
1603 | int err; | ||
1545 | 1604 | ||
1546 | if (dev == sitn->fb_tunnel_dev) | 1605 | if (dev == sitn->fb_tunnel_dev) |
1547 | return -EINVAL; | 1606 | return -EINVAL; |
1548 | 1607 | ||
1608 | if (ipip6_netlink_encap_parms(data, &ipencap)) { | ||
1609 | err = ip_tunnel_encap_setup(t, &ipencap); | ||
1610 | if (err < 0) | ||
1611 | return err; | ||
1612 | } | ||
1613 | |||
1549 | ipip6_netlink_parms(data, &p); | 1614 | ipip6_netlink_parms(data, &p); |
1550 | 1615 | ||
1551 | if (((dev->flags & IFF_POINTOPOINT) && !p.iph.daddr) || | 1616 | if (((dev->flags & IFF_POINTOPOINT) && !p.iph.daddr) || |
@@ -1599,6 +1664,14 @@ static size_t ipip6_get_size(const struct net_device *dev) | |||
1599 | /* IFLA_IPTUN_6RD_RELAY_PREFIXLEN */ | 1664 | /* IFLA_IPTUN_6RD_RELAY_PREFIXLEN */ |
1600 | nla_total_size(2) + | 1665 | nla_total_size(2) + |
1601 | #endif | 1666 | #endif |
1667 | /* IFLA_IPTUN_ENCAP_TYPE */ | ||
1668 | nla_total_size(2) + | ||
1669 | /* IFLA_IPTUN_ENCAP_FLAGS */ | ||
1670 | nla_total_size(2) + | ||
1671 | /* IFLA_IPTUN_ENCAP_SPORT */ | ||
1672 | nla_total_size(2) + | ||
1673 | /* IFLA_IPTUN_ENCAP_DPORT */ | ||
1674 | nla_total_size(2) + | ||
1602 | 0; | 1675 | 0; |
1603 | } | 1676 | } |
1604 | 1677 | ||
@@ -1630,6 +1703,16 @@ static int ipip6_fill_info(struct sk_buff *skb, const struct net_device *dev) | |||
1630 | goto nla_put_failure; | 1703 | goto nla_put_failure; |
1631 | #endif | 1704 | #endif |
1632 | 1705 | ||
1706 | if (nla_put_u16(skb, IFLA_IPTUN_ENCAP_TYPE, | ||
1707 | tunnel->encap.type) || | ||
1708 | nla_put_u16(skb, IFLA_IPTUN_ENCAP_SPORT, | ||
1709 | tunnel->encap.sport) || | ||
1710 | nla_put_u16(skb, IFLA_IPTUN_ENCAP_DPORT, | ||
1711 | tunnel->encap.dport) || | ||
1712 | nla_put_u16(skb, IFLA_IPTUN_ENCAP_FLAGS, | ||
1713 | tunnel->encap.dport)) | ||
1714 | goto nla_put_failure; | ||
1715 | |||
1633 | return 0; | 1716 | return 0; |
1634 | 1717 | ||
1635 | nla_put_failure: | 1718 | nla_put_failure: |
@@ -1651,6 +1734,10 @@ static const struct nla_policy ipip6_policy[IFLA_IPTUN_MAX + 1] = { | |||
1651 | [IFLA_IPTUN_6RD_PREFIXLEN] = { .type = NLA_U16 }, | 1734 | [IFLA_IPTUN_6RD_PREFIXLEN] = { .type = NLA_U16 }, |
1652 | [IFLA_IPTUN_6RD_RELAY_PREFIXLEN] = { .type = NLA_U16 }, | 1735 | [IFLA_IPTUN_6RD_RELAY_PREFIXLEN] = { .type = NLA_U16 }, |
1653 | #endif | 1736 | #endif |
1737 | [IFLA_IPTUN_ENCAP_TYPE] = { .type = NLA_U16 }, | ||
1738 | [IFLA_IPTUN_ENCAP_FLAGS] = { .type = NLA_U16 }, | ||
1739 | [IFLA_IPTUN_ENCAP_SPORT] = { .type = NLA_U16 }, | ||
1740 | [IFLA_IPTUN_ENCAP_DPORT] = { .type = NLA_U16 }, | ||
1654 | }; | 1741 | }; |
1655 | 1742 | ||
1656 | static void ipip6_dellink(struct net_device *dev, struct list_head *head) | 1743 | static void ipip6_dellink(struct net_device *dev, struct list_head *head) |