Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d2108a727 | ||
|
|
f305045a0b | ||
|
|
8bd7e23a2e | ||
|
|
1333e033bb | ||
|
|
b6240ba235 | ||
|
|
5fa3637a5b | ||
|
|
bbf52550be | ||
|
|
5bb4e290a3 |
5
Makefile
5
Makefile
@@ -10,11 +10,6 @@ all: module tools
|
||||
module:
|
||||
$(MAKE) -C $(KDIR) M=$(PWD) modules
|
||||
|
||||
tools: etherip6ctl
|
||||
|
||||
etherip6ctl: etherip6ctl.c etherip6_uapi.h
|
||||
$(CC) $(CFLAGS) -Wall -Wextra -O2 -o $@ etherip6ctl.c
|
||||
|
||||
clean:
|
||||
@if [ -d "$(KDIR)" ]; then \
|
||||
$(MAKE) -C "$(KDIR)" M="$(PWD)" clean; \
|
||||
|
||||
32
README.md
32
README.md
@@ -37,8 +37,38 @@ sudo ip link set eip0 up
|
||||
sudo ip addr add 192.0.2.1/30 dev eip0
|
||||
```
|
||||
|
||||
## MTU と IPv6 フラグメント
|
||||
|
||||
GSO パケットは送信時にソフトウェアで分割してから EtherIP カプセル化します。
|
||||
|
||||
EtherIP over IPv6 では、内側 Ethernet ヘッダー 14 bytes、EtherIP ヘッダー
|
||||
2 bytes、外側 IPv6 ヘッダー 40 bytes の合計 56 bytes が追加されます。そのため、
|
||||
`eip0` と外側インターフェースの MTU がともに 1500 の場合、最大サイズの内側
|
||||
フレームは外側で 1556 bytes となり、IPv6 フラグメントが必ず発生します。
|
||||
|
||||
フラグメントの一部が欠落した場合、受信側ではパケット全体を再構成できません。
|
||||
再構成の状態は次のコマンドで確認できます。
|
||||
|
||||
```sh
|
||||
nstat -az | grep Ip6Reasm
|
||||
```
|
||||
|
||||
`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
|
||||
sudo etherip-client delete -d eip0
|
||||
```
|
||||
```
|
||||
|
||||
64
etherip6.c
64
etherip6.c
@@ -5,6 +5,7 @@
|
||||
#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>
|
||||
@@ -14,8 +15,11 @@
|
||||
#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/protocol.h>
|
||||
@@ -150,8 +154,43 @@ static netdev_tx_t etherip6_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
struct pcpu_sw_netstats *stats;
|
||||
__be16 *etherip;
|
||||
unsigned int payload_len;
|
||||
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;
|
||||
@@ -175,6 +214,7 @@ static netdev_tx_t etherip6_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
}
|
||||
|
||||
payload_len = skb->len + ETHERIP6_HLEN;
|
||||
inner_len = skb->len;
|
||||
|
||||
etherip = skb_push(skb, ETHERIP6_HLEN);
|
||||
*etherip = ETHERIP6_HDR;
|
||||
@@ -194,13 +234,28 @@ static netdev_tx_t etherip6_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
skb->ignore_df = 1;
|
||||
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);
|
||||
u64_stats_update_begin(&stats->syncp);
|
||||
u64_stats_inc(&stats->tx_packets);
|
||||
u64_stats_add(&stats->tx_bytes, payload_len - ETHERIP6_HLEN);
|
||||
u64_stats_add(&stats->tx_bytes, inner_len);
|
||||
u64_stats_update_end(&stats->syncp);
|
||||
|
||||
return ip6_local_out(net, NULL, skb) == 0 ? NETDEV_TX_OK : NETDEV_TX_OK;
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
tx_error:
|
||||
atomic_long_inc(&tun->tx_dropped);
|
||||
@@ -247,8 +302,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;
|
||||
|
||||
Reference in New Issue
Block a user