2 Commits
mss ... flag

Author SHA1 Message Date
tuna2134
ce3fd3cc28 fix 2026-07-06 15:21:59 +09:00
tuna2134
20cf4124c8 fix 2026-07-06 15:03:21 +09:00
3 changed files with 23 additions and 86 deletions

View File

@@ -10,6 +10,11 @@ all: module tools
module: module:
$(MAKE) -C $(KDIR) M=$(PWD) modules $(MAKE) -C $(KDIR) M=$(PWD) modules
tools: etherip6ctl
etherip6ctl: etherip6ctl.c etherip6_uapi.h
$(CC) $(CFLAGS) -Wall -Wextra -O2 -o $@ etherip6ctl.c
clean: clean:
@if [ -d "$(KDIR)" ]; then \ @if [ -d "$(KDIR)" ]; then \
$(MAKE) -C "$(KDIR)" M="$(PWD)" clean; \ $(MAKE) -C "$(KDIR)" M="$(PWD)" clean; \

View File

@@ -37,36 +37,14 @@ sudo ip link set eip0 up
sudo ip addr add 192.0.2.1/30 dev eip0 sudo ip addr add 192.0.2.1/30 dev eip0
``` ```
## MTU と IPv6 フラグメント Overlay MTUのデフォルトは1500です。Underlayの経路MTUも1500の場合、
カプセル化後の外側IPv6パケットはMTUを超えるため、送信元で経路MTU以下の
GSO パケットは送信時にソフトウェアで分割してから EtherIP カプセル化します。 IPv6フラグメントに分割します。
EtherIP over IPv6 では、内側 Ethernet ヘッダー 14 bytes、EtherIP ヘッダー
2 bytes、外側 IPv6 ヘッダー 40 bytes の合計 56 bytes が追加されます。そのため、
`eip0` と外側インターフェースの MTU がともに 1500 の場合、最大サイズの内側
フレームは外側で 1556 bytes となり、IPv6 フラグメントが必ず発生します。
フラグメントの一部が欠落した場合、受信側ではパケット全体を再構成できません。
再構成の状態は次のコマンドで確認できます。
```sh ```sh
nstat -az | grep Ip6Reasm sudo ip link set eip0 mtu 1500
``` ```
`Ip6ReasmFails` または `Ip6ReasmTimeout` が転送中に増える場合は、フラグメントの
欠落、または受信側の再構成キュー不足が発生しています。再構成キュー不足に対する
緩和策として、受信側で次の値を設定できます。
```sh
sudo sysctl -w net.ipv6.ip6frag_high_thresh=268435456
sudo sysctl -w net.ipv6.ip6frag_time=10
```
この設定は経路上でのフラグメント欠落を防ぐものではありません。フラグメントを
避けるには、外側インターフェースと経路の MTU を 1556 以上にするか、`eip0`
MTU を外側 MTU から 56 引いた値以下(外側 MTU 1500 なら 1444 以下)に設定して
ください。
## トンネルの削除 ## トンネルの削除
```sh ```sh

View File

