diff --git a/README.md b/README.md index 699e0e6..1ea3768 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,21 @@ sudo ip link set eip0 up sudo ip addr add 192.0.2.1/30 dev eip0 ``` +MTUを省略した場合、Overlay MTUはUnderlayの経路MTUからIPv6、EtherIP、 +Ethernetヘッダーの合計56 byteを差し引いた値に自動調整されます。例えば +Underlay MTUが1500の場合、Overlay MTUは1444になり、通常の送信では外側IPv6 +フラグメントを生成しません。 + +明示的に大きなMTUを設定することもできます。その場合は、カプセル化後の +パケットが実際のUnderlay経路MTUを超えたときだけ送信元でIPv6フラグメントを +生成します。 + +```sh +sudo ip link set eip0 mtu 1500 +``` + ## トンネルの削除 ```sh sudo etherip-client delete -d eip0 -``` \ No newline at end of file +``` diff --git a/etherip6.c b/etherip6.c index f3d4d24..8747e7c 100644 --- a/etherip6.c +++ b/etherip6.c @@ -32,6 +32,8 @@ #define ETHERIP6_HLEN 2 #define ETHERIP6_DEFAULT_HOP_LIMIT 64 #define ETHERIP6_MAX_MTU 9000 +#define ETHERIP6_ENCAP_HLEN (sizeof(struct ipv6hdr) + ETHERIP6_HLEN) +#define ETHERIP6_MTU_OVERHEAD (ETHERIP6_ENCAP_HLEN + ETH_HLEN) struct etherip6_tunnel { struct list_head list; @@ -140,6 +142,35 @@ static struct etherip6_tunnel *etherip6_lookup_rx(struct net *net, return etherip6_lookup_unique_rx(net, local, remote, iif, false, false); } +static unsigned int etherip6_underlay_mtu(struct net *net, + const struct etherip6_tunnel *tun) +{ + struct dst_entry *dst; + struct flowi6 fl6 = {}; + unsigned int mtu = 0; + + fl6.flowi6_proto = IPPROTO_ETHERIP; + fl6.flowi6_oif = tun->link; + fl6.daddr = tun->remote; + fl6.saddr = tun->local; + + dst = ip6_route_output(net, NULL, &fl6); + if (!dst->error) + mtu = dst_mtu(dst); + dst_release(dst); + + return mtu; +} + +static unsigned int etherip6_overlay_mtu(unsigned int underlay_mtu) +{ + if (underlay_mtu <= ETHERIP6_MTU_OVERHEAD + ETH_MIN_MTU) + return ETH_MIN_MTU; + + return min_t(unsigned int, underlay_mtu - ETHERIP6_MTU_OVERHEAD, + ETHERIP6_MAX_MTU); +} + static netdev_tx_t etherip6_xmit(struct sk_buff *skb, struct net_device *dev) { struct etherip6_tunnel *tun = netdev_priv(dev); @@ -191,7 +222,8 @@ static netdev_tx_t etherip6_xmit(struct sk_buff *skb, struct net_device *dev) skb->protocol = htons(ETH_P_IPV6); skb->dev = dst->dev; - skb->ignore_df = 1; + /* IPv6 source fragmentation is needed only above the underlay PMTU. */ + skb->ignore_df = skb->len > dst_mtu(dst); skb_dst_set(skb, dst); stats = this_cpu_ptr(tun->stats); @@ -252,6 +284,7 @@ static void etherip6_setup(struct net_device *dev) dev->vlan_features = 0; dev->min_mtu = ETH_MIN_MTU; dev->max_mtu = ETHERIP6_MAX_MTU; + dev->needed_headroom = ETHERIP6_ENCAP_HLEN; eth_hw_addr_random(dev); } @@ -318,6 +351,15 @@ static int etherip6_newlink(struct net_device *dev, return -EINVAL; } + /* Keep the default overlay packets below the current underlay PMTU. */ + if (!params->tb[IFLA_MTU]) { + unsigned int underlay_mtu; + + underlay_mtu = etherip6_underlay_mtu(dev_net(dev), tun); + if (underlay_mtu) + dev->mtu = etherip6_overlay_mtu(underlay_mtu); + } + tun->dev = dev; atomic_long_set(&tun->tx_dropped, 0); atomic_long_set(&tun->rx_dropped, 0);