Compare commits
1 Commits
mss
...
5ac8f37aca
| Author | SHA1 | Date | |
|---|---|---|---|
| 5ac8f37aca |
@@ -37,9 +37,11 @@ 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 フラグメント
|
TCP SYN に MSS オプションがある場合、外側 IPv6 経路の MTU を超えないよう、
|
||||||
|
送信時に IPv4/IPv6 の MSS を自動的に縮小します。802.1Q/802.1ad VLAN と IPv6
|
||||||
|
拡張ヘッダーにも対応します。既に十分小さい MSS は変更しません。
|
||||||
|
|
||||||
GSO パケットは送信時にソフトウェアで分割してから EtherIP カプセル化します。
|
## MTU と IPv6 フラグメント
|
||||||
|
|
||||||
EtherIP over IPv6 では、内側 Ethernet ヘッダー 14 bytes、EtherIP ヘッダー
|
EtherIP over IPv6 では、内側 Ethernet ヘッダー 14 bytes、EtherIP ヘッダー
|
||||||
2 bytes、外側 IPv6 ヘッダー 40 bytes の合計 56 bytes が追加されます。そのため、
|
2 bytes、外側 IPv6 ヘッダー 40 bytes の合計 56 bytes が追加されます。そのため、
|
||||||
@@ -65,7 +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 の自動縮小で回避できる場合がありますが、任意の
|
||||||
|
Ethernet フレームには適用できません。
|
||||||
|
|
||||||
## トンネルの削除
|
## トンネルの削除
|
||||||
|
|
||||||
|
|||||||
152
etherip6.c
152
etherip6.c
@@ -16,14 +16,16 @@
|
|||||||
#include <linux/rtnetlink.h>
|
#include <linux/rtnetlink.h>
|
||||||
#include <linux/skbuff.h>
|
#include <linux/skbuff.h>
|
||||||
#include <linux/tcp.h>
|
#include <linux/tcp.h>
|
||||||
|
#include <linux/unaligned.h>
|
||||||
|
#include <net/checksum.h>
|
||||||
#include <net/ip.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>
|
||||||
#include <net/rtnetlink.h>
|
#include <net/rtnetlink.h>
|
||||||
|
#include <net/tcp.h>
|
||||||
|
|
||||||
#include "etherip6_uapi.h"
|
#include "etherip6_uapi.h"
|
||||||
|
|
||||||
@@ -37,6 +39,9 @@
|
|||||||
#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)
|
||||||
|
|
||||||
struct etherip6_tunnel {
|
struct etherip6_tunnel {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct net_device *dev;
|
struct net_device *dev;
|
||||||
@@ -144,6 +149,110 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
static netdev_tx_t etherip6_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||||
{
|
{
|
||||||
struct etherip6_tunnel *tun = netdev_priv(dev);
|
struct etherip6_tunnel *tun = netdev_priv(dev);
|
||||||
@@ -157,40 +266,6 @@ static netdev_tx_t etherip6_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||||||
unsigned int inner_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;
|
||||||
@@ -206,6 +281,8 @@ static netdev_tx_t etherip6_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||||||
goto tx_error;
|
goto tx_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
etherip6_clamp_tcp_mss(skb, dst_mtu(dst));
|
||||||
|
|
||||||
headroom = LL_RESERVED_SPACE(dst->dev) + sizeof(*ip6h) + ETHERIP6_HLEN;
|
headroom = LL_RESERVED_SPACE(dst->dev) + sizeof(*ip6h) + ETHERIP6_HLEN;
|
||||||
err = skb_cow_head(skb, headroom);
|
err = skb_cow_head(skb, headroom);
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -302,9 +379,8 @@ 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;
|
||||||
|
|||||||
Reference in New Issue
Block a user