Jump to content
Калькуляторы

Lunix. Как сделать tun mtu > 1480 ?

Проблема.

Нужно на IPIP туннеле в Linux выставить mtu > 1480. Например, 1520. Команда "ip link set tun0 mtu 1520 up" замечательно работает, не ругается и затем "ip link show" показывает, что mtu стоит 1520, однако реально он равен mtu интерфейса, через который уходят инкапсулированные пакетыв минус 20 байт. Это легко проверить с помощью "ping -s 1500 -M do ...". Если включен PMTU на tunX, то ICMP сообщение четко показывает актуальный mtu на tunX.

 

Предполагаю, что потребуется пересборка ядра. Прежде чем копаться глучже, хотел узнать, может кто решал уже эту проблему. В BSD, кстати, все замечательно работает. Сколько поставишь mtu на туннеле, столько реально и будет (в разумных пределах конечно, на все ведь есть свои ограничения).

 

 

Share this post


Link to post
Share on other sites

Краткий экскурс в проблему:

 

Собственно, поведение Linux в данной ситуации вполне разумно - понятно, что так делается для избежания излишней фрагментации больших пакетов. Однако, может возникать известная проблема. Несовпадение mtu у клиента (обычно 1500) с mtu на некоторых участках в сети провайдера (например, на туннелях, 1480), как известно, может приводить к нежелательной фильтрации пакетов большого размера с меткой DF (не фрагментировать).

Для решения этой проблемы есть специальный инструмент - PMTU, который подбирает MSS таким, чтобы пакеты этого размера с пометкой DF спокойно проходили от клиента до сервера (обычно, WEB). Механизм прост - если пришел слишком большой пакет с пометкой DF и он, например, не может упаковаться без фрагментации в туннель, то этот пакет отбрасывается, а отправителю посылается ICMP пакет с сообщением, каким должен быть максимальный размер сегмента в сети (MSS). Клиент уменьшает размер пакета и отправляется его снова, запоминая MSS для текущего TCP соединения.

Однако по ряду причин, PMTU может не работать. Самая распространенная из них - это фаервол на стороне клиента (к примеру, фильтруют все ICMP), причем клиент часто может отказываться подстраивать фаервол, а просто требует, чтобы все работало.

 

Что делают в таком случае? Есть разные варианты:

 

1. Принудительно заставлять все TCP соединения, проходящие через туннель, использовать подходящий MSS. Делают это с помощью фаервола. В Linux, например, так:

 

iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

 

или так

 

iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss <value>

 

2. Принудительно сбрасывать флаг DF с пакетов, проходящих через туннель. Обычно, для этого используют патч для ядра и, опять-таки, с помощью фаервола, проделывают такой трюк.

 

Оба эти метода замечательно работают, но есть одно исключение - большие пакеты UDP с пометкой DF.

 

В итоге, остается вариант, делать на всем пути следования таких пакетов mtu 1500, а на IPIP туннелях, соответственно, 1520. Вот, откуда и возник изначальный вопрос. Буду благодарен любому опыту в решении этого вопроса.

 

 

Share this post


Link to post
Share on other sites

C ipip не возился. Работал с openvpn - на нем ставится mtu больше чем в несущей сети. Но несчет больше 1500 не пробовал - прокидывал 1500 поверх 1480

Share this post


Link to post
Share on other sites

Проблема оказалась не в том, что mtu на туннеле не выставляется, а в том, что DF флаг переносится в заголовок IPIP пакета (капсулы).

Эксперименты описаны здесь http://forum.openvz.org/index.php?t=msg&am...msg_37870.

 

Share this post


Link to post
Share on other sites

Жаль, что MaxGear не описал решение проблемы. В файле linux/net/ipv4/ipip.c надо изменить вот что:

--- 0ipip.c	2012-05-03 13:49:32.000000000 +0400
+++ ipip.c	2012-05-03 13:49:32.000000000 +0400
@@ -618,14 +618,14 @@
	iph 			=	ip_hdr(skb);
	iph->version		=	4;
	iph->ihl		=	sizeof(struct iphdr)>>2;
-	iph->frag_off		=	df;
+	iph->frag_off		=	0;
	iph->protocol		=	IPPROTO_IPIP;
	iph->tos		=	INET_ECN_encapsulate(tos, old_iph->tos);
	iph->daddr		=	rt->rt_dst;
	iph->saddr		=	rt->rt_src;

	if ((iph->ttl = tiph->ttl) == 0)
-		iph->ttl	=	old_iph->ttl;
+		iph->ttl	=	30;

	nf_reset(skb);

После изменения нужно пересобрать модуль ipip.

Изменение переменной iph->ttl нужно, только если TTL исходного пакета не хватает для прохождения внутри туннеля с учетом "внешних" хопов. В частности, в multicast-пакетах обычно TTL=1, поэтому они через туннель не пройдут, если "снаружи" больше 1 хопа. Число 30 взято из исходников FreeBSD'шного gif-интерфейса.

Share this post


Link to post
Share on other sites

итого - прошло почти 3 года )))

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this