3 Commits
patch ... mss

Author SHA1 Message Date
tuna2134
8d2108a727 fuck 2026-07-11 12:18:03 +09:00
tuna2134
f305045a0b fix 2026-07-11 12:14:36 +09:00
tuna2134
8bd7e23a2e support gso 2026-07-11 12:01:00 +09:00
2 changed files with 48 additions and 54 deletions

View File

@@ -37,12 +37,10 @@ sudo ip link set eip0 up
sudo ip addr add 192.0.2.1/30 dev eip0
```
`eip0` は TCP/UDP/SCTP の software GSO に対応します。GSO パケットは内側の
Ethernet フレームとしてセグメント化してから、それぞれを EtherIP でカプセル化
します。
## MTU と IPv6 フラグメント
GSO パケットは送信時にソフトウェアで分割してから EtherIP カプセル化します。
EtherIP over IPv6 では、内側 Ethernet ヘッダー 14 bytes、EtherIP ヘッダー
2 bytes、外側 IPv6 ヘッダー 40 bytes の合計 56 bytes が追加されます。そのため、
`eip0` と外側インターフェースの MTU がともに 1500 の場合、最大サイズの内側
@@ -69,11 +67,6 @@ sudo sysctl -w net.ipv6.ip6frag_time=10
MTU を外側 MTU から 56 引いた値以下(外側 MTU 1500 なら 1444 以下)に設定して
ください。
一方、`eip0` の MTU は最大 9000 に設定できます。外側経路の MTU より大きい
非 GSO パケット、および GSO の各セグメントは、外側 IPv6 でフラグメント化して
送信されます。したがって MTU 9000 のフレームも転送できますが、経路上で IPv6
フラグメントが欠落しないことと、受信側に十分な再構成キューが必要です。
## トンネルの削除
```sh

View File

@@ -3,7 +3,9 @@
#include <linux/if_ether.h>
#include <linux/if_link.h>
#include <linux/in6.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/if_vlan.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
@@ -13,11 +15,13 @@
#include <linux/rculist.h>
#include <linux/rtnetlink.h>
#include <linux/skbuff.h>
#include <linux/tcp.h>
#include <net/ip.h>
#include <net/ip6_route.h>
#include <net/ipv6.h>
#include <net/gso.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>
#include <net/gso.h>
#include <net/protocol.h>
#include <net/rtnetlink.h>
@@ -32,7 +36,6 @@
#define ETHERIP6_HLEN 2
#define ETHERIP6_DEFAULT_HOP_LIMIT 64
#define ETHERIP6_MAX_MTU 9000
#define ETHERIP6_NEEDED_HEADROOM (sizeof(struct ipv6hdr) + ETHERIP6_HLEN)
struct etherip6_tunnel {
struct list_head list;
@@ -141,7 +144,7 @@ static struct etherip6_tunnel *etherip6_lookup_rx(struct net *net,
return etherip6_lookup_unique_rx(net, local, remote, iif, false, false);
}
static void etherip6_xmit_one(struct sk_buff *skb, struct net_device *dev)
static netdev_tx_t etherip6_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct etherip6_tunnel *tun = netdev_priv(dev);
struct net *net = dev_net(dev);
@@ -154,6 +157,40 @@ static void etherip6_xmit_one(struct sk_buff *skb, struct net_device *dev)
unsigned int inner_len;
int headroom;
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)
goto tx_error;
@@ -209,7 +246,7 @@ static void etherip6_xmit_one(struct sk_buff *skb, struct net_device *dev)
err = ip6_local_out(net, NULL, skb);
if (unlikely(net_xmit_eval(err))) {
atomic_long_inc(&tun->tx_dropped);
return;
return NETDEV_TX_OK;
}
stats = this_cpu_ptr(tun->stats);
@@ -218,45 +255,11 @@ static void etherip6_xmit_one(struct sk_buff *skb, struct net_device *dev)
u64_stats_add(&stats->tx_bytes, inner_len);
u64_stats_update_end(&stats->syncp);
return;
return NETDEV_TX_OK;
tx_error:
atomic_long_inc(&tun->tx_dropped);
dev_kfree_skb(skb);
return;
}
static netdev_tx_t etherip6_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct etherip6_tunnel *tun = netdev_priv(dev);
struct sk_buff *segs, *nskb;
if (!skb_is_gso(skb)) {
etherip6_xmit_one(skb, dev);
return NETDEV_TX_OK;
}
/*
* EtherIP has no hardware GSO type. Segment the inner Ethernet frame
* before adding the EtherIP and outer IPv6 headers. Each resulting skb
* is then a normal IPv6 packet and can be fragmented by ip6_local_out()
* when the outer path MTU is smaller than the tunnel MTU.
*/
segs = skb_gso_segment(skb, 0);
if (IS_ERR_OR_NULL(segs)) {
atomic_long_inc(&tun->tx_dropped);
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
consume_skb(skb);
while (segs) {
nskb = segs;
segs = segs->next;
nskb->next = NULL;
etherip6_xmit_one(nskb, dev);
}
return NETDEV_TX_OK;
}
@@ -299,12 +302,10 @@ 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_SG | NETIF_F_HW_CSUM |
NETIF_F_GSO_SOFTWARE;
dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM |
NETIF_F_GSO_SOFTWARE;
dev->vlan_features = dev->features;
dev->needed_headroom = ETHERIP6_NEEDED_HEADROOM;
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;
eth_hw_addr_random(dev);