@@ -5,7 +5,6 @@
#include <linux/in6.h> #include <linux/in6.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/ipv6.h> #include <linux/ipv6.h>
#include <linux/if_vlan.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/module.h> #include <linux/module.h>
@@ -15,11 +14,8 @@
#include <linux/rculist.h> #include <linux/rculist.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/tcp.h>
#include <net/ip.h>
#include <net/ip6_route.h> #include <net/ip6_route.h>
#include <net/ipv6.h> #include <net/ipv6.h>
#include <net/gso.h>
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/netns/generic.h> #include <net/netns/generic.h>
#include <net/protocol.h> #include <net/protocol.h>
@@ -36,6 +32,7 @@
#define ETHERIP6_HLEN 2 #define ETHERIP6_HLEN 2
#define ETHERIP6_DEFAULT_HOP_LIMIT 64 #define ETHERIP6_DEFAULT_HOP_LIMIT 64
#define ETHERIP6_MAX_MTU 9000 #define ETHERIP6_MAX_MTU 9000
#define ETHERIP6_ENCAP_HLEN (sizeof(struct ipv6hdr) + ETHERIP6_HLEN)
struct etherip6_tunnel { struct etherip6_tunnel {
struct list_head list; struct list_head list;
@@ -154,43 +151,8 @@ static netdev_tx_t etherip6_xmit(struct sk_buff *skb, struct net_device *dev)
struct pcpu_sw_netstats *stats; struct pcpu_sw_netstats *stats;
__be16 *etherip; __be16 *etherip;
unsigned int payload_len; unsigned int payload_len;
unsigned int inner_len;
int headroom; int headroom;
int err; int err;
struct sk_buff *segs, *next;
unsigned int gso_limit;
if (skb_is_gso(skb)) {
/*
* GSO is performed before EtherIP/IPv6 encapsulation. Limit the
* TCP payload according to the configured inner MTU; this preserves
* both 1500-byte and jumbo-frame tunnel configurations.
*/
gso_limit = max_t(unsigned int, dev->mtu,
ETH_HLEN + ETHERIP6_HLEN + sizeof(struct ipv6hdr) +
sizeof(struct tcphdr)) - ETH_HLEN - ETHERIP6_HLEN -
(skb->protocol == htons(ETH_P_IPV6) ?
sizeof(struct ipv6hdr) : sizeof(struct iphdr)) -
sizeof(struct tcphdr);
if (skb_shinfo(skb)->gso_size > gso_limit)
skb_shinfo(skb)->gso_size = gso_limit;
segs = skb_gso_segment(skb, 0);
if (IS_ERR(segs))
goto tx_error;
if (!segs) {
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
consume_skb(skb);
while (segs) {
next = segs->next;
segs->next = NULL;
etherip6_xmit(segs, dev);
segs = next;
}
return NETDEV_TX_OK;
}
if (skb->len + ETHERIP6_HLEN > IPV6_MAXPLEN) if (skb->len + ETHERIP6_HLEN > IPV6_MAXPLEN)
goto tx_error; goto tx_error;
@@ -214,7 +176,6 @@ static netdev_tx_t etherip6_xmit(struct sk_buff *skb, struct net_device *dev)
} }
payload_len = skb->len + ETHERIP6_HLEN; payload_len = skb->len + ETHERIP6_HLEN;
inner_len = skb->len;
etherip = skb_push(skb, ETHERIP6_HLEN); etherip = skb_push(skb, ETHERIP6_HLEN);
*etherip = ETHERIP6_HDR; *etherip = ETHERIP6_HDR;
@@ -231,31 +192,24 @@ static netdev_tx_t etherip6_xmit(struct sk_buff *skb, struct net_device *dev)
skb->protocol = htons(ETH_P_IPV6); skb->protocol = htons(ETH_P_IPV6);
skb->dev = dst->dev; skb->dev = dst->dev;
/*
* This skb used to contain an inner Ethernet frame. Clear its control
* block before handing it to IPv6, then pin source fragmentation to the
* current underlay PMTU. ip6_local_out() adds the Fragment Header and
* splits oversized packets in ip6_finish_output().
*/
memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
IP6CB(skb)->frag_max_size = dst_mtu(dst);
skb->ignore_df = 1; skb->ignore_df = 1;
skb_dst_set(skb, dst); skb_dst_set(skb, dst);
/*
* skb->cb belongs to the protocol currently processing the skb. The
* encapsulated frame may leave bridge, qdisc, or inner IPv6 state in it;
* in particular, stale inet6_skb_parm flags or frag_max_size corrupt the
* outer IPv6 fragmentation path. Native IPv6 tunnels clear this state
* in ip6tunnel_xmit() before calling ip6_local_out().
*/
memset(skb->cb, 0, sizeof(struct inet6_skb_parm));
err = ip6_local_out(net, NULL, skb);
if (unlikely(net_xmit_eval(err))) {
atomic_long_inc(&tun->tx_dropped);
return NETDEV_TX_OK;
}
stats = this_cpu_ptr(tun->stats); stats = this_cpu_ptr(tun->stats);
u64_stats_update_begin(&stats->syncp); u64_stats_update_begin(&stats->syncp);
u64_stats_inc(&stats->tx_packets); u64_stats_inc(&stats->tx_packets);
u64_stats_add(&stats->tx_bytes, inner_len); u64_stats_add(&stats->tx_bytes, payload_len - ETHERIP6_HLEN);
u64_stats_update_end(&stats->syncp); u64_stats_update_end(&stats->syncp);
return NETDEV_TX_OK; return ip6_local_out(net, NULL, skb) == 0 ? NETDEV_TX_OK : NETDEV_TX_OK;
tx_error: tx_error:
atomic_long_inc(&tun->tx_dropped); atomic_long_inc(&tun->tx_dropped);
@@ -302,12 +256,12 @@ static void etherip6_setup(struct net_device *dev)
dev->needs_free_netdev = true; dev->needs_free_netdev = true;
dev->type = ARPHRD_ETHER; dev->type = ARPHRD_ETHER;
dev->flags &= ~IFF_NOARP; dev->flags &= ~IFF_NOARP;
dev->features &= ~NETIF_F_CSUM_MASK; dev->features &= ~(NETIF_F_GSO_MASK | NETIF_F_CSUM_MASK);
dev->features |= NETIF_F_GSO_SOFTWARE; dev->hw_features = 0;
dev->hw_features = NETIF_F_GSO_SOFTWARE;
dev->vlan_features = 0; dev->vlan_features = 0;
dev->min_mtu = ETH_MIN_MTU; dev->min_mtu = ETH_MIN_MTU;
dev->max_mtu = ETHERIP6_MAX_MTU; dev->max_mtu = ETHERIP6_MAX_MTU;
dev->needed_headroom = ETHERIP6_ENCAP_HLEN;
eth_hw_addr_random(dev); eth_hw_addr_random(dev);
} }