2 Commits
patch ... main

Author SHA1 Message Date
tuna2134
249cee8375 fix 2026-07-11 12:42:36 +09:00
tuna2134
3eee844deb fix 2026-07-11 12:36:38 +09:00
2 changed files with 44 additions and 34 deletions

View File

@@ -37,9 +37,9 @@ 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
``` ```
TCP SYN に MSS オプションがある場合、外側 IPv6 経路の MTU を超えないよう、 TCP SYN に MSS オプションがある場合、外側 IPv6 経路 MTU とトンネル MTU の
送信時に IPv4/IPv6 の MSS を自動的に縮小します。802.1Q/802.1ad VLAN と IPv6 小さい方に収まるよう、送信時に MSS を自動的に縮小します。IPv4/IPv6
拡張ヘッダーに対応します。既に十分小さい MSS は変更しません。 802.1Q/802.1ad VLAN、IPv6 拡張ヘッダーに対応し、既に小さい MSS は変更しません。
## MTU と IPv6 フラグメント ## MTU と IPv6 フラグメント
@@ -67,8 +67,8 @@ sudo sysctl -w net.ipv6.ip6frag_time=10
この設定は経路上でのフラグメント欠落を防ぐものではありません。フラグメントを この設定は経路上でのフラグメント欠落を防ぐものではありません。フラグメントを
避けるには、外側インターフェースと経路の MTU を 1556 以上にするか、`eip0` 避けるには、外側インターフェースと経路の MTU を 1556 以上にするか、`eip0`
MTU を外側 MTU から 56 引いた値以下(外側 MTU 1500 なら 1444 以下)に設定して MTU を外側 MTU から 56 引いた値以下(外側 MTU 1500 なら 1444 以下)に設定して
ください。TCP については MSS の自動縮小で回避できる場合がありますが、任意 ください。TCP は上記の MSS clamping でフラグメントを回避できますが、TCP 以外
Ethernet フレームには適用できません。 Ethernet フレームには適用されません。
## トンネルの削除 ## トンネルの削除

View File

