From 8bd7e23a2ed80c692362d68965ac967dcdf446d7 Mon Sep 17 00:00:00 2001 From: tuna2134 Date: Sat, 11 Jul 2026 12:01:00 +0900 Subject: [PATCH] support gso --- README.md | 9 ++-- etherip6.c | 139 +++++++++-------------------------------------------- 2 files changed, 27 insertions(+), 121 deletions(-) diff --git a/README.md b/README.md index 5e63564..405aeca 100644 --- a/README.md +++ b/README.md @@ -37,12 +37,10 @@ sudo ip link set eip0 up sudo ip addr add 192.0.2.1/30 dev eip0 ``` -TCP SYN に MSS オプションがある場合、外側 IPv6 経路の MTU を超えないよう、 -送信時に IPv4/IPv6 の MSS を自動的に縮小します。802.1Q/802.1ad VLAN と IPv6 -拡張ヘッダーにも対応します。既に十分小さい MSS は変更しません。 - ## MTU と IPv6 フラグメント +GSO パケットは送信時にソフトウェアで分割してから EtherIP カプセル化します。 + EtherIP over IPv6 では、内側 Ethernet ヘッダー 14 bytes、EtherIP ヘッダー 2 bytes、外側 IPv6 ヘッダー 40 bytes の合計 56 bytes が追加されます。そのため、 `eip0` と外側インターフェースの MTU がともに 1500 の場合、最大サイズの内側 @@ -67,8 +65,7 @@ sudo sysctl -w net.ipv6.ip6frag_time=10 この設定は経路上でのフラグメント欠落を防ぐものではありません。フラグメントを 避けるには、外側インターフェースと経路の MTU を 1556 以上にするか、`eip0` の MTU を外側 MTU から 56 引いた値以下(外側 MTU 1500 なら 1444 以下)に設定して -ください。TCP については MSS の自動縮小で回避できる場合がありますが、任意の -Ethernet フレームには適用できません。 +ください。 ## トンネルの削除 diff --git a/etherip6.c b/etherip6.c index 4ed5fc4..61d72aa 100644 --- a/etherip6.c +++ b/etherip6.c @@ -15,17 +15,14 @@ #include #include #include -#include -#include -#include #include #include #include +#include #include #include #include #include -#include #include "etherip6_uapi.h" @@ -39,9 +36,6 @@ #define ETHERIP6_DEFAULT_HOP_LIMIT 64 #define ETHERIP6_MAX_MTU 9000 -/* IPv6 + EtherIP headers added outside the encapsulated Ethernet frame. */ -#define ETHERIP6_OUTER_HLEN (sizeof(struct ipv6hdr) + ETHERIP6_HLEN) - struct etherip6_tunnel { struct list_head list; struct net_device *dev; @@ -149,110 +143,6 @@ static struct etherip6_tunnel *etherip6_lookup_rx(struct net *net, return etherip6_lookup_unique_rx(net, local, remote, iif, false, false); } -static void etherip6_clamp_tcp_mss(struct sk_buff *skb, unsigned int path_mtu) -{ - struct vlan_hdr _vh, *vh; - struct tcphdr _th, *th; - struct ethhdr _eth, *eth; - unsigned int nhoff = ETH_HLEN; - unsigned int thoff, tcp_hlen; - unsigned int inner_mtu; - unsigned int min_ip_hlen; - unsigned char *opt; - unsigned int optlen; - __be16 proto; - u16 old_mss, new_mss; - u8 nexthdr; - - eth = skb_header_pointer(skb, 0, sizeof(_eth), &_eth); - if (!eth) - return; - proto = eth->h_proto; - - while (eth_type_vlan(proto)) { - vh = skb_header_pointer(skb, nhoff, sizeof(_vh), &_vh); - if (!vh) - return; - proto = vh->h_vlan_encapsulated_proto; - nhoff += sizeof(*vh); - } - - if (path_mtu <= ETHERIP6_OUTER_HLEN + nhoff) - return; - inner_mtu = min_t(unsigned int, skb->dev->mtu, - path_mtu - ETHERIP6_OUTER_HLEN - nhoff); - - if (proto == htons(ETH_P_IP)) { - struct iphdr _iph, *iph; - - iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph); - if (!iph || iph->version != 4 || iph->ihl < 5 || - iph->protocol != IPPROTO_TCP || - (iph->frag_off & htons(IP_MF | IP_OFFSET))) - return; - thoff = nhoff + iph->ihl * 4; - min_ip_hlen = sizeof(struct iphdr); - } else if (proto == htons(ETH_P_IPV6)) { - struct ipv6hdr _ip6h, *ip6h; - __be16 frag_off = 0; - int offset; - - ip6h = skb_header_pointer(skb, nhoff, sizeof(_ip6h), &_ip6h); - if (!ip6h || ip6h->version != 6) - return; - nexthdr = ip6h->nexthdr; - offset = ipv6_skip_exthdr(skb, nhoff + sizeof(*ip6h), - &nexthdr, &frag_off); - if (offset < 0 || nexthdr != IPPROTO_TCP || frag_off) - return; - thoff = offset; - min_ip_hlen = sizeof(struct ipv6hdr); - } else { - return; - } - - th = skb_header_pointer(skb, thoff, sizeof(_th), &_th); - if (!th || !th->syn || th->doff < sizeof(*th) / 4) - return; - tcp_hlen = th->doff * 4; - if (inner_mtu <= min_ip_hlen + sizeof(*th)) - return; - new_mss = min_t(unsigned int, U16_MAX, - inner_mtu - min_ip_hlen - sizeof(*th)); - - 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); - - while (optlen) { - u8 kind = opt[0]; - u8 len; - - if (kind == TCPOPT_EOL) - return; - if (kind == TCPOPT_NOP) { - opt++; - optlen--; - continue; - } - if (optlen < 2 || (len = opt[1]) < 2 || len > optlen) - return; - if (kind == TCPOPT_MSS && len == TCPOLEN_MSS) { - old_mss = get_unaligned_be16(opt + 2); - if (old_mss > new_mss) { - put_unaligned_be16(new_mss, opt + 2); - inet_proto_csum_replace2(&th->check, skb, - htons(old_mss), htons(new_mss), false); - } - return; - } - opt += len; - optlen -= len; - } -} - static netdev_tx_t etherip6_xmit(struct sk_buff *skb, struct net_device *dev) { struct etherip6_tunnel *tun = netdev_priv(dev); @@ -266,6 +156,26 @@ static netdev_tx_t etherip6_xmit(struct sk_buff *skb, struct net_device *dev) unsigned int inner_len; int headroom; int err; + struct sk_buff *segs, *next; + + if (skb_is_gso(skb)) { + 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) goto tx_error; @@ -281,8 +191,6 @@ static netdev_tx_t etherip6_xmit(struct sk_buff *skb, struct net_device *dev) goto tx_error; } - etherip6_clamp_tcp_mss(skb, dst_mtu(dst)); - headroom = LL_RESERVED_SPACE(dst->dev) + sizeof(*ip6h) + ETHERIP6_HLEN; err = skb_cow_head(skb, headroom); if (err) { @@ -379,8 +287,9 @@ static void etherip6_setup(struct net_device *dev) dev->needs_free_netdev = true; dev->type = ARPHRD_ETHER; dev->flags &= ~IFF_NOARP; - dev->features &= ~(NETIF_F_GSO_MASK | NETIF_F_CSUM_MASK); - dev->hw_features = 0; + dev->features &= ~NETIF_F_CSUM_MASK; + dev->features |= NETIF_F_GSO_SOFTWARE; + dev->hw_features = NETIF_F_GSO_SOFTWARE; dev->vlan_features = 0; dev->min_mtu = ETH_MIN_MTU; dev->max_mtu = ETHERIP6_MAX_MTU;