@@ -2,10 +2,10 @@
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <linux/if_link.h> #include <linux/if_link.h>
#include <linux/if_vlan.h>
#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>
@@ -38,8 +38,6 @@
#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
/* IPv6 + EtherIP headers added outside the encapsulated Ethernet frame. */
#define ETHERIP6_OUTER_HLEN (sizeof(struct ipv6hdr) + ETHERIP6_HLEN) #define ETHERIP6_OUTER_HLEN (sizeof(struct ipv6hdr) + ETHERIP6_HLEN)
struct etherip6_tunnel { struct etherip6_tunnel {
@@ -149,55 +147,61 @@ static struct etherip6_tunnel *etherip6_lookup_rx(struct net *net,
return etherip6_lookup_unique_rx(net, local, remote, iif, false, false); return etherip6_lookup_unique_rx(net, local, remote, iif, false, false);
} }
/*
* Reduce an advertised MSS only when the encapsulated SYN would otherwise
* exceed the smaller of the tunnel MTU and the current outer path MTU.
* skb_header_pointer() keeps the common linear-skb path allocation-free while
* still handling cloned and non-linear packets correctly.
*/
static void etherip6_clamp_tcp_mss(struct sk_buff *skb, unsigned int path_mtu) static void etherip6_clamp_tcp_mss(struct sk_buff *skb, unsigned int path_mtu)
{ {
struct vlan_hdr _vh, *vh; struct vlan_hdr vlan_buf, *vh;
struct tcphdr _th, *th; struct tcphdr tcp_buf, *th;
struct ethhdr _eth, *eth; struct ethhdr eth_buf, *eth;
unsigned int nhoff = ETH_HLEN; unsigned int nhoff = ETH_HLEN;
unsigned int thoff, tcp_hlen; unsigned int thoff, tcp_hlen;
unsigned int inner_mtu; unsigned int inner_mtu, ip_hlen;
unsigned int min_ip_hlen; unsigned char opt_buf[MAX_TCP_OPTION_SPACE], *opt;
unsigned char *opt;
unsigned int optlen; unsigned int optlen;
unsigned int mss_offset;
__be16 proto; __be16 proto;
u16 old_mss, new_mss; u16 old_mss, new_mss;
u8 nexthdr; u8 nexthdr;
eth = skb_header_pointer(skb, 0, sizeof(_eth), &_eth); eth = skb_header_pointer(skb, 0, sizeof(eth_buf), &eth_buf);
if (!eth) if (unlikely(!eth))
return; return;
proto = eth->h_proto; proto = eth->h_proto;
while (eth_type_vlan(proto)) { while (eth_type_vlan(proto)) {
vh = skb_header_pointer(skb, nhoff, sizeof(_vh), &_vh); vh = skb_header_pointer(skb, nhoff, sizeof(vlan_buf), &vlan_buf);
if (!vh) if (unlikely(!vh))
return; return;
proto = vh->h_vlan_encapsulated_proto; proto = vh->h_vlan_encapsulated_proto;
nhoff += sizeof(*vh); nhoff += sizeof(*vh);
} }
if (path_mtu <= ETHERIP6_OUTER_HLEN + nhoff) if (unlikely(path_mtu <= ETHERIP6_OUTER_HLEN + nhoff))
return; return;
inner_mtu = min_t(unsigned int, skb->dev->mtu, inner_mtu = min_t(unsigned int, skb->dev->mtu,
path_mtu - ETHERIP6_OUTER_HLEN - nhoff); path_mtu - ETHERIP6_OUTER_HLEN - nhoff);
if (proto == htons(ETH_P_IP)) { if (proto == htons(ETH_P_IP)) {
struct iphdr _iph, *iph; struct iphdr ip_buf, *iph;
iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph); iph = skb_header_pointer(skb, nhoff, sizeof(ip_buf), &ip_buf);
if (!iph || iph->version != 4 || iph->ihl < 5 || if (!iph || iph->version != 4 || iph->ihl < 5 ||
iph->protocol != IPPROTO_TCP || iph->protocol != IPPROTO_TCP ||
(iph->frag_off & htons(IP_MF | IP_OFFSET))) (iph->frag_off & htons(IP_MF | IP_OFFSET)))
return; return;
thoff = nhoff + iph->ihl * 4; thoff = nhoff + iph->ihl * 4;
min_ip_hlen = sizeof(struct iphdr); ip_hlen = sizeof(struct iphdr);
} else if (proto == htons(ETH_P_IPV6)) { } else if (proto == htons(ETH_P_IPV6)) {
struct ipv6hdr _ip6h, *ip6h; struct ipv6hdr ip6_buf, *ip6h;
__be16 frag_off = 0; __be16 frag_off = 0;
int offset; int offset;
ip6h = skb_header_pointer(skb, nhoff, sizeof(_ip6h), &_ip6h); ip6h = skb_header_pointer(skb, nhoff, sizeof(ip6_buf), &ip6_buf);
if (!ip6h || ip6h->version != 6) if (!ip6h || ip6h->version != 6)
return; return;
nexthdr = ip6h->nexthdr; nexthdr = ip6h->nexthdr;
@@ -206,25 +210,25 @@ static void etherip6_clamp_tcp_mss(struct sk_buff *skb, unsigned int path_mtu)
if (offset < 0 || nexthdr != IPPROTO_TCP || frag_off) if (offset < 0 || nexthdr != IPPROTO_TCP || frag_off)
return; return;
thoff = offset; thoff = offset;
min_ip_hlen = sizeof(struct ipv6hdr); ip_hlen = sizeof(struct ipv6hdr);
} else { } else {
return; return;
} }
th = skb_header_pointer(skb, thoff, sizeof(_th), &_th); th = skb_header_pointer(skb, thoff, sizeof(tcp_buf), &tcp_buf);
if (!th || !th->syn || th->doff < sizeof(*th) / 4) if (!th || !th->syn || th->doff < sizeof(*th) / 4)
return; return;
tcp_hlen = th->doff * 4; tcp_hlen = th->doff * 4;
if (inner_mtu <= min_ip_hlen + sizeof(*th)) if (tcp_hlen > MAX_TCP_HEADER ||
inner_mtu <= ip_hlen + sizeof(struct tcphdr))
return; return;
new_mss = min_t(unsigned int, U16_MAX, new_mss = min_t(unsigned int, U16_MAX,
inner_mtu - min_ip_hlen - sizeof(*th)); inner_mtu - ip_hlen - sizeof(struct tcphdr));
if (skb_ensure_writable(skb, thoff + tcp_hlen))
return;
th = (struct tcphdr *)(skb->data + thoff);
opt = (unsigned char *)(th + 1);
optlen = tcp_hlen - sizeof(*th); optlen = tcp_hlen - sizeof(*th);
opt = skb_header_pointer(skb, thoff + sizeof(*th), optlen, opt_buf);
if (!opt)
return;
while (optlen) { while (optlen) {
u8 kind = opt[0]; u8 kind = opt[0];
@@ -242,9 +246,15 @@ static void etherip6_clamp_tcp_mss(struct sk_buff *skb, unsigned int path_mtu)
if (kind == TCPOPT_MSS && len == TCPOLEN_MSS) { if (kind == TCPOPT_MSS && len == TCPOLEN_MSS) {
old_mss = get_unaligned_be16(opt + 2); old_mss = get_unaligned_be16(opt + 2);
if (old_mss > new_mss) { if (old_mss > new_mss) {
put_unaligned_be16(new_mss, opt + 2); mss_offset = thoff + tcp_hlen - optlen + 2;
if (skb_ensure_writable(skb, mss_offset + 2))
return;
th = (struct tcphdr *)(skb->data + thoff);
put_unaligned_be16(new_mss,
skb->data + mss_offset);
inet_proto_csum_replace2(&th->check, skb, inet_proto_csum_replace2(&th->check, skb,
htons(old_mss), htons(new_mss), false); htons(old_mss), htons(new_mss),
false);
} }
return; return;
} }